]> git.tdb.fi Git - ext/openal.git/blob - utils/makemhr/makemhr.cpp
Import OpenAL Soft 1.23.1 sources
[ext/openal.git] / utils / makemhr / makemhr.cpp
1 /*
2  * HRTF utility for producing and demonstrating the process of creating an
3  * OpenAL Soft compatible HRIR data set.
4  *
5  * Copyright (C) 2011-2019  Christopher Fitzgerald
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Or visit:  http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22  *
23  * --------------------------------------------------------------------------
24  *
25  * A big thanks goes out to all those whose work done in the field of
26  * binaural sound synthesis using measured HRTFs makes this utility and the
27  * OpenAL Soft implementation possible.
28  *
29  * The algorithm for diffuse-field equalization was adapted from the work
30  * done by Rio Emmanuel and Larcher Veronique of IRCAM and Bill Gardner of
31  * MIT Media Laboratory.  It operates as follows:
32  *
33  *  1.  Take the FFT of each HRIR and only keep the magnitude responses.
34  *  2.  Calculate the diffuse-field power-average of all HRIRs weighted by
35  *      their contribution to the total surface area covered by their
36  *      measurement. This has since been modified to use coverage volume for
37  *      multi-field HRIR data sets.
38  *  3.  Take the diffuse-field average and limit its magnitude range.
39  *  4.  Equalize the responses by using the inverse of the diffuse-field
40  *      average.
41  *  5.  Reconstruct the minimum-phase responses.
42  *  5.  Zero the DC component.
43  *  6.  IFFT the result and truncate to the desired-length minimum-phase FIR.
44  *
45  * The spherical head algorithm for calculating propagation delay was adapted
46  * from the paper:
47  *
48  *  Modeling Interaural Time Difference Assuming a Spherical Head
49  *  Joel David Miller
50  *  Music 150, Musical Acoustics, Stanford University
51  *  December 2, 2001
52  *
53  * The formulae for calculating the Kaiser window metrics are from the
54  * the textbook:
55  *
56  *  Discrete-Time Signal Processing
57  *  Alan V. Oppenheim and Ronald W. Schafer
58  *  Prentice-Hall Signal Processing Series
59  *  1999
60  */
61
62 #define _UNICODE
63 #include "config.h"
64
65 #include "makemhr.h"
66
67 #include <algorithm>
68 #include <atomic>
69 #include <chrono>
70 #include <cmath>
71 #include <complex>
72 #include <cstdint>
73 #include <cstdio>
74 #include <cstdlib>
75 #include <cstring>
76 #include <functional>
77 #include <iostream>
78 #include <limits>
79 #include <memory>
80 #include <numeric>
81 #include <thread>
82 #include <utility>
83 #include <vector>
84
85 #ifdef HAVE_GETOPT
86 #include <unistd.h>
87 #else
88 #include "../getopt.h"
89 #endif
90
91 #include "alcomplex.h"
92 #include "alfstream.h"
93 #include "alspan.h"
94 #include "alstring.h"
95 #include "loaddef.h"
96 #include "loadsofa.h"
97
98 #include "win_main_utf8.h"
99
100
101 namespace {
102
103 using namespace std::placeholders;
104
105 } // namespace
106
107 #ifndef M_PI
108 #define M_PI                         (3.14159265358979323846)
109 #endif
110
111
112 HrirDataT::~HrirDataT() = default;
113
114 // Head model used for calculating the impulse delays.
115 enum HeadModelT {
116     HM_NONE,
117     HM_DATASET, // Measure the onset from the dataset.
118     HM_SPHERE   // Calculate the onset using a spherical head model.
119 };
120
121
122 // The epsilon used to maintain signal stability.
123 #define EPSILON                      (1e-9)
124
125 // The limits to the FFT window size override on the command line.
126 #define MIN_FFTSIZE                  (65536)
127 #define MAX_FFTSIZE                  (131072)
128
129 // The limits to the equalization range limit on the command line.
130 #define MIN_LIMIT                    (2.0)
131 #define MAX_LIMIT                    (120.0)
132
133 // The limits to the truncation window size on the command line.
134 #define MIN_TRUNCSIZE                (16)
135 #define MAX_TRUNCSIZE                (128)
136
137 // The limits to the custom head radius on the command line.
138 #define MIN_CUSTOM_RADIUS            (0.05)
139 #define MAX_CUSTOM_RADIUS            (0.15)
140
141 // The defaults for the command line options.
142 #define DEFAULT_FFTSIZE              (65536)
143 #define DEFAULT_EQUALIZE             (1)
144 #define DEFAULT_SURFACE              (1)
145 #define DEFAULT_LIMIT                (24.0)
146 #define DEFAULT_TRUNCSIZE            (64)
147 #define DEFAULT_HEAD_MODEL           (HM_DATASET)
148 #define DEFAULT_CUSTOM_RADIUS        (0.0)
149
150 // The maximum propagation delay value supported by OpenAL Soft.
151 #define MAX_HRTD                     (63.0)
152
153 // The OpenAL Soft HRTF format marker.  It stands for minimum-phase head
154 // response protocol 03.
155 #define MHR_FORMAT                   ("MinPHR03")
156
157 /* Channel index enums. Mono uses LeftChannel only. */
158 enum ChannelIndex : uint {
159     LeftChannel = 0u,
160     RightChannel = 1u
161 };
162
163
164 /* Performs a string substitution.  Any case-insensitive occurrences of the
165  * pattern string are replaced with the replacement string.  The result is
166  * truncated if necessary.
167  */
168 static std::string StrSubst(al::span<const char> in, const al::span<const char> pat,
169     const al::span<const char> rep)
170 {
171     std::string ret;
172     ret.reserve(in.size() + pat.size());
173
174     while(in.size() >= pat.size())
175     {
176         if(al::strncasecmp(in.data(), pat.data(), pat.size()) == 0)
177         {
178             in = in.subspan(pat.size());
179             ret.append(rep.data(), rep.size());
180         }
181         else
182         {
183             size_t endpos{1};
184             while(endpos < in.size() && in[endpos] != pat.front())
185                 ++endpos;
186             ret.append(in.data(), endpos);
187             in = in.subspan(endpos);
188         }
189     }
190     ret.append(in.data(), in.size());
191
192     return ret;
193 }
194
195
196 /*********************
197  *** Math routines ***
198  *********************/
199
200 // Simple clamp routine.
201 static double Clamp(const double val, const double lower, const double upper)
202 {
203     return std::min(std::max(val, lower), upper);
204 }
205
206 static inline uint dither_rng(uint *seed)
207 {
208     *seed = *seed * 96314165 + 907633515;
209     return *seed;
210 }
211
212 // Performs a triangular probability density function dither. The input samples
213 // should be normalized (-1 to +1).
214 static void TpdfDither(double *RESTRICT out, const double *RESTRICT in, const double scale,
215                        const uint count, const uint step, uint *seed)
216 {
217     static constexpr double PRNG_SCALE = 1.0 / std::numeric_limits<uint>::max();
218
219     for(uint i{0};i < count;i++)
220     {
221         uint prn0{dither_rng(seed)};
222         uint prn1{dither_rng(seed)};
223         *out = std::round(*(in++)*scale + (prn0*PRNG_SCALE - prn1*PRNG_SCALE));
224         out += step;
225     }
226 }
227
228
229 /* Calculate the complex helical sequence (or discrete-time analytical signal)
230  * of the given input using the Hilbert transform. Given the natural logarithm
231  * of a signal's magnitude response, the imaginary components can be used as
232  * the angles for minimum-phase reconstruction.
233  */
234 inline static void Hilbert(const uint n, complex_d *inout)
235 { complex_hilbert({inout, n}); }
236
237 /* Calculate the magnitude response of the given input.  This is used in
238  * place of phase decomposition, since the phase residuals are discarded for
239  * minimum phase reconstruction.  The mirrored half of the response is also
240  * discarded.
241  */
242 void MagnitudeResponse(const uint n, const complex_d *in, double *out)
243 {
244     const uint m = 1 + (n / 2);
245     uint i;
246     for(i = 0;i < m;i++)
247         out[i] = std::max(std::abs(in[i]), EPSILON);
248 }
249
250 /* Apply a range limit (in dB) to the given magnitude response.  This is used
251  * to adjust the effects of the diffuse-field average on the equalization
252  * process.
253  */
254 static void LimitMagnitudeResponse(const uint n, const uint m, const double limit, const double *in, double *out)
255 {
256     double halfLim;
257     uint i, lower, upper;
258     double ave;
259
260     halfLim = limit / 2.0;
261     // Convert the response to dB.
262     for(i = 0;i < m;i++)
263         out[i] = 20.0 * std::log10(in[i]);
264     // Use six octaves to calculate the average magnitude of the signal.
265     lower = (static_cast<uint>(std::ceil(n / std::pow(2.0, 8.0)))) - 1;
266     upper = (static_cast<uint>(std::floor(n / std::pow(2.0, 2.0)))) - 1;
267     ave = 0.0;
268     for(i = lower;i <= upper;i++)
269         ave += out[i];
270     ave /= upper - lower + 1;
271     // Keep the response within range of the average magnitude.
272     for(i = 0;i < m;i++)
273         out[i] = Clamp(out[i], ave - halfLim, ave + halfLim);
274     // Convert the response back to linear magnitude.
275     for(i = 0;i < m;i++)
276         out[i] = std::pow(10.0, out[i] / 20.0);
277 }
278
279 /* Reconstructs the minimum-phase component for the given magnitude response
280  * of a signal.  This is equivalent to phase recomposition, sans the missing
281  * residuals (which were discarded).  The mirrored half of the response is
282  * reconstructed.
283  */
284 static void MinimumPhase(const uint n, double *mags, complex_d *out)
285 {
286     const uint m{(n/2) + 1};
287
288     uint i;
289     for(i = 0;i < m;i++)
290         out[i] = std::log(mags[i]);
291     for(;i < n;i++)
292     {
293         mags[i] = mags[n - i];
294         out[i] = out[n - i];
295     }
296     Hilbert(n, out);
297     // Remove any DC offset the filter has.
298     mags[0] = EPSILON;
299     for(i = 0;i < n;i++)
300     {
301         auto a = std::exp(complex_d{0.0, out[i].imag()});
302         out[i] = a * mags[i];
303     }
304 }
305
306
307 /***************************
308  *** File storage output ***
309  ***************************/
310
311 // Write an ASCII string to a file.
312 static int WriteAscii(const char *out, FILE *fp, const char *filename)
313 {
314     size_t len;
315
316     len = strlen(out);
317     if(fwrite(out, 1, len, fp) != len)
318     {
319         fclose(fp);
320         fprintf(stderr, "\nError: Bad write to file '%s'.\n", filename);
321         return 0;
322     }
323     return 1;
324 }
325
326 // Write a binary value of the given byte order and byte size to a file,
327 // loading it from a 32-bit unsigned integer.
328 static int WriteBin4(const uint bytes, const uint32_t in, FILE *fp, const char *filename)
329 {
330     uint8_t out[4];
331     uint i;
332
333     for(i = 0;i < bytes;i++)
334         out[i] = (in>>(i*8)) & 0x000000FF;
335
336     if(fwrite(out, 1, bytes, fp) != bytes)
337     {
338         fprintf(stderr, "\nError: Bad write to file '%s'.\n", filename);
339         return 0;
340     }
341     return 1;
342 }
343
344 // Store the OpenAL Soft HRTF data set.
345 static int StoreMhr(const HrirDataT *hData, const char *filename)
346 {
347     const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
348     const uint n{hData->mIrPoints};
349     uint dither_seed{22222};
350     uint fi, ei, ai, i;
351     FILE *fp;
352
353     if((fp=fopen(filename, "wb")) == nullptr)
354     {
355         fprintf(stderr, "\nError: Could not open MHR file '%s'.\n", filename);
356         return 0;
357     }
358     if(!WriteAscii(MHR_FORMAT, fp, filename))
359         return 0;
360     if(!WriteBin4(4, hData->mIrRate, fp, filename))
361         return 0;
362     if(!WriteBin4(1, static_cast<uint32_t>(hData->mChannelType), fp, filename))
363         return 0;
364     if(!WriteBin4(1, hData->mIrPoints, fp, filename))
365         return 0;
366     if(!WriteBin4(1, static_cast<uint>(hData->mFds.size()), fp, filename))
367         return 0;
368     for(fi = static_cast<uint>(hData->mFds.size()-1);fi < hData->mFds.size();fi--)
369     {
370         auto fdist = static_cast<uint32_t>(std::round(1000.0 * hData->mFds[fi].mDistance));
371         if(!WriteBin4(2, fdist, fp, filename))
372             return 0;
373         if(!WriteBin4(1, static_cast<uint32_t>(hData->mFds[fi].mEvs.size()), fp, filename))
374             return 0;
375         for(ei = 0;ei < hData->mFds[fi].mEvs.size();ei++)
376         {
377             const auto &elev = hData->mFds[fi].mEvs[ei];
378             if(!WriteBin4(1, static_cast<uint32_t>(elev.mAzs.size()), fp, filename))
379                 return 0;
380         }
381     }
382
383     for(fi = static_cast<uint>(hData->mFds.size()-1);fi < hData->mFds.size();fi--)
384     {
385         constexpr double scale{8388607.0};
386         constexpr uint bps{3u};
387
388         for(ei = 0;ei < hData->mFds[fi].mEvs.size();ei++)
389         {
390             for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
391             {
392                 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
393                 double out[2 * MAX_TRUNCSIZE];
394
395                 TpdfDither(out, azd->mIrs[0], scale, n, channels, &dither_seed);
396                 if(hData->mChannelType == CT_STEREO)
397                     TpdfDither(out+1, azd->mIrs[1], scale, n, channels, &dither_seed);
398                 for(i = 0;i < (channels * n);i++)
399                 {
400                     const auto v = static_cast<int>(Clamp(out[i], -scale-1.0, scale));
401                     if(!WriteBin4(bps, static_cast<uint32_t>(v), fp, filename))
402                         return 0;
403                 }
404             }
405         }
406     }
407     for(fi = static_cast<uint>(hData->mFds.size()-1);fi < hData->mFds.size();fi--)
408     {
409         /* Delay storage has 2 bits of extra precision. */
410         constexpr double DelayPrecScale{4.0};
411         for(ei = 0;ei < hData->mFds[fi].mEvs.size();ei++)
412         {
413             for(const auto &azd : hData->mFds[fi].mEvs[ei].mAzs)
414             {
415                 auto v = static_cast<uint>(std::round(azd.mDelays[0]*DelayPrecScale));
416                 if(!WriteBin4(1, v, fp, filename)) return 0;
417                 if(hData->mChannelType == CT_STEREO)
418                 {
419                     v = static_cast<uint>(std::round(azd.mDelays[1]*DelayPrecScale));
420                     if(!WriteBin4(1, v, fp, filename)) return 0;
421                 }
422             }
423         }
424     }
425     fclose(fp);
426     return 1;
427 }
428
429
430 /***********************
431  *** HRTF processing ***
432  ***********************/
433
434 /* Balances the maximum HRIR magnitudes of multi-field data sets by
435  * independently normalizing each field in relation to the overall maximum.
436  * This is done to ignore distance attenuation.
437  */
438 static void BalanceFieldMagnitudes(const HrirDataT *hData, const uint channels, const uint m)
439 {
440     double maxMags[MAX_FD_COUNT];
441     uint fi, ei, ti, i;
442
443     double maxMag{0.0};
444     for(fi = 0;fi < hData->mFds.size();fi++)
445     {
446         maxMags[fi] = 0.0;
447
448         for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++)
449         {
450             for(const auto &azd : hData->mFds[fi].mEvs[ei].mAzs)
451             {
452                 for(ti = 0;ti < channels;ti++)
453                 {
454                     for(i = 0;i < m;i++)
455                         maxMags[fi] = std::max(azd.mIrs[ti][i], maxMags[fi]);
456                 }
457             }
458         }
459
460         maxMag = std::max(maxMags[fi], maxMag);
461     }
462
463     for(fi = 0;fi < hData->mFds.size();fi++)
464     {
465         const double magFactor{maxMag / maxMags[fi]};
466
467         for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++)
468         {
469             for(const auto &azd : hData->mFds[fi].mEvs[ei].mAzs)
470             {
471                 for(ti = 0;ti < channels;ti++)
472                 {
473                     for(i = 0;i < m;i++)
474                         azd.mIrs[ti][i] *= magFactor;
475                 }
476             }
477         }
478     }
479 }
480
481 /* Calculate the contribution of each HRIR to the diffuse-field average based
482  * on its coverage volume.  All volumes are centered at the spherical HRIR
483  * coordinates and measured by extruded solid angle.
484  */
485 static void CalculateDfWeights(const HrirDataT *hData, double *weights)
486 {
487     double sum, innerRa, outerRa, evs, ev, upperEv, lowerEv;
488     double solidAngle, solidVolume;
489     uint fi, ei;
490
491     sum = 0.0;
492     // The head radius acts as the limit for the inner radius.
493     innerRa = hData->mRadius;
494     for(fi = 0;fi < hData->mFds.size();fi++)
495     {
496         // Each volume ends half way between progressive field measurements.
497         if((fi + 1) < hData->mFds.size())
498             outerRa = 0.5f * (hData->mFds[fi].mDistance + hData->mFds[fi + 1].mDistance);
499         // The final volume has its limit extended to some practical value.
500         // This is done to emphasize the far-field responses in the average.
501         else
502             outerRa = 10.0f;
503
504         const double raPowDiff{std::pow(outerRa, 3.0) - std::pow(innerRa, 3.0)};
505         evs = M_PI / 2.0 / static_cast<double>(hData->mFds[fi].mEvs.size() - 1);
506         for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++)
507         {
508             const auto &elev = hData->mFds[fi].mEvs[ei];
509             // For each elevation, calculate the upper and lower limits of
510             // the patch band.
511             ev = elev.mElevation;
512             lowerEv = std::max(-M_PI / 2.0, ev - evs);
513             upperEv = std::min(M_PI / 2.0, ev + evs);
514             // Calculate the surface area of the patch band.
515             solidAngle = 2.0 * M_PI * (std::sin(upperEv) - std::sin(lowerEv));
516             // Then the volume of the extruded patch band.
517             solidVolume = solidAngle * raPowDiff / 3.0;
518             // Each weight is the volume of one extruded patch.
519             weights[(fi*MAX_EV_COUNT) + ei] = solidVolume / static_cast<double>(elev.mAzs.size());
520             // Sum the total coverage volume of the HRIRs for all fields.
521             sum += solidAngle;
522         }
523
524         innerRa = outerRa;
525     }
526
527     for(fi = 0;fi < hData->mFds.size();fi++)
528     {
529         // Normalize the weights given the total surface coverage for all
530         // fields.
531         for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++)
532             weights[(fi * MAX_EV_COUNT) + ei] /= sum;
533     }
534 }
535
536 /* Calculate the diffuse-field average from the given magnitude responses of
537  * the HRIR set.  Weighting can be applied to compensate for the varying
538  * coverage of each HRIR.  The final average can then be limited by the
539  * specified magnitude range (in positive dB; 0.0 to skip).
540  */
541 static void CalculateDiffuseFieldAverage(const HrirDataT *hData, const uint channels, const uint m,
542     const int weighted, const double limit, double *dfa)
543 {
544     std::vector<double> weights(hData->mFds.size() * MAX_EV_COUNT);
545     uint count, ti, fi, ei, i, ai;
546
547     if(weighted)
548     {
549         // Use coverage weighting to calculate the average.
550         CalculateDfWeights(hData, weights.data());
551     }
552     else
553     {
554         double weight;
555
556         // If coverage weighting is not used, the weights still need to be
557         // averaged by the number of existing HRIRs.
558         count = hData->mIrCount;
559         for(fi = 0;fi < hData->mFds.size();fi++)
560         {
561             for(ei = 0;ei < hData->mFds[fi].mEvStart;ei++)
562                 count -= static_cast<uint>(hData->mFds[fi].mEvs[ei].mAzs.size());
563         }
564         weight = 1.0 / count;
565
566         for(fi = 0;fi < hData->mFds.size();fi++)
567         {
568             for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++)
569                 weights[(fi * MAX_EV_COUNT) + ei] = weight;
570         }
571     }
572     for(ti = 0;ti < channels;ti++)
573     {
574         for(i = 0;i < m;i++)
575             dfa[(ti * m) + i] = 0.0;
576         for(fi = 0;fi < hData->mFds.size();fi++)
577         {
578             for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++)
579             {
580                 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
581                 {
582                     HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
583                     // Get the weight for this HRIR's contribution.
584                     double weight = weights[(fi * MAX_EV_COUNT) + ei];
585
586                     // Add this HRIR's weighted power average to the total.
587                     for(i = 0;i < m;i++)
588                         dfa[(ti * m) + i] += weight * azd->mIrs[ti][i] * azd->mIrs[ti][i];
589                 }
590             }
591         }
592         // Finish the average calculation and keep it from being too small.
593         for(i = 0;i < m;i++)
594             dfa[(ti * m) + i] = std::max(sqrt(dfa[(ti * m) + i]), EPSILON);
595         // Apply a limit to the magnitude range of the diffuse-field average
596         // if desired.
597         if(limit > 0.0)
598             LimitMagnitudeResponse(hData->mFftSize, m, limit, &dfa[ti * m], &dfa[ti * m]);
599     }
600 }
601
602 // Perform diffuse-field equalization on the magnitude responses of the HRIR
603 // set using the given average response.
604 static void DiffuseFieldEqualize(const uint channels, const uint m, const double *dfa, const HrirDataT *hData)
605 {
606     uint ti, fi, ei, i;
607
608     for(fi = 0;fi < hData->mFds.size();fi++)
609     {
610         for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvs.size();ei++)
611         {
612             for(auto &azd : hData->mFds[fi].mEvs[ei].mAzs)
613             {
614                 for(ti = 0;ti < channels;ti++)
615                 {
616                     for(i = 0;i < m;i++)
617                         azd.mIrs[ti][i] /= dfa[(ti * m) + i];
618                 }
619             }
620         }
621     }
622 }
623
624 /* Given field and elevation indices and an azimuth, calculate the indices of
625  * the two HRIRs that bound the coordinate along with a factor for
626  * calculating the continuous HRIR using interpolation.
627  */
628 static void CalcAzIndices(const HrirFdT &field, const uint ei, const double az, uint *a0, uint *a1, double *af)
629 {
630     double f{(2.0*M_PI + az) * static_cast<double>(field.mEvs[ei].mAzs.size()) / (2.0*M_PI)};
631     const uint i{static_cast<uint>(f) % static_cast<uint>(field.mEvs[ei].mAzs.size())};
632
633     f -= std::floor(f);
634     *a0 = i;
635     *a1 = (i + 1) % static_cast<uint>(field.mEvs[ei].mAzs.size());
636     *af = f;
637 }
638
639 /* Synthesize any missing onset timings at the bottom elevations of each field.
640  * This just mirrors some top elevations for the bottom, and blends the
641  * remaining elevations (not an accurate model).
642  */
643 static void SynthesizeOnsets(HrirDataT *hData)
644 {
645     const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
646
647     auto proc_field = [channels](HrirFdT &field) -> void
648     {
649         /* Get the starting elevation from the measurements, and use it as the
650          * upper elevation limit for what needs to be calculated.
651          */
652         const uint upperElevReal{field.mEvStart};
653         if(upperElevReal <= 0) return;
654
655         /* Get the lowest half of the missing elevations' delays by mirroring
656          * the top elevation delays. The responses are on a spherical grid
657          * centered between the ears, so these should align.
658          */
659         uint ei{};
660         if(channels > 1)
661         {
662             /* Take the polar opposite position of the desired measurement and
663              * swap the ears.
664              */
665             field.mEvs[0].mAzs[0].mDelays[0] = field.mEvs[field.mEvs.size()-1].mAzs[0].mDelays[1];
666             field.mEvs[0].mAzs[0].mDelays[1] = field.mEvs[field.mEvs.size()-1].mAzs[0].mDelays[0];
667             for(ei = 1u;ei < (upperElevReal+1)/2;++ei)
668             {
669                 const uint topElev{static_cast<uint>(field.mEvs.size()-ei-1)};
670
671                 for(uint ai{0u};ai < field.mEvs[ei].mAzs.size();ai++)
672                 {
673                     uint a0, a1;
674                     double af;
675
676                     /* Rotate this current azimuth by a half-circle, and lookup
677                      * the mirrored elevation to find the indices for the polar
678                      * opposite position (may need blending).
679                      */
680                     const double az{field.mEvs[ei].mAzs[ai].mAzimuth + M_PI};
681                     CalcAzIndices(field, topElev, az, &a0, &a1, &af);
682
683                     /* Blend the delays, and again, swap the ears. */
684                     field.mEvs[ei].mAzs[ai].mDelays[0] = Lerp(
685                         field.mEvs[topElev].mAzs[a0].mDelays[1],
686                         field.mEvs[topElev].mAzs[a1].mDelays[1], af);
687                     field.mEvs[ei].mAzs[ai].mDelays[1] = Lerp(
688                         field.mEvs[topElev].mAzs[a0].mDelays[0],
689                         field.mEvs[topElev].mAzs[a1].mDelays[0], af);
690                 }
691             }
692         }
693         else
694         {
695             field.mEvs[0].mAzs[0].mDelays[0] = field.mEvs[field.mEvs.size()-1].mAzs[0].mDelays[0];
696             for(ei = 1u;ei < (upperElevReal+1)/2;++ei)
697             {
698                 const uint topElev{static_cast<uint>(field.mEvs.size()-ei-1)};
699
700                 for(uint ai{0u};ai < field.mEvs[ei].mAzs.size();ai++)
701                 {
702                     uint a0, a1;
703                     double af;
704
705                     /* For mono data sets, mirror the azimuth front<->back
706                      * since the other ear is a mirror of what we have (e.g.
707                      * the left ear's back-left is simulated with the right
708                      * ear's front-right, which uses the left ear's front-left
709                      * measurement).
710                      */
711                     double az{field.mEvs[ei].mAzs[ai].mAzimuth};
712                     if(az <= M_PI) az = M_PI - az;
713                     else az = (M_PI*2.0)-az + M_PI;
714                     CalcAzIndices(field, topElev, az, &a0, &a1, &af);
715
716                     field.mEvs[ei].mAzs[ai].mDelays[0] = Lerp(
717                         field.mEvs[topElev].mAzs[a0].mDelays[0],
718                         field.mEvs[topElev].mAzs[a1].mDelays[0], af);
719                 }
720             }
721         }
722         /* Record the lowest elevation filled in with the mirrored top. */
723         const uint lowerElevFake{ei-1u};
724
725         /* Fill in the remaining delays using bilinear interpolation. This
726          * helps smooth the transition back to the real delays.
727          */
728         for(;ei < upperElevReal;++ei)
729         {
730             const double ef{(field.mEvs[upperElevReal].mElevation - field.mEvs[ei].mElevation) /
731                 (field.mEvs[upperElevReal].mElevation - field.mEvs[lowerElevFake].mElevation)};
732
733             for(uint ai{0u};ai < field.mEvs[ei].mAzs.size();ai++)
734             {
735                 uint a0, a1, a2, a3;
736                 double af0, af1;
737
738                 double az{field.mEvs[ei].mAzs[ai].mAzimuth};
739                 CalcAzIndices(field, upperElevReal, az, &a0, &a1, &af0);
740                 CalcAzIndices(field, lowerElevFake, az, &a2, &a3, &af1);
741                 double blend[4]{
742                     (1.0-ef) * (1.0-af0),
743                     (1.0-ef) * (    af0),
744                     (    ef) * (1.0-af1),
745                     (    ef) * (    af1)
746                 };
747
748                 for(uint ti{0u};ti < channels;ti++)
749                 {
750                     field.mEvs[ei].mAzs[ai].mDelays[ti] =
751                         field.mEvs[upperElevReal].mAzs[a0].mDelays[ti]*blend[0] +
752                         field.mEvs[upperElevReal].mAzs[a1].mDelays[ti]*blend[1] +
753                         field.mEvs[lowerElevFake].mAzs[a2].mDelays[ti]*blend[2] +
754                         field.mEvs[lowerElevFake].mAzs[a3].mDelays[ti]*blend[3];
755                 }
756             }
757         }
758     };
759     std::for_each(hData->mFds.begin(), hData->mFds.end(), proc_field);
760 }
761
762 /* Attempt to synthesize any missing HRIRs at the bottom elevations of each
763  * field.  Right now this just blends the lowest elevation HRIRs together and
764  * applies a low-pass filter to simulate body occlusion.  It is a simple, if
765  * inaccurate model.
766  */
767 static void SynthesizeHrirs(HrirDataT *hData)
768 {
769     const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
770     auto htemp = std::vector<complex_d>(hData->mFftSize);
771     const uint m{hData->mFftSize/2u + 1u};
772     auto filter = std::vector<double>(m);
773     const double beta{3.5e-6 * hData->mIrRate};
774
775     auto proc_field = [channels,m,beta,&htemp,&filter](HrirFdT &field) -> void
776     {
777         const uint oi{field.mEvStart};
778         if(oi <= 0) return;
779
780         for(uint ti{0u};ti < channels;ti++)
781         {
782             uint a0, a1;
783             double af;
784
785             /* Use the lowest immediate-left response for the left ear and
786              * lowest immediate-right response for the right ear. Given no comb
787              * effects as a result of the left response reaching the right ear
788              * and vice-versa, this produces a decent phantom-center response
789              * underneath the head.
790              */
791             CalcAzIndices(field, oi, ((ti==0) ? -M_PI : M_PI) / 2.0, &a0, &a1, &af);
792             for(uint i{0u};i < m;i++)
793             {
794                 field.mEvs[0].mAzs[0].mIrs[ti][i] = Lerp(field.mEvs[oi].mAzs[a0].mIrs[ti][i],
795                     field.mEvs[oi].mAzs[a1].mIrs[ti][i], af);
796             }
797         }
798
799         for(uint ei{1u};ei < field.mEvStart;ei++)
800         {
801             const double of{static_cast<double>(ei) / field.mEvStart};
802             const double b{(1.0 - of) * beta};
803             double lp[4]{};
804
805             /* Calculate a low-pass filter to simulate body occlusion. */
806             lp[0] = Lerp(1.0, lp[0], b);
807             lp[1] = Lerp(lp[0], lp[1], b);
808             lp[2] = Lerp(lp[1], lp[2], b);
809             lp[3] = Lerp(lp[2], lp[3], b);
810             htemp[0] = lp[3];
811             for(size_t i{1u};i < htemp.size();i++)
812             {
813                 lp[0] = Lerp(0.0, lp[0], b);
814                 lp[1] = Lerp(lp[0], lp[1], b);
815                 lp[2] = Lerp(lp[1], lp[2], b);
816                 lp[3] = Lerp(lp[2], lp[3], b);
817                 htemp[i] = lp[3];
818             }
819             /* Get the filter's frequency-domain response and extract the
820              * frequency magnitudes (phase will be reconstructed later)).
821              */
822             FftForward(static_cast<uint>(htemp.size()), htemp.data());
823             std::transform(htemp.cbegin(), htemp.cbegin()+m, filter.begin(),
824                 [](const complex_d &c) -> double { return std::abs(c); });
825
826             for(uint ai{0u};ai < field.mEvs[ei].mAzs.size();ai++)
827             {
828                 uint a0, a1;
829                 double af;
830
831                 CalcAzIndices(field, oi, field.mEvs[ei].mAzs[ai].mAzimuth, &a0, &a1, &af);
832                 for(uint ti{0u};ti < channels;ti++)
833                 {
834                     for(uint i{0u};i < m;i++)
835                     {
836                         /* Blend the two defined HRIRs closest to this azimuth,
837                          * then blend that with the synthesized -90 elevation.
838                          */
839                         const double s1{Lerp(field.mEvs[oi].mAzs[a0].mIrs[ti][i],
840                             field.mEvs[oi].mAzs[a1].mIrs[ti][i], af)};
841                         const double s{Lerp(field.mEvs[0].mAzs[0].mIrs[ti][i], s1, of)};
842                         field.mEvs[ei].mAzs[ai].mIrs[ti][i] = s * filter[i];
843                     }
844                 }
845             }
846         }
847         const double b{beta};
848         double lp[4]{};
849         lp[0] = Lerp(1.0, lp[0], b);
850         lp[1] = Lerp(lp[0], lp[1], b);
851         lp[2] = Lerp(lp[1], lp[2], b);
852         lp[3] = Lerp(lp[2], lp[3], b);
853         htemp[0] = lp[3];
854         for(size_t i{1u};i < htemp.size();i++)
855         {
856             lp[0] = Lerp(0.0, lp[0], b);
857             lp[1] = Lerp(lp[0], lp[1], b);
858             lp[2] = Lerp(lp[1], lp[2], b);
859             lp[3] = Lerp(lp[2], lp[3], b);
860             htemp[i] = lp[3];
861         }
862         FftForward(static_cast<uint>(htemp.size()), htemp.data());
863         std::transform(htemp.cbegin(), htemp.cbegin()+m, filter.begin(),
864             [](const complex_d &c) -> double { return std::abs(c); });
865
866         for(uint ti{0u};ti < channels;ti++)
867         {
868             for(uint i{0u};i < m;i++)
869                 field.mEvs[0].mAzs[0].mIrs[ti][i] *= filter[i];
870         }
871     };
872     std::for_each(hData->mFds.begin(), hData->mFds.end(), proc_field);
873 }
874
875 // The following routines assume a full set of HRIRs for all elevations.
876
877 /* Perform minimum-phase reconstruction using the magnitude responses of the
878  * HRIR set. Work is delegated to this struct, which runs asynchronously on one
879  * or more threads (sharing the same reconstructor object).
880  */
881 struct HrirReconstructor {
882     std::vector<double*> mIrs;
883     std::atomic<size_t> mCurrent;
884     std::atomic<size_t> mDone;
885     uint mFftSize;
886     uint mIrPoints;
887
888     void Worker()
889     {
890         auto h = std::vector<complex_d>(mFftSize);
891         auto mags = std::vector<double>(mFftSize);
892         size_t m{(mFftSize/2) + 1};
893
894         while(1)
895         {
896             /* Load the current index to process. */
897             size_t idx{mCurrent.load()};
898             do {
899                 /* If the index is at the end, we're done. */
900                 if(idx >= mIrs.size())
901                     return;
902                 /* Otherwise, increment the current index atomically so other
903                  * threads know to go to the next one. If this call fails, the
904                  * current index was just changed by another thread and the new
905                  * value is loaded into idx, which we'll recheck.
906                  */
907             } while(!mCurrent.compare_exchange_weak(idx, idx+1, std::memory_order_relaxed));
908
909             /* Now do the reconstruction, and apply the inverse FFT to get the
910              * time-domain response.
911              */
912             for(size_t i{0};i < m;++i)
913                 mags[i] = std::max(mIrs[idx][i], EPSILON);
914             MinimumPhase(mFftSize, mags.data(), h.data());
915             FftInverse(mFftSize, h.data());
916             for(uint i{0u};i < mIrPoints;++i)
917                 mIrs[idx][i] = h[i].real();
918
919             /* Increment the number of IRs done. */
920             mDone.fetch_add(1);
921         }
922     }
923 };
924
925 static void ReconstructHrirs(const HrirDataT *hData, const uint numThreads)
926 {
927     const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
928
929     /* Set up the reconstructor with the needed size info and pointers to the
930      * IRs to process.
931      */
932     HrirReconstructor reconstructor;
933     reconstructor.mCurrent.store(0, std::memory_order_relaxed);
934     reconstructor.mDone.store(0, std::memory_order_relaxed);
935     reconstructor.mFftSize = hData->mFftSize;
936     reconstructor.mIrPoints = hData->mIrPoints;
937     for(const auto &field : hData->mFds)
938     {
939         for(auto &elev : field.mEvs)
940         {
941             for(const auto &azd : elev.mAzs)
942             {
943                 for(uint ti{0u};ti < channels;ti++)
944                     reconstructor.mIrs.push_back(azd.mIrs[ti]);
945             }
946         }
947     }
948
949     /* Launch threads to work on reconstruction. */
950     std::vector<std::thread> thrds;
951     thrds.reserve(numThreads);
952     for(size_t i{0};i < numThreads;++i)
953         thrds.emplace_back(std::mem_fn(&HrirReconstructor::Worker), &reconstructor);
954
955     /* Keep track of the number of IRs done, periodically reporting it. */
956     size_t count;
957     do {
958         std::this_thread::sleep_for(std::chrono::milliseconds{50});
959
960         count = reconstructor.mDone.load();
961         size_t pcdone{count * 100 / reconstructor.mIrs.size()};
962
963         printf("\r%3zu%% done (%zu of %zu)", pcdone, count, reconstructor.mIrs.size());
964         fflush(stdout);
965     } while(count < reconstructor.mIrs.size());
966     fputc('\n', stdout);
967
968     for(auto &thrd : thrds)
969     {
970         if(thrd.joinable())
971             thrd.join();
972     }
973 }
974
975 // Normalize the HRIR set and slightly attenuate the result.
976 static void NormalizeHrirs(HrirDataT *hData)
977 {
978     const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
979     const uint irSize{hData->mIrPoints};
980
981     /* Find the maximum amplitude and RMS out of all the IRs. */
982     struct LevelPair { double amp, rms; };
983     auto mesasure_channel = [irSize](const LevelPair levels, const double *ir)
984     {
985         /* Calculate the peak amplitude and RMS of this IR. */
986         auto current = std::accumulate(ir, ir+irSize, LevelPair{0.0, 0.0},
987             [](const LevelPair cur, const double impulse)
988             {
989                 return LevelPair{std::max(std::abs(impulse), cur.amp), cur.rms + impulse*impulse};
990             });
991         current.rms = std::sqrt(current.rms / irSize);
992
993         /* Accumulate levels by taking the maximum amplitude and RMS. */
994         return LevelPair{std::max(current.amp, levels.amp), std::max(current.rms, levels.rms)};
995     };
996     auto measure_azi = [channels,mesasure_channel](const LevelPair levels, const HrirAzT &azi)
997     { return std::accumulate(azi.mIrs, azi.mIrs+channels, levels, mesasure_channel); };
998     auto measure_elev = [measure_azi](const LevelPair levels, const HrirEvT &elev)
999     { return std::accumulate(elev.mAzs.cbegin(), elev.mAzs.cend(), levels, measure_azi); };
1000     auto measure_field = [measure_elev](const LevelPair levels, const HrirFdT &field)
1001     { return std::accumulate(field.mEvs.cbegin(), field.mEvs.cend(), levels, measure_elev); };
1002
1003     const auto maxlev = std::accumulate(hData->mFds.begin(), hData->mFds.end(),
1004         LevelPair{0.0, 0.0}, measure_field);
1005
1006     /* Normalize using the maximum RMS of the HRIRs. The RMS measure for the
1007      * non-filtered signal is of an impulse with equal length (to the filter):
1008      *
1009      * rms_impulse = sqrt(sum([ 1^2, 0^2, 0^2, ... ]) / n)
1010      *             = sqrt(1 / n)
1011      *
1012      * This helps keep a more consistent volume between the non-filtered signal
1013      * and various data sets.
1014      */
1015     double factor{std::sqrt(1.0 / irSize) / maxlev.rms};
1016
1017     /* Also ensure the samples themselves won't clip. */
1018     factor = std::min(factor, 0.99/maxlev.amp);
1019
1020     /* Now scale all IRs by the given factor. */
1021     auto proc_channel = [irSize,factor](double *ir)
1022     { std::transform(ir, ir+irSize, ir, [factor](double s){ return s * factor; }); };
1023     auto proc_azi = [channels,proc_channel](HrirAzT &azi)
1024     { std::for_each(azi.mIrs, azi.mIrs+channels, proc_channel); };
1025     auto proc_elev = [proc_azi](HrirEvT &elev)
1026     { std::for_each(elev.mAzs.begin(), elev.mAzs.end(), proc_azi); };
1027     auto proc1_field = [proc_elev](HrirFdT &field)
1028     { std::for_each(field.mEvs.begin(), field.mEvs.end(), proc_elev); };
1029
1030     std::for_each(hData->mFds.begin(), hData->mFds.end(), proc1_field);
1031 }
1032
1033 // Calculate the left-ear time delay using a spherical head model.
1034 static double CalcLTD(const double ev, const double az, const double rad, const double dist)
1035 {
1036     double azp, dlp, l, al;
1037
1038     azp = std::asin(std::cos(ev) * std::sin(az));
1039     dlp = std::sqrt((dist*dist) + (rad*rad) + (2.0*dist*rad*sin(azp)));
1040     l = std::sqrt((dist*dist) - (rad*rad));
1041     al = (0.5 * M_PI) + azp;
1042     if(dlp > l)
1043         dlp = l + (rad * (al - std::acos(rad / dist)));
1044     return dlp / 343.3;
1045 }
1046
1047 // Calculate the effective head-related time delays for each minimum-phase
1048 // HRIR. This is done per-field since distance delay is ignored.
1049 static void CalculateHrtds(const HeadModelT model, const double radius, HrirDataT *hData)
1050 {
1051     uint channels = (hData->mChannelType == CT_STEREO) ? 2 : 1;
1052     double customRatio{radius / hData->mRadius};
1053     uint ti;
1054
1055     if(model == HM_SPHERE)
1056     {
1057         for(auto &field : hData->mFds)
1058         {
1059             for(auto &elev : field.mEvs)
1060             {
1061                 for(auto &azd : elev.mAzs)
1062                 {
1063                     for(ti = 0;ti < channels;ti++)
1064                         azd.mDelays[ti] = CalcLTD(elev.mElevation, azd.mAzimuth, radius, field.mDistance);
1065                 }
1066             }
1067         }
1068     }
1069     else if(customRatio != 1.0)
1070     {
1071         for(auto &field : hData->mFds)
1072         {
1073             for(auto &elev : field.mEvs)
1074             {
1075                 for(auto &azd : elev.mAzs)
1076                 {
1077                     for(ti = 0;ti < channels;ti++)
1078                         azd.mDelays[ti] *= customRatio;
1079                 }
1080             }
1081         }
1082     }
1083
1084     double maxHrtd{0.0};
1085     for(auto &field : hData->mFds)
1086     {
1087         double minHrtd{std::numeric_limits<double>::infinity()};
1088         for(auto &elev : field.mEvs)
1089         {
1090             for(auto &azd : elev.mAzs)
1091             {
1092                 for(ti = 0;ti < channels;ti++)
1093                     minHrtd = std::min(azd.mDelays[ti], minHrtd);
1094             }
1095         }
1096
1097         for(auto &elev : field.mEvs)
1098         {
1099             for(auto &azd : elev.mAzs)
1100             {
1101                 for(ti = 0;ti < channels;ti++)
1102                 {
1103                     azd.mDelays[ti] = (azd.mDelays[ti]-minHrtd) * hData->mIrRate;
1104                     maxHrtd = std::max(maxHrtd, azd.mDelays[ti]);
1105                 }
1106             }
1107         }
1108     }
1109     if(maxHrtd > MAX_HRTD)
1110     {
1111         fprintf(stdout, "  Scaling for max delay of %f samples to %f\n...\n", maxHrtd, MAX_HRTD);
1112         const double scale{MAX_HRTD / maxHrtd};
1113         for(auto &field : hData->mFds)
1114         {
1115             for(auto &elev : field.mEvs)
1116             {
1117                 for(auto &azd : elev.mAzs)
1118                 {
1119                     for(ti = 0;ti < channels;ti++)
1120                         azd.mDelays[ti] *= scale;
1121                 }
1122             }
1123         }
1124     }
1125 }
1126
1127 // Allocate and configure dynamic HRIR structures.
1128 bool PrepareHrirData(const al::span<const double> distances,
1129     const al::span<const uint,MAX_FD_COUNT> evCounts,
1130     const al::span<const std::array<uint,MAX_EV_COUNT>,MAX_FD_COUNT> azCounts, HrirDataT *hData)
1131 {
1132     uint evTotal{0}, azTotal{0};
1133
1134     for(size_t fi{0};fi < distances.size();++fi)
1135     {
1136         evTotal += evCounts[fi];
1137         for(size_t ei{0};ei < evCounts[fi];++ei)
1138             azTotal += azCounts[fi][ei];
1139     }
1140     if(!evTotal || !azTotal)
1141         return false;
1142
1143     hData->mEvsBase.resize(evTotal);
1144     hData->mAzsBase.resize(azTotal);
1145     hData->mFds.resize(distances.size());
1146     hData->mIrCount = azTotal;
1147     evTotal = 0;
1148     azTotal = 0;
1149     for(size_t fi{0};fi < distances.size();++fi)
1150     {
1151         hData->mFds[fi].mDistance = distances[fi];
1152         hData->mFds[fi].mEvStart = 0;
1153         hData->mFds[fi].mEvs = {&hData->mEvsBase[evTotal], evCounts[fi]};
1154         evTotal += evCounts[fi];
1155         for(uint ei{0};ei < evCounts[fi];++ei)
1156         {
1157             uint azCount = azCounts[fi][ei];
1158
1159             hData->mFds[fi].mEvs[ei].mElevation = -M_PI / 2.0 + M_PI * ei / (evCounts[fi] - 1);
1160             hData->mFds[fi].mEvs[ei].mAzs = {&hData->mAzsBase[azTotal], azCount};
1161             for(uint ai{0};ai < azCount;ai++)
1162             {
1163                 hData->mFds[fi].mEvs[ei].mAzs[ai].mAzimuth = 2.0 * M_PI * ai / azCount;
1164                 hData->mFds[fi].mEvs[ei].mAzs[ai].mIndex = azTotal + ai;
1165                 hData->mFds[fi].mEvs[ei].mAzs[ai].mDelays[0] = 0.0;
1166                 hData->mFds[fi].mEvs[ei].mAzs[ai].mDelays[1] = 0.0;
1167                 hData->mFds[fi].mEvs[ei].mAzs[ai].mIrs[0] = nullptr;
1168                 hData->mFds[fi].mEvs[ei].mAzs[ai].mIrs[1] = nullptr;
1169             }
1170             azTotal += azCount;
1171         }
1172     }
1173     return true;
1174 }
1175
1176
1177 /* Parse the data set definition and process the source data, storing the
1178  * resulting data set as desired.  If the input name is NULL it will read
1179  * from standard input.
1180  */
1181 static int ProcessDefinition(const char *inName, const uint outRate, const ChannelModeT chanMode,
1182     const bool farfield, const uint numThreads, const uint fftSize, const int equalize,
1183     const int surface, const double limit, const uint truncSize, const HeadModelT model,
1184     const double radius, const char *outName)
1185 {
1186     HrirDataT hData;
1187
1188     fprintf(stdout, "Using %u thread%s.\n", numThreads, (numThreads==1)?"":"s");
1189     if(!inName)
1190     {
1191         inName = "stdin";
1192         fprintf(stdout, "Reading HRIR definition from %s...\n", inName);
1193         if(!LoadDefInput(std::cin, nullptr, 0, inName, fftSize, truncSize, outRate, chanMode, &hData))
1194             return 0;
1195     }
1196     else
1197     {
1198         std::unique_ptr<al::ifstream> input{new al::ifstream{inName}};
1199         if(!input->is_open())
1200         {
1201             fprintf(stderr, "Error: Could not open input file '%s'\n", inName);
1202             return 0;
1203         }
1204
1205         char startbytes[4]{};
1206         input->read(startbytes, sizeof(startbytes));
1207         std::streamsize startbytecount{input->gcount()};
1208         if(startbytecount != sizeof(startbytes) || !input->good())
1209         {
1210             fprintf(stderr, "Error: Could not read input file '%s'\n", inName);
1211             return 0;
1212         }
1213
1214         if(startbytes[0] == '\x89' && startbytes[1] == 'H' && startbytes[2] == 'D'
1215             && startbytes[3] == 'F')
1216         {
1217             input = nullptr;
1218             fprintf(stdout, "Reading HRTF data from %s...\n", inName);
1219             if(!LoadSofaFile(inName, numThreads, fftSize, truncSize, outRate, chanMode, &hData))
1220                 return 0;
1221         }
1222         else
1223         {
1224             fprintf(stdout, "Reading HRIR definition from %s...\n", inName);
1225             if(!LoadDefInput(*input, startbytes, startbytecount, inName, fftSize, truncSize, outRate, chanMode, &hData))
1226                 return 0;
1227         }
1228     }
1229
1230     if(equalize)
1231     {
1232         uint c{(hData.mChannelType == CT_STEREO) ? 2u : 1u};
1233         uint m{hData.mFftSize/2u + 1u};
1234         auto dfa = std::vector<double>(c * m);
1235
1236         if(hData.mFds.size() > 1)
1237         {
1238             fprintf(stdout, "Balancing field magnitudes...\n");
1239             BalanceFieldMagnitudes(&hData, c, m);
1240         }
1241         fprintf(stdout, "Calculating diffuse-field average...\n");
1242         CalculateDiffuseFieldAverage(&hData, c, m, surface, limit, dfa.data());
1243         fprintf(stdout, "Performing diffuse-field equalization...\n");
1244         DiffuseFieldEqualize(c, m, dfa.data(), &hData);
1245     }
1246     if(hData.mFds.size() > 1)
1247     {
1248         fprintf(stdout, "Sorting %zu fields...\n", hData.mFds.size());
1249         std::sort(hData.mFds.begin(), hData.mFds.end(),
1250             [](const HrirFdT &lhs, const HrirFdT &rhs) noexcept
1251             { return lhs.mDistance < rhs.mDistance; });
1252         if(farfield)
1253         {
1254             fprintf(stdout, "Clearing %zu near field%s...\n", hData.mFds.size()-1,
1255                 (hData.mFds.size()-1 != 1) ? "s" : "");
1256             hData.mFds.erase(hData.mFds.cbegin(), hData.mFds.cend()-1);
1257         }
1258     }
1259     fprintf(stdout, "Synthesizing missing elevations...\n");
1260     if(model == HM_DATASET)
1261         SynthesizeOnsets(&hData);
1262     SynthesizeHrirs(&hData);
1263     fprintf(stdout, "Performing minimum phase reconstruction...\n");
1264     ReconstructHrirs(&hData, numThreads);
1265     fprintf(stdout, "Truncating minimum-phase HRIRs...\n");
1266     hData.mIrPoints = truncSize;
1267     fprintf(stdout, "Normalizing final HRIRs...\n");
1268     NormalizeHrirs(&hData);
1269     fprintf(stdout, "Calculating impulse delays...\n");
1270     CalculateHrtds(model, (radius > DEFAULT_CUSTOM_RADIUS) ? radius : hData.mRadius, &hData);
1271
1272     const auto rateStr = std::to_string(hData.mIrRate);
1273     const auto expName = StrSubst({outName, strlen(outName)}, {"%r", 2},
1274         {rateStr.data(), rateStr.size()});
1275     fprintf(stdout, "Creating MHR data set %s...\n", expName.c_str());
1276     return StoreMhr(&hData, expName.c_str());
1277 }
1278
1279 static void PrintHelp(const char *argv0, FILE *ofile)
1280 {
1281     fprintf(ofile, "Usage:  %s [<option>...]\n\n", argv0);
1282     fprintf(ofile, "Options:\n");
1283     fprintf(ofile, " -r <rate>       Change the data set sample rate to the specified value and\n");
1284     fprintf(ofile, "                 resample the HRIRs accordingly.\n");
1285     fprintf(ofile, " -m              Change the data set to mono, mirroring the left ear for the\n");
1286     fprintf(ofile, "                 right ear.\n");
1287     fprintf(ofile, " -a              Change the data set to single field, using the farthest field.\n");
1288     fprintf(ofile, " -j <threads>    Number of threads used to process HRIRs (default: 2).\n");
1289     fprintf(ofile, " -f <points>     Override the FFT window size (default: %u).\n", DEFAULT_FFTSIZE);
1290     fprintf(ofile, " -e {on|off}     Toggle diffuse-field equalization (default: %s).\n", (DEFAULT_EQUALIZE ? "on" : "off"));
1291     fprintf(ofile, " -s {on|off}     Toggle surface-weighted diffuse-field average (default: %s).\n", (DEFAULT_SURFACE ? "on" : "off"));
1292     fprintf(ofile, " -l {<dB>|none}  Specify a limit to the magnitude range of the diffuse-field\n");
1293     fprintf(ofile, "                 average (default: %.2f).\n", DEFAULT_LIMIT);
1294     fprintf(ofile, " -w <points>     Specify the size of the truncation window that's applied\n");
1295     fprintf(ofile, "                 after minimum-phase reconstruction (default: %u).\n", DEFAULT_TRUNCSIZE);
1296     fprintf(ofile, " -d {dataset|    Specify the model used for calculating the head-delay timing\n");
1297     fprintf(ofile, "     sphere}     values (default: %s).\n", ((DEFAULT_HEAD_MODEL == HM_DATASET) ? "dataset" : "sphere"));
1298     fprintf(ofile, " -c <radius>     Use a customized head radius measured to-ear in meters.\n");
1299     fprintf(ofile, " -i <filename>   Specify an HRIR definition file to use (defaults to stdin).\n");
1300     fprintf(ofile, " -o <filename>   Specify an output file. Use of '%%r' will be substituted with\n");
1301     fprintf(ofile, "                 the data set sample rate.\n");
1302 }
1303
1304 // Standard command line dispatch.
1305 int main(int argc, char *argv[])
1306 {
1307     const char *inName = nullptr, *outName = nullptr;
1308     uint outRate, fftSize;
1309     int equalize, surface;
1310     char *end = nullptr;
1311     ChannelModeT chanMode;
1312     HeadModelT model;
1313     uint numThreads;
1314     uint truncSize;
1315     double radius;
1316     bool farfield;
1317     double limit;
1318     int opt;
1319
1320     if(argc < 2)
1321     {
1322         fprintf(stdout, "HRTF Processing and Composition Utility\n\n");
1323         PrintHelp(argv[0], stdout);
1324         exit(EXIT_SUCCESS);
1325     }
1326
1327     outName = "./oalsoft_hrtf_%r.mhr";
1328     outRate = 0;
1329     chanMode = CM_AllowStereo;
1330     fftSize = DEFAULT_FFTSIZE;
1331     equalize = DEFAULT_EQUALIZE;
1332     surface = DEFAULT_SURFACE;
1333     limit = DEFAULT_LIMIT;
1334     numThreads = 2;
1335     truncSize = DEFAULT_TRUNCSIZE;
1336     model = DEFAULT_HEAD_MODEL;
1337     radius = DEFAULT_CUSTOM_RADIUS;
1338     farfield = false;
1339
1340     while((opt=getopt(argc, argv, "r:maj:f:e:s:l:w:d:c:e:i:o:h")) != -1)
1341     {
1342         switch(opt)
1343         {
1344         case 'r':
1345             outRate = static_cast<uint>(strtoul(optarg, &end, 10));
1346             if(end[0] != '\0' || outRate < MIN_RATE || outRate > MAX_RATE)
1347             {
1348                 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %u to %u.\n", optarg, opt, MIN_RATE, MAX_RATE);
1349                 exit(EXIT_FAILURE);
1350             }
1351             break;
1352
1353         case 'm':
1354             chanMode = CM_ForceMono;
1355             break;
1356
1357         case 'a':
1358             farfield = true;
1359             break;
1360
1361         case 'j':
1362             numThreads = static_cast<uint>(strtoul(optarg, &end, 10));
1363             if(end[0] != '\0' || numThreads > 64)
1364             {
1365                 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %u to %u.\n", optarg, opt, 0, 64);
1366                 exit(EXIT_FAILURE);
1367             }
1368             if(numThreads == 0)
1369                 numThreads = std::thread::hardware_concurrency();
1370             break;
1371
1372         case 'f':
1373             fftSize = static_cast<uint>(strtoul(optarg, &end, 10));
1374             if(end[0] != '\0' || (fftSize&(fftSize-1)) || fftSize < MIN_FFTSIZE || fftSize > MAX_FFTSIZE)
1375             {
1376                 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected a power-of-two between %u to %u.\n", optarg, opt, MIN_FFTSIZE, MAX_FFTSIZE);
1377                 exit(EXIT_FAILURE);
1378             }
1379             break;
1380
1381         case 'e':
1382             if(strcmp(optarg, "on") == 0)
1383                 equalize = 1;
1384             else if(strcmp(optarg, "off") == 0)
1385                 equalize = 0;
1386             else
1387             {
1388                 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected on or off.\n", optarg, opt);
1389                 exit(EXIT_FAILURE);
1390             }
1391             break;
1392
1393         case 's':
1394             if(strcmp(optarg, "on") == 0)
1395                 surface = 1;
1396             else if(strcmp(optarg, "off") == 0)
1397                 surface = 0;
1398             else
1399             {
1400                 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected on or off.\n", optarg, opt);
1401                 exit(EXIT_FAILURE);
1402             }
1403             break;
1404
1405         case 'l':
1406             if(strcmp(optarg, "none") == 0)
1407                 limit = 0.0;
1408             else
1409             {
1410                 limit = strtod(optarg, &end);
1411                 if(end[0] != '\0' || limit < MIN_LIMIT || limit > MAX_LIMIT)
1412                 {
1413                     fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %.0f to %.0f.\n", optarg, opt, MIN_LIMIT, MAX_LIMIT);
1414                     exit(EXIT_FAILURE);
1415                 }
1416             }
1417             break;
1418
1419         case 'w':
1420             truncSize = static_cast<uint>(strtoul(optarg, &end, 10));
1421             if(end[0] != '\0' || truncSize < MIN_TRUNCSIZE || truncSize > MAX_TRUNCSIZE)
1422             {
1423                 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %u to %u.\n", optarg, opt, MIN_TRUNCSIZE, MAX_TRUNCSIZE);
1424                 exit(EXIT_FAILURE);
1425             }
1426             break;
1427
1428         case 'd':
1429             if(strcmp(optarg, "dataset") == 0)
1430                 model = HM_DATASET;
1431             else if(strcmp(optarg, "sphere") == 0)
1432                 model = HM_SPHERE;
1433             else
1434             {
1435                 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected dataset or sphere.\n", optarg, opt);
1436                 exit(EXIT_FAILURE);
1437             }
1438             break;
1439
1440         case 'c':
1441             radius = strtod(optarg, &end);
1442             if(end[0] != '\0' || radius < MIN_CUSTOM_RADIUS || radius > MAX_CUSTOM_RADIUS)
1443             {
1444                 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %.2f to %.2f.\n", optarg, opt, MIN_CUSTOM_RADIUS, MAX_CUSTOM_RADIUS);
1445                 exit(EXIT_FAILURE);
1446             }
1447             break;
1448
1449         case 'i':
1450             inName = optarg;
1451             break;
1452
1453         case 'o':
1454             outName = optarg;
1455             break;
1456
1457         case 'h':
1458             PrintHelp(argv[0], stdout);
1459             exit(EXIT_SUCCESS);
1460
1461         default: /* '?' */
1462             PrintHelp(argv[0], stderr);
1463             exit(EXIT_FAILURE);
1464         }
1465     }
1466
1467     int ret = ProcessDefinition(inName, outRate, chanMode, farfield, numThreads, fftSize, equalize,
1468         surface, limit, truncSize, model, radius, outName);
1469     if(!ret) return -1;
1470     fprintf(stdout, "Operation completed.\n");
1471
1472     return EXIT_SUCCESS;
1473 }