Don't modify the RelWithDebInfo flags
[openal-soft.git] / core / hrtf.cpp
blob9833b125bcb1013c58f8a9bf8beb940145c9604d
2 #include "config.h"
4 #include "hrtf.h"
6 #include <algorithm>
7 #include <array>
8 #include <cassert>
9 #include <cctype>
10 #include <cmath>
11 #include <cstddef>
12 #include <cstdint>
13 #include <cstdio>
14 #include <cstring>
15 #include <filesystem>
16 #include <fstream>
17 #include <iterator>
18 #include <memory>
19 #include <mutex>
20 #include <numeric>
21 #include <optional>
22 #include <tuple>
23 #include <type_traits>
24 #include <utility>
25 #include <vector>
27 #include "albit.h"
28 #include "almalloc.h"
29 #include "alnumbers.h"
30 #include "alnumeric.h"
31 #include "alspan.h"
32 #include "alstring.h"
33 #include "ambidefs.h"
34 #include "filters/splitter.h"
35 #include "helpers.h"
36 #include "logging.h"
37 #include "mixer/hrtfdefs.h"
38 #include "opthelpers.h"
39 #include "polyphase_resampler.h"
42 namespace {
44 using namespace std::string_view_literals;
46 struct HrtfEntry {
47 std::string mDispName;
48 std::string mFilename;
50 template<typename T, typename U>
51 HrtfEntry(T&& dispname, U&& fname)
52 : mDispName{std::forward<T>(dispname)}, mFilename{std::forward<U>(fname)}
53 { }
54 /* GCC warns when it tries to inline this. */
55 ~HrtfEntry();
57 HrtfEntry::~HrtfEntry() = default;
59 struct LoadedHrtf {
60 std::string mFilename;
61 uint mSampleRate{};
62 std::unique_ptr<HrtfStore> mEntry;
64 template<typename T, typename U>
65 LoadedHrtf(T&& name, uint srate, U&& entry)
66 : mFilename{std::forward<T>(name)}, mSampleRate{srate}, mEntry{std::forward<U>(entry)}
67 { }
68 LoadedHrtf(LoadedHrtf&&) = default;
69 /* GCC warns when it tries to inline this. */
70 ~LoadedHrtf();
72 LoadedHrtf& operator=(LoadedHrtf&&) = default;
74 LoadedHrtf::~LoadedHrtf() = default;
77 /* Data set limits must be the same as or more flexible than those defined in
78 * the makemhr utility.
80 constexpr uint MinFdCount{1};
81 constexpr uint MaxFdCount{16};
83 constexpr uint MinFdDistance{50};
84 constexpr uint MaxFdDistance{2500};
86 constexpr uint MinEvCount{5};
87 constexpr uint MaxEvCount{181};
89 constexpr uint MinAzCount{1};
90 constexpr uint MaxAzCount{255};
92 constexpr uint MaxHrirDelay{HrtfHistoryLength - 1};
94 constexpr uint HrirDelayFracBits{2};
95 constexpr uint HrirDelayFracOne{1 << HrirDelayFracBits};
96 constexpr uint HrirDelayFracHalf{HrirDelayFracOne >> 1};
98 /* The sample rate is stored as a 24-bit integer, so 16MHz is the largest
99 * supported.
101 constexpr uint MaxSampleRate{0xff'ff'ff};
103 static_assert(MaxHrirDelay*HrirDelayFracOne < 256, "MAX_HRIR_DELAY or DELAY_FRAC too large");
106 [[nodiscard]] constexpr auto GetMarker00Name() noexcept { return "MinPHR00"sv; }
107 [[nodiscard]] constexpr auto GetMarker01Name() noexcept { return "MinPHR01"sv; }
108 [[nodiscard]] constexpr auto GetMarker02Name() noexcept { return "MinPHR02"sv; }
109 [[nodiscard]] constexpr auto GetMarker03Name() noexcept { return "MinPHR03"sv; }
112 /* First value for pass-through coefficients (remaining are 0), used for omni-
113 * directional sounds. */
114 constexpr auto PassthruCoeff = static_cast<float>(1.0/al::numbers::sqrt2);
116 std::mutex LoadedHrtfLock;
117 std::vector<LoadedHrtf> LoadedHrtfs;
119 std::mutex EnumeratedHrtfLock;
120 std::vector<HrtfEntry> EnumeratedHrtfs;
123 /* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
124 * To access a memory buffer through the std::istream interface, a custom
125 * std::streambuf implementation is needed that has to do pointer manipulation
126 * for seeking. With C++23, we may be able to use std::spanstream instead.
128 class databuf final : public std::streambuf {
129 int_type underflow() override
130 { return traits_type::eof(); }
132 pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override
134 if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
135 return traits_type::eof();
137 switch(whence)
139 case std::ios_base::beg:
140 if(offset < 0 || offset > egptr()-eback())
141 return traits_type::eof();
142 setg(eback(), eback()+offset, egptr());
143 break;
145 case std::ios_base::cur:
146 if((offset >= 0 && offset > egptr()-gptr()) ||
147 (offset < 0 && -offset > gptr()-eback()))
148 return traits_type::eof();
149 setg(eback(), gptr()+offset, egptr());
150 break;
152 case std::ios_base::end:
153 if(offset > 0 || -offset > egptr()-eback())
154 return traits_type::eof();
155 setg(eback(), egptr()+offset, egptr());
156 break;
158 default:
159 return traits_type::eof();
162 return gptr() - eback();
165 pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override
167 // Simplified version of seekoff
168 if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
169 return traits_type::eof();
171 if(pos < 0 || pos > egptr()-eback())
172 return traits_type::eof();
174 setg(eback(), eback()+static_cast<size_t>(pos), egptr());
175 return pos;
178 public:
179 databuf(const al::span<char_type> data) noexcept
181 setg(data.data(), data.data(), al::to_address(data.end()));
184 /* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */
186 class idstream final : public std::istream {
187 databuf mStreamBuf;
189 public:
190 idstream(const al::span<char_type> data) : std::istream{nullptr}, mStreamBuf{data}
191 { init(&mStreamBuf); }
195 struct IdxBlend { uint idx; float blend; };
196 /* Calculate the elevation index given the polar elevation in radians. This
197 * will return an index between 0 and (evcount - 1).
199 IdxBlend CalcEvIndex(uint evcount, float ev)
201 ev = (al::numbers::pi_v<float>*0.5f + ev) * static_cast<float>(evcount-1) *
202 al::numbers::inv_pi_v<float>;
203 uint idx{float2uint(ev)};
205 return IdxBlend{std::min(idx, evcount-1u), ev-static_cast<float>(idx)};
208 /* Calculate the azimuth index given the polar azimuth in radians. This will
209 * return an index between 0 and (azcount - 1).
211 IdxBlend CalcAzIndex(uint azcount, float az)
213 az = (al::numbers::pi_v<float>*2.0f + az) * static_cast<float>(azcount) *
214 (al::numbers::inv_pi_v<float>*0.5f);
215 uint idx{float2uint(az)};
217 return IdxBlend{idx%azcount, az-static_cast<float>(idx)};
220 } // namespace
223 /* Calculates static HRIR coefficients and delays for the given polar elevation
224 * and azimuth in radians. The coefficients are normalized.
226 void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float spread,
227 const HrirSpan coeffs, const al::span<uint,2> delays) const
229 const float dirfact{1.0f - (al::numbers::inv_pi_v<float>/2.0f * spread)};
231 size_t ebase{0};
232 auto match_field = [&ebase,distance](const Field &field) noexcept -> bool
234 if(distance >= field.distance)
235 return true;
236 ebase += field.evCount;
237 return false;
239 auto field = std::find_if(mFields.begin(), mFields.end()-1, match_field);
241 /* Calculate the elevation indices. */
242 const auto elev0 = CalcEvIndex(field->evCount, elevation);
243 const size_t elev1_idx{std::min(elev0.idx+1u, field->evCount-1u)};
244 const size_t ir0offset{mElev[ebase + elev0.idx].irOffset};
245 const size_t ir1offset{mElev[ebase + elev1_idx].irOffset};
247 /* Calculate azimuth indices. */
248 const auto az0 = CalcAzIndex(mElev[ebase + elev0.idx].azCount, azimuth);
249 const auto az1 = CalcAzIndex(mElev[ebase + elev1_idx].azCount, azimuth);
251 /* Calculate the HRIR indices to blend. */
252 const std::array<size_t,4> idx{{
253 ir0offset + az0.idx,
254 ir0offset + ((az0.idx+1) % mElev[ebase + elev0.idx].azCount),
255 ir1offset + az1.idx,
256 ir1offset + ((az1.idx+1) % mElev[ebase + elev1_idx].azCount)
259 /* Calculate bilinear blending weights, attenuated according to the
260 * directional panning factor.
262 const std::array<float,4> blend{{
263 (1.0f-elev0.blend) * (1.0f-az0.blend) * dirfact,
264 (1.0f-elev0.blend) * ( az0.blend) * dirfact,
265 ( elev0.blend) * (1.0f-az1.blend) * dirfact,
266 ( elev0.blend) * ( az1.blend) * dirfact
269 /* Calculate the blended HRIR delays. */
270 float d{float(mDelays[idx[0]][0])*blend[0] + float(mDelays[idx[1]][0])*blend[1]
271 + float(mDelays[idx[2]][0])*blend[2] + float(mDelays[idx[3]][0])*blend[3]};
272 delays[0] = fastf2u(d * float{1.0f/HrirDelayFracOne});
273 d = float(mDelays[idx[0]][1])*blend[0] + float(mDelays[idx[1]][1])*blend[1]
274 + float(mDelays[idx[2]][1])*blend[2] + float(mDelays[idx[3]][1])*blend[3];
275 delays[1] = fastf2u(d * float{1.0f/HrirDelayFracOne});
277 /* Calculate the blended HRIR coefficients. */
278 auto coeffout = coeffs.begin();
279 coeffout[0][0] = PassthruCoeff * (1.0f-dirfact);
280 coeffout[0][1] = PassthruCoeff * (1.0f-dirfact);
281 std::fill_n(coeffout+1, size_t{HrirLength-1}, std::array{0.0f, 0.0f});
282 for(size_t c{0};c < 4;c++)
284 const float mult{blend[c]};
285 auto blend_coeffs = [mult](const float2 &src, const float2 &coeff) noexcept -> float2
286 { return float2{{src[0]*mult + coeff[0], src[1]*mult + coeff[1]}}; };
287 std::transform(mCoeffs[idx[c]].cbegin(), mCoeffs[idx[c]].cend(), coeffout, coeffout,
288 blend_coeffs);
293 std::unique_ptr<DirectHrtfState> DirectHrtfState::Create(size_t num_chans)
294 { return std::unique_ptr<DirectHrtfState>{new(FamCount(num_chans)) DirectHrtfState{num_chans}}; }
296 void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool perHrirMin,
297 const al::span<const AngularPoint> AmbiPoints,
298 const al::span<const std::array<float,MaxAmbiChannels>> AmbiMatrix,
299 const float XOverFreq, const al::span<const float,MaxAmbiOrder+1> AmbiOrderHFGain)
301 using double2 = std::array<double,2>;
302 struct ImpulseResponse {
303 const ConstHrirSpan hrir;
304 uint ldelay, rdelay;
307 const double xover_norm{double{XOverFreq} / Hrtf->mSampleRate};
308 mChannels[0].mSplitter.init(static_cast<float>(xover_norm));
309 mChannels[0].mHfScale = AmbiOrderHFGain[0];
310 for(size_t i{1};i < mChannels.size();++i)
312 const size_t order{AmbiIndex::OrderFromChannel[i]};
313 mChannels[i].mSplitter = mChannels[0].mSplitter;
314 mChannels[i].mHfScale = AmbiOrderHFGain[order];
317 uint min_delay{HrtfHistoryLength*HrirDelayFracOne}, max_delay{0};
318 std::vector<ImpulseResponse> impres; impres.reserve(AmbiPoints.size());
319 auto calc_res = [Hrtf,&max_delay,&min_delay](const AngularPoint &pt) -> ImpulseResponse
321 auto &field = Hrtf->mFields[0];
322 const auto elev0 = CalcEvIndex(field.evCount, pt.Elev.value);
323 const size_t elev1_idx{std::min(elev0.idx+1u, field.evCount-1u)};
324 const size_t ir0offset{Hrtf->mElev[elev0.idx].irOffset};
325 const size_t ir1offset{Hrtf->mElev[elev1_idx].irOffset};
327 const auto az0 = CalcAzIndex(Hrtf->mElev[elev0.idx].azCount, pt.Azim.value);
328 const auto az1 = CalcAzIndex(Hrtf->mElev[elev1_idx].azCount, pt.Azim.value);
330 const std::array<size_t,4> idx{
331 ir0offset + az0.idx,
332 ir0offset + ((az0.idx+1) % Hrtf->mElev[elev0.idx].azCount),
333 ir1offset + az1.idx,
334 ir1offset + ((az1.idx+1) % Hrtf->mElev[elev1_idx].azCount)
337 /* The largest blend factor serves as the closest HRIR. */
338 const size_t irOffset{idx[(elev0.blend >= 0.5f)*2 + (az1.blend >= 0.5f)]};
339 ImpulseResponse res{Hrtf->mCoeffs[irOffset],
340 Hrtf->mDelays[irOffset][0], Hrtf->mDelays[irOffset][1]};
342 min_delay = std::min(min_delay, std::min(res.ldelay, res.rdelay));
343 max_delay = std::max(max_delay, std::max(res.ldelay, res.rdelay));
345 return res;
347 std::transform(AmbiPoints.begin(), AmbiPoints.end(), std::back_inserter(impres), calc_res);
348 auto hrir_delay_round = [](const uint d) noexcept -> uint
349 { return (d+HrirDelayFracHalf) >> HrirDelayFracBits; };
351 TRACE("Min delay: %.2f, max delay: %.2f, FIR length: %u\n",
352 min_delay/double{HrirDelayFracOne}, max_delay/double{HrirDelayFracOne}, irSize);
354 auto tmpres = std::vector<std::array<double2,HrirLength>>(mChannels.size());
355 max_delay = 0;
356 auto matrixline = AmbiMatrix.cbegin();
357 for(auto &impulse : impres)
359 const ConstHrirSpan hrir{impulse.hrir};
360 const uint base_delay{perHrirMin ? std::min(impulse.ldelay, impulse.rdelay) : min_delay};
361 const uint ldelay{hrir_delay_round(impulse.ldelay - base_delay)};
362 const uint rdelay{hrir_delay_round(impulse.rdelay - base_delay)};
363 max_delay = std::max(max_delay, std::max(impulse.ldelay, impulse.rdelay) - base_delay);
365 auto gains = matrixline->cbegin();
366 ++matrixline;
367 for(auto &result : tmpres)
369 const double mult{*(gains++)};
370 const size_t numirs{HrirLength - std::max(ldelay, rdelay)};
371 size_t lidx{ldelay}, ridx{rdelay};
372 for(size_t j{0};j < numirs;++j)
374 result[lidx++][0] += hrir[j][0] * mult;
375 result[ridx++][1] += hrir[j][1] * mult;
379 impres.clear();
381 auto output = mChannels.begin();
382 for(auto &result : tmpres)
384 auto cast_array2 = [](const double2 &in) noexcept -> float2
385 { return float2{{static_cast<float>(in[0]), static_cast<float>(in[1])}}; };
386 std::transform(result.cbegin(), result.cend(), output->mCoeffs.begin(), cast_array2);
387 ++output;
389 tmpres.clear();
391 const uint max_length{std::min(hrir_delay_round(max_delay) + irSize, HrirLength)};
392 TRACE("New max delay: %.2f, FIR length: %u\n", max_delay/double{HrirDelayFracOne},
393 max_length);
394 mIrSize = max_length;
398 namespace {
400 std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize,
401 const al::span<const HrtfStore::Field> fields,
402 const al::span<const HrtfStore::Elevation> elevs, const HrirArray *coeffs,
403 const ubyte2 *delays)
405 static_assert(alignof(HrtfStore::Field) <= alignof(HrtfStore));
406 static_assert(alignof(HrtfStore::Elevation) <= alignof(HrtfStore));
407 static_assert(16 <= alignof(HrtfStore));
409 if(rate > MaxSampleRate)
410 throw std::runtime_error{"Sample rate is too large (max: "+std::to_string(MaxSampleRate)+"hz)"};
412 const size_t irCount{size_t{elevs.back().azCount} + elevs.back().irOffset};
413 size_t total{sizeof(HrtfStore)};
414 total = RoundUp(total, alignof(HrtfStore::Field)); /* Align for field infos */
415 total += sizeof(std::declval<HrtfStore&>().mFields[0])*fields.size();
416 total = RoundUp(total, alignof(HrtfStore::Elevation)); /* Align for elevation infos */
417 total += sizeof(std::declval<HrtfStore&>().mElev[0])*elevs.size();
418 total = RoundUp(total, 16); /* Align for coefficients using SIMD */
419 total += sizeof(std::declval<HrtfStore&>().mCoeffs[0])*irCount;
420 total += sizeof(std::declval<HrtfStore&>().mDelays[0])*irCount;
422 static constexpr auto AlignVal = std::align_val_t{alignof(HrtfStore)};
423 std::unique_ptr<HrtfStore> Hrtf{::new(::operator new[](total, AlignVal)) HrtfStore{}};
424 Hrtf->mRef.store(1u, std::memory_order_relaxed);
425 Hrtf->mSampleRate = rate & 0xff'ff'ff;
426 Hrtf->mIrSize = irSize;
428 /* Set up pointers to storage following the main HRTF struct. */
429 auto storage = al::span{reinterpret_cast<char*>(Hrtf.get()), total};
430 auto base = storage.begin();
431 ptrdiff_t offset{sizeof(HrtfStore)};
433 offset = RoundUp(offset, alignof(HrtfStore::Field)); /* Align for field infos */
434 auto field_ = al::span{reinterpret_cast<HrtfStore::Field*>(al::to_address(base + offset)),
435 fields.size()};
436 offset += ptrdiff_t(sizeof(field_[0])*fields.size());
438 offset = RoundUp(offset, alignof(HrtfStore::Elevation)); /* Align for elevation infos */
439 auto elev_ = al::span{reinterpret_cast<HrtfStore::Elevation*>(al::to_address(base + offset)),
440 elevs.size()};
441 offset += ptrdiff_t(sizeof(elev_[0])*elevs.size());
443 offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */
444 auto coeffs_ = al::span{reinterpret_cast<HrirArray*>(al::to_address(base + offset)), irCount};
445 offset += ptrdiff_t(sizeof(coeffs_[0])*irCount);
447 auto delays_ = al::span{reinterpret_cast<ubyte2*>(al::to_address(base + offset)), irCount};
448 offset += ptrdiff_t(sizeof(delays_[0])*irCount);
450 if(size_t(offset) != total)
451 throw std::runtime_error{"HrtfStore allocation size mismatch"};
453 /* Copy input data to storage. */
454 std::uninitialized_copy(fields.cbegin(), fields.cend(), field_.begin());
455 std::uninitialized_copy(elevs.cbegin(), elevs.cend(), elev_.begin());
456 std::uninitialized_copy_n(coeffs, irCount, coeffs_.begin());
457 std::uninitialized_copy_n(delays, irCount, delays_.begin());
459 /* Finally, assign the storage pointers. */
460 Hrtf->mFields = field_;
461 Hrtf->mElev = elev_;
462 Hrtf->mCoeffs = coeffs_;
463 Hrtf->mDelays = delays_;
465 return Hrtf;
468 void MirrorLeftHrirs(const al::span<const HrtfStore::Elevation> elevs, al::span<HrirArray> coeffs,
469 al::span<ubyte2> delays)
471 for(const auto &elev : elevs)
473 const ushort evoffset{elev.irOffset};
474 const ushort azcount{elev.azCount};
475 for(size_t j{0};j < azcount;j++)
477 const size_t lidx{evoffset + j};
478 const size_t ridx{evoffset + ((azcount-j) % azcount)};
480 const size_t irSize{coeffs[ridx].size()};
481 for(size_t k{0};k < irSize;k++)
482 coeffs[ridx][k][1] = coeffs[lidx][k][0];
483 delays[ridx][1] = delays[lidx][0];
489 template<size_t num_bits, typename T>
490 constexpr std::enable_if_t<std::is_signed<T>::value && num_bits < sizeof(T)*8,
491 T> fixsign(T value) noexcept
493 constexpr auto signbit = static_cast<T>(1u << (num_bits-1));
494 return static_cast<T>((value^signbit) - signbit);
497 template<size_t num_bits, typename T>
498 constexpr std::enable_if_t<!std::is_signed<T>::value || num_bits == sizeof(T)*8,
499 T> fixsign(T value) noexcept
500 { return value; }
502 template<typename T, size_t num_bits=sizeof(T)*8>
503 inline std::enable_if_t<al::endian::native == al::endian::little,
504 T> readle(std::istream &data)
506 static_assert((num_bits&7) == 0, "num_bits must be a multiple of 8");
507 static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type");
509 alignas(T) std::array<char,sizeof(T)> ret{};
510 if(!data.read(ret.data(), num_bits/8))
511 return static_cast<T>(EOF);
513 return fixsign<num_bits>(al::bit_cast<T>(ret));
516 template<typename T, size_t num_bits=sizeof(T)*8>
517 inline std::enable_if_t<al::endian::native == al::endian::big,
518 T> readle(std::istream &data)
520 static_assert((num_bits&7) == 0, "num_bits must be a multiple of 8");
521 static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type");
523 alignas(T) std::array<char,sizeof(T)> ret{};
524 if(!data.read(ret.data(), num_bits/8))
525 return static_cast<T>(EOF);
526 std::reverse(ret.begin(), ret.end());
528 return fixsign<num_bits>(al::bit_cast<T>(ret));
531 template<>
532 inline uint8_t readle<uint8_t,8>(std::istream &data)
533 { return static_cast<uint8_t>(data.get()); }
536 std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data)
538 uint rate{readle<uint32_t>(data)};
539 ushort irCount{readle<uint16_t>(data)};
540 ushort irSize{readle<uint16_t>(data)};
541 ubyte evCount{readle<uint8_t>(data)};
542 if(!data || data.eof())
543 throw std::runtime_error{"Premature end of file"};
545 if(irSize < MinIrLength || irSize > HrirLength)
547 ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength);
548 return nullptr;
550 if(evCount < MinEvCount || evCount > MaxEvCount)
552 ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
553 evCount, MinEvCount, MaxEvCount);
554 return nullptr;
557 auto elevs = std::vector<HrtfStore::Elevation>(evCount);
558 for(auto &elev : elevs)
559 elev.irOffset = readle<uint16_t>(data);
560 if(!data || data.eof())
561 throw std::runtime_error{"Premature end of file"};
563 for(size_t i{1};i < evCount;i++)
565 if(elevs[i].irOffset <= elevs[i-1].irOffset)
567 ERR("Invalid evOffset: evOffset[%zu]=%d (last=%d)\n", i, elevs[i].irOffset,
568 elevs[i-1].irOffset);
569 return nullptr;
572 if(irCount <= elevs.back().irOffset)
574 ERR("Invalid evOffset: evOffset[%zu]=%d (irCount=%d)\n",
575 elevs.size()-1, elevs.back().irOffset, irCount);
576 return nullptr;
579 for(size_t i{1};i < evCount;i++)
581 elevs[i-1].azCount = static_cast<ushort>(elevs[i].irOffset - elevs[i-1].irOffset);
582 if(elevs[i-1].azCount < MinAzCount || elevs[i-1].azCount > MaxAzCount)
584 ERR("Unsupported azimuth count: azCount[%zd]=%d (%d to %d)\n",
585 i-1, elevs[i-1].azCount, MinAzCount, MaxAzCount);
586 return nullptr;
589 elevs.back().azCount = static_cast<ushort>(irCount - elevs.back().irOffset);
590 if(elevs.back().azCount < MinAzCount || elevs.back().azCount > MaxAzCount)
592 ERR("Unsupported azimuth count: azCount[%zu]=%d (%d to %d)\n",
593 elevs.size()-1, elevs.back().azCount, MinAzCount, MaxAzCount);
594 return nullptr;
597 auto coeffs = std::vector<HrirArray>(irCount, HrirArray{});
598 auto delays = std::vector<ubyte2>(irCount);
599 for(auto &hrir : coeffs)
601 for(auto &val : al::span{hrir}.first(irSize))
602 val[0] = float(readle<int16_t>(data)) / 32768.0f;
604 for(auto &val : delays)
605 val[0] = readle<uint8_t>(data);
606 if(!data || data.eof())
607 throw std::runtime_error{"Premature end of file"};
609 for(size_t i{0};i < irCount;i++)
611 if(delays[i][0] > MaxHrirDelay)
613 ERR("Invalid delays[%zd]: %d (%d)\n", i, delays[i][0], MaxHrirDelay);
614 return nullptr;
616 delays[i][0] <<= HrirDelayFracBits;
619 /* Mirror the left ear responses to the right ear. */
620 MirrorLeftHrirs(elevs, coeffs, delays);
622 const std::array field{HrtfStore::Field{0.0f, evCount}};
623 return CreateHrtfStore(rate, static_cast<uint8_t>(irSize), field, elevs, coeffs.data(),
624 delays.data());
627 std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data)
629 uint rate{readle<uint32_t>(data)};
630 uint8_t irSize{readle<uint8_t>(data)};
631 ubyte evCount{readle<uint8_t>(data)};
632 if(!data || data.eof())
633 throw std::runtime_error{"Premature end of file"};
635 if(irSize < MinIrLength || irSize > HrirLength)
637 ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength);
638 return nullptr;
640 if(evCount < MinEvCount || evCount > MaxEvCount)
642 ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
643 evCount, MinEvCount, MaxEvCount);
644 return nullptr;
647 auto elevs = std::vector<HrtfStore::Elevation>(evCount);
648 for(auto &elev : elevs)
649 elev.azCount = readle<uint8_t>(data);
650 if(!data || data.eof())
651 throw std::runtime_error{"Premature end of file"};
653 for(size_t i{0};i < evCount;++i)
655 if(elevs[i].azCount < MinAzCount || elevs[i].azCount > MaxAzCount)
657 ERR("Unsupported azimuth count: azCount[%zd]=%d (%d to %d)\n", i, elevs[i].azCount,
658 MinAzCount, MaxAzCount);
659 return nullptr;
663 elevs[0].irOffset = 0;
664 for(size_t i{1};i < evCount;i++)
665 elevs[i].irOffset = static_cast<ushort>(elevs[i-1].irOffset + elevs[i-1].azCount);
666 const ushort irCount{static_cast<ushort>(elevs.back().irOffset + elevs.back().azCount)};
668 auto coeffs = std::vector<HrirArray>(irCount, HrirArray{});
669 auto delays = std::vector<ubyte2>(irCount);
670 for(auto &hrir : coeffs)
672 for(auto &val : al::span{hrir}.first(irSize))
673 val[0] = float(readle<int16_t>(data)) / 32768.0f;
675 for(auto &val : delays)
676 val[0] = readle<uint8_t>(data);
677 if(!data || data.eof())
678 throw std::runtime_error{"Premature end of file"};
680 for(size_t i{0};i < irCount;i++)
682 if(delays[i][0] > MaxHrirDelay)
684 ERR("Invalid delays[%zd]: %d (%d)\n", i, delays[i][0], MaxHrirDelay);
685 return nullptr;
687 delays[i][0] <<= HrirDelayFracBits;
690 /* Mirror the left ear responses to the right ear. */
691 MirrorLeftHrirs(elevs, coeffs, delays);
693 const std::array field{HrtfStore::Field{0.0f, evCount}};
694 return CreateHrtfStore(rate, irSize, field, elevs, coeffs.data(), delays.data());
697 std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data)
699 static constexpr ubyte SampleType_S16{0};
700 static constexpr ubyte SampleType_S24{1};
701 static constexpr ubyte ChanType_LeftOnly{0};
702 static constexpr ubyte ChanType_LeftRight{1};
704 uint rate{readle<uint32_t>(data)};
705 ubyte sampleType{readle<uint8_t>(data)};
706 ubyte channelType{readle<uint8_t>(data)};
707 uint8_t irSize{readle<uint8_t>(data)};
708 ubyte fdCount{readle<uint8_t>(data)};
709 if(!data || data.eof())
710 throw std::runtime_error{"Premature end of file"};
712 if(sampleType > SampleType_S24)
714 ERR("Unsupported sample type: %d\n", sampleType);
715 return nullptr;
717 if(channelType > ChanType_LeftRight)
719 ERR("Unsupported channel type: %d\n", channelType);
720 return nullptr;
723 if(irSize < MinIrLength || irSize > HrirLength)
725 ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength);
726 return nullptr;
728 if(fdCount < 1 || fdCount > MaxFdCount)
730 ERR("Unsupported number of field-depths: fdCount=%d (%d to %d)\n", fdCount, MinFdCount,
731 MaxFdCount);
732 return nullptr;
735 auto fields = std::vector<HrtfStore::Field>(fdCount);
736 auto elevs = std::vector<HrtfStore::Elevation>{};
737 for(size_t f{0};f < fdCount;f++)
739 const ushort distance{readle<uint16_t>(data)};
740 const ubyte evCount{readle<uint8_t>(data)};
741 if(!data || data.eof())
742 throw std::runtime_error{"Premature end of file"};
744 if(distance < MinFdDistance || distance > MaxFdDistance)
746 ERR("Unsupported field distance[%zu]=%d (%d to %d millimeters)\n", f, distance,
747 MinFdDistance, MaxFdDistance);
748 return nullptr;
750 if(evCount < MinEvCount || evCount > MaxEvCount)
752 ERR("Unsupported elevation count: evCount[%zu]=%d (%d to %d)\n", f, evCount,
753 MinEvCount, MaxEvCount);
754 return nullptr;
757 fields[f].distance = float(distance) / 1000.0f;
758 fields[f].evCount = evCount;
759 if(f > 0 && fields[f].distance <= fields[f-1].distance)
761 ERR("Field distance[%zu] is not after previous (%f > %f)\n", f, fields[f].distance,
762 fields[f-1].distance);
763 return nullptr;
766 const size_t ebase{elevs.size()};
767 elevs.resize(ebase + evCount);
768 for(auto &elev : al::span{elevs}.subspan(ebase, evCount))
769 elev.azCount = readle<uint8_t>(data);
770 if(!data || data.eof())
771 throw std::runtime_error{"Premature end of file"};
773 for(size_t e{0};e < evCount;e++)
775 if(elevs[ebase+e].azCount < MinAzCount || elevs[ebase+e].azCount > MaxAzCount)
777 ERR("Unsupported azimuth count: azCount[%zu][%zu]=%d (%d to %d)\n", f, e,
778 elevs[ebase+e].azCount, MinAzCount, MaxAzCount);
779 return nullptr;
784 elevs[0].irOffset = 0;
785 std::partial_sum(elevs.cbegin(), elevs.cend(), elevs.begin(),
786 [](const HrtfStore::Elevation &last, const HrtfStore::Elevation &cur)
787 -> HrtfStore::Elevation
789 return HrtfStore::Elevation{cur.azCount,
790 static_cast<ushort>(last.azCount + last.irOffset)};
792 const auto irTotal = static_cast<ushort>(elevs.back().azCount + elevs.back().irOffset);
794 auto coeffs = std::vector<HrirArray>(irTotal, HrirArray{});
795 auto delays = std::vector<ubyte2>(irTotal);
796 if(channelType == ChanType_LeftOnly)
798 if(sampleType == SampleType_S16)
800 for(auto &hrir : coeffs)
802 for(auto &val : al::span{hrir}.first(irSize))
803 val[0] = float(readle<int16_t>(data)) / 32768.0f;
806 else if(sampleType == SampleType_S24)
808 for(auto &hrir : coeffs)
810 for(auto &val : al::span{hrir}.first(irSize))
811 val[0] = static_cast<float>(readle<int,24>(data)) / 8388608.0f;
814 for(auto &val : delays)
815 val[0] = readle<uint8_t>(data);
816 if(!data || data.eof())
817 throw std::runtime_error{"Premature end of file"};
819 for(size_t i{0};i < irTotal;++i)
821 if(delays[i][0] > MaxHrirDelay)
823 ERR("Invalid delays[%zu][0]: %d (%d)\n", i, delays[i][0], MaxHrirDelay);
824 return nullptr;
826 delays[i][0] <<= HrirDelayFracBits;
829 /* Mirror the left ear responses to the right ear. */
830 MirrorLeftHrirs(elevs, coeffs, delays);
832 else if(channelType == ChanType_LeftRight)
834 if(sampleType == SampleType_S16)
836 for(auto &hrir : coeffs)
838 for(auto &val : al::span{hrir}.first(irSize))
840 val[0] = float(readle<int16_t>(data)) / 32768.0f;
841 val[1] = float(readle<int16_t>(data)) / 32768.0f;
845 else if(sampleType == SampleType_S24)
847 for(auto &hrir : coeffs)
849 for(auto &val : al::span{hrir}.first(irSize))
851 val[0] = static_cast<float>(readle<int,24>(data)) / 8388608.0f;
852 val[1] = static_cast<float>(readle<int,24>(data)) / 8388608.0f;
856 for(auto &val : delays)
858 val[0] = readle<uint8_t>(data);
859 val[1] = readle<uint8_t>(data);
861 if(!data || data.eof())
862 throw std::runtime_error{"Premature end of file"};
864 for(size_t i{0};i < irTotal;++i)
866 if(delays[i][0] > MaxHrirDelay)
868 ERR("Invalid delays[%zu][0]: %d (%d)\n", i, delays[i][0], MaxHrirDelay);
869 return nullptr;
871 if(delays[i][1] > MaxHrirDelay)
873 ERR("Invalid delays[%zu][1]: %d (%d)\n", i, delays[i][1], MaxHrirDelay);
874 return nullptr;
876 delays[i][0] <<= HrirDelayFracBits;
877 delays[i][1] <<= HrirDelayFracBits;
881 if(fdCount > 1)
883 auto fields_ = std::vector<HrtfStore::Field>(fields.size());
884 auto elevs_ = std::vector<HrtfStore::Elevation>(elevs.size());
885 auto coeffs_ = std::vector<HrirArray>(coeffs.size());
886 auto delays_ = std::vector<ubyte2>(delays.size());
888 /* Simple reverse for the per-field elements. */
889 std::reverse_copy(fields.cbegin(), fields.cend(), fields_.begin());
891 /* Each field has a group of elevations, which each have an azimuth
892 * count. Reverse the order of the groups, keeping the relative order
893 * of per-group azimuth counts.
895 auto elevs_end = elevs_.end();
896 auto copy_azs = [&elevs,&elevs_end](const ptrdiff_t ebase, const HrtfStore::Field &field)
897 -> ptrdiff_t
899 auto elevs_src = elevs.begin()+ebase;
900 elevs_end = std::copy_backward(elevs_src, elevs_src+field.evCount, elevs_end);
901 return ebase + field.evCount;
903 std::ignore = std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_azs);
904 assert(elevs_.begin() == elevs_end);
906 /* Reestablish the IR offset for each elevation index, given the new
907 * ordering of elevations.
909 elevs_[0].irOffset = 0;
910 std::partial_sum(elevs_.cbegin(), elevs_.cend(), elevs_.begin(),
911 [](const HrtfStore::Elevation &last, const HrtfStore::Elevation &cur)
912 -> HrtfStore::Elevation
914 return HrtfStore::Elevation{cur.azCount,
915 static_cast<ushort>(last.azCount + last.irOffset)};
918 /* Reverse the order of each field's group of IRs. */
919 auto coeffs_end = coeffs_.end();
920 auto delays_end = delays_.end();
921 auto copy_irs = [&elevs,&coeffs,&delays,&coeffs_end,&delays_end](
922 const ptrdiff_t ebase, const HrtfStore::Field &field) -> ptrdiff_t
924 auto accum_az = [](const ptrdiff_t count, const HrtfStore::Elevation &elev) noexcept
925 -> ptrdiff_t
926 { return count + elev.azCount; };
927 const auto elev_mid = elevs.cbegin() + ebase;
928 const auto abase = std::accumulate(elevs.cbegin(), elev_mid, ptrdiff_t{0}, accum_az);
929 const auto num_azs = std::accumulate(elev_mid, elev_mid + field.evCount, ptrdiff_t{0},
930 accum_az);
932 coeffs_end = std::copy_backward(coeffs.cbegin() + abase,
933 coeffs.cbegin() + (abase+num_azs), coeffs_end);
934 delays_end = std::copy_backward(delays.cbegin() + abase,
935 delays.cbegin() + (abase+num_azs), delays_end);
937 return ebase + field.evCount;
939 std::ignore = std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_irs);
940 assert(coeffs_.begin() == coeffs_end);
941 assert(delays_.begin() == delays_end);
943 fields = std::move(fields_);
944 elevs = std::move(elevs_);
945 coeffs = std::move(coeffs_);
946 delays = std::move(delays_);
949 return CreateHrtfStore(rate, irSize, fields, elevs, coeffs.data(), delays.data());
952 std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data)
954 static constexpr ubyte ChanType_LeftOnly{0};
955 static constexpr ubyte ChanType_LeftRight{1};
957 uint rate{readle<uint32_t>(data)};
958 ubyte channelType{readle<uint8_t>(data)};
959 uint8_t irSize{readle<uint8_t>(data)};
960 ubyte fdCount{readle<uint8_t>(data)};
961 if(!data || data.eof())
962 throw std::runtime_error{"Premature end of file"};
964 if(channelType > ChanType_LeftRight)
966 ERR("Unsupported channel type: %d\n", channelType);
967 return nullptr;
970 if(irSize < MinIrLength || irSize > HrirLength)
972 ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength);
973 return nullptr;
975 if(fdCount < 1 || fdCount > MaxFdCount)
977 ERR("Unsupported number of field-depths: fdCount=%d (%d to %d)\n", fdCount, MinFdCount,
978 MaxFdCount);
979 return nullptr;
982 auto fields = std::vector<HrtfStore::Field>(fdCount);
983 auto elevs = std::vector<HrtfStore::Elevation>{};
984 for(size_t f{0};f < fdCount;f++)
986 const ushort distance{readle<uint16_t>(data)};
987 const ubyte evCount{readle<uint8_t>(data)};
988 if(!data || data.eof())
989 throw std::runtime_error{"Premature end of file"};
991 if(distance < MinFdDistance || distance > MaxFdDistance)
993 ERR("Unsupported field distance[%zu]=%d (%d to %d millimeters)\n", f, distance,
994 MinFdDistance, MaxFdDistance);
995 return nullptr;
997 if(evCount < MinEvCount || evCount > MaxEvCount)
999 ERR("Unsupported elevation count: evCount[%zu]=%d (%d to %d)\n", f, evCount,
1000 MinEvCount, MaxEvCount);
1001 return nullptr;
1004 fields[f].distance = float(distance) / 1000.0f;
1005 fields[f].evCount = evCount;
1006 if(f > 0 && fields[f].distance > fields[f-1].distance)
1008 ERR("Field distance[%zu] is not before previous (%f <= %f)\n", f, fields[f].distance,
1009 fields[f-1].distance);
1010 return nullptr;
1013 const size_t ebase{elevs.size()};
1014 elevs.resize(ebase + evCount);
1015 for(auto &elev : al::span{elevs}.subspan(ebase, evCount))
1016 elev.azCount = readle<uint8_t>(data);
1017 if(!data || data.eof())
1018 throw std::runtime_error{"Premature end of file"};
1020 for(size_t e{0};e < evCount;e++)
1022 if(elevs[ebase+e].azCount < MinAzCount || elevs[ebase+e].azCount > MaxAzCount)
1024 ERR("Unsupported azimuth count: azCount[%zu][%zu]=%d (%d to %d)\n", f, e,
1025 elevs[ebase+e].azCount, MinAzCount, MaxAzCount);
1026 return nullptr;
1031 elevs[0].irOffset = 0;
1032 std::partial_sum(elevs.cbegin(), elevs.cend(), elevs.begin(),
1033 [](const HrtfStore::Elevation &last, const HrtfStore::Elevation &cur)
1034 -> HrtfStore::Elevation
1036 return HrtfStore::Elevation{cur.azCount,
1037 static_cast<ushort>(last.azCount + last.irOffset)};
1039 const auto irTotal = static_cast<ushort>(elevs.back().azCount + elevs.back().irOffset);
1041 auto coeffs = std::vector<HrirArray>(irTotal, HrirArray{});
1042 auto delays = std::vector<ubyte2>(irTotal);
1043 if(channelType == ChanType_LeftOnly)
1045 for(auto &hrir : coeffs)
1047 for(auto &val : al::span{hrir}.first(irSize))
1048 val[0] = static_cast<float>(readle<int,24>(data)) / 8388608.0f;
1050 for(auto &val : delays)
1051 val[0] = readle<uint8_t>(data);
1052 if(!data || data.eof())
1053 throw std::runtime_error{"Premature end of file"};
1055 for(size_t i{0};i < irTotal;++i)
1057 if(delays[i][0] > MaxHrirDelay<<HrirDelayFracBits)
1059 ERR("Invalid delays[%zu][0]: %f (%d)\n", i,
1060 delays[i][0] / float{HrirDelayFracOne}, MaxHrirDelay);
1061 return nullptr;
1065 /* Mirror the left ear responses to the right ear. */
1066 MirrorLeftHrirs(elevs, coeffs, delays);
1068 else if(channelType == ChanType_LeftRight)
1070 for(auto &hrir : coeffs)
1072 for(auto &val : al::span{hrir}.first(irSize))
1074 val[0] = static_cast<float>(readle<int,24>(data)) / 8388608.0f;
1075 val[1] = static_cast<float>(readle<int,24>(data)) / 8388608.0f;
1078 for(auto &val : delays)
1080 val[0] = readle<uint8_t>(data);
1081 val[1] = readle<uint8_t>(data);
1083 if(!data || data.eof())
1084 throw std::runtime_error{"Premature end of file"};
1086 for(size_t i{0};i < irTotal;++i)
1088 if(delays[i][0] > MaxHrirDelay<<HrirDelayFracBits)
1090 ERR("Invalid delays[%zu][0]: %f (%d)\n", i,
1091 delays[i][0] / float{HrirDelayFracOne}, MaxHrirDelay);
1092 return nullptr;
1094 if(delays[i][1] > MaxHrirDelay<<HrirDelayFracBits)
1096 ERR("Invalid delays[%zu][1]: %f (%d)\n", i,
1097 delays[i][1] / float{HrirDelayFracOne}, MaxHrirDelay);
1098 return nullptr;
1103 return CreateHrtfStore(rate, irSize, fields, elevs, coeffs.data(), delays.data());
1107 bool checkName(const std::string_view name)
1109 auto match_name = [name](const HrtfEntry &entry) -> bool { return name == entry.mDispName; };
1110 auto &enum_names = EnumeratedHrtfs;
1111 return std::find_if(enum_names.cbegin(), enum_names.cend(), match_name) != enum_names.cend();
1114 void AddFileEntry(const std::string_view filename)
1116 /* Check if this file has already been enumerated. */
1117 auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(),
1118 [filename](const HrtfEntry &entry) -> bool
1119 { return entry.mFilename == filename; });
1120 if(enum_iter != EnumeratedHrtfs.cend())
1122 TRACE("Skipping duplicate file entry %.*s\n", al::sizei(filename), filename.data());
1123 return;
1126 /* TODO: Get a human-readable name from the HRTF data (possibly coming in a
1127 * format update). */
1128 size_t namepos{filename.rfind('/')+1};
1129 if(!namepos) namepos = filename.rfind('\\')+1;
1131 size_t extpos{filename.rfind('.')};
1132 if(extpos <= namepos) extpos = std::string::npos;
1134 const std::string_view basename{(extpos == std::string::npos) ?
1135 filename.substr(namepos) : filename.substr(namepos, extpos-namepos)};
1136 std::string newname{basename};
1137 int count{1};
1138 while(checkName(newname))
1140 newname = basename;
1141 newname += " #";
1142 newname += std::to_string(++count);
1144 const HrtfEntry &entry = EnumeratedHrtfs.emplace_back(newname, filename);
1146 TRACE("Adding file entry \"%s\"\n", entry.mFilename.c_str());
1149 /* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer
1150 * for input instead of opening the given filename.
1152 void AddBuiltInEntry(const std::string_view dispname, uint residx)
1154 std::string filename{'!'+std::to_string(residx)+'_'};
1155 filename += dispname;
1157 auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(),
1158 [&filename](const HrtfEntry &entry) -> bool
1159 { return entry.mFilename == filename; });
1160 if(enum_iter != EnumeratedHrtfs.cend())
1162 TRACE("Skipping duplicate file entry %s\n", filename.c_str());
1163 return;
1166 /* TODO: Get a human-readable name from the HRTF data (possibly coming in a
1167 * format update). */
1169 std::string newname{dispname};
1170 int count{1};
1171 while(checkName(newname))
1173 newname = dispname;
1174 newname += " #";
1175 newname += std::to_string(++count);
1177 const HrtfEntry &entry = EnumeratedHrtfs.emplace_back(std::move(newname), std::move(filename));
1179 TRACE("Adding built-in entry \"%s\"\n", entry.mFilename.c_str());
1183 #define IDR_DEFAULT_HRTF_MHR 1
1185 #ifndef ALSOFT_EMBED_HRTF_DATA
1187 al::span<const char> GetResource(int /*name*/)
1188 { return {}; }
1190 #else
1192 /* NOLINTNEXTLINE(*-avoid-c-arrays) */
1193 constexpr unsigned char hrtf_default[]{
1194 #include "default_hrtf.txt"
1197 al::span<const char> GetResource(int name)
1199 if(name == IDR_DEFAULT_HRTF_MHR)
1200 return {reinterpret_cast<const char*>(hrtf_default), sizeof(hrtf_default)};
1201 return {};
1203 #endif
1205 } // namespace
1208 std::vector<std::string> EnumerateHrtf(std::optional<std::string> pathopt)
1210 std::lock_guard<std::mutex> enumlock{EnumeratedHrtfLock};
1211 EnumeratedHrtfs.clear();
1213 for(const auto &fname : SearchDataFiles(".mhr"sv))
1214 AddFileEntry(fname);
1216 bool usedefaults{true};
1217 if(pathopt)
1219 std::string_view pathlist{*pathopt};
1220 while(!pathlist.empty())
1222 while(!pathlist.empty() && (std::isspace(pathlist.front()) || pathlist.front() == ','))
1223 pathlist.remove_prefix(1);
1224 if(pathlist.empty())
1225 break;
1227 auto endpos = std::min(pathlist.find(','), pathlist.size());
1228 auto entry = pathlist.substr(0, endpos);
1229 if(endpos < pathlist.size())
1230 pathlist.remove_prefix(++endpos);
1231 else
1233 pathlist.remove_prefix(endpos);
1234 usedefaults = false;
1237 while(!entry.empty() && std::isspace(entry.back()))
1238 entry.remove_suffix(1);
1239 if(!entry.empty())
1241 for(const auto &fname : SearchDataFiles(".mhr"sv, entry))
1242 AddFileEntry(fname);
1247 if(usedefaults)
1249 for(const auto &fname : SearchDataFiles(".mhr"sv, "openal/hrtf"sv))
1250 AddFileEntry(fname);
1252 if(!GetResource(IDR_DEFAULT_HRTF_MHR).empty())
1253 AddBuiltInEntry("Built-In HRTF", IDR_DEFAULT_HRTF_MHR);
1256 std::vector<std::string> list;
1257 list.reserve(EnumeratedHrtfs.size());
1258 for(auto &entry : EnumeratedHrtfs)
1259 list.emplace_back(entry.mDispName);
1261 return list;
1264 HrtfStorePtr GetLoadedHrtf(const std::string_view name, const uint devrate)
1265 try {
1266 if(devrate > MaxSampleRate)
1268 WARN("Device sample rate too large for HRTF (%uhz > %uhz)\n", devrate, MaxSampleRate);
1269 return nullptr;
1271 std::lock_guard<std::mutex> enumlock{EnumeratedHrtfLock};
1272 auto entry_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(),
1273 [name](const HrtfEntry &entry) -> bool { return entry.mDispName == name; });
1274 if(entry_iter == EnumeratedHrtfs.cend())
1275 return nullptr;
1276 const std::string &fname = entry_iter->mFilename;
1278 std::lock_guard<std::mutex> loadlock{LoadedHrtfLock};
1279 auto hrtf_lt_fname = [devrate](LoadedHrtf &hrtf, const std::string_view filename) -> bool
1281 return hrtf.mSampleRate < devrate
1282 || (hrtf.mSampleRate == devrate && hrtf.mFilename < filename);
1284 auto handle = std::lower_bound(LoadedHrtfs.begin(), LoadedHrtfs.end(), fname, hrtf_lt_fname);
1285 if(handle != LoadedHrtfs.end() && handle->mSampleRate == devrate && handle->mFilename == fname)
1287 if(HrtfStore *hrtf{handle->mEntry.get()})
1289 assert(hrtf->mSampleRate == devrate);
1290 hrtf->add_ref();
1291 return HrtfStorePtr{hrtf};
1295 std::unique_ptr<std::istream> stream;
1296 int residx{};
1297 char ch{};
1298 if(sscanf(fname.c_str(), "!%d%c", &residx, &ch) == 2 && ch == '_')
1300 TRACE("Loading %s...\n", fname.c_str());
1301 al::span<const char> res{GetResource(residx)};
1302 if(res.empty())
1304 ERR("Could not get resource %u, %.*s\n", residx, al::sizei(name), name.data());
1305 return nullptr;
1307 /* NOLINTNEXTLINE(*-const-cast) */
1308 stream = std::make_unique<idstream>(al::span{const_cast<char*>(res.data()), res.size()});
1310 else
1312 TRACE("Loading %s...\n", fname.c_str());
1313 auto fstr = std::make_unique<std::ifstream>(std::filesystem::u8path(fname),
1314 std::ios::binary);
1315 if(!fstr->is_open())
1317 ERR("Could not open %s\n", fname.c_str());
1318 return nullptr;
1320 stream = std::move(fstr);
1323 std::unique_ptr<HrtfStore> hrtf;
1324 std::array<char,GetMarker03Name().size()> magic{};
1325 stream->read(magic.data(), magic.size());
1326 if(stream->gcount() < static_cast<std::streamsize>(GetMarker03Name().size()))
1327 ERR("%.*s data is too short (%zu bytes)\n", al::sizei(name),name.data(), stream->gcount());
1328 else if(GetMarker03Name() == std::string_view{magic.data(), magic.size()})
1330 TRACE("Detected data set format v3\n");
1331 hrtf = LoadHrtf03(*stream);
1333 else if(GetMarker02Name() == std::string_view{magic.data(), magic.size()})
1335 TRACE("Detected data set format v2\n");
1336 hrtf = LoadHrtf02(*stream);
1338 else if(GetMarker01Name() == std::string_view{magic.data(), magic.size()})
1340 TRACE("Detected data set format v1\n");
1341 hrtf = LoadHrtf01(*stream);
1343 else if(GetMarker00Name() == std::string_view{magic.data(), magic.size()})
1345 TRACE("Detected data set format v0\n");
1346 hrtf = LoadHrtf00(*stream);
1348 else
1349 ERR("Invalid header in %.*s: \"%.8s\"\n", al::sizei(name), name.data(), magic.data());
1350 stream.reset();
1352 if(!hrtf)
1353 return nullptr;
1355 if(hrtf->mSampleRate != devrate)
1357 TRACE("Resampling HRTF %.*s (%uhz -> %uhz)\n", al::sizei(name), name.data(),
1358 hrtf->mSampleRate, devrate);
1360 /* Calculate the last elevation's index and get the total IR count. */
1361 const size_t lastEv{std::accumulate(hrtf->mFields.begin(), hrtf->mFields.end(), 0_uz,
1362 [](const size_t curval, const HrtfStore::Field &field) noexcept -> size_t
1363 { return curval + field.evCount; }
1364 ) - 1};
1365 const size_t irCount{size_t{hrtf->mElev[lastEv].irOffset} + hrtf->mElev[lastEv].azCount};
1367 /* Resample all the IRs. */
1368 std::array<std::array<double,HrirLength>,2> inout{};
1369 PPhaseResampler rs;
1370 rs.init(hrtf->mSampleRate, devrate);
1371 for(size_t i{0};i < irCount;++i)
1373 /* NOLINTNEXTLINE(*-const-cast) */
1374 auto coeffs = al::span{const_cast<HrirArray&>(hrtf->mCoeffs[i])};
1375 for(size_t j{0};j < 2;++j)
1377 std::transform(coeffs.cbegin(), coeffs.cend(), inout[0].begin(),
1378 [j](const float2 &in) noexcept -> double { return in[j]; });
1379 rs.process(inout[0], inout[1]);
1380 for(size_t k{0};k < HrirLength;++k)
1381 coeffs[k][j] = static_cast<float>(inout[1][k]);
1384 rs = {};
1386 /* Scale the delays for the new sample rate. */
1387 float max_delay{0.0f};
1388 auto new_delays = std::vector<float2>(irCount);
1389 const float rate_scale{static_cast<float>(devrate)/static_cast<float>(hrtf->mSampleRate)};
1390 for(size_t i{0};i < irCount;++i)
1392 for(size_t j{0};j < 2;++j)
1394 const float new_delay{std::round(float(hrtf->mDelays[i][j]) * rate_scale) /
1395 float{HrirDelayFracOne}};
1396 max_delay = std::max(max_delay, new_delay);
1397 new_delays[i][j] = new_delay;
1401 /* If the new delays exceed the max, scale it down to fit (essentially
1402 * shrinking the head radius; not ideal but better than a per-delay
1403 * clamp).
1405 float delay_scale{HrirDelayFracOne};
1406 if(max_delay > MaxHrirDelay)
1408 WARN("Resampled delay exceeds max (%.2f > %d)\n", max_delay, MaxHrirDelay);
1409 delay_scale *= float{MaxHrirDelay} / max_delay;
1412 for(size_t i{0};i < irCount;++i)
1414 /* NOLINTNEXTLINE(*-const-cast) */
1415 auto delays = al::span{const_cast<ubyte2&>(hrtf->mDelays[i])};
1416 std::transform(new_delays[i].cbegin(), new_delays[i].cend(), delays.begin(),
1417 [delay_scale](const float delay)
1418 { return static_cast<ubyte>(float2int(delay*delay_scale + 0.5f)); });
1421 /* Scale the IR size for the new sample rate and update the stored
1422 * sample rate.
1424 const float newIrSize{std::round(static_cast<float>(hrtf->mIrSize) * rate_scale)};
1425 hrtf->mIrSize = static_cast<uint8_t>(std::min(float{HrirLength}, newIrSize));
1426 hrtf->mSampleRate = devrate & 0xff'ff'ff;
1429 handle = LoadedHrtfs.emplace(handle, fname, devrate, std::move(hrtf));
1430 TRACE("Loaded HRTF %.*s for sample rate %uhz, %u-sample filter\n", al::sizei(name),name.data(),
1431 handle->mEntry->mSampleRate, handle->mEntry->mIrSize);
1433 return HrtfStorePtr{handle->mEntry.get()};
1435 catch(std::exception& e) {
1436 ERR("Failed to load %.*s: %s\n", al::sizei(name), name.data(), e.what());
1437 return nullptr;
1441 void HrtfStore::add_ref()
1443 auto ref = IncrementRef(mRef);
1444 TRACE("HrtfStore %p increasing refcount to %u\n", decltype(std::declval<void*>()){this}, ref);
1447 void HrtfStore::dec_ref()
1449 auto ref = DecrementRef(mRef);
1450 TRACE("HrtfStore %p decreasing refcount to %u\n", decltype(std::declval<void*>()){this}, ref);
1451 if(ref == 0)
1453 std::lock_guard<std::mutex> loadlock{LoadedHrtfLock};
1455 /* Go through and remove all unused HRTFs. */
1456 auto remove_unused = [](LoadedHrtf &hrtf) -> bool
1458 HrtfStore *entry{hrtf.mEntry.get()};
1459 if(entry && entry->mRef.load() == 0)
1461 TRACE("Unloading unused HRTF %s\n", hrtf.mFilename.c_str());
1462 hrtf.mEntry = nullptr;
1463 return true;
1465 return false;
1467 auto iter = std::remove_if(LoadedHrtfs.begin(), LoadedHrtfs.end(), remove_unused);
1468 LoadedHrtfs.erase(iter, LoadedHrtfs.end());