49 template<
class Traits = VBAPStandardTraits, auto cLUTType = VBAP::ELUTSize::KB_983>
56 template<
class Traits = VBAPStandardTraits, auto cLUTType = VBAP::ELUTSize::KB_983>
67 template<
class Traits, auto cLUTType>
70 static_assert(
VBAP::CLUTType<
decltype(cLUTType)> &&
"cLUTType must be enum class VBAP::ELUTSize");
86 &Traits::GetChannelVector,
121 [[nodiscard]]
static Dimensions For(
uint32 numChannels,
float maxGeoDistanceDot = std::numeric_limits<float>::max());
138 bool Initialize(
ChannelMap channelMap,
ChannelMap targetMap,
float shortestEdgeApertureDot = std::numeric_limits<float>::max());
140 void PrecomputeAzimuth();
147 void GenerateSpreadCap(std::span<Vec3Type> outBuffer,
float spreadNormalized)
const;
150 Dimensions mDimensions;
167 return "Failed to initialize Panning3D, provided invalid target channel map. Panning3D requires channel map with top channels.";
170 return "Trying to initialize with < 3 channels, 3D panning is not possible for such layout.";
187#define JPL_ADD_CENTER_VS 0
195 [[nodiscard]]
static JPL_INLINE
float ComputeNeigbourSampleDotFromN(
float angularDiameter,
uint32 numPoints)
199 const float alpha = 0.5f * angularDiameter;
201 const float s2 = s * s;
202 const float c2 = c * c;
203 const float dphi = JPL_TWO_PI /
static_cast<float>(numPoints);
204 const float dot = std::clamp(s2 * std::cos(dphi) + c2, -1.0f, 1.0f);
210 [[nodiscard]]
static JPL_INLINE
float ComputeNeighbourAngleFromN(
float angularDiameter,
uint32 numPoints)
212 return std::acos(ComputeNeigbourSampleDotFromN(angularDiameter, numPoints));
219 [[nodiscard]]
static JPL_INLINE
uint32 ComputeMinNumPointsOnCapRing(
float angularDiameter,
float dotM,
uint32 maxNumSamplesFallback)
221 const float alpha = 0.5f * angularDiameter;
223 const float s2 = s * s;
224 const float c2 = c * c;
230 const float rhs = std::clamp((dotM - c2) / s2, -1.0f, 1.0f);
231 const float dphi_max = std::acos(rhs);
234 return maxNumSamplesFallback;
236 return static_cast<uint32>(std::ceil(JPL_TWO_PI / dphi_max));
241 template<
class Traits, auto cLUTType>
260 mPhiTerm = numChannels == 1
261 ? JPL_PI / (mDimensions.
NumRings + 1)
262 : JPL_PI / (mDimensions.NumRings * numChannels + numChannels * 0.5f);
273 const auto numVirtualSorucesPerChannel =
static_cast<uint32>(mDimensions.
GetSize());
274 return LayoutBase::InitializeBase(channelMap, targetMap, numVirtualSorucesPerChannel);
277 template<
class Traits, auto cLUTType>
278 inline void Panning3D<Traits, cLUTType>::SourceLayout::PrecomputeAzimuth()
286 const std::size_t toMirror =
FloorToDiv2(numSIMDOps);
290 const uint32 halfSamples = toMirror >> 1;
294 const uint32 halfAndTail = halfSamples + tail;
296 mSamplesSin.resize(halfAndTail);
297 mSamplesCos.resize(halfAndTail);
299 const float thetaTerm = JPL_TWO_PI / mDimensions.NumSamplesPerRing;
300 static const simd rampFirst(0.5f, 1.5f, 2.5f, 3.5f);
302 simd theta = simd(thetaTerm) * rampFirst;
303 const simd thetaDelta(thetaTerm * 4.0f);
305 for (
uint32 i = 0; i < halfAndTail; ++i)
313 template<
class Traits, auto cLUTType>
314 inline void Panning3D<Traits, cLUTType>::SourceLayout::GenerateSpreadCap(std::span<Vec3Type> outBuffer,
float spreadNormalized)
const
331 std::pair<float, float> ringSCsBuffer[BUF_SIZE];
335 auto ringCSs = ringSCsBuffer | std::views::take(mDimensions.NumRings);
339 const float capSpread = (1.0f - spreadNormalized);
340 const float phiTerm = capSpread * mPhiTerm;
343 for (
auto& ringCS : ringCSs)
350 Vec3Type* destination = outBuffer.data();
353 for (
const auto& [sinPhi, cosPhi] : ringCSs)
355 for (
uint32 i = 0; i < mSamplesSin.size(); ++i)
357 const float sinTheta = mSamplesSin[i];
358 const float cosTheta = mSamplesCos[i];
369 const uint32 quadrantSamples = mDimensions.NumSamplesPerRing >> 2;
370 const uint32 quadrantSize = mDimensions.NumRings * quadrantSamples;
371 const uint32 halfSphereSize = quadrantSize << 1;
374 std::copy(outBuffer.begin(), outBuffer.begin() + quadrantSize, destination);
377 for (
Vec3Type& mirror : std::span(destination, quadrantSize))
378 mirror.X = -mirror.X;
381 destination += quadrantSize;
384 std::copy(outBuffer.begin(), outBuffer.begin() + halfSphereSize, destination);
387 for (
Vec3Type& mirror : std::span(destination, halfSphereSize))
388 mirror.Y = -mirror.Y;
398 outBuffer[i] = Vec3(0.0f, 0.0f, 1.0f);
403 template<
class Traits, auto cLUTType>
404 inline void Panning3D<Traits, cLUTType>::SourceLayout::GenerateSpreadCap(
Vec3SIMDBufferView& outBuffer,
float spreadNormalized)
const
407 static constexpr size_t BUF_SIZE = cMaxNumSamples;
410 StaticArray<float, BUF_SIZE> ringSs(mDimensions.NumRings);
411 StaticArray<float, BUF_SIZE> ringCs(mDimensions.NumRings);
415 static const simd ramp(1.0f, 2.0f, 3.0f, 4.0f);
417 const float capSpread = (1.0f - spreadNormalized);
418 const float phiTermS = capSpread * mPhiTerm;
420 simd phiTerm = simd(phiTermS) * ramp;
421 const simd delta(4.0f * phiTermS);
426 simd ringSin, ringCos;
428 ringSin.store(&ringSs[i]);
429 ringCos.store(&ringCs[i]);
435 float phiTermTail[4]{};
436 phiTerm.store(phiTermTail);
438 for (
uint32 t = 0; t < tail; ++t, ++i)
440 const auto [ringSin, ringCos] =
Math::SinCos(phiTermTail[t]);
446 simd* destX = outBuffer.X;
447 simd* destY = outBuffer.Y;
448 simd* destZ = outBuffer.Z;
455 const std::size_t toMirror =
FloorToDiv2(numSIMDOps);
459 const uint32 halfSamples = toMirror >> 1;
463 const uint32 halfAndTail = halfSamples + tail;
468 simd* ringsX = destX;
469 simd* ringsY = destY;
470 simd* ringsZ = destZ;
472 for (
uint32 sci = 0; sci < ringSs.size(); ++sci)
474 const auto sinPhi = simd(ringSs[sci]);
475 const auto cosPhi = simd(ringCs[sci]);
477 for (
uint32 i = 0; i < halfSamples; ++i)
479 ringsX[i] = sinPhi * mSamplesCos[i];
480 ringsY[i] = sinPhi * mSamplesSin[i];
484 ringsX += halfSamples;
485 ringsY += halfSamples;
486 ringsZ += halfSamples;
489 const uint32 totalNumHalfSamples = ringSs.size() * halfSamples;
490 const std::size_t sizeOfHalfSamples = totalNumHalfSamples *
sizeof(simd);
493 std::memcpy(ringsX, destX, sizeOfHalfSamples);
494 std::memcpy(ringsZ, destZ, sizeOfHalfSamples);
497 for (
uint32 i = 0; i < totalNumHalfSamples; ++i)
499 ringsY[i] = -destY[i];
501 destX = &ringsX[totalNumHalfSamples];
502 destY = &ringsY[totalNumHalfSamples];
503 destZ = &ringsZ[totalNumHalfSamples];
508 for (
uint32 sci = 0; sci < ringSs.size(); ++sci)
510 const auto sinPhi = simd(ringSs[sci]);
511 const auto cosPhi = simd(ringCs[sci]);
513 destX[sci] = sinPhi * mSamplesCos[halfSamples];
514 destY[sci] = sinPhi * mSamplesSin[halfSamples];
520 template<
class Traits, auto cLUTType>
526 template<
class Traits, auto cLUTType>
533 template<
class Traits, auto cLUTType>
547 uint32 samplesPerRing =
static_cast<uint32>(cMaxNumSamples);
549 if (maxGeoDistanceDot < std::numeric_limits<float>::max())
551 JPL_ASSERT(maxGeoDistanceDot >= -1.000001f && maxGeoDistanceDot <= 1.000001f);
557 const float m = std::acos(maxGeoDistanceDot);
560 totalRings =
static_cast<uint32>(std::ceil(JPL_PI / std::max(m, 1e-6f)));
567 if (numChannels <= 2)
573 samplesPerRing = totalRings << 1;
580 const float angularDiameter = JPL_TWO_PI / numChannels;
581 samplesPerRing = Internal::ComputeMinNumPointsOnCapRing(angularDiameter, maxGeoDistanceDot, cMaxNumSamples);
583#if defined(JPL_DEBUG) || defined(JPL_TEST)
585 const float dotActual = Internal::ComputeNeigbourSampleDotFromN(angularDiameter, samplesPerRing);
587 JPL_ASSERT(dotActual + 1e-6f >= maxGeoDistanceDot);
595 if (
RoundUpBy4(samplesPerRing) * totalRings > cMaxNumVirtualSources)
605 .NumRings = std::max(1u,
606 static_cast<uint32>(std::ceil(totalRings /
static_cast<float>(numChannels)))),
610 .NumSamplesPerRing =
RoundUpBy4(samplesPerRing)
#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
#define JPL_ERROR_TAG(tag, message)
Definition ErrorReporting.h:128
#define JPL_ADD_CENTER_VS
Definition VBAPanning3D.h:187
Definition ChannelMap.h:154
constexpr uint32 GetNumChannels() const noexcept
Definition ChannelMap.h:166
constexpr bool HasTopChannels() const noexcept
Definition ChannelMap.h:163
constexpr bool HasLFE() const noexcept
Definition ChannelMap.h:162
Forward declaration.
Definition DirectionEncoding.h:111
static constexpr size_t cAxisRange
Definition DirectionEncoding.h:122
Definition VBAPLUT3D.h:68
Definition VBAPanning3D.h:69
static std::optional< const char * > IsValidTargetChannelMap(ChannelMap channelMap)
Definition VBAPanning3D.h:164
typename Base::Vec3Type Vec3Type
Definition VBAPanning3D.h:74
static constexpr size_t cLUTSize
Definition VBAPanning3D.h:78
Octahedron16Bit LUTCodec
Definition VBAPanning3D.h:77
Definition PannerBase.h:227
typename Traits::Vec3Type Vec3Type
Aliases to avoid typing wordy templates.
Definition PannerBase.h:230
Definition VBAPLUT3D.h:106
JPL_INLINE constexpr bool IsPositiveAndBelow(T1 value, T2 below) noexcept
Definition Math.h:140
JPL_INLINE std::pair< T, T > SinCos(T value) noexcept
Definition Math.h:164
Forward declaration.
Definition DummySpeakers.h:31
Definition AcousticMaterial.h:36
JPL_INLINE constexpr auto GetNumSIMDOps(std::unsigned_integral auto count) noexcept
Get number of SIMD operations that can fit into the count
Definition SIMDMath.h:51
std::uint32_t uint32
Definition Core.h:311
JPL_INLINE constexpr auto FloorToSIMDSize(std::unsigned_integral auto count) noexcept
Floor count to 4-wide simd vector.
Definition SIMDMath.h:33
JPL_INLINE constexpr auto GetSIMDTail(std::unsigned_integral auto count) noexcept
Get the remaining tail from count that won't fill a simd vector.
Definition SIMDMath.h:45
Vec3BufferView< simd > Vec3SIMDBufferView
View into a Vec3-like SoA buffer, holding separate arrays of Vec3 components X, Y,...
Definition Vec3Buffer.h:56
JPL_INLINE std::optional< const char * > IsValidSourceChannelMap(ChannelMap channelMap)
Definition PannerBase.h:193
constexpr T RoundUpBy4(T n) noexcept
Definition Bits.h:52
std::pmr::memory_resource * GetDefaultMemoryResource() noexcept
Definition Memory.h:42
JPL_INLINE constexpr auto GetDiv2Tail(std::unsigned_integral auto count) noexcept
Definition SIMDMath.h:81
JPL_INLINE constexpr auto FloorToDiv2(std::unsigned_integral auto count) noexcept
Floor count to divisible by 2.
Definition SIMDMath.h:75
OctahedronEncoding< Octahedron::Precision8bits > Octahedron16Bit
Definition DirectionEncoding.h:103
Definition VBAPLUT3D.h:112
Definition VBAPanning3D.h:110
uint32 NumSamplesPerRing
Number samples (virtual soruces) to generate for each ring of a spread cap.
Definition VBAPanning3D.h:115
uint32 NumRings
Number of rings to generate for a spread cap.
Definition VBAPanning3D.h:112
static Dimensions For(uint32 numChannels, float maxGeoDistanceDot=std::numeric_limits< float >::max())
Calculate dimensions for numChannels with maxGeoDistanceDot between points.
Definition VBAPanning3D.h:534
JPL_INLINE size_t GetSize() const noexcept
Get total number of virtual sources for these dimentions.
Definition VBAPanning3D.h:118
Implementation of the source layout for 2D panning.
Definition VBAPanning3D.h:94
static JPL_INLINE constexpr size_t GetMaxNumVirtualSources() noexcept
Definition VBAPanning3D.h:527
static constexpr size_t cMaxNumRings
Definition VBAPanning3D.h:101
JPL_INLINE float GetMinDistanceBetweenSamples() const noexcept
Definition VBAPanning3D.h:132
JPL_INLINE Dimensions GetDimensions() const noexcept
Definition VBAPanning3D.h:125
static constexpr size_t cMaxNumSamples
Definition VBAPanning3D.h:102
static constexpr size_t cMaxNumVirtualSources
Definition VBAPanning3D.h:103
typename Base::VBAPLayoutBase LayoutBase
Definition VBAPanning3D.h:95
JPL_INLINE size_t GetNumVirtualSources() const noexcept
Definition VBAPanning3D.h:521
Definition PannerBase.h:281
Definition Vec3Buffer.h:89
static constexpr std::size_t size() noexcept
Get number of element of the vector.
Definition SIMD.h:97