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
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
48 * Modeling Interaural Time Difference Assuming a Spherical Head
50 * Music 150, Musical Acoustics, Stanford University
53 * The formulae for calculating the Kaiser window metrics are from the
56 * Discrete-Time Signal Processing
57 * Alan V. Oppenheim and Ronald W. Schafer
58 * Prentice-Hall Signal Processing Series
88 #include "../getopt.h"
91 #include "alfstream.h"
97 #include "win_main_utf8.h"
102 using namespace std::placeholders
;
107 #define M_PI (3.14159265358979323846)
111 // Head model used for calculating the impulse delays.
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
{
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
)
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());
181 while(endpos
< in
.size() && in
[endpos
] != pat
.front())
183 ret
.append(in
.data(), endpos
);
184 in
= in
.subspan(endpos
);
187 ret
.append(in
.data(), in
.size());
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;
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
));
225 /* Fast Fourier transform routines. The number of points must be a power of
229 // Performs bit-reversal ordering.
230 static void FftArrange(const uint n
, complex_d
*inout
)
232 // Handle in-place arrangement.
234 for(uint k
{0u};k
< n
;k
++)
237 std::swap(inout
[rk
], inout
[k
]);
246 // Performs the summation.
247 static void FftSummation(const uint n
, const double s
, complex_d
*cplx
)
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};
262 for(k
= i
;k
< n
;k
+= m2
)
265 auto t
= w
* cplx
[mk
];
266 cplx
[mk
] = cplx
[k
] - t
;
267 cplx
[k
] = cplx
[k
] + t
;
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
);
287 for(uint i
{0};i
< n
;i
++)
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
)
300 // Handle in-place operation.
304 FftInverse(n
, inout
);
305 for(i
= 1;i
< (n
+1)/2;i
++)
307 /* Increment i if n is even. */
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
319 void MagnitudeResponse(const uint n
, const complex_d
*in
, double *out
)
321 const uint m
= 1 + (n
/ 2);
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
331 static void LimitMagnitudeResponse(const uint n
, const uint m
, const double limit
, const double *in
, double *out
)
334 uint i
, lower
, upper
;
337 halfLim
= limit
/ 2.0;
338 // Convert the response to dB.
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;
345 for(i
= lower
;i
<= upper
;i
++)
347 ave
/= upper
- lower
+ 1;
348 // Keep the response within range of the average magnitude.
350 out
[i
] = Clamp(out
[i
], ave
- halfLim
, ave
+ halfLim
);
351 // Convert the response back to linear magnitude.
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
361 static void MinimumPhase(const uint n
, double *mags
, complex_d
*out
)
363 const uint m
{(n
/2) + 1};
367 out
[i
] = std::log(mags
[i
]);
370 mags
[i
] = mags
[n
- i
];
374 // Remove any DC offset the filter has.
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
)
394 if(fwrite(out
, 1, len
, fp
) != len
)
397 fprintf(stderr
, "\nError: Bad write to file '%s'.\n", filename
);
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
)
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
);
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};
430 if((fp
=fopen(filename
, "wb")) == nullptr)
432 fprintf(stderr
, "\nError: Could not open MHR file '%s'.\n", filename
);
435 if(!WriteAscii(MHR_FORMAT
, fp
, filename
))
437 if(!WriteBin4(4, hData
->mIrRate
, fp
, filename
))
439 if(!WriteBin4(1, static_cast<uint32_t>(hData
->mChannelType
), fp
, filename
))
441 if(!WriteBin4(1, hData
->mIrPoints
, fp
, filename
))
443 if(!WriteBin4(1, hData
->mFdCount
, fp
, filename
))
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
))
450 if(!WriteBin4(1, hData
->mFds
[fi
].mEvCount
, fp
, filename
))
452 for(ei
= 0;ei
< hData
->mFds
[fi
].mEvCount
;ei
++)
454 if(!WriteBin4(1, hData
->mFds
[fi
].mEvs
[ei
].mAzCount
, fp
, filename
))
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
))
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;
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
;
522 for(fi
= 0;fi
< hData
->mFdCount
;fi
++)
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
++)
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
++)
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
;
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.
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
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.
605 for(fi
= 0;fi
< hData
->mFdCount
;fi
++)
607 // Normalize the weights given the total surface coverage for all
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
;
626 // Use coverage weighting to calculate the average.
627 CalculateDfWeights(hData
, weights
.data());
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
++)
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.
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.
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
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
++)
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
)
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
)};
724 resampled
[out
] += ir
[in
]*(1.0-a
);
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
)};
746 resampled
[out
] = ir
[in
]*(1.0-a
);
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
,
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
};
802 *a1
= (i
+ 1) % field
.mEvs
[ei
].mAzCount
;
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.
829 /* Take the polar opposite position of the desired measurement and
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
++)
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
);
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
++)
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
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
++)
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
);
909 (1.0-ef
) * (1.0-af0
),
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
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
};
947 for(uint ti
{0u};ti
< channels
;ti
++)
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
};
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
);
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
);
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
++)
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
};
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
);
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
);
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
;
1057 auto h
= std::vector
<complex_d
>(mFftSize
);
1058 auto mags
= std::vector
<double>(mFftSize
);
1059 size_t m
{(mFftSize
/2) + 1};
1063 /* Load the current index to process. */
1064 size_t idx
{mCurrent
.load()};
1066 /* If the index is at the end, we're done. */
1067 if(idx
>= mIrs
.size())
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. */
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
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. */
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());
1135 } while(count
< reconstructor
.mIrs
.size());
1136 fputc('\n', stdout
);
1138 for(auto &thrd
: thrds
)
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)
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
;
1228 dlp
= l
+ (rad
* (al
- std::acos(rad
/ dist
)));
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
],
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
)
1340 hData
->mEvsBase
.resize(evTotal
);
1341 hData
->mAzsBase
.resize(azTotal
);
1342 hData
->mFds
.resize(fdCount
);
1343 hData
->mIrCount
= azTotal
;
1344 hData
->mFdCount
= fdCount
;
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;
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
)
1390 fprintf(stdout
, "Using %u thread%s.\n", numThreads
, (numThreads
==1)?"":"s");
1394 fprintf(stdout
, "Reading HRIR definition from %s...\n", inName
);
1395 if(!LoadDefInput(std::cin
, nullptr, 0, inName
, fftSize
, truncSize
, chanMode
, &hData
))
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
);
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
);
1416 if(startbytes
[0] == '\x89' && startbytes
[1] == 'H' && startbytes
[2] == 'D'
1417 && startbytes
[3] == 'F')
1420 fprintf(stdout
, "Reading HRTF data from %s...\n", inName
);
1421 if(!LoadSofaFile(inName
, numThreads
, fftSize
, truncSize
, chanMode
, &hData
))
1426 fprintf(stdout
, "Reading HRIR definition from %s...\n", inName
);
1427 if(!LoadDefInput(*input
, startbytes
, startbytecount
, inName
, fftSize
, truncSize
, chanMode
, &hData
))
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
; });
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);
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
;
1530 fprintf(stdout
, "HRTF Processing and Composition Utility\n\n");
1531 PrintHelp(argv
[0], stdout
);
1535 outName
= "./oalsoft_hrtf_%r.mhr";
1537 chanMode
= CM_AllowStereo
;
1538 fftSize
= DEFAULT_FFTSIZE
;
1539 equalize
= DEFAULT_EQUALIZE
;
1540 surface
= DEFAULT_SURFACE
;
1541 limit
= DEFAULT_LIMIT
;
1543 truncSize
= DEFAULT_TRUNCSIZE
;
1544 model
= DEFAULT_HEAD_MODEL
;
1545 radius
= DEFAULT_CUSTOM_RADIUS
;
1548 while((opt
=getopt(argc
, argv
, "r:maj:f:e:s:l:w:d:c:e:i:o:h")) != -1)
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
);
1562 chanMode
= CM_ForceMono
;
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);
1577 numThreads
= std::thread::hardware_concurrency();
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
);
1590 if(strcmp(optarg
, "on") == 0)
1592 else if(strcmp(optarg
, "off") == 0)
1596 fprintf(stderr
, "\nError: Got unexpected value \"%s\" for option -%c, expected on or off.\n", optarg
, opt
);
1602 if(strcmp(optarg
, "on") == 0)
1604 else if(strcmp(optarg
, "off") == 0)
1608 fprintf(stderr
, "\nError: Got unexpected value \"%s\" for option -%c, expected on or off.\n", optarg
, opt
);
1614 if(strcmp(optarg
, "none") == 0)
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
);
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
);
1637 if(strcmp(optarg
, "dataset") == 0)
1639 else if(strcmp(optarg
, "sphere") == 0)
1643 fprintf(stderr
, "\nError: Got unexpected value \"%s\" for option -%c, expected dataset or sphere.\n", optarg
, opt
);
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
);
1666 PrintHelp(argv
[0], stdout
);
1670 PrintHelp(argv
[0], stderr
);
1675 int ret
= ProcessDefinition(inName
, outRate
, chanMode
, farfield
, numThreads
, fftSize
, equalize
,
1676 surface
, limit
, truncSize
, model
, radius
, outName
);
1678 fprintf(stdout
, "Operation completed.\n");
1680 return EXIT_SUCCESS
;