aboutsummaryrefslogtreecommitdiffstats
path: root/alc/alu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'alc/alu.cpp')
-rw-r--r--alc/alu.cpp483
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)