diff options
Diffstat (limited to 'alc/alu.cpp')
-rw-r--r-- | alc/alu.cpp | 483 |
1 files changed, 272 insertions, 211 deletions
diff --git a/alc/alu.cpp b/alc/alu.cpp index e9ad68b1..e0858b18 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -36,6 +36,7 @@ #include <limits> #include <memory> #include <new> +#include <optional> #include <stdint.h> #include <utility> @@ -76,7 +77,6 @@ #include "opthelpers.h" #include "ringbuffer.h" #include "strutils.h" -#include "threads.h" #include "vecmat.h" #include "vector.h" @@ -135,12 +135,6 @@ float ZScale{1.0f}; float NfcScale{1.0f}; -struct ChanMap { - Channel channel; - float angle; - float elevation; -}; - using HrtfDirectMixerFunc = void(*)(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples, float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize); @@ -285,8 +279,8 @@ ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState void DeviceBase::ProcessHrtf(const size_t SamplesToDo) { /* HRTF is stereo output only. */ - const uint lidx{RealOut.ChannelIndex[FrontLeft]}; - const uint ridx{RealOut.ChannelIndex[FrontRight]}; + const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; + const size_t ridx{RealOut.ChannelIndex[FrontRight]}; MixDirectHrtf(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer, HrtfAccumData, mHrtfState->mTemp.data(), mHrtfState->mChannels.data(), mHrtfState->mIrSize, SamplesToDo); @@ -300,9 +294,9 @@ void DeviceBase::ProcessAmbiDec(const size_t SamplesToDo) void DeviceBase::ProcessAmbiDecStablized(const size_t SamplesToDo) { /* Decode with front image stablization. */ - const uint lidx{RealOut.ChannelIndex[FrontLeft]}; - const uint ridx{RealOut.ChannelIndex[FrontRight]}; - const uint cidx{RealOut.ChannelIndex[FrontCenter]}; + const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; + const size_t ridx{RealOut.ChannelIndex[FrontRight]}; + const size_t cidx{RealOut.ChannelIndex[FrontCenter]}; AmbiDecoder->processStablize(RealOut.Buffer, Dry.Buffer.data(), lidx, ridx, cidx, SamplesToDo); @@ -311,8 +305,8 @@ void DeviceBase::ProcessAmbiDecStablized(const size_t SamplesToDo) void DeviceBase::ProcessUhj(const size_t SamplesToDo) { /* UHJ is stereo output only. */ - const uint lidx{RealOut.ChannelIndex[FrontLeft]}; - const uint ridx{RealOut.ChannelIndex[FrontRight]}; + const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; + const size_t ridx{RealOut.ChannelIndex[FrontRight]}; /* Encode to stereo-compatible 2-channel UHJ output. */ mUhjEncoder->encode(RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(), @@ -325,8 +319,8 @@ void DeviceBase::ProcessBs2b(const size_t SamplesToDo) AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo); /* BS2B is stereo output only. */ - const uint lidx{RealOut.ChannelIndex[FrontLeft]}; - const uint ridx{RealOut.ChannelIndex[FrontRight]}; + const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; + const size_t ridx{RealOut.ChannelIndex[FrontRight]}; /* Now apply the BS2B binaural/crossfeed filter. */ bs2b_cross_feed(Bs2b.get(), RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(), @@ -376,28 +370,28 @@ void UpsampleBFormatTransform( } -inline auto& GetAmbiScales(AmbiScaling scaletype) noexcept +constexpr auto GetAmbiScales(AmbiScaling scaletype) noexcept { switch(scaletype) { - case AmbiScaling::FuMa: return AmbiScale::FromFuMa(); - case AmbiScaling::SN3D: return AmbiScale::FromSN3D(); - case AmbiScaling::UHJ: return AmbiScale::FromUHJ(); + case AmbiScaling::FuMa: return al::span{AmbiScale::FromFuMa}; + case AmbiScaling::SN3D: return al::span{AmbiScale::FromSN3D}; + case AmbiScaling::UHJ: return al::span{AmbiScale::FromUHJ}; case AmbiScaling::N3D: break; } - return AmbiScale::FromN3D(); + return al::span{AmbiScale::FromN3D}; } -inline auto& GetAmbiLayout(AmbiLayout layouttype) noexcept +constexpr auto GetAmbiLayout(AmbiLayout layouttype) noexcept { - if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa(); - return AmbiIndex::FromACN(); + if(layouttype == AmbiLayout::FuMa) return al::span{AmbiIndex::FromFuMa}; + return al::span{AmbiIndex::FromACN}; } -inline auto& GetAmbi2DLayout(AmbiLayout layouttype) noexcept +constexpr auto GetAmbi2DLayout(AmbiLayout layouttype) noexcept { - if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa2D(); - return AmbiIndex::FromACN2D(); + if(layouttype == AmbiLayout::FuMa) return al::span{AmbiIndex::FromFuMa2D}; + return al::span{AmbiIndex::FromACN2D}; } @@ -490,9 +484,8 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa auto evt_vec = ring->getWriteVector(); if(evt_vec.first.len > 0) LIKELY { - AsyncEvent *evt{al::construct_at(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf), - AsyncEvent::ReleaseEffectState)}; - evt->u.mEffectState = oldstate; + auto &evt = InitAsyncEvent<AsyncEffectReleaseEvent>(evt_vec.first.buf); + evt.mEffectState = oldstate; ring->writeAdvance(1); } else @@ -521,27 +514,76 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa } -/* Scales the given azimuth toward the side (+/- pi/2 radians) for positions in - * front. +/* Scales the azimuth of the given vector by 3 if it's in front. Effectively + * scales +/-30 degrees to +/-90 degrees, leaving > +90 and < -90 alone. */ -inline float ScaleAzimuthFront(float azimuth, float scale) +inline std::array<float,3> ScaleAzimuthFront3(std::array<float,3> pos) { - const float abs_azi{std::fabs(azimuth)}; - if(!(abs_azi >= al::numbers::pi_v<float>*0.5f)) - return std::copysign(minf(abs_azi*scale, al::numbers::pi_v<float>*0.5f), azimuth); - return azimuth; + if(pos[2] < 0.0f) + { + /* Normalize the length of the x,z components for a 2D vector of the + * azimuth angle. Negate Z since {0,0,-1} is angle 0. + */ + const float len2d{std::sqrt(pos[0]*pos[0] + pos[2]*pos[2])}; + float x{pos[0] / len2d}; + float z{-pos[2] / len2d}; + + /* Z > cos(pi/6) = -30 < azimuth < 30 degrees. */ + if(z > 0.866025403785f) + { + /* Triple the angle represented by x,z. */ + x = x*3.0f - x*x*x*4.0f; + z = z*z*z*4.0f - z*3.0f; + + /* Scale the vector back to fit in 3D. */ + pos[0] = x * len2d; + pos[2] = -z * len2d; + } + else + { + /* If azimuth >= 30 degrees, clamp to 90 degrees. */ + pos[0] = std::copysign(len2d, pos[0]); + pos[2] = 0.0f; + } + } + return pos; } -/* Wraps the given value in radians to stay between [-pi,+pi] */ -inline float WrapRadians(float r) +/* Scales the azimuth of the given vector by 1.5 (3/2) if it's in front. */ +inline std::array<float,3> ScaleAzimuthFront3_2(std::array<float,3> pos) { - static constexpr float Pi{al::numbers::pi_v<float>}; - static constexpr float Pi2{Pi*2.0f}; - if(r > Pi) return std::fmod(Pi+r, Pi2) - Pi; - if(r < -Pi) return Pi - std::fmod(Pi-r, Pi2); - return r; + if(pos[2] < 0.0f) + { + const float len2d{std::sqrt(pos[0]*pos[0] + pos[2]*pos[2])}; + float x{pos[0] / len2d}; + float z{-pos[2] / len2d}; + + /* Z > cos(pi/3) = -60 < azimuth < 60 degrees. */ + if(z > 0.5f) + { + /* Halve the angle represented by x,z. */ + x = std::copysign(std::sqrt((1.0f - z) * 0.5f), x); + z = std::sqrt((1.0f + z) * 0.5f); + + /* Triple the angle represented by x,z. */ + x = x*3.0f - x*x*x*4.0f; + z = z*z*z*4.0f - z*3.0f; + + /* Scale the vector back to fit in 3D. */ + pos[0] = x * len2d; + pos[2] = -z * len2d; + } + else + { + /* If azimuth >= 60 degrees, clamp to 90 degrees. */ + pos[0] = std::copysign(len2d, pos[0]); + pos[2] = 0.0f; + } + } + return pos; } + /* Begin ambisonic rotation helpers. * * Rotating first-order B-Format just needs a straight-forward X/Y/Z rotation @@ -561,19 +603,18 @@ inline float WrapRadians(float r) * precomputed since they're constant. The second-order coefficients are * followed by the third-order coefficients, etc. */ -template<size_t L> -constexpr size_t CalcRotatorSize() -{ return (L*2 + 1)*(L*2 + 1) + CalcRotatorSize<L-1>(); } - -template<> constexpr size_t CalcRotatorSize<0>() = delete; -template<> constexpr size_t CalcRotatorSize<1>() = delete; -template<> constexpr size_t CalcRotatorSize<2>() { return 5*5; } +constexpr size_t CalcRotatorSize(size_t l) noexcept +{ + if(l >= 2) + return (l*2 + 1)*(l*2 + 1) + CalcRotatorSize(l-1); + return 0; +} struct RotatorCoeffs { struct CoeffValues { float u, v, w; }; - std::array<CoeffValues,CalcRotatorSize<MaxAmbiOrder>()> mCoeffs{}; + std::array<CoeffValues,CalcRotatorSize(MaxAmbiOrder)> mCoeffs{}; RotatorCoeffs() { @@ -585,17 +626,38 @@ struct RotatorCoeffs { { for(int m{-l};m <= l;++m) { - // compute u,v,w terms of Eq.8.1 (Table I) - const bool d{m == 0}; // the delta function d_m0 - const float denom{static_cast<float>((std::abs(n) == l) ? - (2*l) * (2*l - 1) : (l*l - n*n))}; - - const int abs_m{std::abs(m)}; - coeffs->u = std::sqrt(static_cast<float>(l*l - m*m)/denom); - coeffs->v = std::sqrt(static_cast<float>(l+abs_m-1) * - static_cast<float>(l+abs_m) / denom) * (1.0f+d) * (1.0f - 2.0f*d) * 0.5f; - coeffs->w = std::sqrt(static_cast<float>(l-abs_m-1) * - static_cast<float>(l-abs_m) / denom) * (1.0f-d) * -0.5f; + /* compute u,v,w terms of Eq.8.1 (Table I) + * + * const bool d{m == 0}; // the delta function d_m0 + * const double denom{(std::abs(n) == l) ? + * (2*l) * (2*l - 1) : (l*l - n*n)}; + * + * const int abs_m{std::abs(m)}; + * coeffs->u = std::sqrt((l*l - m*m) / denom); + * coeffs->v = std::sqrt((l+abs_m-1) * (l+abs_m) / denom) * + * (1.0+d) * (1.0 - 2.0*d) * 0.5; + * coeffs->w = std::sqrt((l-abs_m-1) * (l-abs_m) / denom) * + * (1.0-d) * -0.5; + */ + + const double denom{static_cast<double>((std::abs(n) == l) ? + (2*l) * (2*l - 1) : (l*l - n*n))}; + + if(m == 0) + { + coeffs->u = static_cast<float>(std::sqrt(l * l / denom)); + coeffs->v = static_cast<float>(std::sqrt((l-1) * l / denom) * -1.0); + coeffs->w = 0.0f; + } + else + { + const int abs_m{std::abs(m)}; + coeffs->u = static_cast<float>(std::sqrt((l*l - m*m) / denom)); + coeffs->v = static_cast<float>(std::sqrt((l+abs_m-1) * (l+abs_m) / denom) * + 0.5); + coeffs->w = static_cast<float>(std::sqrt((l-abs_m-1) * (l-abs_m) / denom) * + -0.5); + } ++coeffs; } } @@ -679,26 +741,36 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order) float r{0.0f}; // computes Eq.8.1 - const float u{coeffs->u}; - if(u != 0.0f) r += u * U(l, m, n, last_band, matrix); - const float v{coeffs->v}; - if(v != 0.0f) r += v * V(l, m, n, last_band, matrix); - const float w{coeffs->w}; - if(w != 0.0f) r += w * W(l, m, n, last_band, matrix); + if(const float u{coeffs->u}; u != 0.0f) + r += u * U(l, m, n, last_band, matrix); + if(const float v{coeffs->v}; v != 0.0f) + r += v * V(l, m, n, last_band, matrix); + if(const float w{coeffs->w}; w != 0.0f) + r += w * W(l, m, n, last_band, matrix); matrix[y][x] = r; ++coeffs; } } last_band = band_idx; - band_idx += static_cast<uint>(l)*size_t{2} + 1; + band_idx += static_cast<uint>(l)*2_uz + 1; } } /* End ambisonic rotation helpers. */ -constexpr float Deg2Rad(float x) noexcept -{ return static_cast<float>(al::numbers::pi / 180.0 * x); } +constexpr float sin30{0.5f}; +constexpr float cos30{0.866025403785f}; +constexpr float sin45{al::numbers::sqrt2_v<float>*0.5f}; +constexpr float cos45{al::numbers::sqrt2_v<float>*0.5f}; +constexpr float sin110{ 0.939692620786f}; +constexpr float cos110{-0.342020143326f}; + +struct ChanPosMap { + Channel channel; + std::array<float,3> pos; +}; + struct GainTriplet { float Base, HF, LF; }; @@ -707,45 +779,45 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con const al::span<const GainTriplet,MAX_SENDS> WetGain, EffectSlot *(&SendSlots)[MAX_SENDS], const VoiceProps *props, const ContextParams &Context, DeviceBase *Device) { - static constexpr ChanMap MonoMap[1]{ - { FrontCenter, 0.0f, 0.0f } + static constexpr ChanPosMap MonoMap[1]{ + { FrontCenter, std::array{0.0f, 0.0f, -1.0f} } }, RearMap[2]{ - { BackLeft, Deg2Rad(-150.0f), Deg2Rad(0.0f) }, - { BackRight, Deg2Rad( 150.0f), Deg2Rad(0.0f) } + { BackLeft, std::array{-sin30, 0.0f, cos30} }, + { BackRight, std::array{ sin30, 0.0f, cos30} }, }, QuadMap[4]{ - { FrontLeft, Deg2Rad( -45.0f), Deg2Rad(0.0f) }, - { FrontRight, Deg2Rad( 45.0f), Deg2Rad(0.0f) }, - { BackLeft, Deg2Rad(-135.0f), Deg2Rad(0.0f) }, - { BackRight, Deg2Rad( 135.0f), Deg2Rad(0.0f) } + { FrontLeft, std::array{-sin45, 0.0f, -cos45} }, + { FrontRight, std::array{ sin45, 0.0f, -cos45} }, + { BackLeft, std::array{-sin45, 0.0f, cos45} }, + { BackRight, std::array{ sin45, 0.0f, cos45} }, }, X51Map[6]{ - { FrontLeft, Deg2Rad( -30.0f), Deg2Rad(0.0f) }, - { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) }, - { FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) }, - { LFE, 0.0f, 0.0f }, - { SideLeft, Deg2Rad(-110.0f), Deg2Rad(0.0f) }, - { SideRight, Deg2Rad( 110.0f), Deg2Rad(0.0f) } + { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, + { FrontRight, std::array{ sin30, 0.0f, -cos30} }, + { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, + { LFE, {} }, + { SideLeft, std::array{-sin110, 0.0f, -cos110} }, + { SideRight, std::array{ sin110, 0.0f, -cos110} }, }, X61Map[7]{ - { FrontLeft, Deg2Rad(-30.0f), Deg2Rad(0.0f) }, - { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) }, - { FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) }, - { LFE, 0.0f, 0.0f }, - { BackCenter, Deg2Rad(180.0f), Deg2Rad(0.0f) }, - { SideLeft, Deg2Rad(-90.0f), Deg2Rad(0.0f) }, - { SideRight, Deg2Rad( 90.0f), Deg2Rad(0.0f) } + { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, + { FrontRight, std::array{ sin30, 0.0f, -cos30} }, + { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, + { LFE, {} }, + { BackCenter, std::array{ 0.0f, 0.0f, 1.0f} }, + { SideLeft, std::array{-1.0f, 0.0f, 0.0f} }, + { SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, }, X71Map[8]{ - { FrontLeft, Deg2Rad( -30.0f), Deg2Rad(0.0f) }, - { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) }, - { FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) }, - { LFE, 0.0f, 0.0f }, - { BackLeft, Deg2Rad(-150.0f), Deg2Rad(0.0f) }, - { BackRight, Deg2Rad( 150.0f), Deg2Rad(0.0f) }, - { SideLeft, Deg2Rad( -90.0f), Deg2Rad(0.0f) }, - { SideRight, Deg2Rad( 90.0f), Deg2Rad(0.0f) } + { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, + { FrontRight, std::array{ sin30, 0.0f, -cos30} }, + { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, + { LFE, {} }, + { BackLeft, std::array{-sin30, 0.0f, cos30} }, + { BackRight, std::array{ sin30, 0.0f, cos30} }, + { SideLeft, std::array{ -1.0f, 0.0f, 0.0f} }, + { SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, }; - ChanMap StereoMap[2]{ - { FrontLeft, Deg2Rad(-30.0f), Deg2Rad(0.0f) }, - { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) } + ChanPosMap StereoMap[2]{ + { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, + { FrontRight, std::array{ sin30, 0.0f, -cos30} }, }; const auto Frequency = static_cast<float>(Device->Frequency); @@ -763,7 +835,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con } DirectMode DirectChannels{props->DirectChannels}; - const ChanMap *chans{nullptr}; + const ChanPosMap *chans{nullptr}; switch(voice->mFmtChannels) { case FmtMono: @@ -775,11 +847,13 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con case FmtStereo: if(DirectChannels == DirectMode::Off) { - /* Convert counter-clockwise to clock-wise, and wrap between - * [-pi,+pi]. - */ - StereoMap[0].angle = WrapRadians(-props->StereoPan[0]); - StereoMap[1].angle = WrapRadians(-props->StereoPan[1]); + for(size_t i{0};i < 2;++i) + { + /* StereoPan is counter-clockwise in radians. */ + const float a{props->StereoPan[i]}; + StereoMap[i].pos[0] = -std::sin(a); + StereoMap[i].pos[2] = -std::cos(a); + } } chans = StereoMap; break; @@ -845,32 +919,21 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con auto calc_coeffs = [xpos,ypos,zpos](RenderMode mode) { if(mode != RenderMode::Pairwise) - return CalcDirectionCoeffs({xpos, ypos, zpos}); - - /* Clamp Y, in case rounding errors caused it to end up outside - * of -1...+1. - */ - const float ev{std::asin(clampf(ypos, -1.0f, 1.0f))}; - /* Negate Z for right-handed coords with -Z in front. */ - const float az{std::atan2(xpos, -zpos)}; - - /* A scalar of 1.5 for plain stereo results in +/-60 degrees - * being moved to +/-90 degrees for direct right and left - * speaker responses. - */ - return CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, 0.0f); + return CalcDirectionCoeffs(std::array{xpos, ypos, zpos}, 0.0f); + const auto pos = ScaleAzimuthFront3_2(std::array{xpos, ypos, zpos}); + return CalcDirectionCoeffs(pos, 0.0f); }; - auto&& scales = GetAmbiScales(voice->mAmbiScaling); + const auto scales = GetAmbiScales(voice->mAmbiScaling); auto coeffs = calc_coeffs(Device->mRenderMode); if(!(coverage > 0.0f)) { - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base*scales[0], + ComputePanGains(&Device->Dry, coeffs, DryGain.Base*scales[0], voice->mChans[0].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base*scales[0], + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base*scales[0], voice->mChans[0].mWetParams[i].Gains.Target); } } @@ -922,25 +985,25 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con { if(voice->mAmbiOrder == 1) { - auto&& upsampler = Is2DAmbisonic(voice->mFmtChannels) ? - AmbiScale::FirstOrder2DUp : AmbiScale::FirstOrderUp; + const auto upsampler = Is2DAmbisonic(voice->mFmtChannels) ? + al::span{AmbiScale::FirstOrder2DUp} : al::span{AmbiScale::FirstOrderUp}; UpsampleBFormatTransform(mixmatrix, upsampler, shrot, Device->mAmbiOrder); } else if(voice->mAmbiOrder == 2) { - auto&& upsampler = Is2DAmbisonic(voice->mFmtChannels) ? - AmbiScale::SecondOrder2DUp : AmbiScale::SecondOrderUp; + const auto upsampler = Is2DAmbisonic(voice->mFmtChannels) ? + al::span{AmbiScale::SecondOrder2DUp} : al::span{AmbiScale::SecondOrderUp}; UpsampleBFormatTransform(mixmatrix, upsampler, shrot, Device->mAmbiOrder); } else if(voice->mAmbiOrder == 3) { - auto&& upsampler = Is2DAmbisonic(voice->mFmtChannels) ? - AmbiScale::ThirdOrder2DUp : AmbiScale::ThirdOrderUp; + const auto upsampler = Is2DAmbisonic(voice->mFmtChannels) ? + al::span{AmbiScale::ThirdOrder2DUp} : al::span{AmbiScale::ThirdOrderUp}; UpsampleBFormatTransform(mixmatrix, upsampler, shrot, Device->mAmbiOrder); } else if(voice->mAmbiOrder == 4) { - auto&& upsampler = AmbiScale::FourthOrder2DUp; + const auto upsampler = al::span{AmbiScale::FourthOrder2DUp}; UpsampleBFormatTransform(mixmatrix, upsampler, shrot, Device->mAmbiOrder); } else @@ -974,13 +1037,13 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con for(size_t x{0};x < MaxAmbiChannels;++x) coeffs[x] += mixmatrix[acn][x] * scale; - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base, + ComputePanGains(&Device->Dry, coeffs, DryGain.Base, voice->mChans[c].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } @@ -1028,12 +1091,12 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con if(chans[c].channel == LFE) continue; - const auto coeffs = CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f); + const auto coeffs = CalcDirectionCoeffs(chans[c].pos, 0.0f); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1047,21 +1110,21 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con if(Distance > std::numeric_limits<float>::epsilon()) { - const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))}; - const float src_az{std::atan2(xpos, -zpos)}; - if(voice->mFmtChannels == FmtMono) { + const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))}; + const float src_az{std::atan2(xpos, -zpos)}; + Device->mHrtf->getCoeffs(src_ev, src_az, Distance*NfcScale, Spread, voice->mChans[0].mDryParams.Hrtf.Target.Coeffs, voice->mChans[0].mDryParams.Hrtf.Target.Delay); voice->mChans[0].mDryParams.Hrtf.Target.Gain = DryGain.Base; - const auto coeffs = CalcAngleCoeffs(src_az, src_ev, Spread); + const auto coeffs = CalcDirectionCoeffs(std::array{xpos, ypos, zpos}, Spread); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[0].mWetParams[i].Gains.Target); } } @@ -1077,28 +1140,32 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con * the source position, at full spread (pi*2), each channel is * left unchanged. */ - const float ev{lerpf(src_ev, chans[c].elevation, inv_pi_v<float>/2.0f * Spread)}; - - float az{chans[c].angle - src_az}; - if(az < -pi_v<float>) az += pi_v<float>*2.0f; - else if(az > pi_v<float>) az -= pi_v<float>*2.0f; - - az *= inv_pi_v<float>/2.0f * Spread; + const float a{1.0f - (inv_pi_v<float>/2.0f)*Spread}; + std::array pos{ + lerpf(chans[c].pos[0], xpos, a), + lerpf(chans[c].pos[1], ypos, a), + lerpf(chans[c].pos[2], zpos, a)}; + const float len{std::sqrt(pos[0]*pos[0] + pos[1]*pos[1] + pos[2]*pos[2])}; + if(len < 1.0f) + { + pos[0] /= len; + pos[1] /= len; + pos[2] /= len; + } - az += src_az; - if(az < -pi_v<float>) az += pi_v<float>*2.0f; - else if(az > pi_v<float>) az -= pi_v<float>*2.0f; + const float ev{std::asin(clampf(pos[1], -1.0f, 1.0f))}; + const float az{std::atan2(pos[0], -pos[2])}; Device->mHrtf->getCoeffs(ev, az, Distance*NfcScale, 0.0f, voice->mChans[c].mDryParams.Hrtf.Target.Coeffs, voice->mChans[c].mDryParams.Hrtf.Target.Delay); voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base; - const auto coeffs = CalcAngleCoeffs(az, ev, 0.0f); + const auto coeffs = CalcDirectionCoeffs(pos, 0.0f); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1124,19 +1191,21 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con /* Get the HRIR coefficients and delays for this channel * position. */ - Device->mHrtf->getCoeffs(chans[c].elevation, chans[c].angle, - std::numeric_limits<float>::infinity(), spread, + const float ev{std::asin(chans[c].pos[1])}; + const float az{std::atan2(chans[c].pos[0], -chans[c].pos[2])}; + + Device->mHrtf->getCoeffs(ev, az, std::numeric_limits<float>::infinity(), spread, voice->mChans[c].mDryParams.Hrtf.Target.Coeffs, voice->mChans[c].mDryParams.Hrtf.Target.Delay); voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base; /* Normal panning for auxiliary sends. */ - const auto coeffs = CalcAngleCoeffs(chans[c].angle, chans[c].elevation, spread); + const auto coeffs = CalcDirectionCoeffs(chans[c].pos, spread); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1171,19 +1240,18 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con auto calc_coeffs = [xpos,ypos,zpos,Spread](RenderMode mode) { if(mode != RenderMode::Pairwise) - return CalcDirectionCoeffs({xpos, ypos, zpos}, Spread); - const float ev{std::asin(clampf(ypos, -1.0f, 1.0f))}; - const float az{std::atan2(xpos, -zpos)}; - return CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, Spread); + return CalcDirectionCoeffs(std::array{xpos, ypos, zpos}, Spread); + const auto pos = ScaleAzimuthFront3_2(std::array{xpos, ypos, zpos}); + return CalcDirectionCoeffs(pos, Spread); }; const auto coeffs = calc_coeffs(Device->mRenderMode); - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base, + ComputePanGains(&Device->Dry, coeffs, DryGain.Base, voice->mChans[0].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[0].mWetParams[i].Gains.Target); } } @@ -1191,9 +1259,6 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con { using namespace al::numbers; - const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))}; - const float src_az{std::atan2(xpos, -zpos)}; - for(size_t c{0};c < num_channels;c++) { /* Special-case LFE */ @@ -1213,29 +1278,29 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con * at the source position, at full spread (pi*2), each * channel position is left unchanged. */ - const float ev{lerpf(src_ev, chans[c].elevation, - inv_pi_v<float>/2.0f * Spread)}; - - float az{chans[c].angle - src_az}; - if(az < -pi_v<float>) az += pi_v<float>*2.0f; - else if(az > pi_v<float>) az -= pi_v<float>*2.0f; - - az *= inv_pi_v<float>/2.0f * Spread; - - az += src_az; - if(az < -pi_v<float>) az += pi_v<float>*2.0f; - else if(az > pi_v<float>) az -= pi_v<float>*2.0f; + const float a{1.0f - (inv_pi_v<float>/2.0f)*Spread}; + std::array pos{ + lerpf(chans[c].pos[0], xpos, a), + lerpf(chans[c].pos[1], ypos, a), + lerpf(chans[c].pos[2], zpos, a)}; + const float len{std::sqrt(pos[0]*pos[0] + pos[1]*pos[1] + pos[2]*pos[2])}; + if(len < 1.0f) + { + pos[0] /= len; + pos[1] /= len; + pos[2] /= len; + } if(Device->mRenderMode == RenderMode::Pairwise) - az = ScaleAzimuthFront(az, 3.0f); - const auto coeffs = CalcAngleCoeffs(az, ev, 0.0f); + pos = ScaleAzimuthFront3(pos); + const auto coeffs = CalcDirectionCoeffs(pos, 0.0f); - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base, + ComputePanGains(&Device->Dry, coeffs, DryGain.Base, voice->mChans[c].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1274,16 +1339,15 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con continue; } - const auto coeffs = CalcAngleCoeffs((Device->mRenderMode == RenderMode::Pairwise) - ? ScaleAzimuthFront(chans[c].angle, 3.0f) : chans[c].angle, - chans[c].elevation, spread); + const auto coeffs = CalcDirectionCoeffs((Device->mRenderMode==RenderMode::Pairwise) + ? ScaleAzimuthFront3(chans[c].pos) : chans[c].pos, spread); - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base, + ComputePanGains(&Device->Dry, coeffs, DryGain.Base, voice->mChans[c].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1700,22 +1764,21 @@ void SendSourceStateEvent(ContextBase *context, uint id, VChangeState state) auto evt_vec = ring->getWriteVector(); if(evt_vec.first.len < 1) return; - AsyncEvent *evt{al::construct_at(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf), - AsyncEvent::SourceStateChange)}; - evt->u.srcstate.id = id; + auto &evt = InitAsyncEvent<AsyncSourceStateEvent>(evt_vec.first.buf); + evt.mId = id; switch(state) { case VChangeState::Reset: - evt->u.srcstate.state = AsyncEvent::SrcState::Reset; + evt.mState = AsyncSrcState::Reset; break; case VChangeState::Stop: - evt->u.srcstate.state = AsyncEvent::SrcState::Stop; + evt.mState = AsyncSrcState::Stop; break; case VChangeState::Play: - evt->u.srcstate.state = AsyncEvent::SrcState::Play; + evt.mState = AsyncSrcState::Play; break; case VChangeState::Pause: - evt->u.srcstate.state = AsyncEvent::SrcState::Pause; + evt.mState = AsyncSrcState::Pause; break; /* Shouldn't happen. */ case VChangeState::Restart: @@ -1812,7 +1875,7 @@ void ProcessVoiceChanges(ContextBase *ctx) } oldvoice->mPendingChange.store(false, std::memory_order_release); } - if(sendevt && enabledevt.test(AsyncEvent::SourceStateChange)) + if(sendevt && enabledevt.test(al::to_underlying(AsyncEnableBits::SourceState))) SendSourceStateEvent(ctx, cur->mSourceID, cur->mState); next = cur->mNext.load(std::memory_order_acquire); @@ -1855,7 +1918,7 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo) const EffectSlotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire); const al::span<Voice*> voices{ctx->getVoicesSpanAcquired()}; - /* Process pending propery updates for objects on the context. */ + /* Process pending property updates for objects on the context. */ ProcessParamUpdates(ctx, auxslots, voices); /* Clear auxiliary effect slot mixing buffers. */ @@ -2165,28 +2228,26 @@ void DeviceBase::handleDisconnect(const char *msg, ...) IncrementRef(MixCount); if(Connected.exchange(false, std::memory_order_acq_rel)) { - AsyncEvent evt{AsyncEvent::Disconnected}; + AsyncEvent evt{std::in_place_type<AsyncDisconnectEvent>}; + auto &disconnect = std::get<AsyncDisconnectEvent>(evt); va_list args; va_start(args, msg); - int msglen{vsnprintf(evt.u.disconnect.msg, sizeof(evt.u.disconnect.msg), msg, args)}; + int msglen{vsnprintf(disconnect.msg, sizeof(disconnect.msg), msg, args)}; va_end(args); - if(msglen < 0 || static_cast<size_t>(msglen) >= sizeof(evt.u.disconnect.msg)) - evt.u.disconnect.msg[sizeof(evt.u.disconnect.msg)-1] = 0; + if(msglen < 0 || static_cast<size_t>(msglen) >= sizeof(disconnect.msg)) + disconnect.msg[sizeof(disconnect.msg)-1] = 0; for(ContextBase *ctx : *mContexts.load()) { - if(ctx->mEnabledEvts.load(std::memory_order_acquire).test(AsyncEvent::Disconnected)) + RingBuffer *ring{ctx->mAsyncEvents.get()}; + auto evt_data = ring->getWriteVector().first; + if(evt_data.len > 0) { - RingBuffer *ring{ctx->mAsyncEvents.get()}; - auto evt_data = ring->getWriteVector().first; - if(evt_data.len > 0) - { - al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), evt); - ring->writeAdvance(1); - ctx->mEventSem.post(); - } + al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), evt); + ring->writeAdvance(1); + ctx->mEventSem.post(); } if(!ctx->mStopVoicesOnDisconnect) |