Remove some custom mix/max/clamp functions
[openal-soft.git] / core / hrtf.cpp
blob61f90a2eafad197da9188a879297b07e944f6662
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 <fstream>
16 #include <iterator>
17 #include <memory>
18 #include <mutex>
19 #include <numeric>
20 #include <optional>
21 #include <tuple>
22 #include <type_traits>
23 #include <utility>
24 #include <vector>
26 #include "albit.h"
27 #include "alfstream.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 class databuf final : public std::streambuf {
124 int_type underflow() override
125 { return traits_type::eof(); }
127 pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override
129 if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
130 return traits_type::eof();
132 switch(whence)
134 case std::ios_base::beg:
135 if(offset < 0 || offset > egptr()-eback())
136 return traits_type::eof();
137 setg(eback(), eback()+offset, egptr());
138 break;
140 case std::ios_base::cur:
141 if((offset >= 0 && offset > egptr()-gptr()) ||
142 (offset < 0 && -offset > gptr()-eback()))
143 return traits_type::eof();
144 setg(eback(), gptr()+offset, egptr());
145 break;
147 case std::ios_base::end:
148 if(offset > 0 || -offset > egptr()-eback())
149 return traits_type::eof();
150 setg(eback(), egptr()+offset, egptr());
151 break;
153 default:
154 return traits_type::eof();
157 return gptr() - eback();
160 pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override
162 // Simplified version of seekoff
163 if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
164 return traits_type::eof();
166 if(pos < 0 || pos > egptr()-eback())
167 return traits_type::eof();
169 setg(eback(), eback()+static_cast<size_t>(pos), egptr());
170 return pos;
173 public:
174 databuf(const char_type *start_, const char_type *end_) noexcept
176 /* NOLINTBEGIN(*-const-cast) */
177 setg(const_cast<char_type*>(start_), const_cast<char_type*>(start_),
178 const_cast<char_type*>(end_));
179 /* NOLINTEND(*-const-cast) */
183 class idstream final : public std::istream {
184 databuf mStreamBuf;
186 public:
187 idstream(const char *start_, const char *end_)
188 : std::istream{nullptr}, mStreamBuf{start_, end_}
189 { init(&mStreamBuf); }
193 struct IdxBlend { uint idx; float blend; };
194 /* Calculate the elevation index given the polar elevation in radians. This
195 * will return an index between 0 and (evcount - 1).
197 IdxBlend CalcEvIndex(uint evcount, float ev)
199 ev = (al::numbers::pi_v<float>*0.5f + ev) * static_cast<float>(evcount-1) *
200 al::numbers::inv_pi_v<float>;
201 uint idx{float2uint(ev)};
203 return IdxBlend{std::min(idx, evcount-1u), ev-static_cast<float>(idx)};
206 /* Calculate the azimuth index given the polar azimuth in radians. This will
207 * return an index between 0 and (azcount - 1).
209 IdxBlend CalcAzIndex(uint azcount, float az)
211 az = (al::numbers::pi_v<float>*2.0f + az) * static_cast<float>(azcount) *
212 (al::numbers::inv_pi_v<float>*0.5f);
213 uint idx{float2uint(az)};
215 return IdxBlend{idx%azcount, az-static_cast<float>(idx)};
218 } // namespace
221 /* Calculates static HRIR coefficients and delays for the given polar elevation
222 * and azimuth in radians. The coefficients are normalized.
224 void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float spread,
225 HrirArray &coeffs, const al::span<uint,2> delays) const
227 const float dirfact{1.0f - (al::numbers::inv_pi_v<float>/2.0f * spread)};
229 size_t ebase{0};
230 auto match_field = [&ebase,distance](const Field &field) noexcept -> bool
232 if(distance >= field.distance)
233 return true;
234 ebase += field.evCount;
235 return false;
237 auto field = std::find_if(mFields.begin(), mFields.end()-1, match_field);
239 /* Calculate the elevation indices. */
240 const auto elev0 = CalcEvIndex(field->evCount, elevation);
241 const size_t elev1_idx{std::min(elev0.idx+1u, field->evCount-1u)};
242 const size_t ir0offset{mElev[ebase + elev0.idx].irOffset};
243 const size_t ir1offset{mElev[ebase + elev1_idx].irOffset};
245 /* Calculate azimuth indices. */
246 const auto az0 = CalcAzIndex(mElev[ebase + elev0.idx].azCount, azimuth);
247 const auto az1 = CalcAzIndex(mElev[ebase + elev1_idx].azCount, azimuth);
249 /* Calculate the HRIR indices to blend. */
250 const std::array<size_t,4> idx{{
251 ir0offset + az0.idx,
252 ir0offset + ((az0.idx+1) % mElev[ebase + elev0.idx].azCount),
253 ir1offset + az1.idx,
254 ir1offset + ((az1.idx+1) % mElev[ebase + elev1_idx].azCount)
257 /* Calculate bilinear blending weights, attenuated according to the
258 * directional panning factor.
260 const std::array<float,4> blend{{
261 (1.0f-elev0.blend) * (1.0f-az0.blend) * dirfact,
262 (1.0f-elev0.blend) * ( az0.blend) * dirfact,
263 ( elev0.blend) * (1.0f-az1.blend) * dirfact,
264 ( elev0.blend) * ( az1.blend) * dirfact
267 /* Calculate the blended HRIR delays. */
268 float d{float(mDelays[idx[0]][0])*blend[0] + float(mDelays[idx[1]][0])*blend[1]
269 + float(mDelays[idx[2]][0])*blend[2] + float(mDelays[idx[3]][0])*blend[3]};
270 delays[0] = fastf2u(d * float{1.0f/HrirDelayFracOne});
271 d = float(mDelays[idx[0]][1])*blend[0] + float(mDelays[idx[1]][1])*blend[1]
272 + float(mDelays[idx[2]][1])*blend[2] + float(mDelays[idx[3]][1])*blend[3];
273 delays[1] = fastf2u(d * float{1.0f/HrirDelayFracOne});
275 /* Calculate the blended HRIR coefficients. */
276 float *coeffout{al::assume_aligned<16>(coeffs[0].data())};
277 coeffout[0] = PassthruCoeff * (1.0f-dirfact);
278 coeffout[1] = PassthruCoeff * (1.0f-dirfact);
279 std::fill_n(coeffout+2, size_t{HrirLength-1}*2, 0.0f);
280 for(size_t c{0};c < 4;c++)
282 const float *srccoeffs{al::assume_aligned<16>(mCoeffs[idx[c]][0].data())};
283 const float mult{blend[c]};
284 auto blend_coeffs = [mult](const float src, const float coeff) noexcept -> float
285 { return src*mult + coeff; };
286 std::transform(srccoeffs, srccoeffs + HrirLength*2_uz, coeffout, coeffout, blend_coeffs);
291 std::unique_ptr<DirectHrtfState> DirectHrtfState::Create(size_t num_chans)
292 { return std::unique_ptr<DirectHrtfState>{new(FamCount(num_chans)) DirectHrtfState{num_chans}}; }
294 void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool perHrirMin,
295 const al::span<const AngularPoint> AmbiPoints,
296 const al::span<const std::array<float,MaxAmbiChannels>> AmbiMatrix,
297 const float XOverFreq, const al::span<const float,MaxAmbiOrder+1> AmbiOrderHFGain)
299 using double2 = std::array<double,2>;
300 struct ImpulseResponse {
301 const ConstHrirSpan hrir;
302 uint ldelay, rdelay;
305 const double xover_norm{double{XOverFreq} / Hrtf->mSampleRate};
306 mChannels[0].mSplitter.init(static_cast<float>(xover_norm));
307 mChannels[0].mHfScale = AmbiOrderHFGain[0];
308 for(size_t i{1};i < mChannels.size();++i)
310 const size_t order{AmbiIndex::OrderFromChannel[i]};
311 mChannels[i].mSplitter = mChannels[0].mSplitter;
312 mChannels[i].mHfScale = AmbiOrderHFGain[order];
315 uint min_delay{HrtfHistoryLength*HrirDelayFracOne}, max_delay{0};
316 std::vector<ImpulseResponse> impres; impres.reserve(AmbiPoints.size());
317 auto calc_res = [Hrtf,&max_delay,&min_delay](const AngularPoint &pt) -> ImpulseResponse
319 auto &field = Hrtf->mFields[0];
320 const auto elev0 = CalcEvIndex(field.evCount, pt.Elev.value);
321 const size_t elev1_idx{std::min(elev0.idx+1u, field.evCount-1u)};
322 const size_t ir0offset{Hrtf->mElev[elev0.idx].irOffset};
323 const size_t ir1offset{Hrtf->mElev[elev1_idx].irOffset};
325 const auto az0 = CalcAzIndex(Hrtf->mElev[elev0.idx].azCount, pt.Azim.value);
326 const auto az1 = CalcAzIndex(Hrtf->mElev[elev1_idx].azCount, pt.Azim.value);
328 const std::array<size_t,4> idx{
329 ir0offset + az0.idx,
330 ir0offset + ((az0.idx+1) % Hrtf->mElev[elev0.idx].azCount),
331 ir1offset + az1.idx,
332 ir1offset + ((az1.idx+1) % Hrtf->mElev[elev1_idx].azCount)
335 /* The largest blend factor serves as the closest HRIR. */
336 const size_t irOffset{idx[(elev0.blend >= 0.5f)*2 + (az1.blend >= 0.5f)]};
337 ImpulseResponse res{Hrtf->mCoeffs[irOffset],
338 Hrtf->mDelays[irOffset][0], Hrtf->mDelays[irOffset][1]};
340 min_delay = std::min(min_delay, std::min(res.ldelay, res.rdelay));
341 max_delay = std::max(max_delay, std::max(res.ldelay, res.rdelay));
343 return res;
345 std::transform(AmbiPoints.begin(), AmbiPoints.end(), std::back_inserter(impres), calc_res);
346 auto hrir_delay_round = [](const uint d) noexcept -> uint
347 { return (d+HrirDelayFracHalf) >> HrirDelayFracBits; };
349 TRACE("Min delay: %.2f, max delay: %.2f, FIR length: %u\n",
350 min_delay/double{HrirDelayFracOne}, max_delay/double{HrirDelayFracOne}, irSize);
352 auto tmpres = std::vector<std::array<double2,HrirLength>>(mChannels.size());
353 max_delay = 0;
354 auto matrixline = AmbiMatrix.cbegin();
355 for(auto &impulse : impres)
357 const ConstHrirSpan hrir{impulse.hrir};
358 const uint base_delay{perHrirMin ? std::min(impulse.ldelay, impulse.rdelay) : min_delay};
359 const uint ldelay{hrir_delay_round(impulse.ldelay - base_delay)};
360 const uint rdelay{hrir_delay_round(impulse.rdelay - base_delay)};
361 max_delay = std::max(max_delay, std::max(impulse.ldelay, impulse.rdelay) - base_delay);
363 auto gains = matrixline->cbegin();
364 ++matrixline;
365 for(auto &result : tmpres)
367 const double mult{*(gains++)};
368 const size_t numirs{HrirLength - std::max(ldelay, rdelay)};
369 size_t lidx{ldelay}, ridx{rdelay};
370 for(size_t j{0};j < numirs;++j)
372 result[lidx++][0] += hrir[j][0] * mult;
373 result[ridx++][1] += hrir[j][1] * mult;
377 impres.clear();
379 auto output = mChannels.begin();
380 for(auto &result : tmpres)
382 auto cast_array2 = [](const double2 &in) noexcept -> float2
383 { return float2{{static_cast<float>(in[0]), static_cast<float>(in[1])}}; };
384 std::transform(result.cbegin(), result.cend(), output->mCoeffs.begin(), cast_array2);
385 ++output;
387 tmpres.clear();
389 const uint max_length{std::min(hrir_delay_round(max_delay) + irSize, HrirLength)};
390 TRACE("New max delay: %.2f, FIR length: %u\n", max_delay/double{HrirDelayFracOne},
391 max_length);
392 mIrSize = max_length;
396 namespace {
398 std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize,
399 const al::span<const HrtfStore::Field> fields,
400 const al::span<const HrtfStore::Elevation> elevs, const HrirArray *coeffs,
401 const ubyte2 *delays)
403 static_assert(alignof(HrtfStore::Field) <= alignof(HrtfStore));
404 static_assert(alignof(HrtfStore::Elevation) <= alignof(HrtfStore));
405 static_assert(16 <= alignof(HrtfStore));
407 if(rate > MaxSampleRate)
408 throw std::runtime_error{"Sample rate is too large (max: "+std::to_string(MaxSampleRate)+"hz)"};
410 const size_t irCount{size_t{elevs.back().azCount} + elevs.back().irOffset};
411 size_t total{sizeof(HrtfStore)};
412 total = RoundUp(total, alignof(HrtfStore::Field)); /* Align for field infos */
413 total += sizeof(std::declval<HrtfStore&>().mFields[0])*fields.size();
414 total = RoundUp(total, alignof(HrtfStore::Elevation)); /* Align for elevation infos */
415 total += sizeof(std::declval<HrtfStore&>().mElev[0])*elevs.size();
416 total = RoundUp(total, 16); /* Align for coefficients using SIMD */
417 total += sizeof(std::declval<HrtfStore&>().mCoeffs[0])*irCount;
418 total += sizeof(std::declval<HrtfStore&>().mDelays[0])*irCount;
420 static constexpr auto AlignVal = std::align_val_t{alignof(HrtfStore)};
421 std::unique_ptr<HrtfStore> Hrtf{::new(::operator new[](total, AlignVal)) HrtfStore{}};
422 Hrtf->mRef.store(1u, std::memory_order_relaxed);
423 Hrtf->mSampleRate = rate & 0xff'ff'ff;
424 Hrtf->mIrSize = irSize;
426 /* Set up pointers to storage following the main HRTF struct. */
427 char *base = reinterpret_cast<char*>(Hrtf.get());
428 size_t offset{sizeof(HrtfStore)};
430 offset = RoundUp(offset, alignof(HrtfStore::Field)); /* Align for field infos */
431 auto field_ = reinterpret_cast<HrtfStore::Field*>(base + offset);
432 offset += sizeof(field_[0])*fields.size();
434 offset = RoundUp(offset, alignof(HrtfStore::Elevation)); /* Align for elevation infos */
435 auto elev_ = reinterpret_cast<HrtfStore::Elevation*>(base + offset);
436 offset += sizeof(elev_[0])*elevs.size();
438 offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */
439 auto coeffs_ = reinterpret_cast<HrirArray*>(base + offset);
440 offset += sizeof(coeffs_[0])*irCount;
442 auto delays_ = reinterpret_cast<ubyte2*>(base + offset);
443 offset += sizeof(delays_[0])*irCount;
445 if(offset != total)
446 throw std::runtime_error{"HrtfStore allocation size mismatch"};
448 /* Copy input data to storage. */
449 std::uninitialized_copy(fields.cbegin(), fields.cend(), field_);
450 std::uninitialized_copy(elevs.cbegin(), elevs.cend(), elev_);
451 std::uninitialized_copy_n(coeffs, irCount, coeffs_);
452 std::uninitialized_copy_n(delays, irCount, delays_);
454 /* Finally, assign the storage pointers. */
455 Hrtf->mFields = {field_, fields.size()};
456 Hrtf->mElev = elev_;
457 Hrtf->mCoeffs = coeffs_;
458 Hrtf->mDelays = delays_;
460 return Hrtf;
463 void MirrorLeftHrirs(const al::span<const HrtfStore::Elevation> elevs, HrirArray *coeffs,
464 ubyte2 *delays)
466 for(const auto &elev : elevs)
468 const ushort evoffset{elev.irOffset};
469 const ushort azcount{elev.azCount};
470 for(size_t j{0};j < azcount;j++)
472 const size_t lidx{evoffset + j};
473 const size_t ridx{evoffset + ((azcount-j) % azcount)};
475 const size_t irSize{coeffs[ridx].size()};
476 for(size_t k{0};k < irSize;k++)
477 coeffs[ridx][k][1] = coeffs[lidx][k][0];
478 delays[ridx][1] = delays[lidx][0];
484 template<size_t num_bits, typename T>
485 constexpr std::enable_if_t<std::is_signed<T>::value && num_bits < sizeof(T)*8,
486 T> fixsign(T value) noexcept
488 constexpr auto signbit = static_cast<T>(1u << (num_bits-1));
489 return static_cast<T>((value^signbit) - signbit);
492 template<size_t num_bits, typename T>
493 constexpr std::enable_if_t<!std::is_signed<T>::value || num_bits == sizeof(T)*8,
494 T> fixsign(T value) noexcept
495 { return value; }
497 template<typename T, size_t num_bits=sizeof(T)*8>
498 inline std::enable_if_t<al::endian::native == al::endian::little,
499 T> readle(std::istream &data)
501 static_assert((num_bits&7) == 0, "num_bits must be a multiple of 8");
502 static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type");
504 alignas(T) std::array<char,sizeof(T)> ret{};
505 if(!data.read(ret.data(), num_bits/8))
506 return static_cast<T>(EOF);
508 return fixsign<num_bits>(al::bit_cast<T>(ret));
511 template<typename T, size_t num_bits=sizeof(T)*8>
512 inline std::enable_if_t<al::endian::native == al::endian::big,
513 T> readle(std::istream &data)
515 static_assert((num_bits&7) == 0, "num_bits must be a multiple of 8");
516 static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type");
518 alignas(T) std::array<char,sizeof(T)> ret{};
519 if(!data.read(ret.data(), num_bits/8))
520 return static_cast<T>(EOF);
521 std::reverse(ret.begin(), ret.end());
523 return fixsign<num_bits>(al::bit_cast<T>(ret));
526 template<>
527 inline uint8_t readle<uint8_t,8>(std::istream &data)
528 { return static_cast<uint8_t>(data.get()); }
531 std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data)
533 uint rate{readle<uint32_t>(data)};
534 ushort irCount{readle<uint16_t>(data)};
535 ushort irSize{readle<uint16_t>(data)};
536 ubyte evCount{readle<uint8_t>(data)};
537 if(!data || data.eof())
538 throw std::runtime_error{"Premature end of file"};
540 if(irSize < MinIrLength || irSize > HrirLength)
542 ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength);
543 return nullptr;
545 if(evCount < MinEvCount || evCount > MaxEvCount)
547 ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
548 evCount, MinEvCount, MaxEvCount);
549 return nullptr;
552 auto elevs = std::vector<HrtfStore::Elevation>(evCount);
553 for(auto &elev : elevs)
554 elev.irOffset = readle<uint16_t>(data);
555 if(!data || data.eof())
556 throw std::runtime_error{"Premature end of file"};
558 for(size_t i{1};i < evCount;i++)
560 if(elevs[i].irOffset <= elevs[i-1].irOffset)
562 ERR("Invalid evOffset: evOffset[%zu]=%d (last=%d)\n", i, elevs[i].irOffset,
563 elevs[i-1].irOffset);
564 return nullptr;
567 if(irCount <= elevs.back().irOffset)
569 ERR("Invalid evOffset: evOffset[%zu]=%d (irCount=%d)\n",
570 elevs.size()-1, elevs.back().irOffset, irCount);
571 return nullptr;
574 for(size_t i{1};i < evCount;i++)
576 elevs[i-1].azCount = static_cast<ushort>(elevs[i].irOffset - elevs[i-1].irOffset);
577 if(elevs[i-1].azCount < MinAzCount || elevs[i-1].azCount > MaxAzCount)
579 ERR("Unsupported azimuth count: azCount[%zd]=%d (%d to %d)\n",
580 i-1, elevs[i-1].azCount, MinAzCount, MaxAzCount);
581 return nullptr;
584 elevs.back().azCount = static_cast<ushort>(irCount - elevs.back().irOffset);
585 if(elevs.back().azCount < MinAzCount || elevs.back().azCount > MaxAzCount)
587 ERR("Unsupported azimuth count: azCount[%zu]=%d (%d to %d)\n",
588 elevs.size()-1, elevs.back().azCount, MinAzCount, MaxAzCount);
589 return nullptr;
592 auto coeffs = std::vector<HrirArray>(irCount, HrirArray{});
593 auto delays = std::vector<ubyte2>(irCount);
594 for(auto &hrir : coeffs)
596 for(auto &val : al::span<float2>{hrir.data(), irSize})
597 val[0] = float(readle<int16_t>(data)) / 32768.0f;
599 for(auto &val : delays)
600 val[0] = readle<uint8_t>(data);
601 if(!data || data.eof())
602 throw std::runtime_error{"Premature end of file"};
604 for(size_t i{0};i < irCount;i++)
606 if(delays[i][0] > MaxHrirDelay)
608 ERR("Invalid delays[%zd]: %d (%d)\n", i, delays[i][0], MaxHrirDelay);
609 return nullptr;
611 delays[i][0] <<= HrirDelayFracBits;
614 /* Mirror the left ear responses to the right ear. */
615 MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data());
617 const std::array field{HrtfStore::Field{0.0f, evCount}};
618 return CreateHrtfStore(rate, static_cast<uint8_t>(irSize), field, elevs, coeffs.data(),
619 delays.data());
622 std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data)
624 uint rate{readle<uint32_t>(data)};
625 uint8_t irSize{readle<uint8_t>(data)};
626 ubyte evCount{readle<uint8_t>(data)};
627 if(!data || data.eof())
628 throw std::runtime_error{"Premature end of file"};
630 if(irSize < MinIrLength || irSize > HrirLength)
632 ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength);
633 return nullptr;
635 if(evCount < MinEvCount || evCount > MaxEvCount)
637 ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
638 evCount, MinEvCount, MaxEvCount);
639 return nullptr;
642 auto elevs = std::vector<HrtfStore::Elevation>(evCount);
643 for(auto &elev : elevs)
644 elev.azCount = readle<uint8_t>(data);
645 if(!data || data.eof())
646 throw std::runtime_error{"Premature end of file"};
648 for(size_t i{0};i < evCount;++i)
650 if(elevs[i].azCount < MinAzCount || elevs[i].azCount > MaxAzCount)
652 ERR("Unsupported azimuth count: azCount[%zd]=%d (%d to %d)\n", i, elevs[i].azCount,
653 MinAzCount, MaxAzCount);
654 return nullptr;
658 elevs[0].irOffset = 0;
659 for(size_t i{1};i < evCount;i++)
660 elevs[i].irOffset = static_cast<ushort>(elevs[i-1].irOffset + elevs[i-1].azCount);
661 const ushort irCount{static_cast<ushort>(elevs.back().irOffset + elevs.back().azCount)};
663 auto coeffs = std::vector<HrirArray>(irCount, HrirArray{});
664 auto delays = std::vector<ubyte2>(irCount);
665 for(auto &hrir : coeffs)
667 for(auto &val : al::span<float2>{hrir.data(), irSize})
668 val[0] = float(readle<int16_t>(data)) / 32768.0f;
670 for(auto &val : delays)
671 val[0] = readle<uint8_t>(data);
672 if(!data || data.eof())
673 throw std::runtime_error{"Premature end of file"};
675 for(size_t i{0};i < irCount;i++)
677 if(delays[i][0] > MaxHrirDelay)
679 ERR("Invalid delays[%zd]: %d (%d)\n", i, delays[i][0], MaxHrirDelay);
680 return nullptr;
682 delays[i][0] <<= HrirDelayFracBits;
685 /* Mirror the left ear responses to the right ear. */
686 MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data());
688 const std::array field{HrtfStore::Field{0.0f, evCount}};
689 return CreateHrtfStore(rate, irSize, field, elevs, coeffs.data(), delays.data());
692 std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data)
694 static constexpr ubyte SampleType_S16{0};
695 static constexpr ubyte SampleType_S24{1};
696 static constexpr ubyte ChanType_LeftOnly{0};
697 static constexpr ubyte ChanType_LeftRight{1};
699 uint rate{readle<uint32_t>(data)};
700 ubyte sampleType{readle<uint8_t>(data)};
701 ubyte channelType{readle<uint8_t>(data)};
702 uint8_t irSize{readle<uint8_t>(data)};
703 ubyte fdCount{readle<uint8_t>(data)};
704 if(!data || data.eof())
705 throw std::runtime_error{"Premature end of file"};
707 if(sampleType > SampleType_S24)
709 ERR("Unsupported sample type: %d\n", sampleType);
710 return nullptr;
712 if(channelType > ChanType_LeftRight)
714 ERR("Unsupported channel type: %d\n", channelType);
715 return nullptr;
718 if(irSize < MinIrLength || irSize > HrirLength)
720 ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength);
721 return nullptr;
723 if(fdCount < 1 || fdCount > MaxFdCount)
725 ERR("Unsupported number of field-depths: fdCount=%d (%d to %d)\n", fdCount, MinFdCount,
726 MaxFdCount);
727 return nullptr;
730 auto fields = std::vector<HrtfStore::Field>(fdCount);
731 auto elevs = std::vector<HrtfStore::Elevation>{};
732 for(size_t f{0};f < fdCount;f++)
734 const ushort distance{readle<uint16_t>(data)};
735 const ubyte evCount{readle<uint8_t>(data)};
736 if(!data || data.eof())
737 throw std::runtime_error{"Premature end of file"};
739 if(distance < MinFdDistance || distance > MaxFdDistance)
741 ERR("Unsupported field distance[%zu]=%d (%d to %d millimeters)\n", f, distance,
742 MinFdDistance, MaxFdDistance);
743 return nullptr;
745 if(evCount < MinEvCount || evCount > MaxEvCount)
747 ERR("Unsupported elevation count: evCount[%zu]=%d (%d to %d)\n", f, evCount,
748 MinEvCount, MaxEvCount);
749 return nullptr;
752 fields[f].distance = float(distance) / 1000.0f;
753 fields[f].evCount = evCount;
754 if(f > 0 && fields[f].distance <= fields[f-1].distance)
756 ERR("Field distance[%zu] is not after previous (%f > %f)\n", f, fields[f].distance,
757 fields[f-1].distance);
758 return nullptr;
761 const size_t ebase{elevs.size()};
762 elevs.resize(ebase + evCount);
763 for(auto &elev : al::span<HrtfStore::Elevation>(elevs.data()+ebase, evCount))
764 elev.azCount = readle<uint8_t>(data);
765 if(!data || data.eof())
766 throw std::runtime_error{"Premature end of file"};
768 for(size_t e{0};e < evCount;e++)
770 if(elevs[ebase+e].azCount < MinAzCount || elevs[ebase+e].azCount > MaxAzCount)
772 ERR("Unsupported azimuth count: azCount[%zu][%zu]=%d (%d to %d)\n", f, e,
773 elevs[ebase+e].azCount, MinAzCount, MaxAzCount);
774 return nullptr;
779 elevs[0].irOffset = 0;
780 std::partial_sum(elevs.cbegin(), elevs.cend(), elevs.begin(),
781 [](const HrtfStore::Elevation &last, const HrtfStore::Elevation &cur)
782 -> HrtfStore::Elevation
784 return HrtfStore::Elevation{cur.azCount,
785 static_cast<ushort>(last.azCount + last.irOffset)};
787 const auto irTotal = static_cast<ushort>(elevs.back().azCount + elevs.back().irOffset);
789 auto coeffs = std::vector<HrirArray>(irTotal, HrirArray{});
790 auto delays = std::vector<ubyte2>(irTotal);
791 if(channelType == ChanType_LeftOnly)
793 if(sampleType == SampleType_S16)
795 for(auto &hrir : coeffs)
797 for(auto &val : al::span<float2>{hrir.data(), irSize})
798 val[0] = float(readle<int16_t>(data)) / 32768.0f;
801 else if(sampleType == SampleType_S24)
803 for(auto &hrir : coeffs)
805 for(auto &val : al::span<float2>{hrir.data(), irSize})
806 val[0] = static_cast<float>(readle<int,24>(data)) / 8388608.0f;
809 for(auto &val : delays)
810 val[0] = readle<uint8_t>(data);
811 if(!data || data.eof())
812 throw std::runtime_error{"Premature end of file"};
814 for(size_t i{0};i < irTotal;++i)
816 if(delays[i][0] > MaxHrirDelay)
818 ERR("Invalid delays[%zu][0]: %d (%d)\n", i, delays[i][0], MaxHrirDelay);
819 return nullptr;
821 delays[i][0] <<= HrirDelayFracBits;
824 /* Mirror the left ear responses to the right ear. */
825 MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data());
827 else if(channelType == ChanType_LeftRight)
829 if(sampleType == SampleType_S16)
831 for(auto &hrir : coeffs)
833 for(auto &val : al::span<float2>{hrir.data(), irSize})
835 val[0] = float(readle<int16_t>(data)) / 32768.0f;
836 val[1] = float(readle<int16_t>(data)) / 32768.0f;
840 else if(sampleType == SampleType_S24)
842 for(auto &hrir : coeffs)
844 for(auto &val : al::span<float2>{hrir.data(), irSize})
846 val[0] = static_cast<float>(readle<int,24>(data)) / 8388608.0f;
847 val[1] = static_cast<float>(readle<int,24>(data)) / 8388608.0f;
851 for(auto &val : delays)
853 val[0] = readle<uint8_t>(data);
854 val[1] = readle<uint8_t>(data);
856 if(!data || data.eof())
857 throw std::runtime_error{"Premature end of file"};
859 for(size_t i{0};i < irTotal;++i)
861 if(delays[i][0] > MaxHrirDelay)
863 ERR("Invalid delays[%zu][0]: %d (%d)\n", i, delays[i][0], MaxHrirDelay);
864 return nullptr;
866 if(delays[i][1] > MaxHrirDelay)
868 ERR("Invalid delays[%zu][1]: %d (%d)\n", i, delays[i][1], MaxHrirDelay);
869 return nullptr;
871 delays[i][0] <<= HrirDelayFracBits;
872 delays[i][1] <<= HrirDelayFracBits;
876 if(fdCount > 1)
878 auto fields_ = std::vector<HrtfStore::Field>(fields.size());
879 auto elevs_ = std::vector<HrtfStore::Elevation>(elevs.size());
880 auto coeffs_ = std::vector<HrirArray>(coeffs.size());
881 auto delays_ = std::vector<ubyte2>(delays.size());
883 /* Simple reverse for the per-field elements. */
884 std::reverse_copy(fields.cbegin(), fields.cend(), fields_.begin());
886 /* Each field has a group of elevations, which each have an azimuth
887 * count. Reverse the order of the groups, keeping the relative order
888 * of per-group azimuth counts.
890 auto elevs_end = elevs_.end();
891 auto copy_azs = [&elevs,&elevs_end](const ptrdiff_t ebase, const HrtfStore::Field &field)
892 -> ptrdiff_t
894 auto elevs_src = elevs.begin()+ebase;
895 elevs_end = std::copy_backward(elevs_src, elevs_src+field.evCount, elevs_end);
896 return ebase + field.evCount;
898 std::ignore = std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_azs);
899 assert(elevs_.begin() == elevs_end);
901 /* Reestablish the IR offset for each elevation index, given the new
902 * ordering of elevations.
904 elevs_[0].irOffset = 0;
905 std::partial_sum(elevs_.cbegin(), elevs_.cend(), elevs_.begin(),
906 [](const HrtfStore::Elevation &last, const HrtfStore::Elevation &cur)
907 -> HrtfStore::Elevation
909 return HrtfStore::Elevation{cur.azCount,
910 static_cast<ushort>(last.azCount + last.irOffset)};
913 /* Reverse the order of each field's group of IRs. */
914 auto coeffs_end = coeffs_.end();
915 auto delays_end = delays_.end();
916 auto copy_irs = [&elevs,&coeffs,&delays,&coeffs_end,&delays_end](
917 const ptrdiff_t ebase, const HrtfStore::Field &field) -> ptrdiff_t
919 auto accum_az = [](const ptrdiff_t count, const HrtfStore::Elevation &elev) noexcept
920 -> ptrdiff_t
921 { return count + elev.azCount; };
922 const auto elev_mid = elevs.cbegin() + ebase;
923 const auto abase = std::accumulate(elevs.cbegin(), elev_mid, ptrdiff_t{0}, accum_az);
924 const auto num_azs = std::accumulate(elev_mid, elev_mid + field.evCount, ptrdiff_t{0},
925 accum_az);
927 coeffs_end = std::copy_backward(coeffs.cbegin() + abase,
928 coeffs.cbegin() + (abase+num_azs), coeffs_end);
929 delays_end = std::copy_backward(delays.cbegin() + abase,
930 delays.cbegin() + (abase+num_azs), delays_end);
932 return ebase + field.evCount;
934 std::ignore = std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_irs);
935 assert(coeffs_.begin() == coeffs_end);
936 assert(delays_.begin() == delays_end);
938 fields = std::move(fields_);
939 elevs = std::move(elevs_);
940 coeffs = std::move(coeffs_);
941 delays = std::move(delays_);
944 return CreateHrtfStore(rate, irSize, fields, elevs, coeffs.data(), delays.data());
947 std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data)
949 static constexpr ubyte ChanType_LeftOnly{0};
950 static constexpr ubyte ChanType_LeftRight{1};
952 uint rate{readle<uint32_t>(data)};
953 ubyte channelType{readle<uint8_t>(data)};
954 uint8_t irSize{readle<uint8_t>(data)};
955 ubyte fdCount{readle<uint8_t>(data)};
956 if(!data || data.eof())
957 throw std::runtime_error{"Premature end of file"};
959 if(channelType > ChanType_LeftRight)
961 ERR("Unsupported channel type: %d\n", channelType);
962 return nullptr;
965 if(irSize < MinIrLength || irSize > HrirLength)
967 ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength);
968 return nullptr;
970 if(fdCount < 1 || fdCount > MaxFdCount)
972 ERR("Unsupported number of field-depths: fdCount=%d (%d to %d)\n", fdCount, MinFdCount,
973 MaxFdCount);
974 return nullptr;
977 auto fields = std::vector<HrtfStore::Field>(fdCount);
978 auto elevs = std::vector<HrtfStore::Elevation>{};
979 for(size_t f{0};f < fdCount;f++)
981 const ushort distance{readle<uint16_t>(data)};
982 const ubyte evCount{readle<uint8_t>(data)};
983 if(!data || data.eof())
984 throw std::runtime_error{"Premature end of file"};
986 if(distance < MinFdDistance || distance > MaxFdDistance)
988 ERR("Unsupported field distance[%zu]=%d (%d to %d millimeters)\n", f, distance,
989 MinFdDistance, MaxFdDistance);
990 return nullptr;
992 if(evCount < MinEvCount || evCount > MaxEvCount)
994 ERR("Unsupported elevation count: evCount[%zu]=%d (%d to %d)\n", f, evCount,
995 MinEvCount, MaxEvCount);
996 return nullptr;
999 fields[f].distance = float(distance) / 1000.0f;
1000 fields[f].evCount = evCount;
1001 if(f > 0 && fields[f].distance > fields[f-1].distance)
1003 ERR("Field distance[%zu] is not before previous (%f <= %f)\n", f, fields[f].distance,
1004 fields[f-1].distance);
1005 return nullptr;
1008 const size_t ebase{elevs.size()};
1009 elevs.resize(ebase + evCount);
1010 for(auto &elev : al::span<HrtfStore::Elevation>(elevs.data()+ebase, evCount))
1011 elev.azCount = readle<uint8_t>(data);
1012 if(!data || data.eof())
1013 throw std::runtime_error{"Premature end of file"};
1015 for(size_t e{0};e < evCount;e++)
1017 if(elevs[ebase+e].azCount < MinAzCount || elevs[ebase+e].azCount > MaxAzCount)
1019 ERR("Unsupported azimuth count: azCount[%zu][%zu]=%d (%d to %d)\n", f, e,
1020 elevs[ebase+e].azCount, MinAzCount, MaxAzCount);
1021 return nullptr;
1026 elevs[0].irOffset = 0;
1027 std::partial_sum(elevs.cbegin(), elevs.cend(), elevs.begin(),
1028 [](const HrtfStore::Elevation &last, const HrtfStore::Elevation &cur)
1029 -> HrtfStore::Elevation
1031 return HrtfStore::Elevation{cur.azCount,
1032 static_cast<ushort>(last.azCount + last.irOffset)};
1034 const auto irTotal = static_cast<ushort>(elevs.back().azCount + elevs.back().irOffset);
1036 auto coeffs = std::vector<HrirArray>(irTotal, HrirArray{});
1037 auto delays = std::vector<ubyte2>(irTotal);
1038 if(channelType == ChanType_LeftOnly)
1040 for(auto &hrir : coeffs)
1042 for(auto &val : al::span<float2>{hrir.data(), irSize})
1043 val[0] = static_cast<float>(readle<int,24>(data)) / 8388608.0f;
1045 for(auto &val : delays)
1046 val[0] = readle<uint8_t>(data);
1047 if(!data || data.eof())
1048 throw std::runtime_error{"Premature end of file"};
1050 for(size_t i{0};i < irTotal;++i)
1052 if(delays[i][0] > MaxHrirDelay<<HrirDelayFracBits)
1054 ERR("Invalid delays[%zu][0]: %f (%d)\n", i,
1055 delays[i][0] / float{HrirDelayFracOne}, MaxHrirDelay);
1056 return nullptr;
1060 /* Mirror the left ear responses to the right ear. */
1061 MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data());
1063 else if(channelType == ChanType_LeftRight)
1065 for(auto &hrir : coeffs)
1067 for(auto &val : al::span<float2>{hrir.data(), irSize})
1069 val[0] = static_cast<float>(readle<int,24>(data)) / 8388608.0f;
1070 val[1] = static_cast<float>(readle<int,24>(data)) / 8388608.0f;
1073 for(auto &val : delays)
1075 val[0] = readle<uint8_t>(data);
1076 val[1] = readle<uint8_t>(data);
1078 if(!data || data.eof())
1079 throw std::runtime_error{"Premature end of file"};
1081 for(size_t i{0};i < irTotal;++i)
1083 if(delays[i][0] > MaxHrirDelay<<HrirDelayFracBits)
1085 ERR("Invalid delays[%zu][0]: %f (%d)\n", i,
1086 delays[i][0] / float{HrirDelayFracOne}, MaxHrirDelay);
1087 return nullptr;
1089 if(delays[i][1] > MaxHrirDelay<<HrirDelayFracBits)
1091 ERR("Invalid delays[%zu][1]: %f (%d)\n", i,
1092 delays[i][1] / float{HrirDelayFracOne}, MaxHrirDelay);
1093 return nullptr;
1098 return CreateHrtfStore(rate, irSize, fields, elevs, coeffs.data(), delays.data());
1102 bool checkName(const std::string_view name)
1104 auto match_name = [name](const HrtfEntry &entry) -> bool { return name == entry.mDispName; };
1105 auto &enum_names = EnumeratedHrtfs;
1106 return std::find_if(enum_names.cbegin(), enum_names.cend(), match_name) != enum_names.cend();
1109 void AddFileEntry(const std::string_view filename)
1111 /* Check if this file has already been enumerated. */
1112 auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(),
1113 [filename](const HrtfEntry &entry) -> bool
1114 { return entry.mFilename == filename; });
1115 if(enum_iter != EnumeratedHrtfs.cend())
1117 TRACE("Skipping duplicate file entry %.*s\n", al::sizei(filename), filename.data());
1118 return;
1121 /* TODO: Get a human-readable name from the HRTF data (possibly coming in a
1122 * format update). */
1123 size_t namepos{filename.rfind('/')+1};
1124 if(!namepos) namepos = filename.rfind('\\')+1;
1126 size_t extpos{filename.rfind('.')};
1127 if(extpos <= namepos) extpos = std::string::npos;
1129 const std::string_view basename{(extpos == std::string::npos) ?
1130 filename.substr(namepos) : filename.substr(namepos, extpos-namepos)};
1131 std::string newname{basename};
1132 int count{1};
1133 while(checkName(newname))
1135 newname = basename;
1136 newname += " #";
1137 newname += std::to_string(++count);
1139 const HrtfEntry &entry = EnumeratedHrtfs.emplace_back(newname, filename);
1141 TRACE("Adding file entry \"%s\"\n", entry.mFilename.c_str());
1144 /* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer
1145 * for input instead of opening the given filename.
1147 void AddBuiltInEntry(const std::string_view dispname, uint residx)
1149 std::string filename{'!'+std::to_string(residx)+'_'};
1150 filename += dispname;
1152 auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(),
1153 [&filename](const HrtfEntry &entry) -> bool
1154 { return entry.mFilename == filename; });
1155 if(enum_iter != EnumeratedHrtfs.cend())
1157 TRACE("Skipping duplicate file entry %s\n", filename.c_str());
1158 return;
1161 /* TODO: Get a human-readable name from the HRTF data (possibly coming in a
1162 * format update). */
1164 std::string newname{dispname};
1165 int count{1};
1166 while(checkName(newname))
1168 newname = dispname;
1169 newname += " #";
1170 newname += std::to_string(++count);
1172 const HrtfEntry &entry = EnumeratedHrtfs.emplace_back(std::move(newname), std::move(filename));
1174 TRACE("Adding built-in entry \"%s\"\n", entry.mFilename.c_str());
1178 #define IDR_DEFAULT_HRTF_MHR 1
1180 #ifndef ALSOFT_EMBED_HRTF_DATA
1182 al::span<const char> GetResource(int /*name*/)
1183 { return {}; }
1185 #else
1187 /* NOLINTNEXTLINE(*-avoid-c-arrays) */
1188 constexpr unsigned char hrtf_default[]{
1189 #include "default_hrtf.txt"
1192 al::span<const char> GetResource(int name)
1194 if(name == IDR_DEFAULT_HRTF_MHR)
1195 return {reinterpret_cast<const char*>(hrtf_default), sizeof(hrtf_default)};
1196 return {};
1198 #endif
1200 } // namespace
1203 std::vector<std::string> EnumerateHrtf(std::optional<std::string> pathopt)
1205 std::lock_guard<std::mutex> enumlock{EnumeratedHrtfLock};
1206 EnumeratedHrtfs.clear();
1208 bool usedefaults{true};
1209 if(pathopt)
1211 std::string_view pathlist{*pathopt};
1212 while(!pathlist.empty())
1214 while(!pathlist.empty() && (std::isspace(pathlist.front()) || pathlist.front() == ','))
1215 pathlist.remove_prefix(1);
1216 if(pathlist.empty())
1217 break;
1219 auto endpos = std::min(pathlist.find(','), pathlist.size());
1220 auto entry = pathlist.substr(0, endpos);
1221 if(endpos < pathlist.size())
1222 pathlist.remove_prefix(++endpos);
1223 else
1225 pathlist.remove_prefix(endpos);
1226 usedefaults = false;
1229 while(!entry.empty() && std::isspace(entry.back()))
1230 entry.remove_suffix(1);
1231 if(!entry.empty())
1233 for(const auto &fname : SearchDataFiles(".mhr"sv, entry))
1234 AddFileEntry(fname);
1239 if(usedefaults)
1241 for(const auto &fname : SearchDataFiles(".mhr"sv, "openal/hrtf"sv))
1242 AddFileEntry(fname);
1244 if(!GetResource(IDR_DEFAULT_HRTF_MHR).empty())
1245 AddBuiltInEntry("Built-In HRTF", IDR_DEFAULT_HRTF_MHR);
1248 std::vector<std::string> list;
1249 list.reserve(EnumeratedHrtfs.size());
1250 for(auto &entry : EnumeratedHrtfs)
1251 list.emplace_back(entry.mDispName);
1253 return list;
1256 HrtfStorePtr GetLoadedHrtf(const std::string_view name, const uint devrate)
1257 try {
1258 if(devrate > MaxSampleRate)
1260 WARN("Device sample rate too large for HRTF (%uhz > %uhz)\n", devrate, MaxSampleRate);
1261 return nullptr;
1263 std::lock_guard<std::mutex> enumlock{EnumeratedHrtfLock};
1264 auto entry_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(),
1265 [name](const HrtfEntry &entry) -> bool { return entry.mDispName == name; });
1266 if(entry_iter == EnumeratedHrtfs.cend())
1267 return nullptr;
1268 const std::string &fname = entry_iter->mFilename;
1270 std::lock_guard<std::mutex> loadlock{LoadedHrtfLock};
1271 auto hrtf_lt_fname = [devrate](LoadedHrtf &hrtf, const std::string_view filename) -> bool
1273 return hrtf.mSampleRate < devrate
1274 || (hrtf.mSampleRate == devrate && hrtf.mFilename < filename);
1276 auto handle = std::lower_bound(LoadedHrtfs.begin(), LoadedHrtfs.end(), fname, hrtf_lt_fname);
1277 if(handle != LoadedHrtfs.end() && handle->mSampleRate == devrate && handle->mFilename == fname)
1279 if(HrtfStore *hrtf{handle->mEntry.get()})
1281 assert(hrtf->mSampleRate == devrate);
1282 hrtf->add_ref();
1283 return HrtfStorePtr{hrtf};
1287 std::unique_ptr<std::istream> stream;
1288 int residx{};
1289 char ch{};
1290 if(sscanf(fname.c_str(), "!%d%c", &residx, &ch) == 2 && ch == '_')
1292 TRACE("Loading %s...\n", fname.c_str());
1293 al::span<const char> res{GetResource(residx)};
1294 if(res.empty())
1296 ERR("Could not get resource %u, %.*s\n", residx, al::sizei(name), name.data());
1297 return nullptr;
1299 stream = std::make_unique<idstream>(res.begin(), res.end());
1301 else
1303 TRACE("Loading %s...\n", fname.c_str());
1304 auto fstr = std::make_unique<al::ifstream>(fname.c_str(), std::ios::binary);
1305 if(!fstr->is_open())
1307 ERR("Could not open %s\n", fname.c_str());
1308 return nullptr;
1310 stream = std::move(fstr);
1313 std::unique_ptr<HrtfStore> hrtf;
1314 std::array<char,GetMarker03Name().size()> magic{};
1315 stream->read(magic.data(), magic.size());
1316 if(stream->gcount() < static_cast<std::streamsize>(GetMarker03Name().size()))
1317 ERR("%.*s data is too short (%zu bytes)\n", al::sizei(name),name.data(), stream->gcount());
1318 else if(GetMarker03Name() == std::string_view{magic.data(), magic.size()})
1320 TRACE("Detected data set format v3\n");
1321 hrtf = LoadHrtf03(*stream);
1323 else if(GetMarker02Name() == std::string_view{magic.data(), magic.size()})
1325 TRACE("Detected data set format v2\n");
1326 hrtf = LoadHrtf02(*stream);
1328 else if(GetMarker01Name() == std::string_view{magic.data(), magic.size()})
1330 TRACE("Detected data set format v1\n");
1331 hrtf = LoadHrtf01(*stream);
1333 else if(GetMarker00Name() == std::string_view{magic.data(), magic.size()})
1335 TRACE("Detected data set format v0\n");
1336 hrtf = LoadHrtf00(*stream);
1338 else
1339 ERR("Invalid header in %.*s: \"%.8s\"\n", al::sizei(name), name.data(), magic.data());
1340 stream.reset();
1342 if(!hrtf)
1343 return nullptr;
1345 if(hrtf->mSampleRate != devrate)
1347 TRACE("Resampling HRTF %.*s (%uhz -> %uhz)\n", al::sizei(name), name.data(),
1348 hrtf->mSampleRate, devrate);
1350 /* Calculate the last elevation's index and get the total IR count. */
1351 const size_t lastEv{std::accumulate(hrtf->mFields.begin(), hrtf->mFields.end(), 0_uz,
1352 [](const size_t curval, const HrtfStore::Field &field) noexcept -> size_t
1353 { return curval + field.evCount; }
1354 ) - 1};
1355 const size_t irCount{size_t{hrtf->mElev[lastEv].irOffset} + hrtf->mElev[lastEv].azCount};
1357 /* Resample all the IRs. */
1358 std::array<std::array<double,HrirLength>,2> inout;
1359 PPhaseResampler rs;
1360 rs.init(hrtf->mSampleRate, devrate);
1361 for(size_t i{0};i < irCount;++i)
1363 /* NOLINTNEXTLINE(*-const-cast) */
1364 auto coeffs = al::span{const_cast<HrirArray&>(hrtf->mCoeffs[i])};
1365 for(size_t j{0};j < 2;++j)
1367 std::transform(coeffs.cbegin(), coeffs.cend(), inout[0].begin(),
1368 [j](const float2 &in) noexcept -> double { return in[j]; });
1369 rs.process(HrirLength, inout[0].data(), HrirLength, inout[1].data());
1370 for(size_t k{0};k < HrirLength;++k)
1371 coeffs[k][j] = static_cast<float>(inout[1][k]);
1374 rs = {};
1376 /* Scale the delays for the new sample rate. */
1377 float max_delay{0.0f};
1378 auto new_delays = std::vector<float2>(irCount);
1379 const float rate_scale{static_cast<float>(devrate)/static_cast<float>(hrtf->mSampleRate)};
1380 for(size_t i{0};i < irCount;++i)
1382 for(size_t j{0};j < 2;++j)
1384 const float new_delay{std::round(float(hrtf->mDelays[i][j]) * rate_scale) /
1385 float{HrirDelayFracOne}};
1386 max_delay = std::max(max_delay, new_delay);
1387 new_delays[i][j] = new_delay;
1391 /* If the new delays exceed the max, scale it down to fit (essentially
1392 * shrinking the head radius; not ideal but better than a per-delay
1393 * clamp).
1395 float delay_scale{HrirDelayFracOne};
1396 if(max_delay > MaxHrirDelay)
1398 WARN("Resampled delay exceeds max (%.2f > %d)\n", max_delay, MaxHrirDelay);
1399 delay_scale *= float{MaxHrirDelay} / max_delay;
1402 for(size_t i{0};i < irCount;++i)
1404 /* NOLINTNEXTLINE(*-const-cast) */
1405 auto delays = al::span{const_cast<ubyte2&>(hrtf->mDelays[i])};
1406 std::transform(new_delays[i].cbegin(), new_delays[i].cend(), delays.begin(),
1407 [delay_scale](const float delay)
1408 { return static_cast<ubyte>(float2int(delay*delay_scale + 0.5f)); });
1411 /* Scale the IR size for the new sample rate and update the stored
1412 * sample rate.
1414 const float newIrSize{std::round(static_cast<float>(hrtf->mIrSize) * rate_scale)};
1415 hrtf->mIrSize = static_cast<uint8_t>(std::min(float{HrirLength}, newIrSize));
1416 hrtf->mSampleRate = devrate & 0xff'ff'ff;
1419 handle = LoadedHrtfs.emplace(handle, fname, devrate, std::move(hrtf));
1420 TRACE("Loaded HRTF %.*s for sample rate %uhz, %u-sample filter\n", al::sizei(name),name.data(),
1421 handle->mEntry->mSampleRate, handle->mEntry->mIrSize);
1423 return HrtfStorePtr{handle->mEntry.get()};
1425 catch(std::exception& e) {
1426 ERR("Failed to load %.*s: %s\n", al::sizei(name), name.data(), e.what());
1427 return nullptr;
1431 void HrtfStore::add_ref()
1433 auto ref = IncrementRef(mRef);
1434 TRACE("HrtfStore %p increasing refcount to %u\n", decltype(std::declval<void*>()){this}, ref);
1437 void HrtfStore::dec_ref()
1439 auto ref = DecrementRef(mRef);
1440 TRACE("HrtfStore %p decreasing refcount to %u\n", decltype(std::declval<void*>()){this}, ref);
1441 if(ref == 0)
1443 std::lock_guard<std::mutex> loadlock{LoadedHrtfLock};
1445 /* Go through and remove all unused HRTFs. */
1446 auto remove_unused = [](LoadedHrtf &hrtf) -> bool
1448 HrtfStore *entry{hrtf.mEntry.get()};
1449 if(entry && entry->mRef.load() == 0)
1451 TRACE("Unloading unused HRTF %s\n", hrtf.mFilename.c_str());
1452 hrtf.mEntry = nullptr;
1453 return true;
1455 return false;
1457 auto iter = std::remove_if(LoadedHrtfs.begin(), LoadedHrtfs.end(), remove_unused);
1458 LoadedHrtfs.erase(iter, LoadedHrtfs.end());