JPL Spatial
Sound spatialization and propagation library
Loading...
Searching...
No Matches
VBAPLUT2D.h
Go to the documentation of this file.
1//
2// ██╗██████╗ ██╗ ██╗██████╗ ███████╗
3// ██║██╔══██╗ ██║ ██║██╔══██╗██╔════╝ ** JPLSpatial **
4// ██║██████╔╝ ██║ ██║██████╔╝███████╗
5// ██ ██║██╔═══╝ ██║ ██║██╔══██╗╚════██║ https://github.com/Jaytheway/JPLSpatial
6// ╚█████╔╝██║ ███████╗██║██████╔╝███████║
7// ╚════╝ ╚═╝ ╚══════╝╚═╝╚═════╝ ╚══════╝
8//
9// Copyright 2024 Jaroslav Pevno, JPLSpatial is offered under the terms of the ISC license:
10//
11// Permission to use, copy, modify, and/or distribute this software for any purpose with or
12// without fee is hereby granted, provided that the above copyright notice and this permission
13// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
14// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
16// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
17// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#pragma once
21
22#include "JPLSpatial/Core.h"
26
35
37
38#include <cmath>
39#include <limits>
40#include <span>
41#include <type_traits>
42#include <bit>
43#include <vector>
44#include <cstring> // std::memcpy
45
46namespace JPL::VBAP
47{
48 //======================================================================
50 template<auto GetSpeakerAngleFunction>
51 class LUTBuilder2D;
52
53 //======================================================================
55 class LUT2D
56 {
57
58 public:
59 //==================================================================
60 /* Note on resolution:
61 (values in degrees)
62
63 at 256:
64 - LUT step mean width: 1.40625
65 - step variance: 0.288304
66 - step min: 0.909371
67 - step max: 1.78992
68 - step max-min: 0.880548
69
70 at 512:
71 - LUT step mean width: 0.703125
72 - step variance: 0.144059
73 - step min: 0.45112
74 - step max: 0.895192
75 - step max-min: 0.444072
76
77 at 1024:
78 - LUT step mean width: 0.351562
79 - step variance: 0.072
80 - step min: 0.224686
81 - step max: 0.447623
82 - step max-min: 0.222937
83
84 at 2048:
85 - LUT step mean width: 0.175781
86 - step variance: 0.0359921
87 - step min: 0.112124
88 - step max: 0.223812
89 - step max-min: 0.111687
90 */
91 struct LUTStats // These values were pre-computed in tests
92 {
93 static constexpr uint16 Resolution = 1024;
94 static constexpr float StepWidth = 0.351562f;
95 static constexpr float StepVariance = 0.072f;
96 static constexpr float StepMin = 0.224686f;
97 static constexpr float StepMax = 0.447623f;
98 static constexpr float StepMinMaxGap = StepMax - StepMin;
99 };
100
101 template<class T>
102 using Array = std::pmr::vector<T>;
103
104 //==================================================================
105 LUT2D() = default;
106
107 [[nodiscard]] JPL_INLINE bool IsInitialized() const noexcept { return !mData.empty(); }
108
121
122 [[nodiscard]] JPL_INLINE float& operator[](int i) { return mData[i]; }
123 [[nodiscard]] JPL_INLINE const float& operator[](int i) const { return mData[i]; }
124
126 [[nodiscard]] JPL_INLINE size_t GetLUTSize() const noexcept { return mData.size(); }
127
129 [[nodiscard]] JPL_INLINE size_t GetLUTResolution() const noexcept { return mLUTResolution; }
130
134
138
141 [[nodiscard]] JPL_INLINE float LUTPositionToAngle(int pos) const;
142
144 JPL_INLINE void GetSpeakerGains(int lutPosition, std::span<float> outGains) const;
145
147 JPL_INLINE void GetSpeakerGains(const Vec2& direction, std::span<float> outGains) const;
148
150 JPL_INLINE void GetSpeakerGains(const simd& dirX, const simd& dirY, std::span<simd> outGains) const;
151
153 [[nodiscard]] JPL_INLINE int CartesianToLUTPosition(float x, float y) const;
154
157
161
165
166 private:
168
169 private:
170 template<auto GetSpeakerAngleFunction>
171 friend class LUTBuilder2D;
173
176 uint16 mLUTResolution = 0;
177 uint16 mLUTResolutionMask = 0;
178 float mInvLUTResolution = 0.0f;
179 uint8 mNumTargetChannels = 0;
180 };
181
182 //=======================================================================
185 {
186 public:
188
192 template<CVec3 Vec3Type>
193 JPL_INLINE void GainsFor(const Vec3Type& direction, std::span<float> outGains) const
194 {
195 // Normalize Vec2 we query.
197 Vec3Type dir(direction);
198 SetY(dir, 0.0f);
199 Normalize(dir);
200
201 LUT.GetSpeakerGains({
202 static_cast<float>(GetX(dir)),
203 static_cast<float>(GetZ(dir)) },
204 outGains);
205 }
206
210 JPL_INLINE void GainsFor(const simd& dirX, const simd& dirY, const simd& dirZ, std::span<simd> outGains) const
211 {
212 LUT.GetSpeakerGains(
213 dirX,
214 dirZ,
215 outGains);
216 }
217
218 public:
219 const LUT2D& LUT;
220 };
221
222 //======================================================================
225 template<auto GetSpeakerVectorFunction, auto GetSpeakerAngleFunction>
227 {
228 public:
229 using Vec3Type = std::remove_cvref_t<decltype(GetSpeakerVectorFunction(EChannel{}))>;
230 using LUTType = LUT2D;
233
234 //======================================================================
240
243 {
244 return QueryType(LUT);
245 }
246 };
247
248 //======================================================================
250 template<auto GetSpeakerAngleFunction>
252 {
253 template<class T>
254 using Array = std::pmr::vector<T>;
255 using ChannelAngleArray = Array<ChannelAngle>;
256 using LUTType = LUT2D;
257
258 public:
260
264 [[nodiscard]] JPL_INLINE bool RequiresChannelConversion() const noexcept { return mChannelMapInternal != mChannelMapTarget; }
265
267 [[nodiscard]] float FindShortestAperture() const;
268
270 [[nodiscard]] bool ComputeCellFor(const Vec2& direction, int lutOffset);
271
274
275#if JPL_VALIDATE_VBAP_LUT
276 void ValidateLUT() const;
277#endif
278
279 private:
280 void ComputePairMatrices();
281
282 // 'ThisType' is to deduce constness and avoid two otherwise identical member functions.
283 // "Deducing this" is not available until C++23.
284 template<class ThisType, class CallbackType>
285 static void ForEachChannelAnglePair(ThisType& self, CallbackType&& callback);
286
287 // If target channel map requires conversion (i.e. if it has < 4 channels, and we use intermediary quad map),
288 // then we call this function to convert and store converted gains in the LUT
289 JPL_INLINE void StoreChannelGainsConverted(uint32 channelId1, uint32 channelId2, const Vec2& gains, uint32 lutOffset);
290
291 void ApplyChannelConversion(const std::pair<uint32, uint32>& inChannelIds, const Vec2& inGains, std::span<float> outValues) const;
292
293 private:
294 LUT2D& mLUT;
295
296 ChannelMap mChannelMapInternal;
297 ChannelMap mChannelMapTarget;
298
299 uint32 mNumInternalChannels;
300 uint32 mNumTargetChannels;
301 uint32 mLFEIndex;
302
304
305 ChannelConversionWeights mChannelConversionWeights;
306
307 struct ChannelPair
308 {
309 Math::Mat2<Vec2> invL;
310 uint32 ChannelId1;
311 uint32 ChannelId2;
312 };
313
315 };
316} // namespace JPL::VBAP
317
318//==============================================================================
319//
320// Code beyond this point is implementation detail...
321//
322//==============================================================================
323namespace JPL::VBAP
324{
325 //==========================================================================
327 {
329
330 return static_cast<int>(
332 * mLUTResolution + 0.5f
333 ) & mLUTResolutionMask;
334 }
335
337 {
338 // Normalize to [0, 2Pi]
339 if (angleInRadians < float(0.0))
340 angleInRadians += JPL_TWO_PI;
342 }
343
345 {
346#if 1 // The LUT built with uniform steps of diamond encoding
347 const float diamond = ToDiamond(Vec2(x, y));
348 return static_cast<int>(Math::FMA(static_cast<float>(mLUTResolution), diamond, 0.5f)) & mLUTResolutionMask;
349#elif 0
351
352 // Diamond to uniform index
353 const float diamond = ToDiamond(Vec2(x, y));
354 const auto angleT = static_cast<int>(diamond * sCorrectionLUTSize + 0.5f) & sCorrectionLUTMask;
355
356 //return static_cast<int>(diamond * mLUTResolution + 0.5f) & mLUTResolutionMask;
358
359#if 0 // If correction LUT is just uniforming the angles, not directly to target indices
360 // normalized angle in [0,1)
362
363 // final uniform-angle index in [0, mLUTResolution)
364 return static_cast<int>(uniformAngleT * mLUTResolution + 0.5f) & mLUTResolutionMask;
365#endif
366
367#else
368 // Angle in [-Pi, Pi]
369 const float angle = std::atan2(x, z);
371#endif
372 }
373
375 {
377 const simd invLen = Math:: InvSqrtFast(x * x + y * y);
378 const simd xN = x * invLen;
379 const simd yN = y * invLen;
380
381 const simd diamond = ToDiamond(xN, yN);
382 return Math::FMA(diamond, simd(mLUTResolution), 0.5f).to_mask() & mLUTResolutionMask;
383 }
384
385 inline void LUT2D::Resize(uint16 resolution, uint32 numTargetChannels)
386 {
387 // We should not have more than 255 channels
388 JPL_ASSERT(numTargetChannels <= std::numeric_limits<uint8>::max());
389
390 // for the sake of sanity
391 resolution = std::bit_ceil(resolution);
392
393 mData.clear();
394 mData.resize(numTargetChannels * resolution, 0.0f);
395
396 mLUTResolution = resolution;
397 mLUTResolutionMask = mLUTResolution - 1;
398 mInvLUTResolution = 1.0f / mLUTResolution;
399 mNumTargetChannels = static_cast<uint8>(numTargetChannels);
400 }
401
402 JPL_INLINE auto LUT2D::LUTPositionToAngle(int pos) const -> float
403 {
404 const Vec2 direction = FromDiamond((static_cast<float>(pos) * static_cast<float>(mInvLUTResolution)));
405 float angle = atan2f(direction.Y, direction.X);
406 if (angle < 0.0f)
407 angle += JPL_TWO_PI;
408 return angle;
409 }
410
411 JPL_INLINE void LUT2D::GetSpeakerGains(int lutPosition, std::span<float> outGains) const
412 {
413 JPL_ASSERT(outGains.size() <= mNumTargetChannels);
414
415 const float* speakerGain = &mData[mNumTargetChannels * lutPosition];
416 std::memcpy(outGains.data(), speakerGain, sizeof(float) * outGains.size());
417 }
418
423
424 JPL_INLINE void LUT2D::GetSpeakerGains(const simd& dirX, const simd& dirY, std::span<simd> outGains) const
425 {
426 // For each speaker we compute 4 directions.
427 // We can vectorize the encoding of the direction into LUT index,
428 // however we have to retrieve the channel gains individually per direction index,
429 // since the location for each simd lane in LUT differs.
430
432 (CartesianToLUTPosition(dirX, dirY) * mNumTargetChannels).store(lutPositions);
433
434 static constexpr std::size_t bufferSize = 32 * simd::size();
436
437 float buffer[bufferSize];
438 const uint32 offsets[]{
439 0,
440 mNumTargetChannels,
441 static_cast<uint32>(mNumTargetChannels) << 1,
442 (static_cast<uint32>(mNumTargetChannels) << 1) + mNumTargetChannels
443 };
444
445 // Retrieve gains from the LUT and write contiguously
446 // d1[ch1, ch2], dr2[ch1, ch2]...
447 for (uint32 i = 0, dest = 0; i < simd_mask::size(); ++i, dest += mNumTargetChannels)
448 {
449 std::memcpy(&buffer[dest], &mData[lutPositions[i]], sizeof(float) * mNumTargetChannels);
450 }
451
452 // Copy gains from the buffer strided into out simd lanes
453 for (uint32 si = 0; si < outGains.size(); ++si)
454 {
455 // dr1[ch1], dr2[ch1], dr3[ch1], dr4[ch1]
456 outGains[si] = simd(buffer[si], buffer[si + offsets[1]], buffer[si + offsets[2]], buffer[si + offsets[3]]);
457 }
458 }
459
461 {
462 // Require power-of-two sizes for cheap masking
463 auto isPow2 = [](uint32 n) { return n && ((n & (n - 1)) == 0); };
464 JPL_ASSERT(isPow2(N_uniform) && "N_uniform must be power-of-two");
465 JPL_ASSERT(isPow2(M_corr) && "M_corr must be power-of-two");
466
467 outCorrectionLut.resize(M_corr);
468
469 const float invM_Corr = 1.0f / M_corr;
470
471 for (uint32 j = 0; j < M_corr; ++j)
472 {
473 // diamond parameter for this table cell
474 const float p = static_cast<float>(j) * invM_Corr;
475
476 // decode to unit vector on circle
477 const Vec2 v = FromDiamond(p);
478
479 // recover true polar angle in [theta, 2PI)
480 float theta = std::atan2(v.X, v.Y);
481 if (theta < 0.0f)
482 theta += JPL_TWO_PI;
483
484 // map to our uniform-angle LUT index
485 const float t = (theta * JPL_INV_TWO_PI) * static_cast<float>(N_uniform);
486 const auto idx = static_cast<uint32>(std::llround(t)) & (N_uniform - 1);
487
489 }
490 }
491
493 {
494 auto isPow2 = [](uint32_t n) { return n && ((n & (n - 1)) == 0); };
496
497 outCorrectionLUT.resize(M_corr);
498
499 const float invM_Corr = 1.0f / M_corr;
500
501 for (uint32_t j = 0; j < M_corr; ++j)
502 {
503 // diamond parameter for this table cell
504 const float p = static_cast<float>(j) * invM_Corr;
505
506 // decode to unit vector on circle
507 const Vec2 v = FromDiamond(p);
508
509 float theta = std::atan2(v.X, v.Y); // (-PI, PI]
510 if (theta < 0.0f)
511 theta += JPL_TWO_PI; // [0, 2PI)
512
513 outCorrectionLUT[j] = theta * JPL_INV_TWO_PI; // normalized angle
514 }
515 }
516
517
518#if 0
519 JPL_INLINE float CircularLerp01(float a, float b, float t)
520 {
521 // both in [0,1); treat 1 as 0 on the circle
522 float d = b - a;
523 if (d > 0.5f) d -= 1.0f;
524 if (d < -0.5f) d += 1.0f;
525 float u = a + t * d;
526 if (u < 0.0f) u += 1.0f;
527 if (u >= 1.0f) u -= 1.0f;
528 return u;
529 }
530#endif
531
532 //==========================================================================
533 template<auto GetSpeakerAngleFunction>
535 : mLUT(LUT)
536 {
537 mChannelMapTarget = channelMap;
538 mNumTargetChannels = channelMap.GetNumChannels();
539
540 // We need at least quad layout for VBAP to work
541 mChannelMapInternal = channelMap.GetNumChannels() < 4 ? ChannelMap::FromChannelMask(ChannelMask::Quad) : channelMap;
542 mNumInternalChannels = mChannelMapInternal.GetNumChannels();
543 mLFEIndex = mChannelMapInternal.GetChannelIndex(EChannel::LFE);
544
545 // Extract sortet speaker angles
546 static constexpr bool skipLFE = false;
548 JPL_ASSERT(mChannelAngels.size() == mNumInternalChannels);
549
550 //JPL_ASSERT(intermNumChannels <= Traits::MAX_CHANNELS);
551
552 ComputePairMatrices();
553
555 {
556 mChannelConversionWeights.Resize(mNumTargetChannels, mNumInternalChannels);
557 ComputeChannelConversionRectangularWeights(mChannelMapInternal, mChannelMapTarget, mChannelConversionWeights);
558 }
559
560 mLUT.Resize(LUTType::LUTStats::Resolution, mNumTargetChannels);
561 }
562
563 template<auto GetSpeakerAngleFunction>
565 {
566 float shortestAperture = std::numeric_limits<float>::max();
567
568 const uint32 lastSpeakerId = mChannelAngels.back().ChannelId;
569
570 auto findShortestAperture = [&](const ChannelAngle& cha1, const ChannelAngle& cha2)
571 {
572 if (cha1.ChannelId == lastSpeakerId)
573 shortestAperture = std::min(shortestAperture, JPL_TWO_PI - cha1.Angle + cha2.Angle);
574 else
575 shortestAperture = std::min(shortestAperture, cha2.Angle - cha1.Angle);
576 };
577
578 ForEachChannelAnglePair(*this, findShortestAperture);
579
580 return shortestAperture;
581 }
582
583 template<auto GetSpeakerAngleFunction>
585 {
586 // Assign speaker contribution values
587 for (const ChannelPair& pair : mChannelPairs)
588 {
589 Vec2 gains = pair.invL.Transform(direction);
590
591 if (gains.X < 0.0f || gains.Y < 0.0f)
592 continue; // not our pair
593
594 gains.Normalize();
595
596 if (RequiresChannelConversion())
597 {
598 StoreChannelGainsConverted(pair.ChannelId1, pair.ChannelId2, gains, lutOffset);
599 }
600 else
601 {
604 mLUT.mData[lutOffset + pair.ChannelId1] = gains.X;
605 mLUT.mData[lutOffset + pair.ChannelId2] = gains.Y;
606 }
607
608 return true;
609 }
610
611 // This should be unreachable
612 JPL_ASSERT(false);
613 return false;
614 }
615
616 template<auto GetSpeakerAngleFunction>
618 {
619 bool bAnyFailed = false;
620
621 // Build a LUT with uniform diamond steps
622 const float step = 1.0f / mLUT.GetLUTResolution();
623 float diamond = 0.0f;
624
625 for (uint32 pos = 0; pos < mLUT.GetLUTResolution(); ++pos, diamond += step)
626 {
627 JPL_ASSERT(diamond <= 1.0f);
628
630
631 // Position of the next diamond step value in the LUT
632 // (number of channels stride)
633 const uint32 offset = mNumTargetChannels * pos;
634
635 // TODO: we may or may not want to terminate if any cell fails
636 bAnyFailed |= ComputeCellFor(direction, offset);
637 }
638
639 return bAnyFailed;
640 }
641
642 template<auto GetSpeakerAngleFunction>
644 {
645 auto makeInvMat = [&](const ChannelAngle& cha1, const ChannelAngle& cha2)
646 {
647 //JPL_ASSERT(Math::IsPositiveAndBelow(cha2.Angle - cha1.Angle, JPL_PI + 1e-6f));
648
649 const auto [s1, c1] = Math::SinCos(cha1.Angle);
650 const auto [s2, c2] = Math::SinCos(cha2.Angle);
651
652 const auto mat =
654 Vec2(s1, -c1),
655 Vec2(s2, -c2));
656
657 // Inverse will fail for wide aperture angles,
658 // therefore we need at least quad layout for <= 90 degree max aperture
659 Math::Mat2<Vec2> invL;
660 (void)JPL_ENSURE(mat.TryInverse(invL));
661
662 mChannelPairs.emplace_back(
663 invL,
664 cha1.ChannelId,
665 cha2.ChannelId);
666 };
667
668 ForEachChannelAnglePair(*this, makeInvMat);
669 }
670
671 template<auto GetSpeakerAngleFunction>
672 template<class ThisType, class CallbackType>
673 inline void LUTBuilder2D<GetSpeakerAngleFunction>::ForEachChannelAnglePair(ThisType& self,
675 {
677 {
678 return idx + (self.mChannelAngels[idx].ChannelId == self.mLFEIndex);
679 };
680
681 for (uint32 ch = 0; ch < self.mChannelAngels.size() - 1; ++ch)
682 {
683 const uint32 ch1 = sanitizeLFEIndex(ch);
684 const uint32 ch2 = sanitizeLFEIndex(ch1 + 1);
685
686 const ChannelAngle& cha1 = self.mChannelAngels[ch1];
687 const ChannelAngle& cha2 = self.mChannelAngels[ch2];
689 }
690
691 // Handle wrap around
692 {
693 const uint32 ch1 = static_cast<uint32>(self.mChannelAngels.size()) - 1;
694 const uint32 ch2 = sanitizeLFEIndex(0);
695 const ChannelAngle& cha1 = self.mChannelAngels[ch1];
696 const ChannelAngle& cha2 = self.mChannelAngels[ch2];
698 }
699 }
700
701 template<auto GetSpeakerAngleFunction>
702 JPL_INLINE void LUTBuilder2D<GetSpeakerAngleFunction>::StoreChannelGainsConverted(uint32 channelId1,
704 const Vec2& gains,
706 {
707 std::span<float> targetGains(&mLUT.mData[lutOffset], mNumTargetChannels);
708
709 // Convert from intermediary to target
710 // channel map that we store in the LUT
711 ApplyChannelConversion({ channelId1, channelId2 }, gains, targetGains);
712
713 // Normalize
716 }
717
718 template<auto GetSpeakerAngleFunction>
719 inline void LUTBuilder2D<GetSpeakerAngleFunction>::ApplyChannelConversion(const std::pair<uint32, uint32>& inChannelIds,
720 const Vec2& inGains,
721 std::span<float> outValues) const
722 {
724 {
725 const float accumulation =
726 inGains.X * mChannelConversionWeights[iChannelOut][inChannelIds.first] +
727 inGains.Y * mChannelConversionWeights[iChannelOut][inChannelIds.second];
728
730 }
731 }
732
733#if JPL_VALIDATE_VBAP_LUT
734 template<auto GetSpeakerAngleFunction>
735 void LUTBuilder2D<GetSpeakerAngleFunction>::ValidateLUT() const
736 {
737
738#if 0 // TODO: implement for 2D
739
740 JPL_ASSERT(mLUT != nullptr);
741
742 for (uint32 i = 0; i < mLUT->Speakers.size(); ++i)
743 {
744 if (!LUTCodec::IsValidCode(i))
745 continue;
746
747 const auto& speakers = mLUT->Speakers[i];
748 JPL_ASSERT(speakers[0] != speakers[1]);
749 JPL_ASSERT(speakers[1] != speakers[2]);
750 JPL_ASSERT(speakers[2] != speakers[0]);
751 }
752
753 for (uint32 i = 0; i < mLUT->Gains.size(); ++i)
754 {
755 if (!LUTCodec::IsValidCode(i))
756 continue;
757
758 const auto& gains = mLUT->Gains[i];
759 // Gains can be encoded in 24 or even 16 bit
760 const std::array<float, 3> gainsDecoded
761 {
762 gains[0],
763 gains[1],
764 gains[2]
765 };
767 }
768#endif
769 }
770#endif
771
772} // namespace JPL::VBAP
#define JPL_ASSERT(inExpression,...)
Main assert macro, usage: JPL_ASSERT(condition, message) or JPL_ASSERT(condition)
Definition ErrorReporting.h:76
#define JPL_ENSURE(inExpression,...)
Define ENSURE.
Definition ErrorReporting.h:90
Utility helper to access 2D array kind of weights.
Definition ChannelConversion.h:100
JPL_INLINE void Resize(uint32 numOutputs, uint32 numInputs)
Definition ChannelConversion.h:113
Definition ChannelMap.h:150
constexpr uint32 GetNumChannels() const noexcept
Definition ChannelMap.h:162
static constexpr ChannelMap FromChannelMask(uint32 channelMask)
Definition ChannelMap.h:198
constexpr uint32 GetChannelIndex(EChannel channel) const
Definition ChannelMap.h:163
Look-up table containing channel gains for each direction.
Definition VBAPLUT2D.h:56
JPL_INLINE int AngleNormalizedToLUTPosition(float angleNormalised) const
Definition VBAPLUT2D.h:326
JPL_INLINE bool IsInitialized() const noexcept
Definition VBAPLUT2D.h:107
static void BuildDiamondToAngleNormLUT(Array< float > &outCorrectionLUT, uint32 M_corr=1024)
Definition VBAPLUT2D.h:492
std::pmr::vector< T > Array
Definition VBAPLUT2D.h:102
JPL_INLINE size_t GetLUTSize() const noexcept
Size of the LUT = LUT Resolution * Number of Channels.
Definition VBAPLUT2D.h:126
JPL_INLINE int CartesianToLUTPosition(float x, float y) const
Get LUT position from direction vector.
Definition VBAPLUT2D.h:344
static void BuildDiamondToUniformIndexLUT(Array< uint32 > &outCorrectionLUT, uint32 N_uniform, uint32 M_corr=1024)
Definition VBAPLUT2D.h:460
JPL_INLINE size_t GetLUTResolution() const noexcept
Resolution of the LUT.
Definition VBAPLUT2D.h:129
JPL_INLINE float & operator[](int i)
Definition VBAPLUT2D.h:122
JPL_INLINE const float & operator[](int i) const
Definition VBAPLUT2D.h:123
JPL_INLINE int AngleToLUTPosition(float angleInRadians) const
Definition VBAPLUT2D.h:336
JPL_INLINE float LUTPositionToAngle(int pos) const
Definition VBAPLUT2D.h:402
JPL_INLINE void GetSpeakerGains(int lutPosition, std::span< float > outGains) const
Get preprocessed speaker gains at specific LUT posotion.
Definition VBAPLUT2D.h:411
JPL_INLINE float GetLUTValue(uint32 positionInLUT) const
Definition VBAPLUT2D.h:120
Forward declarations.
Definition VBAPLUT2D.h:252
bool BuildForAllDirections()
Build the entire LUT for all directions.
Definition VBAPLUT2D.h:617
LUTBuilder2D(ChannelMap channelMap, LUTType &LUT)
Definition VBAPLUT2D.h:534
bool ComputeCellFor(const Vec2 &direction, int lutOffset)
Comput LUT gains for given direction and LUT offset.
Definition VBAPLUT2D.h:584
JPL_INLINE bool RequiresChannelConversion() const noexcept
Definition VBAPLUT2D.h:264
float FindShortestAperture() const
Find shortest aperture between two speakers of the target map.
Definition VBAPLUT2D.h:564
Definition VBAPLUT2D.h:227
static JPL_INLINE QueryType Query(const LUT2D &LUT)
Make LUTQuery object to query 'LUT' for speaker gains.
Definition VBAPLUT2D.h:242
std::remove_cvref_t< decltype(GetSpeakerVectorFunction(EChannel{}))> Vec3Type
Definition VBAPLUT2D.h:229
LUTBuilder2D< GetSpeakerAngleFunction > BuilderType
Definition VBAPLUT2D.h:231
static JPL_INLINE BuilderType MakeBuilder(ChannelMap channelMap, LUT2D &lut)
Make LUTBuilder object to build LUT for given 'channelMap' and 'LUTType'.
Definition VBAPLUT2D.h:236
LUTQuery2D QueryType
Definition VBAPLUT2D.h:232
Interface to query LUT gains for a direction.
Definition VBAPLUT2D.h:185
JPL_INLINE void GainsFor(const Vec3Type &direction, std::span< float > outGains) const
Definition VBAPLUT2D.h:193
JPL_INLINE void GainsFor(const simd &dirX, const simd &dirY, const simd &dirZ, std::span< simd > outGains) const
Definition VBAPLUT2D.h:210
JPL_INLINE LUTQuery2D(const LUT2D &lut) noexcept
Definition VBAPLUT2D.h:187
const LUT2D & LUT
Definition VBAPLUT2D.h:219
JPL_INLINE constexpr void NormalizeL2(ContainerType &&data)
Apply unit vector scaling, so that the magnitude of the vector = 1.
Definition Algorithm.h:98
JPL_INLINE constexpr bool IsNormalizedL2(const ContainerType &data, float tolerance=JPL_FLOAT_EPS)
Definition Algorithm.h:115
JPL_INLINE constexpr T FMA(T a, T b, T c) noexcept
Inlined fuse multiply-add. Compiler in some circumstances is more eager to optimize this than std::fm...
Definition Math.h:186
JPL_INLINE simd InvSqrtFast(const simd &vec) noexcept
Definition SIMD.h:1783
JPL_INLINE std::pair< T, T > SinCos(T value) noexcept
Definition Math.h:164
Forward declaration.
Definition DummySpeakers.h:31
constexpr Vec2 FromDiamond(float p) noexcept
Decode scalar [0, 1] to a 2D unit vector.
Definition DirectionEncoding.h:212
JPL_INLINE auto GetX(const Vec3Type &v) noexcept
Definition Vec3Traits.h:35
JPL_INLINE void SetY(Vec3Type &v, Vec3FloatType< Vec3Type > value) noexcept
Definition Vec3Traits.h:40
JPL_INLINE auto GetZ(const Vec3Type &v) noexcept
Definition Vec3Traits.h:37
std::uint32_t uint32
Definition Core.h:311
std::pmr::memory_resource * GetDefaultMemoryResource() noexcept
Definition Memory.h:42
std::uint16_t uint16
Definition Core.h:310
EChannel
Definition ChannelMap.h:39
@ LFE
Definition ChannelMap.h:43
std::uint8_t uint8
Definition Core.h:309
JPL_INLINE simd min(const simd &a, const simd &b) noexcept
Element-wise min.
Definition SIMD.h:1813
constexpr float ToDiamond(Vec2 dir) noexcept
"Diamond Encoding" of a 2D unit vector as per:
Definition DirectionEncoding.h:197
Minimal 2x2 matrix interface.
Definition MinimalMat.h:35
static JPL_INLINE constexpr Mat2 FromColumns(const Vec2 &l1, const Vec2 &l2) noexcept
Definition MinimalMat.h:42
Definition VBAPEx.h:238
static void GetSortedChannelAngles(ChannelMap channelMap, ArrayType< ChannelAngle, Args... > &sortedChannelAngles, std::function< float(EChannel)> getChannelAngle, bool skipLFE=true)
Get channel angles from ChannelMap, normalize to [0, Pi] and sort in assending order.
Definition VBAPEx.h:255
Definition VBAPLUT2D.h:92
static constexpr float StepVariance
Definition VBAPLUT2D.h:95
static constexpr float StepMinMaxGap
Definition VBAPLUT2D.h:98
static constexpr float StepMax
Definition VBAPLUT2D.h:97
static constexpr float StepMin
Definition VBAPLUT2D.h:96
static constexpr float StepWidth
Definition VBAPLUT2D.h:94
static constexpr uint16 Resolution
Definition VBAPLUT2D.h:93
Definition VBAPLUT3D.h:112
Definition MinimalVec2.h:29
Definition SIMD.h:207
static constexpr std::size_t size() noexcept
Get number of element of the vector.
Definition SIMD.h:231
Minimal 4-wide 32-bit float vector implementation for SIMD.
Definition SIMD.h:60
static constexpr std::size_t size() noexcept
Get number of element of the vector.
Definition SIMD.h:97