Merge pull request #483 from jhasse/silence-nodiscard
[openal-soft.git] / utils / makemhr / makemhr.cpp
blob554bdfe092d4df20cfc250bd8cdc1c69cdf939b9
1 /*
2 * HRTF utility for producing and demonstrating the process of creating an
3 * OpenAL Soft compatible HRIR data set.
5 * Copyright (C) 2011-2019 Christopher Fitzgerald
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.
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.
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.
21 * Or visit: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23 * --------------------------------------------------------------------------
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.
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:
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.
45 * The spherical head algorithm for calculating propagation delay was adapted
46 * from the paper:
48 * Modeling Interaural Time Difference Assuming a Spherical Head
49 * Joel David Miller
50 * Music 150, Musical Acoustics, Stanford University
51 * December 2, 2001
53 * The formulae for calculating the Kaiser window metrics are from the
54 * the textbook:
56 * Discrete-Time Signal Processing
57 * Alan V. Oppenheim and Ronald W. Schafer
58 * Prentice-Hall Signal Processing Series
59 * 1999
62 #define _UNICODE
63 #include "config.h"
65 #include "makemhr.h"
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>
85 #ifdef HAVE_GETOPT
86 #include <unistd.h>
87 #else
88 #include "../getopt.h"
89 #endif
91 #include "alfstream.h"
92 #include "alspan.h"
93 #include "alstring.h"
94 #include "loaddef.h"
95 #include "loadsofa.h"
97 #include "win_main_utf8.h"
100 namespace {
102 using namespace std::placeholders;
104 } // namespace
106 #ifndef M_PI
107 #define M_PI (3.14159265358979323846)
108 #endif
111 // Head model used for calculating the impulse delays.
112 enum HeadModelT {
113 HM_NONE,
114 HM_DATASET, // Measure the onset from the dataset.
115 HM_SPHERE // Calculate the onset using a spherical head model.
119 // The epsilon used to maintain signal stability.
120 #define EPSILON (1e-9)
122 // The limits to the FFT window size override on the command line.
123 #define MIN_FFTSIZE (65536)
124 #define MAX_FFTSIZE (131072)
126 // The limits to the equalization range limit on the command line.
127 #define MIN_LIMIT (2.0)
128 #define MAX_LIMIT (120.0)
130 // The limits to the truncation window size on the command line.
131 #define MIN_TRUNCSIZE (16)
132 #define MAX_TRUNCSIZE (128)
134 // The limits to the custom head radius on the command line.
135 #define MIN_CUSTOM_RADIUS (0.05)
136 #define MAX_CUSTOM_RADIUS (0.15)
138 // The defaults for the command line options.
139 #define DEFAULT_FFTSIZE (65536)
140 #define DEFAULT_EQUALIZE (1)
141 #define DEFAULT_SURFACE (1)
142 #define DEFAULT_LIMIT (24.0)
143 #define DEFAULT_TRUNCSIZE (32)
144 #define DEFAULT_HEAD_MODEL (HM_DATASET)
145 #define DEFAULT_CUSTOM_RADIUS (0.0)
147 // The maximum propagation delay value supported by OpenAL Soft.
148 #define MAX_HRTD (63.0)
150 // The OpenAL Soft HRTF format marker. It stands for minimum-phase head
151 // response protocol 03.
152 #define MHR_FORMAT ("MinPHR03")
154 /* Channel index enums. Mono uses LeftChannel only. */
155 enum ChannelIndex : uint {
156 LeftChannel = 0u,
157 RightChannel = 1u
161 /* Performs a string substitution. Any case-insensitive occurrences of the
162 * pattern string are replaced with the replacement string. The result is
163 * truncated if necessary.
165 static std::string StrSubst(al::span<const char> in, const al::span<const char> pat,
166 const al::span<const char> rep)
168 std::string ret;
169 ret.reserve(in.size() + pat.size());
171 while(in.size() >= pat.size())
173 if(al::strncasecmp(in.data(), pat.data(), pat.size()) == 0)
175 in = in.subspan(pat.size());
176 ret.append(rep.data(), rep.size());
178 else
180 size_t endpos{1};
181 while(endpos < in.size() && in[endpos] != pat.front())
182 ++endpos;
183 ret.append(in.data(), endpos);
184 in = in.subspan(endpos);
187 ret.append(in.data(), in.size());
189 return ret;
193 /*********************
194 *** Math routines ***
195 *********************/
197 // Simple clamp routine.
198 static double Clamp(const double val, const double lower, const double upper)
200 return std::min(std::max(val, lower), upper);
203 static inline uint dither_rng(uint *seed)
205 *seed = *seed * 96314165 + 907633515;
206 return *seed;
209 // Performs a triangular probability density function dither. The input samples
210 // should be normalized (-1 to +1).
211 static void TpdfDither(double *RESTRICT out, const double *RESTRICT in, const double scale,
212 const uint count, const uint step, uint *seed)
214 static constexpr double PRNG_SCALE = 1.0 / std::numeric_limits<uint>::max();
216 for(uint i{0};i < count;i++)
218 uint prn0{dither_rng(seed)};
219 uint prn1{dither_rng(seed)};
220 *out = std::round(*(in++)*scale + (prn0*PRNG_SCALE - prn1*PRNG_SCALE));
221 out += step;
225 /* Fast Fourier transform routines. The number of points must be a power of
226 * two.
229 // Performs bit-reversal ordering.
230 static void FftArrange(const uint n, complex_d *inout)
232 // Handle in-place arrangement.
233 uint rk{0u};
234 for(uint k{0u};k < n;k++)
236 if(rk > k)
237 std::swap(inout[rk], inout[k]);
239 uint m{n};
240 while(rk&(m >>= 1))
241 rk &= ~m;
242 rk |= m;
246 // Performs the summation.
247 static void FftSummation(const uint n, const double s, complex_d *cplx)
249 double pi;
250 uint m, m2;
251 uint i, k, mk;
253 pi = s * M_PI;
254 for(m = 1, m2 = 2;m < n; m <<= 1, m2 <<= 1)
256 // v = Complex (-2.0 * sin (0.5 * pi / m) * sin (0.5 * pi / m), -sin (pi / m))
257 double sm = std::sin(0.5 * pi / m);
258 auto v = complex_d{-2.0*sm*sm, -std::sin(pi / m)};
259 auto w = complex_d{1.0, 0.0};
260 for(i = 0;i < m;i++)
262 for(k = i;k < n;k += m2)
264 mk = k + m;
265 auto t = w * cplx[mk];
266 cplx[mk] = cplx[k] - t;
267 cplx[k] = cplx[k] + t;
269 w += v*w;
274 // Performs a forward FFT.
275 void FftForward(const uint n, complex_d *inout)
277 FftArrange(n, inout);
278 FftSummation(n, 1.0, inout);
281 // Performs an inverse FFT.
282 void FftInverse(const uint n, complex_d *inout)
284 FftArrange(n, inout);
285 FftSummation(n, -1.0, inout);
286 double f{1.0 / n};
287 for(uint i{0};i < n;i++)
288 inout[i] *= f;
291 /* Calculate the complex helical sequence (or discrete-time analytical signal)
292 * of the given input using the Hilbert transform. Given the natural logarithm
293 * of a signal's magnitude response, the imaginary components can be used as
294 * the angles for minimum-phase reconstruction.
296 static void Hilbert(const uint n, complex_d *inout)
298 uint i;
300 // Handle in-place operation.
301 for(i = 0;i < n;i++)
302 inout[i].imag(0.0);
304 FftInverse(n, inout);
305 for(i = 1;i < (n+1)/2;i++)
306 inout[i] *= 2.0;
307 /* Increment i if n is even. */
308 i += (n&1)^1;
309 for(;i < n;i++)
310 inout[i] = complex_d{0.0, 0.0};
311 FftForward(n, inout);
314 /* Calculate the magnitude response of the given input. This is used in
315 * place of phase decomposition, since the phase residuals are discarded for
316 * minimum phase reconstruction. The mirrored half of the response is also
317 * discarded.
319 void MagnitudeResponse(const uint n, const complex_d *in, double *out)
321 const uint m = 1 + (n / 2);
322 uint i;
323 for(i = 0;i < m;i++)
324 out[i] = std::max(std::abs(in[i]), EPSILON);
327 /* Apply a range limit (in dB) to the given magnitude response. This is used
328 * to adjust the effects of the diffuse-field average on the equalization
329 * process.
331 static void LimitMagnitudeResponse(const uint n, const uint m, const double limit, const double *in, double *out)
333 double halfLim;
334 uint i, lower, upper;
335 double ave;
337 halfLim = limit / 2.0;
338 // Convert the response to dB.
339 for(i = 0;i < m;i++)
340 out[i] = 20.0 * std::log10(in[i]);
341 // Use six octaves to calculate the average magnitude of the signal.
342 lower = (static_cast<uint>(std::ceil(n / std::pow(2.0, 8.0)))) - 1;
343 upper = (static_cast<uint>(std::floor(n / std::pow(2.0, 2.0)))) - 1;
344 ave = 0.0;
345 for(i = lower;i <= upper;i++)
346 ave += out[i];
347 ave /= upper - lower + 1;
348 // Keep the response within range of the average magnitude.
349 for(i = 0;i < m;i++)
350 out[i] = Clamp(out[i], ave - halfLim, ave + halfLim);
351 // Convert the response back to linear magnitude.
352 for(i = 0;i < m;i++)
353 out[i] = std::pow(10.0, out[i] / 20.0);
356 /* Reconstructs the minimum-phase component for the given magnitude response
357 * of a signal. This is equivalent to phase recomposition, sans the missing
358 * residuals (which were discarded). The mirrored half of the response is
359 * reconstructed.
361 static void MinimumPhase(const uint n, double *mags, complex_d *out)
363 const uint m{(n/2) + 1};
365 uint i;
366 for(i = 0;i < m;i++)
367 out[i] = std::log(mags[i]);
368 for(;i < n;i++)
370 mags[i] = mags[n - i];
371 out[i] = out[n - i];
373 Hilbert(n, out);
374 // Remove any DC offset the filter has.
375 mags[0] = EPSILON;
376 for(i = 0;i < n;i++)
378 auto a = std::exp(complex_d{0.0, out[i].imag()});
379 out[i] = a * mags[i];
384 /***************************
385 *** File storage output ***
386 ***************************/
388 // Write an ASCII string to a file.
389 static int WriteAscii(const char *out, FILE *fp, const char *filename)
391 size_t len;
393 len = strlen(out);
394 if(fwrite(out, 1, len, fp) != len)
396 fclose(fp);
397 fprintf(stderr, "\nError: Bad write to file '%s'.\n", filename);
398 return 0;
400 return 1;
403 // Write a binary value of the given byte order and byte size to a file,
404 // loading it from a 32-bit unsigned integer.
405 static int WriteBin4(const uint bytes, const uint32_t in, FILE *fp, const char *filename)
407 uint8_t out[4];
408 uint i;
410 for(i = 0;i < bytes;i++)
411 out[i] = (in>>(i*8)) & 0x000000FF;
413 if(fwrite(out, 1, bytes, fp) != bytes)
415 fprintf(stderr, "\nError: Bad write to file '%s'.\n", filename);
416 return 0;
418 return 1;
421 // Store the OpenAL Soft HRTF data set.
422 static int StoreMhr(const HrirDataT *hData, const char *filename)
424 const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
425 const uint n{hData->mIrPoints};
426 uint dither_seed{22222};
427 uint fi, ei, ai, i;
428 FILE *fp;
430 if((fp=fopen(filename, "wb")) == nullptr)
432 fprintf(stderr, "\nError: Could not open MHR file '%s'.\n", filename);
433 return 0;
435 if(!WriteAscii(MHR_FORMAT, fp, filename))
436 return 0;
437 if(!WriteBin4(4, hData->mIrRate, fp, filename))
438 return 0;
439 if(!WriteBin4(1, static_cast<uint32_t>(hData->mChannelType), fp, filename))
440 return 0;
441 if(!WriteBin4(1, hData->mIrPoints, fp, filename))
442 return 0;
443 if(!WriteBin4(1, hData->mFdCount, fp, filename))
444 return 0;
445 for(fi = hData->mFdCount-1;fi < hData->mFdCount;fi--)
447 auto fdist = static_cast<uint32_t>(std::round(1000.0 * hData->mFds[fi].mDistance));
448 if(!WriteBin4(2, fdist, fp, filename))
449 return 0;
450 if(!WriteBin4(1, hData->mFds[fi].mEvCount, fp, filename))
451 return 0;
452 for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
454 if(!WriteBin4(1, hData->mFds[fi].mEvs[ei].mAzCount, fp, filename))
455 return 0;
459 for(fi = hData->mFdCount-1;fi < hData->mFdCount;fi--)
461 constexpr double scale{8388607.0};
462 constexpr uint bps{3u};
464 for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
466 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
468 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
469 double out[2 * MAX_TRUNCSIZE];
471 TpdfDither(out, azd->mIrs[0], scale, n, channels, &dither_seed);
472 if(hData->mChannelType == CT_STEREO)
473 TpdfDither(out+1, azd->mIrs[1], scale, n, channels, &dither_seed);
474 for(i = 0;i < (channels * n);i++)
476 const auto v = static_cast<int>(Clamp(out[i], -scale-1.0, scale));
477 if(!WriteBin4(bps, static_cast<uint32_t>(v), fp, filename))
478 return 0;
483 for(fi = hData->mFdCount-1;fi < hData->mFdCount;fi--)
485 /* Delay storage has 2 bits of extra precision. */
486 constexpr double DelayPrecScale{4.0};
487 for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
489 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
491 const HrirAzT &azd = hData->mFds[fi].mEvs[ei].mAzs[ai];
493 auto v = static_cast<uint>(std::round(azd.mDelays[0]*DelayPrecScale));
494 if(!WriteBin4(1, v, fp, filename)) return 0;
495 if(hData->mChannelType == CT_STEREO)
497 v = static_cast<uint>(std::round(azd.mDelays[1]*DelayPrecScale));
498 if(!WriteBin4(1, v, fp, filename)) return 0;
503 fclose(fp);
504 return 1;
508 /***********************
509 *** HRTF processing ***
510 ***********************/
512 /* Balances the maximum HRIR magnitudes of multi-field data sets by
513 * independently normalizing each field in relation to the overall maximum.
514 * This is done to ignore distance attenuation.
516 static void BalanceFieldMagnitudes(const HrirDataT *hData, const uint channels, const uint m)
518 double maxMags[MAX_FD_COUNT];
519 uint fi, ei, ai, ti, i;
521 double maxMag{0.0};
522 for(fi = 0;fi < hData->mFdCount;fi++)
524 maxMags[fi] = 0.0;
526 for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
528 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
530 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
531 for(ti = 0;ti < channels;ti++)
533 for(i = 0;i < m;i++)
534 maxMags[fi] = std::max(azd->mIrs[ti][i], maxMags[fi]);
539 maxMag = std::max(maxMags[fi], maxMag);
542 for(fi = 0;fi < hData->mFdCount;fi++)
544 const double magFactor{maxMag / maxMags[fi]};
546 for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
548 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
550 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
551 for(ti = 0;ti < channels;ti++)
553 for(i = 0;i < m;i++)
554 azd->mIrs[ti][i] *= magFactor;
561 /* Calculate the contribution of each HRIR to the diffuse-field average based
562 * on its coverage volume. All volumes are centered at the spherical HRIR
563 * coordinates and measured by extruded solid angle.
565 static void CalculateDfWeights(const HrirDataT *hData, double *weights)
567 double sum, innerRa, outerRa, evs, ev, upperEv, lowerEv;
568 double solidAngle, solidVolume;
569 uint fi, ei;
571 sum = 0.0;
572 // The head radius acts as the limit for the inner radius.
573 innerRa = hData->mRadius;
574 for(fi = 0;fi < hData->mFdCount;fi++)
576 // Each volume ends half way between progressive field measurements.
577 if((fi + 1) < hData->mFdCount)
578 outerRa = 0.5f * (hData->mFds[fi].mDistance + hData->mFds[fi + 1].mDistance);
579 // The final volume has its limit extended to some practical value.
580 // This is done to emphasize the far-field responses in the average.
581 else
582 outerRa = 10.0f;
584 evs = M_PI / 2.0 / (hData->mFds[fi].mEvCount - 1);
585 for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
587 // For each elevation, calculate the upper and lower limits of
588 // the patch band.
589 ev = hData->mFds[fi].mEvs[ei].mElevation;
590 lowerEv = std::max(-M_PI / 2.0, ev - evs);
591 upperEv = std::min(M_PI / 2.0, ev + evs);
592 // Calculate the surface area of the patch band.
593 solidAngle = 2.0 * M_PI * (std::sin(upperEv) - std::sin(lowerEv));
594 // Then the volume of the extruded patch band.
595 solidVolume = solidAngle * (std::pow(outerRa, 3.0) - std::pow(innerRa, 3.0)) / 3.0;
596 // Each weight is the volume of one extruded patch.
597 weights[(fi * MAX_EV_COUNT) + ei] = solidVolume / hData->mFds[fi].mEvs[ei].mAzCount;
598 // Sum the total coverage volume of the HRIRs for all fields.
599 sum += solidAngle;
602 innerRa = outerRa;
605 for(fi = 0;fi < hData->mFdCount;fi++)
607 // Normalize the weights given the total surface coverage for all
608 // fields.
609 for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
610 weights[(fi * MAX_EV_COUNT) + ei] /= sum;
614 /* Calculate the diffuse-field average from the given magnitude responses of
615 * the HRIR set. Weighting can be applied to compensate for the varying
616 * coverage of each HRIR. The final average can then be limited by the
617 * specified magnitude range (in positive dB; 0.0 to skip).
619 static void CalculateDiffuseFieldAverage(const HrirDataT *hData, const uint channels, const uint m, const int weighted, const double limit, double *dfa)
621 std::vector<double> weights(hData->mFdCount * MAX_EV_COUNT);
622 uint count, ti, fi, ei, i, ai;
624 if(weighted)
626 // Use coverage weighting to calculate the average.
627 CalculateDfWeights(hData, weights.data());
629 else
631 double weight;
633 // If coverage weighting is not used, the weights still need to be
634 // averaged by the number of existing HRIRs.
635 count = hData->mIrCount;
636 for(fi = 0;fi < hData->mFdCount;fi++)
638 for(ei = 0;ei < hData->mFds[fi].mEvStart;ei++)
639 count -= hData->mFds[fi].mEvs[ei].mAzCount;
641 weight = 1.0 / count;
643 for(fi = 0;fi < hData->mFdCount;fi++)
645 for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
646 weights[(fi * MAX_EV_COUNT) + ei] = weight;
649 for(ti = 0;ti < channels;ti++)
651 for(i = 0;i < m;i++)
652 dfa[(ti * m) + i] = 0.0;
653 for(fi = 0;fi < hData->mFdCount;fi++)
655 for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
657 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
659 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
660 // Get the weight for this HRIR's contribution.
661 double weight = weights[(fi * MAX_EV_COUNT) + ei];
663 // Add this HRIR's weighted power average to the total.
664 for(i = 0;i < m;i++)
665 dfa[(ti * m) + i] += weight * azd->mIrs[ti][i] * azd->mIrs[ti][i];
669 // Finish the average calculation and keep it from being too small.
670 for(i = 0;i < m;i++)
671 dfa[(ti * m) + i] = std::max(sqrt(dfa[(ti * m) + i]), EPSILON);
672 // Apply a limit to the magnitude range of the diffuse-field average
673 // if desired.
674 if(limit > 0.0)
675 LimitMagnitudeResponse(hData->mFftSize, m, limit, &dfa[ti * m], &dfa[ti * m]);
679 // Perform diffuse-field equalization on the magnitude responses of the HRIR
680 // set using the given average response.
681 static void DiffuseFieldEqualize(const uint channels, const uint m, const double *dfa, const HrirDataT *hData)
683 uint ti, fi, ei, ai, i;
685 for(fi = 0;fi < hData->mFdCount;fi++)
687 for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
689 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
691 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
693 for(ti = 0;ti < channels;ti++)
695 for(i = 0;i < m;i++)
696 azd->mIrs[ti][i] /= dfa[(ti * m) + i];
703 // Resamples the HRIRs for use at the given sampling rate.
704 static void ResampleHrirs(const uint rate, HrirDataT *hData)
706 struct Resampler {
707 const double scale;
708 const size_t m;
710 /* Resampling from a lower rate to a higher rate. This likely only
711 * works properly when 1 <= scale <= 2.
713 void upsample(double *resampled, const double *ir) const
715 std::fill_n(resampled, m, 0.0);
716 resampled[0] = ir[0];
717 for(size_t in{1};in < m;++in)
719 const auto offset = static_cast<double>(in) / scale;
720 const auto out = static_cast<size_t>(offset);
722 const double a{offset - static_cast<double>(out)};
723 if(out == m-1)
724 resampled[out] += ir[in]*(1.0-a);
725 else
727 resampled[out ] += ir[in]*(1.0-a);
728 resampled[out+1] += ir[in]*a;
733 /* Resampling from a higher rate to a lower rate. This likely only
734 * works properly when 0.5 <= scale <= 1.0.
736 void downsample(double *resampled, const double *ir) const
738 resampled[0] = ir[0];
739 for(size_t out{1};out < m;++out)
741 const auto offset = static_cast<double>(out) * scale;
742 const auto in = static_cast<size_t>(offset);
744 const double a{offset - static_cast<double>(in)};
745 if(in == m-1)
746 resampled[out] = ir[in]*(1.0-a);
747 else
748 resampled[out] = ir[in]*(1.0-a) + ir[in+1]*a;
753 while(rate > hData->mIrRate*2)
754 ResampleHrirs(hData->mIrRate*2, hData);
755 while(rate < (hData->mIrRate+1)/2)
756 ResampleHrirs((hData->mIrRate+1)/2, hData);
758 const auto scale = static_cast<double>(rate) / hData->mIrRate;
759 const size_t m{hData->mFftSize/2u + 1u};
760 auto resampled = std::vector<double>(m);
762 const Resampler resampler{scale, m};
763 auto do_resample = std::bind(
764 std::mem_fn((scale > 1.0) ? &Resampler::upsample : &Resampler::downsample), &resampler,
765 _1, _2);
767 const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
768 for(uint fi{0};fi < hData->mFdCount;++fi)
770 for(uint ei{hData->mFds[fi].mEvStart};ei < hData->mFds[fi].mEvCount;++ei)
772 for(uint ai{0};ai < hData->mFds[fi].mEvs[ei].mAzCount;++ai)
774 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
775 for(uint ti{0};ti < channels;++ti)
777 do_resample(resampled.data(), azd->mIrs[ti]);
778 /* This should probably be rescaled according to the scale,
779 * however it'll all be normalized in the end so a constant
780 * scalar is fine to leave.
782 std::transform(resampled.cbegin(), resampled.cend(), azd->mIrs[ti],
783 [](const double d) { return std::max(d, EPSILON); });
788 hData->mIrRate = rate;
791 /* Given field and elevation indices and an azimuth, calculate the indices of
792 * the two HRIRs that bound the coordinate along with a factor for
793 * calculating the continuous HRIR using interpolation.
795 static void CalcAzIndices(const HrirFdT &field, const uint ei, const double az, uint *a0, uint *a1, double *af)
797 double f{(2.0*M_PI + az) * field.mEvs[ei].mAzCount / (2.0*M_PI)};
798 uint i{static_cast<uint>(f) % field.mEvs[ei].mAzCount};
800 f -= std::floor(f);
801 *a0 = i;
802 *a1 = (i + 1) % field.mEvs[ei].mAzCount;
803 *af = f;
806 /* Synthesize any missing onset timings at the bottom elevations of each field.
807 * This just mirrors some top elevations for the bottom, and blends the
808 * remaining elevations (not an accurate model).
810 static void SynthesizeOnsets(HrirDataT *hData)
812 const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
814 auto proc_field = [channels](HrirFdT &field) -> void
816 /* Get the starting elevation from the measurements, and use it as the
817 * upper elevation limit for what needs to be calculated.
819 const uint upperElevReal{field.mEvStart};
820 if(upperElevReal <= 0) return;
822 /* Get the lowest half of the missing elevations' delays by mirroring
823 * the top elevation delays. The responses are on a spherical grid
824 * centered between the ears, so these should align.
826 uint ei{};
827 if(channels > 1)
829 /* Take the polar opposite position of the desired measurement and
830 * swap the ears.
832 field.mEvs[0].mAzs[0].mDelays[0] = field.mEvs[field.mEvCount-1].mAzs[0].mDelays[1];
833 field.mEvs[0].mAzs[0].mDelays[1] = field.mEvs[field.mEvCount-1].mAzs[0].mDelays[0];
834 for(ei = 1u;ei < (upperElevReal+1)/2;++ei)
836 const uint topElev{field.mEvCount-ei-1};
838 for(uint ai{0u};ai < field.mEvs[ei].mAzCount;ai++)
840 uint a0, a1;
841 double af;
843 /* Rotate this current azimuth by a half-circle, and lookup
844 * the mirrored elevation to find the indices for the polar
845 * opposite position (may need blending).
847 const double az{field.mEvs[ei].mAzs[ai].mAzimuth + M_PI};
848 CalcAzIndices(field, topElev, az, &a0, &a1, &af);
850 /* Blend the delays, and again, swap the ears. */
851 field.mEvs[ei].mAzs[ai].mDelays[0] = Lerp(
852 field.mEvs[topElev].mAzs[a0].mDelays[1],
853 field.mEvs[topElev].mAzs[a1].mDelays[1], af);
854 field.mEvs[ei].mAzs[ai].mDelays[1] = Lerp(
855 field.mEvs[topElev].mAzs[a0].mDelays[0],
856 field.mEvs[topElev].mAzs[a1].mDelays[0], af);
860 else
862 field.mEvs[0].mAzs[0].mDelays[0] = field.mEvs[field.mEvCount-1].mAzs[0].mDelays[0];
863 for(ei = 1u;ei < (upperElevReal+1)/2;++ei)
865 const uint topElev{field.mEvCount-ei-1};
867 for(uint ai{0u};ai < field.mEvs[ei].mAzCount;ai++)
869 uint a0, a1;
870 double af;
872 /* For mono data sets, mirror the azimuth front<->back
873 * since the other ear is a mirror of what we have (e.g.
874 * the left ear's back-left is simulated with the right
875 * ear's front-right, which uses the left ear's front-left
876 * measurement).
878 double az{field.mEvs[ei].mAzs[ai].mAzimuth};
879 if(az <= M_PI) az = M_PI - az;
880 else az = (M_PI*2.0)-az + M_PI;
881 CalcAzIndices(field, topElev, az, &a0, &a1, &af);
883 field.mEvs[ei].mAzs[ai].mDelays[0] = Lerp(
884 field.mEvs[topElev].mAzs[a0].mDelays[0],
885 field.mEvs[topElev].mAzs[a1].mDelays[0], af);
889 /* Record the lowest elevation filled in with the mirrored top. */
890 const uint lowerElevFake{ei-1u};
892 /* Fill in the remaining delays using bilinear interpolation. This
893 * helps smooth the transition back to the real delays.
895 for(;ei < upperElevReal;++ei)
897 const double ef{(field.mEvs[upperElevReal].mElevation - field.mEvs[ei].mElevation) /
898 (field.mEvs[upperElevReal].mElevation - field.mEvs[lowerElevFake].mElevation)};
900 for(uint ai{0u};ai < field.mEvs[ei].mAzCount;ai++)
902 uint a0, a1, a2, a3;
903 double af0, af1;
905 double az{field.mEvs[ei].mAzs[ai].mAzimuth};
906 CalcAzIndices(field, upperElevReal, az, &a0, &a1, &af0);
907 CalcAzIndices(field, lowerElevFake, az, &a2, &a3, &af1);
908 double blend[4]{
909 (1.0-ef) * (1.0-af0),
910 (1.0-ef) * ( af0),
911 ( ef) * (1.0-af1),
912 ( ef) * ( af1)
915 for(uint ti{0u};ti < channels;ti++)
917 field.mEvs[ei].mAzs[ai].mDelays[ti] =
918 field.mEvs[upperElevReal].mAzs[a0].mDelays[ti]*blend[0] +
919 field.mEvs[upperElevReal].mAzs[a1].mDelays[ti]*blend[1] +
920 field.mEvs[lowerElevFake].mAzs[a2].mDelays[ti]*blend[2] +
921 field.mEvs[lowerElevFake].mAzs[a3].mDelays[ti]*blend[3];
926 std::for_each(hData->mFds.begin(), hData->mFds.begin()+hData->mFdCount, proc_field);
929 /* Attempt to synthesize any missing HRIRs at the bottom elevations of each
930 * field. Right now this just blends the lowest elevation HRIRs together and
931 * applies a low-pass filter to simulate body occlusion. It is a simple, if
932 * inaccurate model.
934 static void SynthesizeHrirs(HrirDataT *hData)
936 const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
937 auto htemp = std::vector<complex_d>(hData->mFftSize);
938 const uint m{hData->mFftSize/2u + 1u};
939 auto filter = std::vector<double>(m);
940 const double beta{3.5e-6 * hData->mIrRate};
942 auto proc_field = [channels,m,beta,&htemp,&filter](HrirFdT &field) -> void
944 const uint oi{field.mEvStart};
945 if(oi <= 0) return;
947 for(uint ti{0u};ti < channels;ti++)
949 uint a0, a1;
950 double af;
952 /* Use the lowest immediate-left response for the left ear and
953 * lowest immediate-right response for the right ear. Given no comb
954 * effects as a result of the left response reaching the right ear
955 * and vice-versa, this produces a decent phantom-center response
956 * underneath the head.
958 CalcAzIndices(field, oi, ((ti==0) ? -M_PI : M_PI) / 2.0, &a0, &a1, &af);
959 for(uint i{0u};i < m;i++)
961 field.mEvs[0].mAzs[0].mIrs[ti][i] = Lerp(field.mEvs[oi].mAzs[a0].mIrs[ti][i],
962 field.mEvs[oi].mAzs[a1].mIrs[ti][i], af);
966 for(uint ei{1u};ei < field.mEvStart;ei++)
968 const double of{static_cast<double>(ei) / field.mEvStart};
969 const double b{(1.0 - of) * beta};
970 double lp[4]{};
972 /* Calculate a low-pass filter to simulate body occlusion. */
973 lp[0] = Lerp(1.0, lp[0], b);
974 lp[1] = Lerp(lp[0], lp[1], b);
975 lp[2] = Lerp(lp[1], lp[2], b);
976 lp[3] = Lerp(lp[2], lp[3], b);
977 htemp[0] = lp[3];
978 for(size_t i{1u};i < htemp.size();i++)
980 lp[0] = Lerp(0.0, lp[0], b);
981 lp[1] = Lerp(lp[0], lp[1], b);
982 lp[2] = Lerp(lp[1], lp[2], b);
983 lp[3] = Lerp(lp[2], lp[3], b);
984 htemp[i] = lp[3];
986 /* Get the filter's frequency-domain response and extract the
987 * frequency magnitudes (phase will be reconstructed later)).
989 FftForward(static_cast<uint>(htemp.size()), htemp.data());
990 std::transform(htemp.cbegin(), htemp.cbegin()+m, filter.begin(),
991 [](const complex_d &c) -> double { return std::abs(c); });
993 for(uint ai{0u};ai < field.mEvs[ei].mAzCount;ai++)
995 uint a0, a1;
996 double af;
998 CalcAzIndices(field, oi, field.mEvs[ei].mAzs[ai].mAzimuth, &a0, &a1, &af);
999 for(uint ti{0u};ti < channels;ti++)
1001 for(uint i{0u};i < m;i++)
1003 /* Blend the two defined HRIRs closest to this azimuth,
1004 * then blend that with the synthesized -90 elevation.
1006 const double s1{Lerp(field.mEvs[oi].mAzs[a0].mIrs[ti][i],
1007 field.mEvs[oi].mAzs[a1].mIrs[ti][i], af)};
1008 const double s{Lerp(field.mEvs[0].mAzs[0].mIrs[ti][i], s1, of)};
1009 field.mEvs[ei].mAzs[ai].mIrs[ti][i] = s * filter[i];
1014 const double b{beta};
1015 double lp[4]{};
1016 lp[0] = Lerp(1.0, lp[0], b);
1017 lp[1] = Lerp(lp[0], lp[1], b);
1018 lp[2] = Lerp(lp[1], lp[2], b);
1019 lp[3] = Lerp(lp[2], lp[3], b);
1020 htemp[0] = lp[3];
1021 for(size_t i{1u};i < htemp.size();i++)
1023 lp[0] = Lerp(0.0, lp[0], b);
1024 lp[1] = Lerp(lp[0], lp[1], b);
1025 lp[2] = Lerp(lp[1], lp[2], b);
1026 lp[3] = Lerp(lp[2], lp[3], b);
1027 htemp[i] = lp[3];
1029 FftForward(static_cast<uint>(htemp.size()), htemp.data());
1030 std::transform(htemp.cbegin(), htemp.cbegin()+m, filter.begin(),
1031 [](const complex_d &c) -> double { return std::abs(c); });
1033 for(uint ti{0u};ti < channels;ti++)
1035 for(uint i{0u};i < m;i++)
1036 field.mEvs[0].mAzs[0].mIrs[ti][i] *= filter[i];
1039 std::for_each(hData->mFds.begin(), hData->mFds.begin()+hData->mFdCount, proc_field);
1042 // The following routines assume a full set of HRIRs for all elevations.
1044 /* Perform minimum-phase reconstruction using the magnitude responses of the
1045 * HRIR set. Work is delegated to this struct, which runs asynchronously on one
1046 * or more threads (sharing the same reconstructor object).
1048 struct HrirReconstructor {
1049 std::vector<double*> mIrs;
1050 std::atomic<size_t> mCurrent;
1051 std::atomic<size_t> mDone;
1052 uint mFftSize;
1053 uint mIrPoints;
1055 void Worker()
1057 auto h = std::vector<complex_d>(mFftSize);
1058 auto mags = std::vector<double>(mFftSize);
1059 size_t m{(mFftSize/2) + 1};
1061 while(1)
1063 /* Load the current index to process. */
1064 size_t idx{mCurrent.load()};
1065 do {
1066 /* If the index is at the end, we're done. */
1067 if(idx >= mIrs.size())
1068 return;
1069 /* Otherwise, increment the current index atomically so other
1070 * threads know to go to the next one. If this call fails, the
1071 * current index was just changed by another thread and the new
1072 * value is loaded into idx, which we'll recheck.
1074 } while(!mCurrent.compare_exchange_weak(idx, idx+1, std::memory_order_relaxed));
1076 /* Now do the reconstruction, and apply the inverse FFT to get the
1077 * time-domain response.
1079 for(size_t i{0};i < m;++i)
1080 mags[i] = std::max(mIrs[idx][i], EPSILON);
1081 MinimumPhase(mFftSize, mags.data(), h.data());
1082 FftInverse(mFftSize, h.data());
1083 for(uint i{0u};i < mIrPoints;++i)
1084 mIrs[idx][i] = h[i].real();
1086 /* Increment the number of IRs done. */
1087 mDone.fetch_add(1);
1092 static void ReconstructHrirs(const HrirDataT *hData, const uint numThreads)
1094 const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
1096 /* Set up the reconstructor with the needed size info and pointers to the
1097 * IRs to process.
1099 HrirReconstructor reconstructor;
1100 reconstructor.mCurrent.store(0, std::memory_order_relaxed);
1101 reconstructor.mDone.store(0, std::memory_order_relaxed);
1102 reconstructor.mFftSize = hData->mFftSize;
1103 reconstructor.mIrPoints = hData->mIrPoints;
1104 for(uint fi{0u};fi < hData->mFdCount;fi++)
1106 const HrirFdT &field = hData->mFds[fi];
1107 for(uint ei{0};ei < field.mEvCount;ei++)
1109 const HrirEvT &elev = field.mEvs[ei];
1110 for(uint ai{0u};ai < elev.mAzCount;ai++)
1112 const HrirAzT &azd = elev.mAzs[ai];
1113 for(uint ti{0u};ti < channels;ti++)
1114 reconstructor.mIrs.push_back(azd.mIrs[ti]);
1119 /* Launch threads to work on reconstruction. */
1120 std::vector<std::thread> thrds;
1121 thrds.reserve(numThreads);
1122 for(size_t i{0};i < numThreads;++i)
1123 thrds.emplace_back(std::mem_fn(&HrirReconstructor::Worker), &reconstructor);
1125 /* Keep track of the number of IRs done, periodically reporting it. */
1126 size_t count;
1127 do {
1128 std::this_thread::sleep_for(std::chrono::milliseconds{50});
1130 count = reconstructor.mDone.load();
1131 size_t pcdone{count * 100 / reconstructor.mIrs.size()};
1133 printf("\r%3zu%% done (%zu of %zu)", pcdone, count, reconstructor.mIrs.size());
1134 fflush(stdout);
1135 } while(count < reconstructor.mIrs.size());
1136 fputc('\n', stdout);
1138 for(auto &thrd : thrds)
1140 if(thrd.joinable())
1141 thrd.join();
1145 // Normalize the HRIR set and slightly attenuate the result.
1146 static void NormalizeHrirs(HrirDataT *hData)
1148 const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
1149 const uint irSize{hData->mIrPoints};
1151 /* Find the maximum amplitude and RMS out of all the IRs. */
1152 struct LevelPair { double amp, rms; };
1153 auto proc0_field = [channels,irSize](const LevelPair levels0, const HrirFdT &field) -> LevelPair
1155 auto proc_elev = [channels,irSize](const LevelPair levels1, const HrirEvT &elev) -> LevelPair
1157 auto proc_azi = [channels,irSize](const LevelPair levels2, const HrirAzT &azi) -> LevelPair
1159 auto proc_channel = [irSize](const LevelPair levels3, const double *ir) -> LevelPair
1161 /* Calculate the peak amplitude and RMS of this IR. */
1162 auto current = std::accumulate(ir, ir+irSize, LevelPair{0.0, 0.0},
1163 [](const LevelPair cur, const double impulse) -> LevelPair
1165 return {std::max(std::abs(impulse), cur.amp),
1166 cur.rms + impulse*impulse};
1168 current.rms = std::sqrt(current.rms / irSize);
1170 /* Accumulate levels by taking the maximum amplitude and RMS. */
1171 return LevelPair{std::max(current.amp, levels3.amp),
1172 std::max(current.rms, levels3.rms)};
1174 return std::accumulate(azi.mIrs, azi.mIrs+channels, levels2, proc_channel);
1176 return std::accumulate(elev.mAzs, elev.mAzs+elev.mAzCount, levels1, proc_azi);
1178 return std::accumulate(field.mEvs, field.mEvs+field.mEvCount, levels0, proc_elev);
1180 const auto maxlev = std::accumulate(hData->mFds.begin(), hData->mFds.begin()+hData->mFdCount,
1181 LevelPair{0.0, 0.0}, proc0_field);
1183 /* Normalize using the maximum RMS of the HRIRs. The RMS measure for the
1184 * non-filtered signal is of an impulse with equal length (to the filter):
1186 * rms_impulse = sqrt(sum([ 1^2, 0^2, 0^2, ... ]) / n)
1187 * = sqrt(1 / n)
1189 * This helps keep a more consistent volume between the non-filtered signal
1190 * and various data sets.
1192 double factor{std::sqrt(1.0 / irSize) / maxlev.rms};
1194 /* Also ensure the samples themselves won't clip. */
1195 factor = std::min(factor, 0.99/maxlev.amp);
1197 /* Now scale all IRs by the given factor. */
1198 auto proc1_field = [channels,irSize,factor](HrirFdT &field) -> void
1200 auto proc_elev = [channels,irSize,factor](HrirEvT &elev) -> void
1202 auto proc_azi = [channels,irSize,factor](HrirAzT &azi) -> void
1204 auto proc_channel = [irSize,factor](double *ir) -> void
1206 std::transform(ir, ir+irSize, ir,
1207 std::bind(std::multiplies<double>{}, _1, factor));
1209 std::for_each(azi.mIrs, azi.mIrs+channels, proc_channel);
1211 std::for_each(elev.mAzs, elev.mAzs+elev.mAzCount, proc_azi);
1213 std::for_each(field.mEvs, field.mEvs+field.mEvCount, proc_elev);
1215 std::for_each(hData->mFds.begin(), hData->mFds.begin()+hData->mFdCount, proc1_field);
1218 // Calculate the left-ear time delay using a spherical head model.
1219 static double CalcLTD(const double ev, const double az, const double rad, const double dist)
1221 double azp, dlp, l, al;
1223 azp = std::asin(std::cos(ev) * std::sin(az));
1224 dlp = std::sqrt((dist*dist) + (rad*rad) + (2.0*dist*rad*sin(azp)));
1225 l = std::sqrt((dist*dist) - (rad*rad));
1226 al = (0.5 * M_PI) + azp;
1227 if(dlp > l)
1228 dlp = l + (rad * (al - std::acos(rad / dist)));
1229 return dlp / 343.3;
1232 // Calculate the effective head-related time delays for each minimum-phase
1233 // HRIR. This is done per-field since distance delay is ignored.
1234 static void CalculateHrtds(const HeadModelT model, const double radius, HrirDataT *hData)
1236 uint channels = (hData->mChannelType == CT_STEREO) ? 2 : 1;
1237 double customRatio{radius / hData->mRadius};
1238 uint ti, fi, ei, ai;
1240 if(model == HM_SPHERE)
1242 for(fi = 0;fi < hData->mFdCount;fi++)
1244 for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
1246 HrirEvT *evd = &hData->mFds[fi].mEvs[ei];
1248 for(ai = 0;ai < evd->mAzCount;ai++)
1250 HrirAzT *azd = &evd->mAzs[ai];
1252 for(ti = 0;ti < channels;ti++)
1253 azd->mDelays[ti] = CalcLTD(evd->mElevation, azd->mAzimuth, radius, hData->mFds[fi].mDistance);
1258 else if(customRatio != 1.0)
1260 for(fi = 0;fi < hData->mFdCount;fi++)
1262 for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
1264 HrirEvT *evd = &hData->mFds[fi].mEvs[ei];
1266 for(ai = 0;ai < evd->mAzCount;ai++)
1268 HrirAzT *azd = &evd->mAzs[ai];
1269 for(ti = 0;ti < channels;ti++)
1270 azd->mDelays[ti] *= customRatio;
1276 double maxHrtd{0.0};
1277 for(fi = 0;fi < hData->mFdCount;fi++)
1279 double minHrtd{std::numeric_limits<double>::infinity()};
1280 for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
1282 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
1284 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
1286 for(ti = 0;ti < channels;ti++)
1287 minHrtd = std::min(azd->mDelays[ti], minHrtd);
1291 for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
1293 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
1295 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
1297 for(ti = 0;ti < channels;ti++)
1299 azd->mDelays[ti] = (azd->mDelays[ti]-minHrtd) * hData->mIrRate;
1300 maxHrtd = std::max(maxHrtd, azd->mDelays[ti]);
1305 if(maxHrtd > MAX_HRTD)
1307 fprintf(stdout, " Scaling for max delay of %f samples to %f\n...\n", maxHrtd, MAX_HRTD);
1308 const double scale{MAX_HRTD / maxHrtd};
1309 for(fi = 0;fi < hData->mFdCount;fi++)
1311 for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
1313 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
1315 HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
1316 for(ti = 0;ti < channels;ti++)
1317 azd->mDelays[ti] *= scale;
1324 // Allocate and configure dynamic HRIR structures.
1325 int PrepareHrirData(const uint fdCount, const double (&distances)[MAX_FD_COUNT],
1326 const uint (&evCounts)[MAX_FD_COUNT], const uint azCounts[MAX_FD_COUNT * MAX_EV_COUNT],
1327 HrirDataT *hData)
1329 uint evTotal = 0, azTotal = 0, fi, ei, ai;
1331 for(fi = 0;fi < fdCount;fi++)
1333 evTotal += evCounts[fi];
1334 for(ei = 0;ei < evCounts[fi];ei++)
1335 azTotal += azCounts[(fi * MAX_EV_COUNT) + ei];
1337 if(!fdCount || !evTotal || !azTotal)
1338 return 0;
1340 hData->mEvsBase.resize(evTotal);
1341 hData->mAzsBase.resize(azTotal);
1342 hData->mFds.resize(fdCount);
1343 hData->mIrCount = azTotal;
1344 hData->mFdCount = fdCount;
1345 evTotal = 0;
1346 azTotal = 0;
1347 for(fi = 0;fi < fdCount;fi++)
1349 hData->mFds[fi].mDistance = distances[fi];
1350 hData->mFds[fi].mEvCount = evCounts[fi];
1351 hData->mFds[fi].mEvStart = 0;
1352 hData->mFds[fi].mEvs = &hData->mEvsBase[evTotal];
1353 evTotal += evCounts[fi];
1354 for(ei = 0;ei < evCounts[fi];ei++)
1356 uint azCount = azCounts[(fi * MAX_EV_COUNT) + ei];
1358 hData->mFds[fi].mIrCount += azCount;
1359 hData->mFds[fi].mEvs[ei].mElevation = -M_PI / 2.0 + M_PI * ei / (evCounts[fi] - 1);
1360 hData->mFds[fi].mEvs[ei].mIrCount += azCount;
1361 hData->mFds[fi].mEvs[ei].mAzCount = azCount;
1362 hData->mFds[fi].mEvs[ei].mAzs = &hData->mAzsBase[azTotal];
1363 for(ai = 0;ai < azCount;ai++)
1365 hData->mFds[fi].mEvs[ei].mAzs[ai].mAzimuth = 2.0 * M_PI * ai / azCount;
1366 hData->mFds[fi].mEvs[ei].mAzs[ai].mIndex = azTotal + ai;
1367 hData->mFds[fi].mEvs[ei].mAzs[ai].mDelays[0] = 0.0;
1368 hData->mFds[fi].mEvs[ei].mAzs[ai].mDelays[1] = 0.0;
1369 hData->mFds[fi].mEvs[ei].mAzs[ai].mIrs[0] = nullptr;
1370 hData->mFds[fi].mEvs[ei].mAzs[ai].mIrs[1] = nullptr;
1372 azTotal += azCount;
1375 return 1;
1379 /* Parse the data set definition and process the source data, storing the
1380 * resulting data set as desired. If the input name is NULL it will read
1381 * from standard input.
1383 static int ProcessDefinition(const char *inName, const uint outRate, const ChannelModeT chanMode,
1384 const bool farfield, const uint numThreads, const uint fftSize, const int equalize,
1385 const int surface, const double limit, const uint truncSize, const HeadModelT model,
1386 const double radius, const char *outName)
1388 HrirDataT hData;
1390 fprintf(stdout, "Using %u thread%s.\n", numThreads, (numThreads==1)?"":"s");
1391 if(!inName)
1393 inName = "stdin";
1394 fprintf(stdout, "Reading HRIR definition from %s...\n", inName);
1395 if(!LoadDefInput(std::cin, nullptr, 0, inName, fftSize, truncSize, chanMode, &hData))
1396 return 0;
1398 else
1400 std::unique_ptr<al::ifstream> input{new al::ifstream{inName}};
1401 if(!input->is_open())
1403 fprintf(stderr, "Error: Could not open input file '%s'\n", inName);
1404 return 0;
1407 char startbytes[4]{};
1408 input->read(startbytes, sizeof(startbytes));
1409 std::streamsize startbytecount{input->gcount()};
1410 if(startbytecount != sizeof(startbytes) || !input->good())
1412 fprintf(stderr, "Error: Could not read input file '%s'\n", inName);
1413 return 0;
1416 if(startbytes[0] == '\x89' && startbytes[1] == 'H' && startbytes[2] == 'D'
1417 && startbytes[3] == 'F')
1419 input = nullptr;
1420 fprintf(stdout, "Reading HRTF data from %s...\n", inName);
1421 if(!LoadSofaFile(inName, numThreads, fftSize, truncSize, chanMode, &hData))
1422 return 0;
1424 else
1426 fprintf(stdout, "Reading HRIR definition from %s...\n", inName);
1427 if(!LoadDefInput(*input, startbytes, startbytecount, inName, fftSize, truncSize, chanMode, &hData))
1428 return 0;
1432 if(equalize)
1434 uint c{(hData.mChannelType == CT_STEREO) ? 2u : 1u};
1435 uint m{hData.mFftSize/2u + 1u};
1436 auto dfa = std::vector<double>(c * m);
1438 if(hData.mFdCount > 1)
1440 fprintf(stdout, "Balancing field magnitudes...\n");
1441 BalanceFieldMagnitudes(&hData, c, m);
1443 fprintf(stdout, "Calculating diffuse-field average...\n");
1444 CalculateDiffuseFieldAverage(&hData, c, m, surface, limit, dfa.data());
1445 fprintf(stdout, "Performing diffuse-field equalization...\n");
1446 DiffuseFieldEqualize(c, m, dfa.data(), &hData);
1448 if(hData.mFds.size() > 1)
1450 fprintf(stdout, "Sorting %zu fields...\n", hData.mFds.size());
1451 std::sort(hData.mFds.begin(), hData.mFds.end(),
1452 [](const HrirFdT &lhs, const HrirFdT &rhs) noexcept
1453 { return lhs.mDistance < rhs.mDistance; });
1454 if(farfield)
1456 fprintf(stdout, "Clearing %zu near field%s...\n", hData.mFds.size()-1,
1457 (hData.mFds.size()-1 != 1) ? "s" : "");
1458 hData.mFds.erase(hData.mFds.cbegin(), hData.mFds.cend()-1);
1459 hData.mFdCount = 1;
1462 if(outRate != 0 && outRate != hData.mIrRate)
1464 fprintf(stdout, "Resampling HRIRs...\n");
1465 ResampleHrirs(outRate, &hData);
1467 fprintf(stdout, "Synthesizing missing elevations...\n");
1468 if(model == HM_DATASET)
1469 SynthesizeOnsets(&hData);
1470 SynthesizeHrirs(&hData);
1471 fprintf(stdout, "Performing minimum phase reconstruction...\n");
1472 ReconstructHrirs(&hData, numThreads);
1473 fprintf(stdout, "Truncating minimum-phase HRIRs...\n");
1474 hData.mIrPoints = truncSize;
1475 fprintf(stdout, "Normalizing final HRIRs...\n");
1476 NormalizeHrirs(&hData);
1477 fprintf(stdout, "Calculating impulse delays...\n");
1478 CalculateHrtds(model, (radius > DEFAULT_CUSTOM_RADIUS) ? radius : hData.mRadius, &hData);
1480 const auto rateStr = std::to_string(hData.mIrRate);
1481 const auto expName = StrSubst({outName, strlen(outName)}, {"%r", 2},
1482 {rateStr.data(), rateStr.size()});
1483 fprintf(stdout, "Creating MHR data set %s...\n", expName.c_str());
1484 return StoreMhr(&hData, expName.c_str());
1487 static void PrintHelp(const char *argv0, FILE *ofile)
1489 fprintf(ofile, "Usage: %s [<option>...]\n\n", argv0);
1490 fprintf(ofile, "Options:\n");
1491 fprintf(ofile, " -r <rate> Change the data set sample rate to the specified value and\n");
1492 fprintf(ofile, " resample the HRIRs accordingly.\n");
1493 fprintf(ofile, " -m Change the data set to mono, mirroring the left ear for the\n");
1494 fprintf(ofile, " right ear.\n");
1495 fprintf(ofile, " -a Change the data set to single field, using the farthest field.\n");
1496 fprintf(ofile, " -j <threads> Number of threads used to process HRIRs (default: 2).\n");
1497 fprintf(ofile, " -f <points> Override the FFT window size (default: %u).\n", DEFAULT_FFTSIZE);
1498 fprintf(ofile, " -e {on|off} Toggle diffuse-field equalization (default: %s).\n", (DEFAULT_EQUALIZE ? "on" : "off"));
1499 fprintf(ofile, " -s {on|off} Toggle surface-weighted diffuse-field average (default: %s).\n", (DEFAULT_SURFACE ? "on" : "off"));
1500 fprintf(ofile, " -l {<dB>|none} Specify a limit to the magnitude range of the diffuse-field\n");
1501 fprintf(ofile, " average (default: %.2f).\n", DEFAULT_LIMIT);
1502 fprintf(ofile, " -w <points> Specify the size of the truncation window that's applied\n");
1503 fprintf(ofile, " after minimum-phase reconstruction (default: %u).\n", DEFAULT_TRUNCSIZE);
1504 fprintf(ofile, " -d {dataset| Specify the model used for calculating the head-delay timing\n");
1505 fprintf(ofile, " sphere} values (default: %s).\n", ((DEFAULT_HEAD_MODEL == HM_DATASET) ? "dataset" : "sphere"));
1506 fprintf(ofile, " -c <radius> Use a customized head radius measured to-ear in meters.\n");
1507 fprintf(ofile, " -i <filename> Specify an HRIR definition file to use (defaults to stdin).\n");
1508 fprintf(ofile, " -o <filename> Specify an output file. Use of '%%r' will be substituted with\n");
1509 fprintf(ofile, " the data set sample rate.\n");
1512 // Standard command line dispatch.
1513 int main(int argc, char *argv[])
1515 const char *inName = nullptr, *outName = nullptr;
1516 uint outRate, fftSize;
1517 int equalize, surface;
1518 char *end = nullptr;
1519 ChannelModeT chanMode;
1520 HeadModelT model;
1521 uint numThreads;
1522 uint truncSize;
1523 double radius;
1524 bool farfield;
1525 double limit;
1526 int opt;
1528 if(argc < 2)
1530 fprintf(stdout, "HRTF Processing and Composition Utility\n\n");
1531 PrintHelp(argv[0], stdout);
1532 exit(EXIT_SUCCESS);
1535 outName = "./oalsoft_hrtf_%r.mhr";
1536 outRate = 0;
1537 chanMode = CM_AllowStereo;
1538 fftSize = DEFAULT_FFTSIZE;
1539 equalize = DEFAULT_EQUALIZE;
1540 surface = DEFAULT_SURFACE;
1541 limit = DEFAULT_LIMIT;
1542 numThreads = 2;
1543 truncSize = DEFAULT_TRUNCSIZE;
1544 model = DEFAULT_HEAD_MODEL;
1545 radius = DEFAULT_CUSTOM_RADIUS;
1546 farfield = false;
1548 while((opt=getopt(argc, argv, "r:maj:f:e:s:l:w:d:c:e:i:o:h")) != -1)
1550 switch(opt)
1552 case 'r':
1553 outRate = static_cast<uint>(strtoul(optarg, &end, 10));
1554 if(end[0] != '\0' || outRate < MIN_RATE || outRate > MAX_RATE)
1556 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %u to %u.\n", optarg, opt, MIN_RATE, MAX_RATE);
1557 exit(EXIT_FAILURE);
1559 break;
1561 case 'm':
1562 chanMode = CM_ForceMono;
1563 break;
1565 case 'a':
1566 farfield = true;
1567 break;
1569 case 'j':
1570 numThreads = static_cast<uint>(strtoul(optarg, &end, 10));
1571 if(end[0] != '\0' || numThreads > 64)
1573 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %u to %u.\n", optarg, opt, 0, 64);
1574 exit(EXIT_FAILURE);
1576 if(numThreads == 0)
1577 numThreads = std::thread::hardware_concurrency();
1578 break;
1580 case 'f':
1581 fftSize = static_cast<uint>(strtoul(optarg, &end, 10));
1582 if(end[0] != '\0' || (fftSize&(fftSize-1)) || fftSize < MIN_FFTSIZE || fftSize > MAX_FFTSIZE)
1584 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);
1585 exit(EXIT_FAILURE);
1587 break;
1589 case 'e':
1590 if(strcmp(optarg, "on") == 0)
1591 equalize = 1;
1592 else if(strcmp(optarg, "off") == 0)
1593 equalize = 0;
1594 else
1596 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected on or off.\n", optarg, opt);
1597 exit(EXIT_FAILURE);
1599 break;
1601 case 's':
1602 if(strcmp(optarg, "on") == 0)
1603 surface = 1;
1604 else if(strcmp(optarg, "off") == 0)
1605 surface = 0;
1606 else
1608 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected on or off.\n", optarg, opt);
1609 exit(EXIT_FAILURE);
1611 break;
1613 case 'l':
1614 if(strcmp(optarg, "none") == 0)
1615 limit = 0.0;
1616 else
1618 limit = strtod(optarg, &end);
1619 if(end[0] != '\0' || limit < MIN_LIMIT || limit > MAX_LIMIT)
1621 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %.0f to %.0f.\n", optarg, opt, MIN_LIMIT, MAX_LIMIT);
1622 exit(EXIT_FAILURE);
1625 break;
1627 case 'w':
1628 truncSize = static_cast<uint>(strtoul(optarg, &end, 10));
1629 if(end[0] != '\0' || truncSize < MIN_TRUNCSIZE || truncSize > MAX_TRUNCSIZE)
1631 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %u to %u.\n", optarg, opt, MIN_TRUNCSIZE, MAX_TRUNCSIZE);
1632 exit(EXIT_FAILURE);
1634 break;
1636 case 'd':
1637 if(strcmp(optarg, "dataset") == 0)
1638 model = HM_DATASET;
1639 else if(strcmp(optarg, "sphere") == 0)
1640 model = HM_SPHERE;
1641 else
1643 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected dataset or sphere.\n", optarg, opt);
1644 exit(EXIT_FAILURE);
1646 break;
1648 case 'c':
1649 radius = strtod(optarg, &end);
1650 if(end[0] != '\0' || radius < MIN_CUSTOM_RADIUS || radius > MAX_CUSTOM_RADIUS)
1652 fprintf(stderr, "\nError: Got unexpected value \"%s\" for option -%c, expected between %.2f to %.2f.\n", optarg, opt, MIN_CUSTOM_RADIUS, MAX_CUSTOM_RADIUS);
1653 exit(EXIT_FAILURE);
1655 break;
1657 case 'i':
1658 inName = optarg;
1659 break;
1661 case 'o':
1662 outName = optarg;
1663 break;
1665 case 'h':
1666 PrintHelp(argv[0], stdout);
1667 exit(EXIT_SUCCESS);
1669 default: /* '?' */
1670 PrintHelp(argv[0], stderr);
1671 exit(EXIT_FAILURE);
1675 int ret = ProcessDefinition(inName, outRate, chanMode, farfield, numThreads, fftSize, equalize,
1676 surface, limit, truncSize, model, radius, outName);
1677 if(!ret) return -1;
1678 fprintf(stdout, "Operation completed.\n");
1680 return EXIT_SUCCESS;