1 /**
2  * OpenAL cross platform audio library
3  * Copyright (C) 1999-2007 by authors.
4  * This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Library General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  *  License along with this library; if not, write to the
16  *  Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  * Or go to http://www.gnu.org/copyleft/lgpl.html
19  */
20 
21 #include "config.h"
22 
23 #include "alu.h"
24 
25 #include <algorithm>
26 #include <array>
27 #include <atomic>
28 #include <cassert>
29 #include <chrono>
30 #include <climits>
31 #include <cmath>
32 #include <cstdarg>
33 #include <cstdio>
34 #include <cstdlib>
35 #include <functional>
36 #include <iterator>
37 #include <limits>
38 #include <memory>
39 #include <new>
40 #include <numeric>
41 #include <utility>
42 
43 #include "AL/al.h"
44 #include "AL/alc.h"
45 #include "AL/efx.h"
46 
47 #include "alcmain.h"
48 #include "alcontext.h"
49 #include "almalloc.h"
50 #include "alnumeric.h"
51 #include "alspan.h"
52 #include "alstring.h"
53 #include "async_event.h"
54 #include "atomic.h"
55 #include "bformatdec.h"
56 #include "core/ambidefs.h"
57 #include "core/bs2b.h"
58 #include "core/bsinc_tables.h"
59 #include "core/cpu_caps.h"
60 #include "core/devformat.h"
61 #include "core/filters/biquad.h"
62 #include "core/filters/nfc.h"
63 #include "core/filters/splitter.h"
64 #include "core/fpu_ctrl.h"
65 #include "core/mastering.h"
66 #include "core/mixer/defs.h"
67 #include "core/uhjfilter.h"
68 #include "effects/base.h"
69 #include "effectslot.h"
70 #include "front_stablizer.h"
71 #include "hrtf.h"
72 #include "inprogext.h"
73 #include "math_defs.h"
74 #include "opthelpers.h"
75 #include "ringbuffer.h"
76 #include "strutils.h"
77 #include "threads.h"
78 #include "vecmat.h"
79 #include "voice.h"
80 #include "voice_change.h"
81 
82 struct CTag;
83 #ifdef HAVE_SSE
84 struct SSETag;
85 #endif
86 #ifdef HAVE_SSE2
87 struct SSE2Tag;
88 #endif
89 #ifdef HAVE_SSE4_1
90 struct SSE4Tag;
91 #endif
92 #ifdef HAVE_NEON
93 struct NEONTag;
94 #endif
95 struct CopyTag;
96 struct PointTag;
97 struct LerpTag;
98 struct CubicTag;
99 struct BSincTag;
100 struct FastBSincTag;
101 
102 
103 static_assert(MaxResamplerPadding >= BSincPointsMax, "MaxResamplerPadding is too small");
104 static_assert(!(MaxResamplerPadding&1), "MaxResamplerPadding is not a multiple of two");
105 
106 
107 namespace {
108 
109 constexpr uint MaxPitch{10};
110 
111 static_assert((BufferLineSize-1)/MaxPitch > 0, "MaxPitch is too large for BufferLineSize!");
112 static_assert((INT_MAX>>MixerFracBits)/MaxPitch > BufferLineSize,
113     "MaxPitch and/or BufferLineSize are too large for MixerFracBits!");
114 
115 using namespace std::placeholders;
116 
InitConeScale()117 float InitConeScale()
118 {
119     float ret{1.0f};
120     if(auto optval = al::getenv("__ALSOFT_HALF_ANGLE_CONES"))
121     {
122         if(al::strcasecmp(optval->c_str(), "true") == 0
123             || strtol(optval->c_str(), nullptr, 0) == 1)
124             ret *= 0.5f;
125     }
126     return ret;
127 }
128 
InitZScale()129 float InitZScale()
130 {
131     float ret{1.0f};
132     if(auto optval = al::getenv("__ALSOFT_REVERSE_Z"))
133     {
134         if(al::strcasecmp(optval->c_str(), "true") == 0
135             || strtol(optval->c_str(), nullptr, 0) == 1)
136             ret *= -1.0f;
137     }
138     return ret;
139 }
140 
141 } // namespace
142 
143 /* Cone scalar */
144 const float ConeScale{InitConeScale()};
145 
146 /* Localized Z scalar for mono sources */
147 const float ZScale{InitZScale()};
148 
149 namespace {
150 
151 struct ChanMap {
152     Channel channel;
153     float angle;
154     float elevation;
155 };
156 
157 using HrtfDirectMixerFunc = void(*)(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
158     const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples,
159     float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize);
160 
161 HrtfDirectMixerFunc MixDirectHrtf{MixDirectHrtf_<CTag>};
162 
SelectHrtfMixer(void)163 inline HrtfDirectMixerFunc SelectHrtfMixer(void)
164 {
165 #ifdef HAVE_NEON
166     if((CPUCapFlags&CPU_CAP_NEON))
167         return MixDirectHrtf_<NEONTag>;
168 #endif
169 #ifdef HAVE_SSE
170     if((CPUCapFlags&CPU_CAP_SSE))
171         return MixDirectHrtf_<SSETag>;
172 #endif
173 
174     return MixDirectHrtf_<CTag>;
175 }
176 
177 
BsincPrepare(const uint increment,BsincState * state,const BSincTable * table)178 inline void BsincPrepare(const uint increment, BsincState *state, const BSincTable *table)
179 {
180     size_t si{BSincScaleCount - 1};
181     float sf{0.0f};
182 
183     if(increment > MixerFracOne)
184     {
185         sf = MixerFracOne / static_cast<float>(increment);
186         sf = maxf(0.0f, (BSincScaleCount-1) * (sf-table->scaleBase) * table->scaleRange);
187         si = float2uint(sf);
188         /* The interpolation factor is fit to this diagonally-symmetric curve
189          * to reduce the transition ripple caused by interpolating different
190          * scales of the sinc function.
191          */
192         sf = 1.0f - std::cos(std::asin(sf - static_cast<float>(si)));
193     }
194 
195     state->sf = sf;
196     state->m = table->m[si];
197     state->l = (state->m/2) - 1;
198     state->filter = table->Tab + table->filterOffset[si];
199 }
200 
SelectResampler(Resampler resampler,uint increment)201 inline ResamplerFunc SelectResampler(Resampler resampler, uint increment)
202 {
203     switch(resampler)
204     {
205     case Resampler::Point:
206         return Resample_<PointTag,CTag>;
207     case Resampler::Linear:
208 #ifdef HAVE_NEON
209         if((CPUCapFlags&CPU_CAP_NEON))
210             return Resample_<LerpTag,NEONTag>;
211 #endif
212 #ifdef HAVE_SSE4_1
213         if((CPUCapFlags&CPU_CAP_SSE4_1))
214             return Resample_<LerpTag,SSE4Tag>;
215 #endif
216 #ifdef HAVE_SSE2
217         if((CPUCapFlags&CPU_CAP_SSE2))
218             return Resample_<LerpTag,SSE2Tag>;
219 #endif
220         return Resample_<LerpTag,CTag>;
221     case Resampler::Cubic:
222         return Resample_<CubicTag,CTag>;
223     case Resampler::BSinc12:
224     case Resampler::BSinc24:
225         if(increment <= MixerFracOne)
226         {
227             /* fall-through */
228         case Resampler::FastBSinc12:
229         case Resampler::FastBSinc24:
230 #ifdef HAVE_NEON
231             if((CPUCapFlags&CPU_CAP_NEON))
232                 return Resample_<FastBSincTag,NEONTag>;
233 #endif
234 #ifdef HAVE_SSE
235             if((CPUCapFlags&CPU_CAP_SSE))
236                 return Resample_<FastBSincTag,SSETag>;
237 #endif
238             return Resample_<FastBSincTag,CTag>;
239         }
240 #ifdef HAVE_NEON
241         if((CPUCapFlags&CPU_CAP_NEON))
242             return Resample_<BSincTag,NEONTag>;
243 #endif
244 #ifdef HAVE_SSE
245         if((CPUCapFlags&CPU_CAP_SSE))
246             return Resample_<BSincTag,SSETag>;
247 #endif
248         return Resample_<BSincTag,CTag>;
249     }
250 
251     return Resample_<PointTag,CTag>;
252 }
253 
254 } // namespace
255 
aluInit(void)256 void aluInit(void)
257 {
258     MixDirectHrtf = SelectHrtfMixer();
259 }
260 
261 
PrepareResampler(Resampler resampler,uint increment,InterpState * state)262 ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState *state)
263 {
264     switch(resampler)
265     {
266     case Resampler::Point:
267     case Resampler::Linear:
268     case Resampler::Cubic:
269         break;
270     case Resampler::FastBSinc12:
271     case Resampler::BSinc12:
272         BsincPrepare(increment, &state->bsinc, &bsinc12);
273         break;
274     case Resampler::FastBSinc24:
275     case Resampler::BSinc24:
276         BsincPrepare(increment, &state->bsinc, &bsinc24);
277         break;
278     }
279     return SelectResampler(resampler, increment);
280 }
281 
282 
ProcessHrtf(const size_t SamplesToDo)283 void ALCdevice::ProcessHrtf(const size_t SamplesToDo)
284 {
285     /* HRTF is stereo output only. */
286     const uint lidx{RealOut.ChannelIndex[FrontLeft]};
287     const uint ridx{RealOut.ChannelIndex[FrontRight]};
288 
289     MixDirectHrtf(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer, HrtfAccumData,
290         mHrtfState->mTemp.data(), mHrtfState->mChannels.data(), mHrtfState->mIrSize, SamplesToDo);
291 }
292 
ProcessAmbiDec(const size_t SamplesToDo)293 void ALCdevice::ProcessAmbiDec(const size_t SamplesToDo)
294 {
295     AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo);
296 }
297 
ProcessAmbiDecStablized(const size_t SamplesToDo)298 void ALCdevice::ProcessAmbiDecStablized(const size_t SamplesToDo)
299 {
300     /* Decode with front image stablization. */
301     const uint lidx{RealOut.ChannelIndex[FrontLeft]};
302     const uint ridx{RealOut.ChannelIndex[FrontRight]};
303     const uint cidx{RealOut.ChannelIndex[FrontCenter]};
304 
305     AmbiDecoder->processStablize(RealOut.Buffer, Dry.Buffer.data(), lidx, ridx, cidx,
306         SamplesToDo);
307 }
308 
ProcessUhj(const size_t SamplesToDo)309 void ALCdevice::ProcessUhj(const size_t SamplesToDo)
310 {
311     /* UHJ is stereo output only. */
312     const uint lidx{RealOut.ChannelIndex[FrontLeft]};
313     const uint ridx{RealOut.ChannelIndex[FrontRight]};
314 
315     /* Encode to stereo-compatible 2-channel UHJ output. */
316     Uhj_Encoder->encode(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer.data(),
317         SamplesToDo);
318 }
319 
ProcessBs2b(const size_t SamplesToDo)320 void ALCdevice::ProcessBs2b(const size_t SamplesToDo)
321 {
322     /* First, decode the ambisonic mix to the "real" output. */
323     AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo);
324 
325     /* BS2B is stereo output only. */
326     const uint lidx{RealOut.ChannelIndex[FrontLeft]};
327     const uint ridx{RealOut.ChannelIndex[FrontRight]};
328 
329     /* Now apply the BS2B binaural/crossfeed filter. */
330     bs2b_cross_feed(Bs2b.get(), RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(),
331         SamplesToDo);
332 }
333 
334 
335 namespace {
336 
337 /* This RNG method was created based on the math found in opusdec. It's quick,
338  * and starting with a seed value of 22222, is suitable for generating
339  * whitenoise.
340  */
dither_rng(uint * seed)341 inline uint dither_rng(uint *seed) noexcept
342 {
343     *seed = (*seed * 96314165) + 907633515;
344     return *seed;
345 }
346 
347 
GetAmbiScales(AmbiScaling scaletype)348 inline auto& GetAmbiScales(AmbiScaling scaletype) noexcept
349 {
350     if(scaletype == AmbiScaling::FuMa) return AmbiScale::FromFuMa();
351     if(scaletype == AmbiScaling::SN3D) return AmbiScale::FromSN3D();
352     return AmbiScale::FromN3D();
353 }
354 
GetAmbiLayout(AmbiLayout layouttype)355 inline auto& GetAmbiLayout(AmbiLayout layouttype) noexcept
356 {
357     if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa();
358     return AmbiIndex::FromACN();
359 }
360 
GetAmbi2DLayout(AmbiLayout layouttype)361 inline auto& GetAmbi2DLayout(AmbiLayout layouttype) noexcept
362 {
363     if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa2D();
364     return AmbiIndex::FromACN2D();
365 }
366 
367 
CalcContextParams(ALCcontext * ctx)368 bool CalcContextParams(ALCcontext *ctx)
369 {
370     ContextProps *props{ctx->mParams.ContextUpdate.exchange(nullptr, std::memory_order_acq_rel)};
371     if(!props) return false;
372 
373     ctx->mParams.DopplerFactor = props->DopplerFactor;
374     ctx->mParams.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
375 
376     ctx->mParams.SourceDistanceModel = props->SourceDistanceModel;
377     ctx->mParams.mDistanceModel = props->mDistanceModel;
378 
379     AtomicReplaceHead(ctx->mFreeContextProps, props);
380     return true;
381 }
382 
CalcListenerParams(ALCcontext * ctx)383 bool CalcListenerParams(ALCcontext *ctx)
384 {
385     ListenerProps *props{ctx->mParams.ListenerUpdate.exchange(nullptr,
386         std::memory_order_acq_rel)};
387     if(!props) return false;
388 
389     /* AT then UP */
390     alu::Vector N{props->OrientAt[0], props->OrientAt[1], props->OrientAt[2], 0.0f};
391     N.normalize();
392     alu::Vector V{props->OrientUp[0], props->OrientUp[1], props->OrientUp[2], 0.0f};
393     V.normalize();
394     /* Build and normalize right-vector */
395     alu::Vector U{N.cross_product(V)};
396     U.normalize();
397 
398     const alu::MatrixR<double> rot{
399         U[0], V[0], -N[0], 0.0,
400         U[1], V[1], -N[1], 0.0,
401         U[2], V[2], -N[2], 0.0,
402          0.0,  0.0,   0.0, 1.0};
403     const alu::VectorR<double> pos{props->Position[0],props->Position[1],props->Position[2],1.0};
404     const alu::VectorR<double> vel{props->Velocity[0],props->Velocity[1],props->Velocity[2],0.0};
405     const alu::Vector P{alu::cast_to<float>(rot * pos)};
406 
407     ctx->mParams.Matrix = alu::Matrix{
408          U[0],  V[0], -N[0], 0.0f,
409          U[1],  V[1], -N[1], 0.0f,
410          U[2],  V[2], -N[2], 0.0f,
411         -P[0], -P[1], -P[2], 1.0f};
412     ctx->mParams.Velocity = alu::cast_to<float>(rot * vel);
413 
414     ctx->mParams.Gain = props->Gain * ctx->mGainBoost;
415     ctx->mParams.MetersPerUnit = props->MetersPerUnit;
416 
417     AtomicReplaceHead(ctx->mFreeListenerProps, props);
418     return true;
419 }
420 
CalcEffectSlotParams(EffectSlot * slot,EffectSlot ** sorted_slots,ALCcontext * context)421 bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ALCcontext *context)
422 {
423     EffectSlotProps *props{slot->Update.exchange(nullptr, std::memory_order_acq_rel)};
424     if(!props) return false;
425 
426     /* If the effect slot target changed, clear the first sorted entry to force
427      * a re-sort.
428      */
429     if(slot->Target != props->Target)
430         *sorted_slots = nullptr;
431     slot->Gain = props->Gain;
432     slot->AuxSendAuto = props->AuxSendAuto;
433     slot->Target = props->Target;
434     slot->EffectType = props->Type;
435     slot->mEffectProps = props->Props;
436     if(props->Type == EffectSlotType::Reverb || props->Type == EffectSlotType::EAXReverb)
437     {
438         slot->RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
439         slot->DecayTime = props->Props.Reverb.DecayTime;
440         slot->DecayLFRatio = props->Props.Reverb.DecayLFRatio;
441         slot->DecayHFRatio = props->Props.Reverb.DecayHFRatio;
442         slot->DecayHFLimit = props->Props.Reverb.DecayHFLimit;
443         slot->AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
444     }
445     else
446     {
447         slot->RoomRolloff = 0.0f;
448         slot->DecayTime = 0.0f;
449         slot->DecayLFRatio = 0.0f;
450         slot->DecayHFRatio = 0.0f;
451         slot->DecayHFLimit = false;
452         slot->AirAbsorptionGainHF = 1.0f;
453     }
454 
455     EffectState *state{props->State.release()};
456     EffectState *oldstate{slot->mEffectState};
457     slot->mEffectState = state;
458 
459     /* Only release the old state if it won't get deleted, since we can't be
460      * deleting/freeing anything in the mixer.
461      */
462     if(!oldstate->releaseIfNoDelete())
463     {
464         /* Otherwise, if it would be deleted send it off with a release event. */
465         RingBuffer *ring{context->mAsyncEvents.get()};
466         auto evt_vec = ring->getWriteVector();
467         if LIKELY(evt_vec.first.len > 0)
468         {
469             AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_ReleaseEffectState}};
470             evt->u.mEffectState = oldstate;
471             ring->writeAdvance(1);
472         }
473         else
474         {
475             /* If writing the event failed, the queue was probably full. Store
476              * the old state in the property object where it can eventually be
477              * cleaned up sometime later (not ideal, but better than blocking
478              * or leaking).
479              */
480             props->State.reset(oldstate);
481         }
482     }
483 
484     AtomicReplaceHead(context->mFreeEffectslotProps, props);
485 
486     EffectTarget output;
487     if(EffectSlot *target{slot->Target})
488         output = EffectTarget{&target->Wet, nullptr};
489     else
490     {
491         ALCdevice *device{context->mDevice.get()};
492         output = EffectTarget{&device->Dry, &device->RealOut};
493     }
494     state->update(context, slot, &slot->mEffectProps, output);
495     return true;
496 }
497 
498 
499 /* Scales the given azimuth toward the side (+/- pi/2 radians) for positions in
500  * front.
501  */
ScaleAzimuthFront(float azimuth,float scale)502 inline float ScaleAzimuthFront(float azimuth, float scale)
503 {
504     const float abs_azi{std::fabs(azimuth)};
505     if(!(abs_azi >= al::MathDefs<float>::Pi()*0.5f))
506         return std::copysign(minf(abs_azi*scale, al::MathDefs<float>::Pi()*0.5f), azimuth);
507     return azimuth;
508 }
509 
510 /* Wraps the given value in radians to stay between [-pi,+pi] */
WrapRadians(float r)511 inline float WrapRadians(float r)
512 {
513     constexpr float Pi{al::MathDefs<float>::Pi()};
514     constexpr float Pi2{al::MathDefs<float>::Tau()};
515     if(r >  Pi) return std::fmod(Pi+r, Pi2) - Pi;
516     if(r < -Pi) return Pi - std::fmod(Pi-r, Pi2);
517     return r;
518 }
519 
520 /* Begin ambisonic rotation helpers.
521  *
522  * Rotating first-order B-Format just needs a straight-forward X/Y/Z rotation
523  * matrix. Higher orders, however, are more complicated. The method implemented
524  * here is a recursive algorithm (the rotation for first-order is used to help
525  * generate the second-order rotation, which helps generate the third-order
526  * rotation, etc).
527  *
528  * Adapted from
529  * <https://github.com/polarch/Spherical-Harmonic-Transform/blob/master/getSHrotMtx.m>,
530  * provided under the BSD 3-Clause license.
531  *
532  * Copyright (c) 2015, Archontis Politis
533  * Copyright (c) 2019, Christopher Robinson
534  *
535  * The u, v, and w coefficients used for generating higher-order rotations are
536  * precomputed since they're constant. The second-order coefficients are
537  * followed by the third-order coefficients, etc.
538  */
539 struct RotatorCoeffs {
540     float u, v, w;
541 
542     template<size_t N0, size_t N1>
ConcatArrays__anon2eaeabf90311::RotatorCoeffs543     static std::array<RotatorCoeffs,N0+N1> ConcatArrays(const std::array<RotatorCoeffs,N0> &lhs,
544         const std::array<RotatorCoeffs,N1> &rhs)
545     {
546         std::array<RotatorCoeffs,N0+N1> ret;
547         auto iter = std::copy(lhs.cbegin(), lhs.cend(), ret.begin());
548         std::copy(rhs.cbegin(), rhs.cend(), iter);
549         return ret;
550     }
551 
552     template<int l, int num_elems=l*2+1>
GenCoeffs__anon2eaeabf90311::RotatorCoeffs553     static std::array<RotatorCoeffs,num_elems*num_elems> GenCoeffs()
554     {
555         std::array<RotatorCoeffs,num_elems*num_elems> ret{};
556         auto coeffs = ret.begin();
557 
558         for(int m{-l};m <= l;++m)
559         {
560             for(int n{-l};n <= l;++n)
561             {
562                 // compute u,v,w terms of Eq.8.1 (Table I)
563                 const bool d{m == 0}; // the delta function d_m0
564                 const float denom{static_cast<float>((std::abs(n) == l) ?
565                     (2*l) * (2*l - 1) : (l*l - n*n))};
566 
567                 const int abs_m{std::abs(m)};
568                 coeffs->u = std::sqrt(static_cast<float>(l*l - m*m)/denom);
569                 coeffs->v = std::sqrt(static_cast<float>(l+abs_m-1) * static_cast<float>(l+abs_m) /
570                     denom) * (1.0f+d) * (1.0f - 2.0f*d) * 0.5f;
571                 coeffs->w = std::sqrt(static_cast<float>(l-abs_m-1) * static_cast<float>(l-abs_m) /
572                     denom) * (1.0f-d) * -0.5f;
573                 ++coeffs;
574             }
575         }
576 
577         return ret;
578     }
579 };
580 const auto RotatorCoeffArray = RotatorCoeffs::ConcatArrays(RotatorCoeffs::GenCoeffs<2>(),
581     RotatorCoeffs::GenCoeffs<3>());
582 
583 /**
584  * Given the matrix, pre-filled with the (zeroth- and) first-order rotation
585  * coefficients, this fills in the coefficients for the higher orders up to and
586  * including the given order. The matrix is in ACN layout.
587  */
AmbiRotator(std::array<std::array<float,MaxAmbiChannels>,MaxAmbiChannels> & matrix,const int order)588 void AmbiRotator(std::array<std::array<float,MaxAmbiChannels>,MaxAmbiChannels> &matrix,
589     const int order)
590 {
591     /* Don't do anything for < 2nd order. */
592     if(order < 2) return;
593 
594     auto P = [](const int i, const int l, const int a, const int n, const size_t last_band,
595         const std::array<std::array<float,MaxAmbiChannels>,MaxAmbiChannels> &R)
596     {
597         const float ri1{ R[static_cast<uint>(i+2)][ 1+2]};
598         const float rim1{R[static_cast<uint>(i+2)][-1+2]};
599         const float ri0{ R[static_cast<uint>(i+2)][ 0+2]};
600 
601         auto vec = R[static_cast<uint>(a+l-1) + last_band].cbegin() + last_band;
602         if(n == -l)
603             return ri1*vec[0] + rim1*vec[static_cast<uint>(l-1)*size_t{2}];
604         if(n == l)
605             return ri1*vec[static_cast<uint>(l-1)*size_t{2}] - rim1*vec[0];
606         return ri0*vec[static_cast<uint>(n+l-1)];
607     };
608 
609     auto U = [P](const int l, const int m, const int n, const size_t last_band,
610         const std::array<std::array<float,MaxAmbiChannels>,MaxAmbiChannels> &R)
611     {
612         return P(0, l, m, n, last_band, R);
613     };
614     auto V = [P](const int l, const int m, const int n, const size_t last_band,
615         const std::array<std::array<float,MaxAmbiChannels>,MaxAmbiChannels> &R)
616     {
617         if(m > 0)
618         {
619             const bool d{m == 1};
620             const float p0{P( 1, l,  m-1, n, last_band, R)};
621             const float p1{P(-1, l, -m+1, n, last_band, R)};
622             return d ? p0*std::sqrt(2.0f) : (p0 - p1);
623         }
624         const bool d{m == -1};
625         const float p0{P( 1, l,  m+1, n, last_band, R)};
626         const float p1{P(-1, l, -m-1, n, last_band, R)};
627         return d ? p1*std::sqrt(2.0f) : (p0 + p1);
628     };
629     auto W = [P](const int l, const int m, const int n, const size_t last_band,
630         const std::array<std::array<float,MaxAmbiChannels>,MaxAmbiChannels> &R)
631     {
632         assert(m != 0);
633         if(m > 0)
634         {
635             const float p0{P( 1, l,  m+1, n, last_band, R)};
636             const float p1{P(-1, l, -m-1, n, last_band, R)};
637             return p0 + p1;
638         }
639         const float p0{P( 1, l,  m-1, n, last_band, R)};
640         const float p1{P(-1, l, -m+1, n, last_band, R)};
641         return p0 - p1;
642     };
643 
644     // compute rotation matrix of each subsequent band recursively
645     auto coeffs = RotatorCoeffArray.cbegin();
646     size_t band_idx{4}, last_band{1};
647     for(int l{2};l <= order;++l)
648     {
649         size_t y{band_idx};
650         for(int m{-l};m <= l;++m,++y)
651         {
652             size_t x{band_idx};
653             for(int n{-l};n <= l;++n,++x)
654             {
655                 float r{0.0f};
656 
657                 // computes Eq.8.1
658                 const float u{coeffs->u};
659                 if(u != 0.0f) r += u * U(l, m, n, last_band, matrix);
660                 const float v{coeffs->v};
661                 if(v != 0.0f) r += v * V(l, m, n, last_band, matrix);
662                 const float w{coeffs->w};
663                 if(w != 0.0f) r += w * W(l, m, n, last_band, matrix);
664 
665                 matrix[y][x] = r;
666                 ++coeffs;
667             }
668         }
669         last_band = band_idx;
670         band_idx += static_cast<uint>(l)*size_t{2} + 1;
671     }
672 }
673 /* End ambisonic rotation helpers. */
674 
675 
676 struct GainTriplet { float Base, HF, LF; };
677 
CalcPanningAndFilters(Voice * voice,const float xpos,const float ypos,const float zpos,const float Distance,const float Spread,const GainTriplet & DryGain,const al::span<const GainTriplet,MAX_SENDS> WetGain,EffectSlot * (& SendSlots)[MAX_SENDS],const VoiceProps * props,const ContextParams & Context,const ALCdevice * Device)678 void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, const float zpos,
679     const float Distance, const float Spread, const GainTriplet &DryGain,
680     const al::span<const GainTriplet,MAX_SENDS> WetGain, EffectSlot *(&SendSlots)[MAX_SENDS],
681     const VoiceProps *props, const ContextParams &Context, const ALCdevice *Device)
682 {
683     static const ChanMap MonoMap[1]{
684         { FrontCenter, 0.0f, 0.0f }
685     }, RearMap[2]{
686         { BackLeft,  Deg2Rad(-150.0f), Deg2Rad(0.0f) },
687         { BackRight, Deg2Rad( 150.0f), Deg2Rad(0.0f) }
688     }, QuadMap[4]{
689         { FrontLeft,  Deg2Rad( -45.0f), Deg2Rad(0.0f) },
690         { FrontRight, Deg2Rad(  45.0f), Deg2Rad(0.0f) },
691         { BackLeft,   Deg2Rad(-135.0f), Deg2Rad(0.0f) },
692         { BackRight,  Deg2Rad( 135.0f), Deg2Rad(0.0f) }
693     }, X51Map[6]{
694         { FrontLeft,   Deg2Rad( -30.0f), Deg2Rad(0.0f) },
695         { FrontRight,  Deg2Rad(  30.0f), Deg2Rad(0.0f) },
696         { FrontCenter, Deg2Rad(   0.0f), Deg2Rad(0.0f) },
697         { LFE, 0.0f, 0.0f },
698         { SideLeft,    Deg2Rad(-110.0f), Deg2Rad(0.0f) },
699         { SideRight,   Deg2Rad( 110.0f), Deg2Rad(0.0f) }
700     }, X61Map[7]{
701         { FrontLeft,   Deg2Rad(-30.0f), Deg2Rad(0.0f) },
702         { FrontRight,  Deg2Rad( 30.0f), Deg2Rad(0.0f) },
703         { FrontCenter, Deg2Rad(  0.0f), Deg2Rad(0.0f) },
704         { LFE, 0.0f, 0.0f },
705         { BackCenter,  Deg2Rad(180.0f), Deg2Rad(0.0f) },
706         { SideLeft,    Deg2Rad(-90.0f), Deg2Rad(0.0f) },
707         { SideRight,   Deg2Rad( 90.0f), Deg2Rad(0.0f) }
708     }, X71Map[8]{
709         { FrontLeft,   Deg2Rad( -30.0f), Deg2Rad(0.0f) },
710         { FrontRight,  Deg2Rad(  30.0f), Deg2Rad(0.0f) },
711         { FrontCenter, Deg2Rad(   0.0f), Deg2Rad(0.0f) },
712         { LFE, 0.0f, 0.0f },
713         { BackLeft,    Deg2Rad(-150.0f), Deg2Rad(0.0f) },
714         { BackRight,   Deg2Rad( 150.0f), Deg2Rad(0.0f) },
715         { SideLeft,    Deg2Rad( -90.0f), Deg2Rad(0.0f) },
716         { SideRight,   Deg2Rad(  90.0f), Deg2Rad(0.0f) }
717     };
718 
719     ChanMap StereoMap[2]{
720         { FrontLeft,  Deg2Rad(-30.0f), Deg2Rad(0.0f) },
721         { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) }
722     };
723 
724     const auto Frequency = static_cast<float>(Device->Frequency);
725     const uint NumSends{Device->NumAuxSends};
726 
727     const size_t num_channels{voice->mChans.size()};
728     ASSUME(num_channels > 0);
729 
730     for(auto &chandata : voice->mChans)
731     {
732         chandata.mDryParams.Hrtf.Target = HrtfFilter{};
733         chandata.mDryParams.Gains.Target.fill(0.0f);
734         std::for_each(chandata.mWetParams.begin(), chandata.mWetParams.begin()+NumSends,
735             [](SendParams &params) -> void { params.Gains.Target.fill(0.0f); });
736     }
737 
738     DirectMode DirectChannels{props->DirectChannels};
739     const ChanMap *chans{nullptr};
740     float downmix_gain{1.0f};
741     switch(voice->mFmtChannels)
742     {
743     case FmtMono:
744         chans = MonoMap;
745         /* Mono buffers are never played direct. */
746         DirectChannels = DirectMode::Off;
747         break;
748 
749     case FmtStereo:
750         if(DirectChannels == DirectMode::Off)
751         {
752             /* Convert counter-clockwise to clock-wise, and wrap between
753              * [-pi,+pi].
754              */
755             StereoMap[0].angle = WrapRadians(-props->StereoPan[0]);
756             StereoMap[1].angle = WrapRadians(-props->StereoPan[1]);
757         }
758 
759         chans = StereoMap;
760         downmix_gain = 1.0f / 2.0f;
761         break;
762 
763     case FmtRear:
764         chans = RearMap;
765         downmix_gain = 1.0f / 2.0f;
766         break;
767 
768     case FmtQuad:
769         chans = QuadMap;
770         downmix_gain = 1.0f / 4.0f;
771         break;
772 
773     case FmtX51:
774         chans = X51Map;
775         /* NOTE: Excludes LFE. */
776         downmix_gain = 1.0f / 5.0f;
777         break;
778 
779     case FmtX61:
780         chans = X61Map;
781         /* NOTE: Excludes LFE. */
782         downmix_gain = 1.0f / 6.0f;
783         break;
784 
785     case FmtX71:
786         chans = X71Map;
787         /* NOTE: Excludes LFE. */
788         downmix_gain = 1.0f / 7.0f;
789         break;
790 
791     case FmtBFormat2D:
792     case FmtBFormat3D:
793         DirectChannels = DirectMode::Off;
794         break;
795     }
796 
797     voice->mFlags &= ~(VoiceHasHrtf | VoiceHasNfc);
798     if(voice->mFmtChannels == FmtBFormat2D || voice->mFmtChannels == FmtBFormat3D)
799     {
800         /* Special handling for B-Format sources. */
801 
802         if(Device->AvgSpeakerDist > 0.0f)
803         {
804             if(!(Distance > std::numeric_limits<float>::epsilon()))
805             {
806                 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
807                  * is what we want for FOA input. The first channel may have
808                  * been previously re-adjusted if panned, so reset it.
809                  */
810                 voice->mChans[0].mDryParams.NFCtrlFilter.adjust(0.0f);
811             }
812             else
813             {
814                 /* Clamp the distance for really close sources, to prevent
815                  * excessive bass.
816                  */
817                 const float mdist{maxf(Distance, Device->AvgSpeakerDist/4.0f)};
818                 const float w0{SpeedOfSoundMetersPerSec / (mdist * Frequency)};
819 
820                 /* Only need to adjust the first channel of a B-Format source. */
821                 voice->mChans[0].mDryParams.NFCtrlFilter.adjust(w0);
822             }
823 
824             voice->mFlags |= VoiceHasNfc;
825         }
826 
827         /* Panning a B-Format sound toward some direction is easy. Just pan the
828          * first (W) channel as a normal mono sound. The angular spread is used
829          * as a directional scalar to blend between full coverage and full
830          * panning.
831          */
832         const float coverage{!(Distance > std::numeric_limits<float>::epsilon()) ? 1.0f :
833             (Spread * (1.0f/al::MathDefs<float>::Tau()))};
834 
835         auto calc_coeffs = [xpos,ypos,zpos](RenderMode mode)
836         {
837             if(mode != RenderMode::Pairwise)
838                 return CalcDirectionCoeffs({xpos, ypos, zpos}, 0.0f);
839 
840             /* Clamp Y, in case rounding errors caused it to end up outside
841              * of -1...+1.
842              */
843             const float ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
844             /* Negate Z for right-handed coords with -Z in front. */
845             const float az{std::atan2(xpos, -zpos)};
846 
847             /* A scalar of 1.5 for plain stereo results in +/-60 degrees
848              * being moved to +/-90 degrees for direct right and left
849              * speaker responses.
850              */
851             return CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, 0.0f);
852         };
853         auto coeffs = calc_coeffs(Device->mRenderMode);
854         std::transform(coeffs.begin()+1, coeffs.end(), coeffs.begin()+1,
855             std::bind(std::multiplies<float>{}, _1, 1.0f-coverage));
856 
857         /* NOTE: W needs to be scaled according to channel scaling. */
858         auto&& scales = GetAmbiScales(voice->mAmbiScaling);
859         ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base*scales[0],
860             voice->mChans[0].mDryParams.Gains.Target);
861         for(uint i{0};i < NumSends;i++)
862         {
863             if(const EffectSlot *Slot{SendSlots[i]})
864                 ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base*scales[0],
865                     voice->mChans[0].mWetParams[i].Gains.Target);
866         }
867 
868         if(coverage > 0.0f)
869         {
870             /* Local B-Format sources have their XYZ channels rotated according
871              * to the orientation.
872              */
873             /* AT then UP */
874             alu::Vector N{props->OrientAt[0], props->OrientAt[1], props->OrientAt[2], 0.0f};
875             N.normalize();
876             alu::Vector V{props->OrientUp[0], props->OrientUp[1], props->OrientUp[2], 0.0f};
877             V.normalize();
878             if(!props->HeadRelative)
879             {
880                 N = Context.Matrix * N;
881                 V = Context.Matrix * V;
882             }
883             /* Build and normalize right-vector */
884             alu::Vector U{N.cross_product(V)};
885             U.normalize();
886 
887             /* Build a rotation matrix. Manually fill the zeroth- and first-
888              * order elements, then construct the rotation for the higher
889              * orders.
890              */
891             std::array<std::array<float,MaxAmbiChannels>,MaxAmbiChannels> shrot{};
892             shrot[0][0] = 1.0f;
893             shrot[1][1] =  U[0]; shrot[1][2] = -V[0]; shrot[1][3] = -N[0];
894             shrot[2][1] = -U[1]; shrot[2][2] =  V[1]; shrot[2][3] =  N[1];
895             shrot[3][1] =  U[2]; shrot[3][2] = -V[2]; shrot[3][3] = -N[2];
896             AmbiRotator(shrot, static_cast<int>(minu(voice->mAmbiOrder, Device->mAmbiOrder)));
897 
898             /* Convert the rotation matrix for input ordering and scaling, and
899              * whether input is 2D or 3D.
900              */
901             const uint8_t *index_map{(voice->mFmtChannels == FmtBFormat2D) ?
902                 GetAmbi2DLayout(voice->mAmbiLayout).data() :
903                 GetAmbiLayout(voice->mAmbiLayout).data()};
904 
905             static const uint8_t ChansPerOrder[MaxAmbiOrder+1]{1, 3, 5, 7,};
906             static const uint8_t OrderOffset[MaxAmbiOrder+1]{0, 1, 4, 9,};
907             for(size_t c{1};c < num_channels;c++)
908             {
909                 const size_t acn{index_map[c]};
910                 const size_t order{AmbiIndex::OrderFromChannel()[acn]};
911                 const size_t tocopy{ChansPerOrder[order]};
912                 const size_t offset{OrderOffset[order]};
913                 const float scale{scales[acn] * coverage};
914                 auto in = shrot.cbegin() + offset;
915 
916                 coeffs = std::array<float,MaxAmbiChannels>{};
917                 for(size_t x{0};x < tocopy;++x)
918                     coeffs[offset+x] = in[x][acn] * scale;
919 
920                 ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base,
921                     voice->mChans[c].mDryParams.Gains.Target);
922 
923                 for(uint i{0};i < NumSends;i++)
924                 {
925                     if(const EffectSlot *Slot{SendSlots[i]})
926                         ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
927                             voice->mChans[c].mWetParams[i].Gains.Target);
928                 }
929             }
930         }
931     }
932     else if(DirectChannels != DirectMode::Off && Device->FmtChans != DevFmtAmbi3D)
933     {
934         /* Direct source channels always play local. Skip the virtual channels
935          * and write inputs to the matching real outputs.
936          */
937         voice->mDirect.Buffer = Device->RealOut.Buffer;
938 
939         for(size_t c{0};c < num_channels;c++)
940         {
941             uint idx{GetChannelIdxByName(Device->RealOut, chans[c].channel)};
942             if(idx != INVALID_CHANNEL_INDEX)
943                 voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base;
944             else if(DirectChannels == DirectMode::RemixMismatch)
945             {
946                 auto match_channel = [chans,c](const InputRemixMap &map) noexcept -> bool
947                 { return chans[c].channel == map.channel; };
948                 auto remap = std::find_if(Device->RealOut.RemixMap.cbegin(),
949                     Device->RealOut.RemixMap.cend(), match_channel);
950                 if(remap != Device->RealOut.RemixMap.cend())
951                     for(const auto &target : remap->targets)
952                     {
953                         idx = GetChannelIdxByName(Device->RealOut, target.channel);
954                         if(idx != INVALID_CHANNEL_INDEX)
955                             voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base *
956                                 target.mix;
957                     }
958             }
959         }
960 
961         /* Auxiliary sends still use normal channel panning since they mix to
962          * B-Format, which can't channel-match.
963          */
964         for(size_t c{0};c < num_channels;c++)
965         {
966             const auto coeffs = CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f);
967 
968             for(uint i{0};i < NumSends;i++)
969             {
970                 if(const EffectSlot *Slot{SendSlots[i]})
971                     ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
972                         voice->mChans[c].mWetParams[i].Gains.Target);
973             }
974         }
975     }
976     else if(Device->mRenderMode == RenderMode::Hrtf)
977     {
978         /* Full HRTF rendering. Skip the virtual channels and render to the
979          * real outputs.
980          */
981         voice->mDirect.Buffer = Device->RealOut.Buffer;
982 
983         if(Distance > std::numeric_limits<float>::epsilon())
984         {
985             const float ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
986             const float az{std::atan2(xpos, -zpos)};
987 
988             /* Get the HRIR coefficients and delays just once, for the given
989              * source direction.
990              */
991             GetHrtfCoeffs(Device->mHrtf.get(), ev, az, Distance, Spread,
992                 voice->mChans[0].mDryParams.Hrtf.Target.Coeffs,
993                 voice->mChans[0].mDryParams.Hrtf.Target.Delay);
994             voice->mChans[0].mDryParams.Hrtf.Target.Gain = DryGain.Base * downmix_gain;
995 
996             /* Remaining channels use the same results as the first. */
997             for(size_t c{1};c < num_channels;c++)
998             {
999                 /* Skip LFE */
1000                 if(chans[c].channel == LFE) continue;
1001                 voice->mChans[c].mDryParams.Hrtf.Target = voice->mChans[0].mDryParams.Hrtf.Target;
1002             }
1003 
1004             /* Calculate the directional coefficients once, which apply to all
1005              * input channels of the source sends.
1006              */
1007             const auto coeffs = CalcDirectionCoeffs({xpos, ypos, zpos}, Spread);
1008 
1009             for(size_t c{0};c < num_channels;c++)
1010             {
1011                 /* Skip LFE */
1012                 if(chans[c].channel == LFE)
1013                     continue;
1014                 for(uint i{0};i < NumSends;i++)
1015                 {
1016                     if(const EffectSlot *Slot{SendSlots[i]})
1017                         ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base * downmix_gain,
1018                             voice->mChans[c].mWetParams[i].Gains.Target);
1019                 }
1020             }
1021         }
1022         else
1023         {
1024             /* Local sources on HRTF play with each channel panned to its
1025              * relative location around the listener, providing "virtual
1026              * speaker" responses.
1027              */
1028             for(size_t c{0};c < num_channels;c++)
1029             {
1030                 /* Skip LFE */
1031                 if(chans[c].channel == LFE)
1032                     continue;
1033 
1034                 /* Get the HRIR coefficients and delays for this channel
1035                  * position.
1036                  */
1037                 GetHrtfCoeffs(Device->mHrtf.get(), chans[c].elevation, chans[c].angle,
1038                     std::numeric_limits<float>::infinity(), Spread,
1039                     voice->mChans[c].mDryParams.Hrtf.Target.Coeffs,
1040                     voice->mChans[c].mDryParams.Hrtf.Target.Delay);
1041                 voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base;
1042 
1043                 /* Normal panning for auxiliary sends. */
1044                 const auto coeffs = CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread);
1045 
1046                 for(uint i{0};i < NumSends;i++)
1047                 {
1048                     if(const EffectSlot *Slot{SendSlots[i]})
1049                         ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
1050                             voice->mChans[c].mWetParams[i].Gains.Target);
1051                 }
1052             }
1053         }
1054 
1055         voice->mFlags |= VoiceHasHrtf;
1056     }
1057     else
1058     {
1059         /* Non-HRTF rendering. Use normal panning to the output. */
1060 
1061         if(Distance > std::numeric_limits<float>::epsilon())
1062         {
1063             /* Calculate NFC filter coefficient if needed. */
1064             if(Device->AvgSpeakerDist > 0.0f)
1065             {
1066                 /* Clamp the distance for really close sources, to prevent
1067                  * excessive bass.
1068                  */
1069                 const float mdist{maxf(Distance, Device->AvgSpeakerDist/4.0f)};
1070                 const float w0{SpeedOfSoundMetersPerSec / (mdist * Frequency)};
1071 
1072                 /* Adjust NFC filters. */
1073                 for(size_t c{0};c < num_channels;c++)
1074                     voice->mChans[c].mDryParams.NFCtrlFilter.adjust(w0);
1075 
1076                 voice->mFlags |= VoiceHasNfc;
1077             }
1078 
1079             /* Calculate the directional coefficients once, which apply to all
1080              * input channels.
1081              */
1082             auto calc_coeffs = [xpos,ypos,zpos,Spread](RenderMode mode)
1083             {
1084                 if(mode != RenderMode::Pairwise)
1085                     return CalcDirectionCoeffs({xpos, ypos, zpos}, Spread);
1086                 const float ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
1087                 const float az{std::atan2(xpos, -zpos)};
1088                 return CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, Spread);
1089             };
1090             const auto coeffs = calc_coeffs(Device->mRenderMode);
1091 
1092             for(size_t c{0};c < num_channels;c++)
1093             {
1094                 /* Special-case LFE */
1095                 if(chans[c].channel == LFE)
1096                 {
1097                     if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data())
1098                     {
1099                         const uint idx{GetChannelIdxByName(Device->RealOut, chans[c].channel)};
1100                         if(idx != INVALID_CHANNEL_INDEX)
1101                             voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base;
1102                     }
1103                     continue;
1104                 }
1105 
1106                 ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base * downmix_gain,
1107                     voice->mChans[c].mDryParams.Gains.Target);
1108                 for(uint i{0};i < NumSends;i++)
1109                 {
1110                     if(const EffectSlot *Slot{SendSlots[i]})
1111                         ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base * downmix_gain,
1112                             voice->mChans[c].mWetParams[i].Gains.Target);
1113                 }
1114             }
1115         }
1116         else
1117         {
1118             if(Device->AvgSpeakerDist > 0.0f)
1119             {
1120                 /* If the source distance is 0, simulate a plane-wave by using
1121                  * infinite distance, which results in a w0 of 0.
1122                  */
1123                 constexpr float w0{0.0f};
1124                 for(size_t c{0};c < num_channels;c++)
1125                     voice->mChans[c].mDryParams.NFCtrlFilter.adjust(w0);
1126 
1127                 voice->mFlags |= VoiceHasNfc;
1128             }
1129 
1130             for(size_t c{0};c < num_channels;c++)
1131             {
1132                 /* Special-case LFE */
1133                 if(chans[c].channel == LFE)
1134                 {
1135                     if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data())
1136                     {
1137                         const uint idx{GetChannelIdxByName(Device->RealOut, chans[c].channel)};
1138                         if(idx != INVALID_CHANNEL_INDEX)
1139                             voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base;
1140                     }
1141                     continue;
1142                 }
1143 
1144                 const auto coeffs = CalcAngleCoeffs((Device->mRenderMode == RenderMode::Pairwise)
1145                     ? ScaleAzimuthFront(chans[c].angle, 3.0f) : chans[c].angle,
1146                     chans[c].elevation, Spread);
1147 
1148                 ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base,
1149                     voice->mChans[c].mDryParams.Gains.Target);
1150                 for(uint i{0};i < NumSends;i++)
1151                 {
1152                     if(const EffectSlot *Slot{SendSlots[i]})
1153                         ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
1154                             voice->mChans[c].mWetParams[i].Gains.Target);
1155                 }
1156             }
1157         }
1158     }
1159 
1160     {
1161         const float hfNorm{props->Direct.HFReference / Frequency};
1162         const float lfNorm{props->Direct.LFReference / Frequency};
1163 
1164         voice->mDirect.FilterType = AF_None;
1165         if(DryGain.HF != 1.0f) voice->mDirect.FilterType |= AF_LowPass;
1166         if(DryGain.LF != 1.0f) voice->mDirect.FilterType |= AF_HighPass;
1167 
1168         auto &lowpass = voice->mChans[0].mDryParams.LowPass;
1169         auto &highpass = voice->mChans[0].mDryParams.HighPass;
1170         lowpass.setParamsFromSlope(BiquadType::HighShelf, hfNorm, DryGain.HF, 1.0f);
1171         highpass.setParamsFromSlope(BiquadType::LowShelf, lfNorm, DryGain.LF, 1.0f);
1172         for(size_t c{1};c < num_channels;c++)
1173         {
1174             voice->mChans[c].mDryParams.LowPass.copyParamsFrom(lowpass);
1175             voice->mChans[c].mDryParams.HighPass.copyParamsFrom(highpass);
1176         }
1177     }
1178     for(uint i{0};i < NumSends;i++)
1179     {
1180         const float hfNorm{props->Send[i].HFReference / Frequency};
1181         const float lfNorm{props->Send[i].LFReference / Frequency};
1182 
1183         voice->mSend[i].FilterType = AF_None;
1184         if(WetGain[i].HF != 1.0f) voice->mSend[i].FilterType |= AF_LowPass;
1185         if(WetGain[i].LF != 1.0f) voice->mSend[i].FilterType |= AF_HighPass;
1186 
1187         auto &lowpass = voice->mChans[0].mWetParams[i].LowPass;
1188         auto &highpass = voice->mChans[0].mWetParams[i].HighPass;
1189         lowpass.setParamsFromSlope(BiquadType::HighShelf, hfNorm, WetGain[i].HF, 1.0f);
1190         highpass.setParamsFromSlope(BiquadType::LowShelf, lfNorm, WetGain[i].LF, 1.0f);
1191         for(size_t c{1};c < num_channels;c++)
1192         {
1193             voice->mChans[c].mWetParams[i].LowPass.copyParamsFrom(lowpass);
1194             voice->mChans[c].mWetParams[i].HighPass.copyParamsFrom(highpass);
1195         }
1196     }
1197 }
1198 
CalcNonAttnSourceParams(Voice * voice,const VoiceProps * props,const ALCcontext * context)1199 void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const ALCcontext *context)
1200 {
1201     const ALCdevice *Device{context->mDevice.get()};
1202     EffectSlot *SendSlots[MAX_SENDS];
1203 
1204     voice->mDirect.Buffer = Device->Dry.Buffer;
1205     for(uint i{0};i < Device->NumAuxSends;i++)
1206     {
1207         SendSlots[i] = props->Send[i].Slot;
1208         if(!SendSlots[i] || SendSlots[i]->EffectType == EffectSlotType::None)
1209         {
1210             SendSlots[i] = nullptr;
1211             voice->mSend[i].Buffer = {};
1212         }
1213         else
1214             voice->mSend[i].Buffer = SendSlots[i]->Wet.Buffer;
1215     }
1216 
1217     /* Calculate the stepping value */
1218     const auto Pitch = static_cast<float>(voice->mFrequency) /
1219         static_cast<float>(Device->Frequency) * props->Pitch;
1220     if(Pitch > float{MaxPitch})
1221         voice->mStep = MaxPitch<<MixerFracBits;
1222     else
1223         voice->mStep = maxu(fastf2u(Pitch * MixerFracOne), 1);
1224     voice->mResampler = PrepareResampler(props->mResampler, voice->mStep, &voice->mResampleState);
1225 
1226     /* Calculate gains */
1227     GainTriplet DryGain;
1228     DryGain.Base  = minf(clampf(props->Gain, props->MinGain, props->MaxGain) * props->Direct.Gain *
1229         context->mParams.Gain, GainMixMax);
1230     DryGain.HF = props->Direct.GainHF;
1231     DryGain.LF = props->Direct.GainLF;
1232     GainTriplet WetGain[MAX_SENDS];
1233     for(uint i{0};i < Device->NumAuxSends;i++)
1234     {
1235         WetGain[i].Base = minf(clampf(props->Gain, props->MinGain, props->MaxGain) *
1236             props->Send[i].Gain * context->mParams.Gain, GainMixMax);
1237         WetGain[i].HF = props->Send[i].GainHF;
1238         WetGain[i].LF = props->Send[i].GainLF;
1239     }
1240 
1241     CalcPanningAndFilters(voice, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, DryGain, WetGain, SendSlots, props,
1242         context->mParams, Device);
1243 }
1244 
CalcAttnSourceParams(Voice * voice,const VoiceProps * props,const ALCcontext * context)1245 void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ALCcontext *context)
1246 {
1247     const ALCdevice *Device{context->mDevice.get()};
1248     const uint NumSends{Device->NumAuxSends};
1249 
1250     /* Set mixing buffers and get send parameters. */
1251     voice->mDirect.Buffer = Device->Dry.Buffer;
1252     EffectSlot *SendSlots[MAX_SENDS];
1253     float RoomRolloff[MAX_SENDS];
1254     GainTriplet DecayDistance[MAX_SENDS];
1255     for(uint i{0};i < NumSends;i++)
1256     {
1257         SendSlots[i] = props->Send[i].Slot;
1258         if(!SendSlots[i] || SendSlots[i]->EffectType == EffectSlotType::None)
1259         {
1260             SendSlots[i] = nullptr;
1261             RoomRolloff[i] = 0.0f;
1262             DecayDistance[i].Base = 0.0f;
1263             DecayDistance[i].LF = 0.0f;
1264             DecayDistance[i].HF = 0.0f;
1265         }
1266         else if(SendSlots[i]->AuxSendAuto)
1267         {
1268             RoomRolloff[i] = SendSlots[i]->RoomRolloff + props->RoomRolloffFactor;
1269             /* Calculate the distances to where this effect's decay reaches
1270              * -60dB.
1271              */
1272             DecayDistance[i].Base = SendSlots[i]->DecayTime * SpeedOfSoundMetersPerSec;
1273             DecayDistance[i].LF = DecayDistance[i].Base * SendSlots[i]->DecayLFRatio;
1274             DecayDistance[i].HF = DecayDistance[i].Base * SendSlots[i]->DecayHFRatio;
1275             if(SendSlots[i]->DecayHFLimit)
1276             {
1277                 const float airAbsorption{SendSlots[i]->AirAbsorptionGainHF};
1278                 if(airAbsorption < 1.0f)
1279                 {
1280                     /* Calculate the distance to where this effect's air
1281                      * absorption reaches -60dB, and limit the effect's HF
1282                      * decay distance (so it doesn't take any longer to decay
1283                      * than the air would allow).
1284                      */
1285                     constexpr float log10_decaygain{-3.0f/*std::log10(ReverbDecayGain)*/};
1286                     const float absorb_dist{log10_decaygain / std::log10(airAbsorption)};
1287                     DecayDistance[i].HF = minf(absorb_dist, DecayDistance[i].HF);
1288                 }
1289             }
1290         }
1291         else
1292         {
1293             /* If the slot's auxiliary send auto is off, the data sent to the
1294              * effect slot is the same as the dry path, sans filter effects */
1295             RoomRolloff[i] = props->RolloffFactor;
1296             DecayDistance[i].Base = 0.0f;
1297             DecayDistance[i].LF = 0.0f;
1298             DecayDistance[i].HF = 0.0f;
1299         }
1300 
1301         if(!SendSlots[i])
1302             voice->mSend[i].Buffer = {};
1303         else
1304             voice->mSend[i].Buffer = SendSlots[i]->Wet.Buffer;
1305     }
1306 
1307     /* Transform source to listener space (convert to head relative) */
1308     alu::Vector Position{props->Position[0], props->Position[1], props->Position[2], 1.0f};
1309     alu::Vector Velocity{props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f};
1310     alu::Vector Direction{props->Direction[0], props->Direction[1], props->Direction[2], 0.0f};
1311     if(!props->HeadRelative)
1312     {
1313         /* Transform source vectors */
1314         Position = context->mParams.Matrix * Position;
1315         Velocity = context->mParams.Matrix * Velocity;
1316         Direction = context->mParams.Matrix * Direction;
1317     }
1318     else
1319     {
1320         /* Offset the source velocity to be relative of the listener velocity */
1321         Velocity += context->mParams.Velocity;
1322     }
1323 
1324     const bool directional{Direction.normalize() > 0.0f};
1325     alu::Vector ToSource{Position[0], Position[1], Position[2], 0.0f};
1326     const float Distance{ToSource.normalize(props->RefDistance / 1024.0f)};
1327 
1328     /* Initial source gain */
1329     GainTriplet DryGain{props->Gain, 1.0f, 1.0f};
1330     GainTriplet WetGain[MAX_SENDS];
1331     for(uint i{0};i < NumSends;i++)
1332         WetGain[i] = DryGain;
1333 
1334     /* Calculate distance attenuation */
1335     float ClampedDist{Distance};
1336 
1337     switch(context->mParams.SourceDistanceModel ? props->mDistanceModel
1338         : context->mParams.mDistanceModel)
1339     {
1340         case DistanceModel::InverseClamped:
1341             ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1342             if(props->MaxDistance < props->RefDistance) break;
1343             /*fall-through*/
1344         case DistanceModel::Inverse:
1345             if(!(props->RefDistance > 0.0f))
1346                 ClampedDist = props->RefDistance;
1347             else
1348             {
1349                 float dist{lerp(props->RefDistance, ClampedDist, props->RolloffFactor)};
1350                 if(dist > 0.0f) DryGain.Base *= props->RefDistance / dist;
1351                 for(uint i{0};i < NumSends;i++)
1352                 {
1353                     dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1354                     if(dist > 0.0f) WetGain[i].Base *= props->RefDistance / dist;
1355                 }
1356             }
1357             break;
1358 
1359         case DistanceModel::LinearClamped:
1360             ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1361             if(props->MaxDistance < props->RefDistance) break;
1362             /*fall-through*/
1363         case DistanceModel::Linear:
1364             if(!(props->MaxDistance != props->RefDistance))
1365                 ClampedDist = props->RefDistance;
1366             else
1367             {
1368                 float attn{props->RolloffFactor * (ClampedDist-props->RefDistance) /
1369                     (props->MaxDistance-props->RefDistance)};
1370                 DryGain.Base *= maxf(1.0f - attn, 0.0f);
1371                 for(uint i{0};i < NumSends;i++)
1372                 {
1373                     attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1374                         (props->MaxDistance-props->RefDistance);
1375                     WetGain[i].Base *= maxf(1.0f - attn, 0.0f);
1376                 }
1377             }
1378             break;
1379 
1380         case DistanceModel::ExponentClamped:
1381             ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1382             if(props->MaxDistance < props->RefDistance) break;
1383             /*fall-through*/
1384         case DistanceModel::Exponent:
1385             if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1386                 ClampedDist = props->RefDistance;
1387             else
1388             {
1389                 const float dist_ratio{ClampedDist/props->RefDistance};
1390                 DryGain.Base *= std::pow(dist_ratio, -props->RolloffFactor);
1391                 for(uint i{0};i < NumSends;i++)
1392                     WetGain[i].Base *= std::pow(dist_ratio, -RoomRolloff[i]);
1393             }
1394             break;
1395 
1396         case DistanceModel::Disable:
1397             ClampedDist = props->RefDistance;
1398             break;
1399     }
1400 
1401     /* Calculate directional soundcones */
1402     if(directional && props->InnerAngle < 360.0f)
1403     {
1404         const float Angle{Rad2Deg(std::acos(Direction.dot_product(ToSource)) * ConeScale * -2.0f)};
1405 
1406         float ConeGain, ConeHF;
1407         if(!(Angle > props->InnerAngle))
1408         {
1409             ConeGain = 1.0f;
1410             ConeHF = 1.0f;
1411         }
1412         else if(Angle < props->OuterAngle)
1413         {
1414             const float scale{(Angle-props->InnerAngle) / (props->OuterAngle-props->InnerAngle)};
1415             ConeGain = lerp(1.0f, props->OuterGain, scale);
1416             ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1417         }
1418         else
1419         {
1420             ConeGain = props->OuterGain;
1421             ConeHF = props->OuterGainHF;
1422         }
1423 
1424         DryGain.Base *= ConeGain;
1425         if(props->DryGainHFAuto)
1426             DryGain.HF *= ConeHF;
1427         if(props->WetGainAuto)
1428             std::for_each(std::begin(WetGain), std::begin(WetGain)+NumSends,
1429                 [ConeGain](GainTriplet &gain) noexcept -> void { gain.Base *= ConeGain; });
1430         if(props->WetGainHFAuto)
1431             std::for_each(std::begin(WetGain), std::begin(WetGain)+NumSends,
1432                 [ConeHF](GainTriplet &gain) noexcept -> void { gain.HF *= ConeHF; });
1433     }
1434 
1435     /* Apply gain and frequency filters */
1436     DryGain.Base = minf(clampf(DryGain.Base, props->MinGain, props->MaxGain) * props->Direct.Gain *
1437         context->mParams.Gain, GainMixMax);
1438     DryGain.HF *= props->Direct.GainHF;
1439     DryGain.LF *= props->Direct.GainLF;
1440     for(uint i{0};i < NumSends;i++)
1441     {
1442         WetGain[i].Base = minf(clampf(WetGain[i].Base, props->MinGain, props->MaxGain) *
1443             props->Send[i].Gain * context->mParams.Gain, GainMixMax);
1444         WetGain[i].HF *= props->Send[i].GainHF;
1445         WetGain[i].LF *= props->Send[i].GainLF;
1446     }
1447 
1448     /* Distance-based air absorption and initial send decay. */
1449     if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1450     {
1451         const float meters_base{(ClampedDist-props->RefDistance) * props->RolloffFactor *
1452             context->mParams.MetersPerUnit};
1453         if(props->AirAbsorptionFactor > 0.0f)
1454         {
1455             const float hfattn{std::pow(AirAbsorbGainHF, meters_base*props->AirAbsorptionFactor)};
1456             DryGain.HF *= hfattn;
1457             std::for_each(std::begin(WetGain), std::begin(WetGain)+NumSends,
1458                 [hfattn](GainTriplet &gain) noexcept -> void { gain.HF *= hfattn; });
1459         }
1460 
1461         if(props->WetGainAuto)
1462         {
1463             /* Apply a decay-time transformation to the wet path, based on the
1464              * source distance in meters. The initial decay of the reverb
1465              * effect is calculated and applied to the wet path.
1466              */
1467             for(uint i{0};i < NumSends;i++)
1468             {
1469                 if(!(DecayDistance[i].Base > 0.0f))
1470                     continue;
1471 
1472                 const float gain{std::pow(ReverbDecayGain, meters_base/DecayDistance[i].Base)};
1473                 WetGain[i].Base *= gain;
1474                 /* Yes, the wet path's air absorption is applied with
1475                  * WetGainAuto on, rather than WetGainHFAuto.
1476                  */
1477                 if(gain > 0.0f)
1478                 {
1479                     float gainhf{std::pow(ReverbDecayGain, meters_base/DecayDistance[i].HF)};
1480                     WetGain[i].HF *= minf(gainhf / gain, 1.0f);
1481                     float gainlf{std::pow(ReverbDecayGain, meters_base/DecayDistance[i].LF)};
1482                     WetGain[i].LF *= minf(gainlf / gain, 1.0f);
1483                 }
1484             }
1485         }
1486     }
1487 
1488 
1489     /* Initial source pitch */
1490     float Pitch{props->Pitch};
1491 
1492     /* Calculate velocity-based doppler effect */
1493     float DopplerFactor{props->DopplerFactor * context->mParams.DopplerFactor};
1494     if(DopplerFactor > 0.0f)
1495     {
1496         const alu::Vector &lvelocity = context->mParams.Velocity;
1497         float vss{Velocity.dot_product(ToSource) * -DopplerFactor};
1498         float vls{lvelocity.dot_product(ToSource) * -DopplerFactor};
1499 
1500         const float SpeedOfSound{context->mParams.SpeedOfSound};
1501         if(!(vls < SpeedOfSound))
1502         {
1503             /* Listener moving away from the source at the speed of sound.
1504              * Sound waves can't catch it.
1505              */
1506             Pitch = 0.0f;
1507         }
1508         else if(!(vss < SpeedOfSound))
1509         {
1510             /* Source moving toward the listener at the speed of sound. Sound
1511              * waves bunch up to extreme frequencies.
1512              */
1513             Pitch = std::numeric_limits<float>::infinity();
1514         }
1515         else
1516         {
1517             /* Source and listener movement is nominal. Calculate the proper
1518              * doppler shift.
1519              */
1520             Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1521         }
1522     }
1523 
1524     /* Adjust pitch based on the buffer and output frequencies, and calculate
1525      * fixed-point stepping value.
1526      */
1527     Pitch *= static_cast<float>(voice->mFrequency) / static_cast<float>(Device->Frequency);
1528     if(Pitch > float{MaxPitch})
1529         voice->mStep = MaxPitch<<MixerFracBits;
1530     else
1531         voice->mStep = maxu(fastf2u(Pitch * MixerFracOne), 1);
1532     voice->mResampler = PrepareResampler(props->mResampler, voice->mStep, &voice->mResampleState);
1533 
1534     float spread{0.0f};
1535     if(props->Radius > Distance)
1536         spread = al::MathDefs<float>::Tau() - Distance/props->Radius*al::MathDefs<float>::Pi();
1537     else if(Distance > 0.0f)
1538         spread = std::asin(props->Radius/Distance) * 2.0f;
1539 
1540     CalcPanningAndFilters(voice, ToSource[0], ToSource[1], ToSource[2]*ZScale,
1541         Distance*context->mParams.MetersPerUnit, spread, DryGain, WetGain, SendSlots, props,
1542         context->mParams, Device);
1543 }
1544 
CalcSourceParams(Voice * voice,ALCcontext * context,bool force)1545 void CalcSourceParams(Voice *voice, ALCcontext *context, bool force)
1546 {
1547     VoicePropsItem *props{voice->mUpdate.exchange(nullptr, std::memory_order_acq_rel)};
1548     if(!props && !force) return;
1549 
1550     if(props)
1551     {
1552         voice->mProps = *props;
1553 
1554         AtomicReplaceHead(context->mFreeVoiceProps, props);
1555     }
1556 
1557     if((voice->mProps.DirectChannels != DirectMode::Off && voice->mFmtChannels != FmtMono
1558             && voice->mFmtChannels != FmtBFormat2D && voice->mFmtChannels != FmtBFormat3D)
1559         || voice->mProps.mSpatializeMode==SpatializeMode::Off
1560         || (voice->mProps.mSpatializeMode==SpatializeMode::Auto && voice->mFmtChannels != FmtMono))
1561         CalcNonAttnSourceParams(voice, &voice->mProps, context);
1562     else
1563         CalcAttnSourceParams(voice, &voice->mProps, context);
1564 }
1565 
1566 
SendSourceStateEvent(ALCcontext * context,uint id,VChangeState state)1567 void SendSourceStateEvent(ALCcontext *context, uint id, VChangeState state)
1568 {
1569     RingBuffer *ring{context->mAsyncEvents.get()};
1570     auto evt_vec = ring->getWriteVector();
1571     if(evt_vec.first.len < 1) return;
1572 
1573     AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}};
1574     evt->u.srcstate.id = id;
1575     evt->u.srcstate.state = state;
1576 
1577     ring->writeAdvance(1);
1578 }
1579 
ProcessVoiceChanges(ALCcontext * ctx)1580 void ProcessVoiceChanges(ALCcontext *ctx)
1581 {
1582     VoiceChange *cur{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
1583     VoiceChange *next{cur->mNext.load(std::memory_order_acquire)};
1584     if(!next) return;
1585 
1586     const uint enabledevt{ctx->mEnabledEvts.load(std::memory_order_acquire)};
1587     do {
1588         cur = next;
1589 
1590         bool sendevt{false};
1591         if(cur->mState == VChangeState::Reset || cur->mState == VChangeState::Stop)
1592         {
1593             if(Voice *voice{cur->mVoice})
1594             {
1595                 voice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
1596                 voice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
1597                 /* A source ID indicates the voice was playing or paused, which
1598                  * gets a reset/stop event.
1599                  */
1600                 sendevt = voice->mSourceID.exchange(0u, std::memory_order_relaxed) != 0u;
1601                 Voice::State oldvstate{Voice::Playing};
1602                 voice->mPlayState.compare_exchange_strong(oldvstate, Voice::Stopping,
1603                     std::memory_order_relaxed, std::memory_order_acquire);
1604                 voice->mPendingChange.store(false, std::memory_order_release);
1605             }
1606             /* Reset state change events are always sent, even if the voice is
1607              * already stopped or even if there is no voice.
1608              */
1609             sendevt |= (cur->mState == VChangeState::Reset);
1610         }
1611         else if(cur->mState == VChangeState::Pause)
1612         {
1613             Voice *voice{cur->mVoice};
1614             Voice::State oldvstate{Voice::Playing};
1615             sendevt = voice->mPlayState.compare_exchange_strong(oldvstate, Voice::Stopping,
1616                 std::memory_order_release, std::memory_order_acquire);
1617         }
1618         else if(cur->mState == VChangeState::Play)
1619         {
1620             /* NOTE: When playing a voice, sending a source state change event
1621              * depends if there's an old voice to stop and if that stop is
1622              * successful. If there is no old voice, a playing event is always
1623              * sent. If there is an old voice, an event is sent only if the
1624              * voice is already stopped.
1625              */
1626             if(Voice *oldvoice{cur->mOldVoice})
1627             {
1628                 oldvoice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
1629                 oldvoice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
1630                 oldvoice->mSourceID.store(0u, std::memory_order_relaxed);
1631                 Voice::State oldvstate{Voice::Playing};
1632                 sendevt = !oldvoice->mPlayState.compare_exchange_strong(oldvstate, Voice::Stopping,
1633                     std::memory_order_relaxed, std::memory_order_acquire);
1634                 oldvoice->mPendingChange.store(false, std::memory_order_release);
1635             }
1636             else
1637                 sendevt = true;
1638 
1639             Voice *voice{cur->mVoice};
1640             voice->mPlayState.store(Voice::Playing, std::memory_order_release);
1641         }
1642         else if(cur->mState == VChangeState::Restart)
1643         {
1644             /* Restarting a voice never sends a source change event. */
1645             Voice *oldvoice{cur->mOldVoice};
1646             oldvoice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
1647             oldvoice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
1648             /* If there's no sourceID, the old voice finished so don't start
1649              * the new one at its new offset.
1650              */
1651             if(oldvoice->mSourceID.exchange(0u, std::memory_order_relaxed) != 0u)
1652             {
1653                 /* Otherwise, set the voice to stopping if it's not already (it
1654                  * might already be, if paused), and play the new voice as
1655                  * appropriate.
1656                  */
1657                 Voice::State oldvstate{Voice::Playing};
1658                 oldvoice->mPlayState.compare_exchange_strong(oldvstate, Voice::Stopping,
1659                     std::memory_order_relaxed, std::memory_order_acquire);
1660 
1661                 Voice *voice{cur->mVoice};
1662                 voice->mPlayState.store((oldvstate == Voice::Playing) ? Voice::Playing
1663                     : Voice::Stopped, std::memory_order_release);
1664             }
1665             oldvoice->mPendingChange.store(false, std::memory_order_release);
1666         }
1667         if(sendevt && (enabledevt&EventType_SourceStateChange))
1668             SendSourceStateEvent(ctx, cur->mSourceID, cur->mState);
1669 
1670         next = cur->mNext.load(std::memory_order_acquire);
1671     } while(next);
1672     ctx->mCurrentVoiceChange.store(cur, std::memory_order_release);
1673 }
1674 
ProcessParamUpdates(ALCcontext * ctx,const EffectSlotArray & slots,const al::span<Voice * > voices)1675 void ProcessParamUpdates(ALCcontext *ctx, const EffectSlotArray &slots,
1676     const al::span<Voice*> voices)
1677 {
1678     ProcessVoiceChanges(ctx);
1679 
1680     IncrementRef(ctx->mUpdateCount);
1681     if LIKELY(!ctx->mHoldUpdates.load(std::memory_order_acquire))
1682     {
1683         bool force{CalcContextParams(ctx)};
1684         force |= CalcListenerParams(ctx);
1685         auto sorted_slots = const_cast<EffectSlot**>(slots.data() + slots.size());
1686         for(EffectSlot *slot : slots)
1687             force |= CalcEffectSlotParams(slot, sorted_slots, ctx);
1688 
1689         for(Voice *voice : voices)
1690         {
1691             /* Only update voices that have a source. */
1692             if(voice->mSourceID.load(std::memory_order_relaxed) != 0)
1693                 CalcSourceParams(voice, ctx, force);
1694         }
1695     }
1696     IncrementRef(ctx->mUpdateCount);
1697 }
1698 
ProcessContexts(ALCdevice * device,const uint SamplesToDo)1699 void ProcessContexts(ALCdevice *device, const uint SamplesToDo)
1700 {
1701     ASSUME(SamplesToDo > 0);
1702 
1703     for(ALCcontext *ctx : *device->mContexts.load(std::memory_order_acquire))
1704     {
1705         const EffectSlotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire);
1706         const al::span<Voice*> voices{ctx->getVoicesSpanAcquired()};
1707 
1708         /* Process pending propery updates for objects on the context. */
1709         ProcessParamUpdates(ctx, auxslots, voices);
1710 
1711         /* Clear auxiliary effect slot mixing buffers. */
1712         for(EffectSlot *slot : auxslots)
1713         {
1714             for(auto &buffer : slot->Wet.Buffer)
1715                 buffer.fill(0.0f);
1716         }
1717 
1718         /* Process voices that have a playing source. */
1719         for(Voice *voice : voices)
1720         {
1721             const Voice::State vstate{voice->mPlayState.load(std::memory_order_acquire)};
1722             if(vstate != Voice::Stopped && vstate != Voice::Pending)
1723                 voice->mix(vstate, ctx, SamplesToDo);
1724         }
1725 
1726         /* Process effects. */
1727         if(const size_t num_slots{auxslots.size()})
1728         {
1729             auto slots = auxslots.data();
1730             auto slots_end = slots + num_slots;
1731 
1732             /* Sort the slots into extra storage, so that effect slots come
1733              * before their effect slot target (or their targets' target).
1734              */
1735             const al::span<EffectSlot*> sorted_slots{const_cast<EffectSlot**>(slots_end),
1736                 num_slots};
1737             /* Skip sorting if it has already been done. */
1738             if(!sorted_slots[0])
1739             {
1740                 /* First, copy the slots to the sorted list, then partition the
1741                  * sorted list so that all slots without a target slot go to
1742                  * the end.
1743                  */
1744                 std::copy(slots, slots_end, sorted_slots.begin());
1745                 auto split_point = std::partition(sorted_slots.begin(), sorted_slots.end(),
1746                     [](const EffectSlot *slot) noexcept -> bool
1747                     { return slot->Target != nullptr; });
1748                 /* There must be at least one slot without a slot target. */
1749                 assert(split_point != sorted_slots.end());
1750 
1751                 /* Simple case: no more than 1 slot has a target slot. Either
1752                  * all slots go right to the output, or the remaining one must
1753                  * target an already-partitioned slot.
1754                  */
1755                 if(split_point - sorted_slots.begin() > 1)
1756                 {
1757                     /* At least two slots target other slots. Starting from the
1758                      * back of the sorted list, continue partitioning the front
1759                      * of the list given each target until all targets are
1760                      * accounted for. This ensures all slots without a target
1761                      * go last, all slots directly targeting those last slots
1762                      * go second-to-last, all slots directly targeting those
1763                      * second-last slots go third-to-last, etc.
1764                      */
1765                     auto next_target = sorted_slots.end();
1766                     do {
1767                         /* This shouldn't happen, but if there's unsorted slots
1768                          * left that don't target any sorted slots, they can't
1769                          * contribute to the output, so leave them.
1770                          */
1771                         if UNLIKELY(next_target == split_point)
1772                             break;
1773 
1774                         --next_target;
1775                         split_point = std::partition(sorted_slots.begin(), split_point,
1776                             [next_target](const EffectSlot *slot) noexcept -> bool
1777                             { return slot->Target != *next_target; });
1778                     } while(split_point - sorted_slots.begin() > 1);
1779                 }
1780             }
1781 
1782             for(const EffectSlot *slot : sorted_slots)
1783             {
1784                 EffectState *state{slot->mEffectState};
1785                 state->process(SamplesToDo, slot->Wet.Buffer, state->mOutTarget);
1786             }
1787         }
1788 
1789         /* Signal the event handler if there are any events to read. */
1790         RingBuffer *ring{ctx->mAsyncEvents.get()};
1791         if(ring->readSpace() > 0)
1792             ctx->mEventSem.post();
1793     }
1794 }
1795 
1796 
ApplyDistanceComp(const al::span<FloatBufferLine> Samples,const size_t SamplesToDo,const DistanceComp::ChanData * distcomp)1797 void ApplyDistanceComp(const al::span<FloatBufferLine> Samples, const size_t SamplesToDo,
1798     const DistanceComp::ChanData *distcomp)
1799 {
1800     ASSUME(SamplesToDo > 0);
1801 
1802     for(auto &chanbuffer : Samples)
1803     {
1804         const float gain{distcomp->Gain};
1805         const size_t base{distcomp->Length};
1806         float *distbuf{al::assume_aligned<16>(distcomp->Buffer)};
1807         ++distcomp;
1808 
1809         if(base < 1)
1810             continue;
1811 
1812         float *inout{al::assume_aligned<16>(chanbuffer.data())};
1813         auto inout_end = inout + SamplesToDo;
1814         if LIKELY(SamplesToDo >= base)
1815         {
1816             auto delay_end = std::rotate(inout, inout_end - base, inout_end);
1817             std::swap_ranges(inout, delay_end, distbuf);
1818         }
1819         else
1820         {
1821             auto delay_start = std::swap_ranges(inout, inout_end, distbuf);
1822             std::rotate(distbuf, delay_start, distbuf + base);
1823         }
1824         std::transform(inout, inout_end, inout, std::bind(std::multiplies<float>{}, _1, gain));
1825     }
1826 }
1827 
ApplyDither(const al::span<FloatBufferLine> Samples,uint * dither_seed,const float quant_scale,const size_t SamplesToDo)1828 void ApplyDither(const al::span<FloatBufferLine> Samples, uint *dither_seed,
1829     const float quant_scale, const size_t SamplesToDo)
1830 {
1831     ASSUME(SamplesToDo > 0);
1832 
1833     /* Dithering. Generate whitenoise (uniform distribution of random values
1834      * between -1 and +1) and add it to the sample values, after scaling up to
1835      * the desired quantization depth amd before rounding.
1836      */
1837     const float invscale{1.0f / quant_scale};
1838     uint seed{*dither_seed};
1839     auto dither_sample = [&seed,invscale,quant_scale](const float sample) noexcept -> float
1840     {
1841         float val{sample * quant_scale};
1842         uint rng0{dither_rng(&seed)};
1843         uint rng1{dither_rng(&seed)};
1844         val += static_cast<float>(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1845         return fast_roundf(val) * invscale;
1846     };
1847     for(FloatBufferLine &inout : Samples)
1848         std::transform(inout.begin(), inout.begin()+SamplesToDo, inout.begin(), dither_sample);
1849     *dither_seed = seed;
1850 }
1851 
1852 
1853 /* Base template left undefined. Should be marked =delete, but Clang 3.8.1
1854  * chokes on that given the inline specializations.
1855  */
1856 template<typename T>
1857 inline T SampleConv(float) noexcept;
1858 
SampleConv(float val)1859 template<> inline float SampleConv(float val) noexcept
1860 { return val; }
SampleConv(float val)1861 template<> inline int32_t SampleConv(float val) noexcept
1862 {
1863     /* Floats have a 23-bit mantissa, plus an implied 1 bit and a sign bit.
1864      * This means a normalized float has at most 25 bits of signed precision.
1865      * When scaling and clamping for a signed 32-bit integer, these following
1866      * values are the best a float can give.
1867      */
1868     return fastf2i(clampf(val*2147483648.0f, -2147483648.0f, 2147483520.0f));
1869 }
SampleConv(float val)1870 template<> inline int16_t SampleConv(float val) noexcept
1871 { return static_cast<int16_t>(fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f))); }
SampleConv(float val)1872 template<> inline int8_t SampleConv(float val) noexcept
1873 { return static_cast<int8_t>(fastf2i(clampf(val*128.0f, -128.0f, 127.0f))); }
1874 
1875 /* Define unsigned output variations. */
SampleConv(float val)1876 template<> inline uint32_t SampleConv(float val) noexcept
1877 { return static_cast<uint32_t>(SampleConv<int32_t>(val)) + 2147483648u; }
SampleConv(float val)1878 template<> inline uint16_t SampleConv(float val) noexcept
1879 { return static_cast<uint16_t>(SampleConv<int16_t>(val) + 32768); }
SampleConv(float val)1880 template<> inline uint8_t SampleConv(float val) noexcept
1881 { return static_cast<uint8_t>(SampleConv<int8_t>(val) + 128); }
1882 
1883 template<DevFmtType T>
Write(const al::span<const FloatBufferLine> InBuffer,void * OutBuffer,const size_t Offset,const size_t SamplesToDo,const size_t FrameStep)1884 void Write(const al::span<const FloatBufferLine> InBuffer, void *OutBuffer, const size_t Offset,
1885     const size_t SamplesToDo, const size_t FrameStep)
1886 {
1887     ASSUME(FrameStep > 0);
1888     ASSUME(SamplesToDo > 0);
1889 
1890     DevFmtType_t<T> *outbase = static_cast<DevFmtType_t<T>*>(OutBuffer) + Offset*FrameStep;
1891     for(const FloatBufferLine &inbuf : InBuffer)
1892     {
1893         DevFmtType_t<T> *out{outbase++};
1894         auto conv_sample = [FrameStep,&out](const float s) noexcept -> void
1895         {
1896             *out = SampleConv<DevFmtType_t<T>>(s);
1897             out += FrameStep;
1898         };
1899         std::for_each(inbuf.begin(), inbuf.begin()+SamplesToDo, conv_sample);
1900     }
1901 }
1902 
1903 } // namespace
1904 
renderSamples(void * outBuffer,const uint numSamples,const size_t frameStep)1905 void ALCdevice::renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep)
1906 {
1907     FPUCtl mixer_mode{};
1908     for(uint written{0u};written < numSamples;)
1909     {
1910         const uint samplesToDo{minu(numSamples-written, BufferLineSize)};
1911 
1912         /* Clear main mixing buffers. */
1913         for(FloatBufferLine &buffer : MixBuffer)
1914             buffer.fill(0.0f);
1915 
1916         /* Increment the mix count at the start (lsb should now be 1). */
1917         IncrementRef(MixCount);
1918 
1919         /* Process and mix each context's sources and effects. */
1920         ProcessContexts(this, samplesToDo);
1921 
1922         /* Increment the clock time. Every second's worth of samples is
1923          * converted and added to clock base so that large sample counts don't
1924          * overflow during conversion. This also guarantees a stable
1925          * conversion.
1926          */
1927         SamplesDone += samplesToDo;
1928         ClockBase += std::chrono::seconds{SamplesDone / Frequency};
1929         SamplesDone %= Frequency;
1930 
1931         /* Increment the mix count at the end (lsb should now be 0). */
1932         IncrementRef(MixCount);
1933 
1934         /* Apply any needed post-process for finalizing the Dry mix to the
1935          * RealOut (Ambisonic decode, UHJ encode, etc).
1936          */
1937         postProcess(samplesToDo);
1938 
1939         /* Apply compression, limiting sample amplitude if needed or desired. */
1940         if(Limiter) Limiter->process(samplesToDo, RealOut.Buffer.data());
1941 
1942         /* Apply delays and attenuation for mismatched speaker distances. */
1943         if(ChannelDelays)
1944             ApplyDistanceComp(RealOut.Buffer, samplesToDo, ChannelDelays->mChannels.data());
1945 
1946         /* Apply dithering. The compressor should have left enough headroom for
1947          * the dither noise to not saturate.
1948          */
1949         if(DitherDepth > 0.0f)
1950             ApplyDither(RealOut.Buffer, &DitherSeed, DitherDepth, samplesToDo);
1951 
1952         if LIKELY(outBuffer)
1953         {
1954             /* Finally, interleave and convert samples, writing to the device's
1955              * output buffer.
1956              */
1957             switch(FmtType)
1958             {
1959 #define HANDLE_WRITE(T) case T:                                               \
1960     Write<T>(RealOut.Buffer, outBuffer, written, samplesToDo, frameStep); break;
1961             HANDLE_WRITE(DevFmtByte)
1962             HANDLE_WRITE(DevFmtUByte)
1963             HANDLE_WRITE(DevFmtShort)
1964             HANDLE_WRITE(DevFmtUShort)
1965             HANDLE_WRITE(DevFmtInt)
1966             HANDLE_WRITE(DevFmtUInt)
1967             HANDLE_WRITE(DevFmtFloat)
1968 #undef HANDLE_WRITE
1969             }
1970         }
1971 
1972         written += samplesToDo;
1973     }
1974 }
1975 
handleDisconnect(const char * msg,...)1976 void ALCdevice::handleDisconnect(const char *msg, ...)
1977 {
1978     if(!Connected.exchange(false, std::memory_order_acq_rel))
1979         return;
1980 
1981     AsyncEvent evt{EventType_Disconnected};
1982 
1983     va_list args;
1984     va_start(args, msg);
1985     int msglen{vsnprintf(evt.u.disconnect.msg, sizeof(evt.u.disconnect.msg), msg, args)};
1986     va_end(args);
1987 
1988     if(msglen < 0 || static_cast<size_t>(msglen) >= sizeof(evt.u.disconnect.msg))
1989         evt.u.disconnect.msg[sizeof(evt.u.disconnect.msg)-1] = 0;
1990 
1991     IncrementRef(MixCount);
1992     for(ALCcontext *ctx : *mContexts.load())
1993     {
1994         const uint enabledevt{ctx->mEnabledEvts.load(std::memory_order_acquire)};
1995         if((enabledevt&EventType_Disconnected))
1996         {
1997             RingBuffer *ring{ctx->mAsyncEvents.get()};
1998             auto evt_data = ring->getWriteVector().first;
1999             if(evt_data.len > 0)
2000             {
2001                 ::new(evt_data.buf) AsyncEvent{evt};
2002                 ring->writeAdvance(1);
2003                 ctx->mEventSem.post();
2004             }
2005         }
2006 
2007         auto voicelist = ctx->getVoicesSpanAcquired();
2008         auto stop_voice = [](Voice *voice) -> void
2009         {
2010             voice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
2011             voice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
2012             voice->mSourceID.store(0u, std::memory_order_relaxed);
2013             voice->mPlayState.store(Voice::Stopped, std::memory_order_release);
2014         };
2015         std::for_each(voicelist.begin(), voicelist.end(), stop_voice);
2016     }
2017     IncrementRef(MixCount);
2018 }
2019