50 template<auto GetSpeakerAngleFunction>
96 static constexpr float StepMin = 0.224686f;
97 static constexpr float StepMax = 0.447623f;
107 [[nodiscard]] JPL_INLINE
bool IsInitialized() const noexcept {
return !mData.empty(); }
120 [[nodiscard]] JPL_INLINE
float GetLUTValue(
uint32 positionInLUT)
const {
return mData[positionInLUT]; }
122 [[nodiscard]] JPL_INLINE
float&
operator[](
int i) {
return mData[i]; }
123 [[nodiscard]] JPL_INLINE
const float&
operator[](
int i)
const {
return mData[i]; }
126 [[nodiscard]] JPL_INLINE
size_t GetLUTSize() const noexcept {
return mData.size(); }
129 [[nodiscard]] JPL_INLINE
size_t GetLUTResolution() const noexcept {
return mLUTResolution; }
144 JPL_INLINE
void GetSpeakerGains(
int lutPosition, std::span<float> outGains)
const;
167 void Resize(
uint16 resolution,
uint32 numTargetChannels);
170 template<auto GetSpeakerAngleFunction>
176 uint16 mLUTResolution = 0;
177 uint16 mLUTResolutionMask = 0;
178 float mInvLUTResolution = 0.0f;
179 uint8 mNumTargetChannels = 0;
192 template<CVec3 Vec3Type>
193 JPL_INLINE
void GainsFor(
const Vec3Type& direction, std::span<float> outGains)
const
197 Vec3Type dir(direction);
201 LUT.GetSpeakerGains({
202 static_cast<float>(
GetX(dir)),
203 static_cast<float>(
GetZ(dir)) },
227 template<auto GetSpeakerVectorFunction, auto GetSpeakerAngleFunction>
252 template<auto GetSpeakerAngleFunction>
256 using Array = std::pmr::vector<T>;
257 using ChannelAngleArray = Array<ChannelAngle>;
277#if JPL_VALIDATE_VBAP_LUT
278 void ValidateLUT()
const;
282 void ComputePairMatrices();
286 template<
class ThisType,
class CallbackType>
287 static void ForEachChannelAnglePair(ThisType& self, CallbackType&& callback);
291 JPL_INLINE
void StoreChannelGainsConverted(
uint32 channelId1,
uint32 channelId2,
const Vec2& gains,
uint32 lutOffset);
293 void ApplyChannelConversion(
const std::pair<uint32, uint32>& inChannelIds,
const Vec2& inGains, std::span<float> outValues)
const;
301 uint32 mNumInternalChannels;
302 uint32 mNumTargetChannels;
332 return static_cast<int>(
333 ToDiamond(
Vec2{ sinf(angleNormalised), -cosf(angleNormalised) })
334 * mLUTResolution + 0.5f
335 ) & mLUTResolutionMask;
341 if (angleInRadians <
float(0.0))
342 angleInRadians += JPL_TWO_PI;
350 return static_cast<int>(
Math::FMA(
static_cast<float>(mLUTResolution), diamond, 0.5f)) & mLUTResolutionMask;
356 const auto angleT =
static_cast<int>(diamond * sCorrectionLUTSize + 0.5f) & sCorrectionLUTMask;
359 return mDiamondCorrectionLUT[angleT];
363 const float uniformAngleT = mDiamondCorrectionLUT[angleT];
366 return static_cast<int>(uniformAngleT * mLUTResolution + 0.5f) & mLUTResolutionMask;
371 const float angle = std::atan2(x, z);
380 const simd xN = x * invLen;
381 const simd yN = y * invLen;
384 return Math::FMA(diamond,
simd(mLUTResolution), 0.5f).to_mask() & mLUTResolutionMask;
387 inline void LUT2D::Resize(
uint16 resolution,
uint32 numTargetChannels)
390 JPL_ASSERT(numTargetChannels <= std::numeric_limits<uint8>::max());
393 resolution = std::bit_ceil(resolution);
396 mData.resize(numTargetChannels * resolution, 0.0f);
398 mLUTResolution = resolution;
399 mLUTResolutionMask = mLUTResolution - 1;
400 mInvLUTResolution = 1.0f / mLUTResolution;
401 mNumTargetChannels =
static_cast<uint8>(numTargetChannels);
406 const Vec2 direction =
FromDiamond((
static_cast<float>(pos) *
static_cast<float>(mInvLUTResolution)));
407 float angle = atan2f(direction.
Y, direction.
X);
415 JPL_ASSERT(outGains.size() <= mNumTargetChannels);
417 const float* speakerGain = &mData[mNumTargetChannels * lutPosition];
418 std::memcpy(outGains.data(), speakerGain,
sizeof(
float) * outGains.size());
436 static constexpr std::size_t bufferSize = 32 *
simd::size();
439 float buffer[bufferSize];
443 static_cast<uint32>(mNumTargetChannels) << 1,
444 (
static_cast<uint32>(mNumTargetChannels) << 1) + mNumTargetChannels
451 std::memcpy(&buffer[dest], &mData[lutPositions[i]],
sizeof(
float) * mNumTargetChannels);
455 for (
uint32 si = 0; si < outGains.size(); ++si)
458 outGains[si] =
simd(buffer[si], buffer[si + offsets[1]], buffer[si + offsets[2]], buffer[si + offsets[3]]);
465 auto isPow2 = [](
uint32 n) {
return n && ((n & (n - 1)) == 0); };
466 JPL_ASSERT(isPow2(N_uniform) &&
"N_uniform must be power-of-two");
467 JPL_ASSERT(isPow2(M_corr) &&
"M_corr must be power-of-two");
469 outCorrectionLut.resize(M_corr);
471 const float invM_Corr = 1.0f / M_corr;
473 for (
uint32 j = 0; j < M_corr; ++j)
476 const float p =
static_cast<float>(j) * invM_Corr;
482 float theta = std::atan2(v.
X, v.
Y);
487 const float t = (theta * JPL_INV_TWO_PI) *
static_cast<float>(N_uniform);
488 const auto idx =
static_cast<uint32>(std::llround(t)) & (N_uniform - 1);
490 outCorrectionLut[j] = idx;
496 auto isPow2 = [](uint32_t n) {
return n && ((n & (n - 1)) == 0); };
499 outCorrectionLUT.resize(M_corr);
501 const float invM_Corr = 1.0f / M_corr;
503 for (uint32_t j = 0; j < M_corr; ++j)
506 const float p =
static_cast<float>(j) * invM_Corr;
511 float theta = std::atan2(v.
X, v.
Y);
515 outCorrectionLUT[j] = theta * JPL_INV_TWO_PI;
521 JPL_INLINE
float CircularLerp01(
float a,
float b,
float t)
525 if (d > 0.5f) d -= 1.0f;
526 if (d < -0.5f) d += 1.0f;
528 if (u < 0.0f) u += 1.0f;
529 if (u >= 1.0f) u -= 1.0f;
535 template<auto GetSpeakerAngleFunction>
539 mChannelMapTarget = channelMap;
548 static constexpr bool skipLFE =
false;
550 JPL_ASSERT(mChannelAngels.size() == mNumInternalChannels);
554 ComputePairMatrices();
558 mChannelConversionWeights.
Resize(mNumTargetChannels, mNumInternalChannels);
559 ComputeChannelConversionRectangularWeights(mChannelMapInternal, mChannelMapTarget, mChannelConversionWeights);
562 mLUT.Resize(LUTType::LUTStats::Resolution, mNumTargetChannels);
565 template<auto GetSpeakerAngleFunction>
568 float shortestAperture = std::numeric_limits<float>::max();
570 const uint32 lastSpeakerId = mChannelAngels.back().ChannelId;
575 shortestAperture = std::min(shortestAperture, JPL_TWO_PI - cha1.
Angle + cha2.Angle);
577 shortestAperture = std::min(shortestAperture, cha2.Angle - cha1.
Angle);
580 ForEachChannelAnglePair(*
this, findShortestAperture);
582 return shortestAperture;
585 template<auto GetSpeakerAngleFunction>
589 for (
const ChannelPair& pair : mChannelPairs)
591 Vec2 gains = pair.invL.Transform(direction);
593 if (gains.
X < 0.0f || gains.
Y < 0.0f)
598 if (RequiresChannelConversion())
600 StoreChannelGainsConverted(pair.ChannelId1, pair.ChannelId2, gains, lutOffset);
606 mLUT.mData[lutOffset + pair.ChannelId1] = gains.
X;
607 mLUT.mData[lutOffset + pair.ChannelId2] = gains.
Y;
618 template<auto GetSpeakerAngleFunction>
621 bool bAnyFailed =
false;
624 const float step = 1.0f / mLUT.GetLUTResolution();
625 float diamond = 0.0f;
627 for (
uint32 pos = 0; pos < mLUT.GetLUTResolution(); ++pos, diamond += step)
635 const uint32 offset = mNumTargetChannels * pos;
638 bAnyFailed |= ComputeCellFor(direction, offset);
644 template<auto GetSpeakerAngleFunction>
664 mChannelPairs.emplace_back(
670 ForEachChannelAnglePair(*
this, makeInvMat);
673 template<auto GetSpeakerAngleFunction>
674 template<
class ThisType,
class CallbackType>
675 inline void LUTBuilder2D<GetSpeakerAngleFunction>::ForEachChannelAnglePair(ThisType& self,
676 CallbackType&& callback)
678 auto sanitizeLFEIndex = [&self](
uint32 idx)
680 return idx + (self.mChannelAngels[idx].ChannelId == self.mLFEIndex);
683 for (
uint32 ch = 0; ch < self.mChannelAngels.size() - 1; ++ch)
685 const uint32 ch1 = sanitizeLFEIndex(ch);
686 const uint32 ch2 = sanitizeLFEIndex(ch1 + 1);
688 const ChannelAngle& cha1 = self.mChannelAngels[ch1];
689 const ChannelAngle& cha2 = self.mChannelAngels[ch2];
690 callback(cha1, cha2);
695 const uint32 ch1 =
static_cast<uint32>(self.mChannelAngels.size()) - 1;
696 const uint32 ch2 = sanitizeLFEIndex(0);
697 const ChannelAngle& cha1 = self.mChannelAngels[ch1];
698 const ChannelAngle& cha2 = self.mChannelAngels[ch2];
699 callback(cha1, cha2);
703 template<auto GetSpeakerAngleFunction>
704 JPL_INLINE
void LUTBuilder2D<GetSpeakerAngleFunction>::StoreChannelGainsConverted(
uint32 channelId1,
709 std::span<float> targetGains(&mLUT.mData[lutOffset], mNumTargetChannels);
713 ApplyChannelConversion({ channelId1, channelId2 }, gains, targetGains);
720 template<auto GetSpeakerAngleFunction>
721 inline void LUTBuilder2D<GetSpeakerAngleFunction>::ApplyChannelConversion(
const std::pair<uint32, uint32>& inChannelIds,
723 std::span<float> outValues)
const
725 for (
uint32 iChannelOut = 0; iChannelOut < outValues.size(); ++iChannelOut)
727 const float accumulation =
728 inGains.X * mChannelConversionWeights[iChannelOut][inChannelIds.first] +
729 inGains.Y * mChannelConversionWeights[iChannelOut][inChannelIds.second];
731 outValues[iChannelOut] = accumulation;
735#if JPL_VALIDATE_VBAP_LUT
736 template<auto GetSpeakerAngleFunction>
737 void LUTBuilder2D<GetSpeakerAngleFunction>::ValidateLUT()
const
744 for (
uint32 i = 0; i < mLUT->Speakers.size(); ++i)
746 if (!LUTCodec::IsValidCode(i))
749 const auto& speakers = mLUT->Speakers[i];
755 for (
uint32 i = 0; i < mLUT->Gains.size(); ++i)
757 if (!LUTCodec::IsValidCode(i))
760 const auto& gains = mLUT->Gains[i];
762 const std::array<float, 3> gainsDecoded
#define JPL_ASSERT(inExpression,...)
Main assert macro, usage: JPL_ASSERT(condition, message) or JPL_ASSERT(condition)
Definition ErrorReporting.h:80
#define JPL_ENSURE(inExpression,...)
Define ENSURE.
Definition ErrorReporting.h:94
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:154
constexpr uint32 GetNumChannels() const noexcept
Definition ChannelMap.h:166
static constexpr ChannelMap FromChannelMask(uint32 channelMask)
Definition ChannelMap.h:202
constexpr uint32 GetChannelIndex(EChannel channel) const
Definition ChannelMap.h:167
Look-up table containing channel gains for each direction.
Definition VBAPLUT2D.h:56
JPL_INLINE int AngleNormalizedToLUTPosition(float angleNormalised) const
Definition VBAPLUT2D.h:328
JPL_INLINE bool IsInitialized() const noexcept
Definition VBAPLUT2D.h:107
static void BuildDiamondToAngleNormLUT(Array< float > &outCorrectionLUT, uint32 M_corr=1024)
Definition VBAPLUT2D.h:494
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:346
static void BuildDiamondToUniformIndexLUT(Array< uint32 > &outCorrectionLUT, uint32 N_uniform, uint32 M_corr=1024)
Definition VBAPLUT2D.h:462
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:338
JPL_INLINE float LUTPositionToAngle(int pos) const
Definition VBAPLUT2D.h:404
JPL_INLINE void GetSpeakerGains(int lutPosition, std::span< float > outGains) const
Get preprocessed speaker gains at specific LUT posotion.
Definition VBAPLUT2D.h:413
JPL_INLINE float GetLUTValue(uint32 positionInLUT) const
Definition VBAPLUT2D.h:120
Forward declarations.
Definition VBAPLUT2D.h:254
bool BuildForAllDirections()
Build the entire LUT for all directions.
Definition VBAPLUT2D.h:619
LUTBuilder2D(ChannelMap channelMap, LUTType &LUT)
Definition VBAPLUT2D.h:536
bool ComputeCellFor(const Vec2 &direction, int lutOffset)
Comput LUT gains for given direction and LUT offset.
Definition VBAPLUT2D.h:586
JPL_INLINE bool RequiresChannelConversion() const noexcept
Definition VBAPLUT2D.h:266
float FindShortestAperture() const
Find shortest aperture between two speakers of the target map.
Definition VBAPLUT2D.h:566
Definition VBAPLUT2D.h:229
static JPL_INLINE QueryType Query(const LUT2D &LUT)
Make LUTQuery object to query 'LUT' for speaker gains.
Definition VBAPLUT2D.h:244
std::remove_cvref_t< decltype(GetSpeakerVectorFunction(EChannel{}))> Vec3Type
Definition VBAPLUT2D.h:231
LUTBuilder2D< GetSpeakerAngleFunction > BuilderType
Definition VBAPLUT2D.h:233
static JPL_INLINE BuilderType MakeBuilder(ChannelMap channelMap, LUT2D &lut)
Make LUTBuilder object to build LUT for given 'channelMap' and 'LUTType'.
Definition VBAPLUT2D.h:238
LUTQuery2D QueryType
Definition VBAPLUT2D.h:234
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:212
JPL_INLINE LUTQuery2D(const LUT2D &lut) noexcept
Definition VBAPLUT2D.h:187
const LUT2D & LUT
Definition VBAPLUT2D.h:221
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
constexpr uint32 Quad
Definition ChannelMap.h:101
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
std::uint32_t uint32
Definition Core.h:311
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
std::uint8_t uint8
Definition Core.h:309
JPL_INLINE auto GetZ(const Vec3Type &v) noexcept
Definition Vec3Traits.h:37
std::pmr::memory_resource * GetDefaultMemoryResource() noexcept
Definition Memory.h:42
EChannel
Definition ChannelMap.h:39
@ LFE
Definition ChannelMap.h:43
constexpr float ToDiamond(Vec2 dir) noexcept
"Diamond Encoding" of a 2D unit vector as per:
Definition DirectionEncoding.h:197
std::uint16_t uint16
Definition Core.h:310
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
float Angle
Definition VBAPEx.h:242
uint32 ChannelId
Definition VBAPEx.h:243
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:258
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
JPL_INLINE constexpr Vec2 & Normalize() noexcept
Definition MinimalVec2.h:37
float X
Definition MinimalVec2.h:30
float Y
Definition MinimalVec2.h:31
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