Use proper inheritence for the effect state objects
[openal-soft.git] / Alc / alu.cpp
blob6a2f0c01a6239ccc5db4e750f1f087954c34b037
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.
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.
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
21 #include "config.h"
23 #include <math.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
29 #include "alMain.h"
30 #include "alcontext.h"
31 #include "alSource.h"
32 #include "alBuffer.h"
33 #include "alListener.h"
34 #include "alAuxEffectSlot.h"
35 #include "alu.h"
36 #include "bs2b.h"
37 #include "hrtf.h"
38 #include "mastering.h"
39 #include "uhjfilter.h"
40 #include "bformatdec.h"
41 #include "ringbuffer.h"
42 #include "filters/splitter.h"
44 #include "mixer/defs.h"
45 #include "fpu_modes.h"
46 #include "cpu_caps.h"
47 #include "bsinc_inc.h"
50 /* Cone scalar */
51 ALfloat ConeScale = 1.0f;
53 /* Localized Z scalar for mono sources */
54 ALfloat ZScale = 1.0f;
56 /* Force default speed of sound for distance-related reverb decay. */
57 ALboolean OverrideReverbSpeedOfSound = AL_FALSE;
60 static void ClearArray(ALfloat f[MAX_OUTPUT_CHANNELS])
62 size_t i;
63 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
64 f[i] = 0.0f;
67 struct ChanMap {
68 enum Channel channel;
69 ALfloat angle;
70 ALfloat elevation;
73 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
76 void DeinitVoice(ALvoice *voice)
78 al_free(voice->Update.exchange(nullptr));
82 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
84 #ifdef HAVE_NEON
85 if((CPUCapFlags&CPU_CAP_NEON))
86 return MixDirectHrtf_Neon;
87 #endif
88 #ifdef HAVE_SSE
89 if((CPUCapFlags&CPU_CAP_SSE))
90 return MixDirectHrtf_SSE;
91 #endif
93 return MixDirectHrtf_C;
97 /* This RNG method was created based on the math found in opusdec. It's quick,
98 * and starting with a seed value of 22222, is suitable for generating
99 * whitenoise.
101 static inline ALuint dither_rng(ALuint *seed)
103 *seed = (*seed * 96314165) + 907633515;
104 return *seed;
108 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
110 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
111 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
112 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
115 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
117 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
120 static ALfloat aluNormalize(ALfloat *vec)
122 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
123 if(length > FLT_EPSILON)
125 ALfloat inv_length = 1.0f/length;
126 vec[0] *= inv_length;
127 vec[1] *= inv_length;
128 vec[2] *= inv_length;
129 return length;
131 vec[0] = vec[1] = vec[2] = 0.0f;
132 return 0.0f;
135 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
137 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
139 vec[0] = v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0];
140 vec[1] = v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1];
141 vec[2] = v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2];
144 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
146 aluVector v;
147 v.v[0] = vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0];
148 v.v[1] = vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1];
149 v.v[2] = vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2];
150 v.v[3] = vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3];
151 return v;
155 void aluInit(void)
157 MixDirectHrtf = SelectHrtfMixer();
161 static void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
163 AsyncEvent evt = ASYNC_EVENT(EventType_SourceStateChange);
164 ALbitfieldSOFT enabledevt;
165 size_t strpos;
166 ALuint scale;
168 enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
169 if(!(enabledevt&EventType_SourceStateChange)) return;
171 evt.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
172 evt.u.user.id = id;
173 evt.u.user.param = AL_STOPPED;
175 /* Normally snprintf would be used, but this is called from the mixer and
176 * that function's not real-time safe, so we have to construct it manually.
178 strcpy(evt.u.user.msg, "Source ID "); strpos = 10;
179 scale = 1000000000;
180 while(scale > 0 && scale > id)
181 scale /= 10;
182 while(scale > 0)
184 evt.u.user.msg[strpos++] = '0' + ((id/scale)%10);
185 scale /= 10;
187 strcpy(evt.u.user.msg+strpos, " state changed to AL_STOPPED");
189 if(ll_ringbuffer_write(context->AsyncEvents, &evt, 1) == 1)
190 alsem_post(&context->EventSem);
194 static void ProcessHrtf(ALCdevice *device, ALsizei SamplesToDo)
196 DirectHrtfState *state;
197 int lidx, ridx;
198 ALsizei c;
200 if(device->AmbiUp)
201 ambiup_process(device->AmbiUp,
202 device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer,
203 SamplesToDo
206 lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
207 ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
208 assert(lidx != -1 && ridx != -1);
210 state = device->Hrtf;
211 for(c = 0;c < device->Dry.NumChannels;c++)
213 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
214 device->Dry.Buffer[c], state->Offset, state->IrSize,
215 state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo
218 state->Offset += SamplesToDo;
221 static void ProcessAmbiDec(ALCdevice *device, ALsizei SamplesToDo)
223 if(device->Dry.Buffer != device->FOAOut.Buffer)
224 bformatdec_upSample(device->AmbiDecoder,
225 device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
226 SamplesToDo
228 bformatdec_process(device->AmbiDecoder,
229 device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
230 SamplesToDo
234 static void ProcessAmbiUp(ALCdevice *device, ALsizei SamplesToDo)
236 ambiup_process(device->AmbiUp,
237 device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer,
238 SamplesToDo
242 static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
244 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
245 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
246 assert(lidx != -1 && ridx != -1);
248 /* Encode to stereo-compatible 2-channel UHJ output. */
249 EncodeUhj2(device->Uhj_Encoder,
250 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
251 device->Dry.Buffer, SamplesToDo
255 static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
257 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
258 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
259 assert(lidx != -1 && ridx != -1);
261 /* Apply binaural/crossfeed filter */
262 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
263 device->RealOut.Buffer[ridx], SamplesToDo);
266 void aluSelectPostProcess(ALCdevice *device)
268 if(device->HrtfHandle)
269 device->PostProcess = ProcessHrtf;
270 else if(device->AmbiDecoder)
271 device->PostProcess = ProcessAmbiDec;
272 else if(device->AmbiUp)
273 device->PostProcess = ProcessAmbiUp;
274 else if(device->Uhj_Encoder)
275 device->PostProcess = ProcessUhj;
276 else if(device->Bs2b)
277 device->PostProcess = ProcessBs2b;
278 else
279 device->PostProcess = NULL;
283 /* Prepares the interpolator for a given rate (determined by increment).
285 * With a bit of work, and a trade of memory for CPU cost, this could be
286 * modified for use with an interpolated increment for buttery-smooth pitch
287 * changes.
289 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
291 ALfloat sf = 0.0f;
292 ALsizei si = BSINC_SCALE_COUNT-1;
294 if(increment > FRACTIONONE)
296 sf = (ALfloat)FRACTIONONE / increment;
297 sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
298 si = float2int(sf);
299 /* The interpolation factor is fit to this diagonally-symmetric curve
300 * to reduce the transition ripple caused by interpolating different
301 * scales of the sinc function.
303 sf = 1.0f - cosf(asinf(sf - si));
306 state->sf = sf;
307 state->m = table->m[si];
308 state->l = (state->m/2) - 1;
309 state->filter = table->Tab + table->filterOffset[si];
313 static bool CalcContextParams(ALCcontext *Context)
315 ALlistener &Listener = Context->Listener;
316 struct ALcontextProps *props;
318 props = Context->Update.exchange(nullptr, std::memory_order_acq_rel);
319 if(!props) return false;
321 Listener.Params.MetersPerUnit = props->MetersPerUnit;
323 Listener.Params.DopplerFactor = props->DopplerFactor;
324 Listener.Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
325 if(!OverrideReverbSpeedOfSound)
326 Listener.Params.ReverbSpeedOfSound = Listener.Params.SpeedOfSound *
327 Listener.Params.MetersPerUnit;
329 Listener.Params.SourceDistanceModel = props->SourceDistanceModel;
330 Listener.Params.mDistanceModel = props->mDistanceModel;
332 AtomicReplaceHead(Context->FreeContextProps, props);
333 return true;
336 static bool CalcListenerParams(ALCcontext *Context)
338 ALlistener &Listener = Context->Listener;
339 ALfloat N[3], V[3], U[3], P[3];
340 struct ALlistenerProps *props;
341 aluVector vel;
343 props = Listener.Update.exchange(nullptr, std::memory_order_acq_rel);
344 if(!props) return false;
346 /* AT then UP */
347 N[0] = props->Forward[0];
348 N[1] = props->Forward[1];
349 N[2] = props->Forward[2];
350 aluNormalize(N);
351 V[0] = props->Up[0];
352 V[1] = props->Up[1];
353 V[2] = props->Up[2];
354 aluNormalize(V);
355 /* Build and normalize right-vector */
356 aluCrossproduct(N, V, U);
357 aluNormalize(U);
359 aluMatrixfSet(&Listener.Params.Matrix,
360 U[0], V[0], -N[0], 0.0,
361 U[1], V[1], -N[1], 0.0,
362 U[2], V[2], -N[2], 0.0,
363 0.0, 0.0, 0.0, 1.0
366 P[0] = props->Position[0];
367 P[1] = props->Position[1];
368 P[2] = props->Position[2];
369 aluMatrixfFloat3(P, 1.0, &Listener.Params.Matrix);
370 aluMatrixfSetRow(&Listener.Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
372 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
373 Listener.Params.Velocity = aluMatrixfVector(&Listener.Params.Matrix, &vel);
375 Listener.Params.Gain = props->Gain * Context->GainBoost;
377 AtomicReplaceHead(Context->FreeListenerProps, props);
378 return true;
381 static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
383 struct ALeffectslotProps *props;
384 EffectState *state;
386 props = slot->Update.exchange(nullptr, std::memory_order_acq_rel);
387 if(!props && !force) return false;
389 if(props)
391 slot->Params.Gain = props->Gain;
392 slot->Params.AuxSendAuto = props->AuxSendAuto;
393 slot->Params.EffectType = props->Type;
394 slot->Params.EffectProps = props->Props;
395 if(IsReverbEffect(props->Type))
397 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
398 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
399 slot->Params.DecayLFRatio = props->Props.Reverb.DecayLFRatio;
400 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
401 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
402 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
404 else
406 slot->Params.RoomRolloff = 0.0f;
407 slot->Params.DecayTime = 0.0f;
408 slot->Params.DecayLFRatio = 0.0f;
409 slot->Params.DecayHFRatio = 0.0f;
410 slot->Params.DecayHFLimit = AL_FALSE;
411 slot->Params.AirAbsorptionGainHF = 1.0f;
414 state = props->State;
416 if(state == slot->Params.EffectState)
418 /* If the effect state is the same as current, we can decrement its
419 * count safely to remove it from the update object (it can't reach
420 * 0 refs since the current params also hold a reference).
422 DecrementRef(&state->mRef);
423 props->State = nullptr;
425 else
427 /* Otherwise, replace it and send off the old one with a release
428 * event.
430 AsyncEvent evt = ASYNC_EVENT(EventType_ReleaseEffectState);
431 evt.u.mEffectState = slot->Params.EffectState;
433 slot->Params.EffectState = state;
434 props->State = NULL;
436 if(LIKELY(ll_ringbuffer_write(context->AsyncEvents, &evt, 1) != 0))
437 alsem_post(&context->EventSem);
438 else
440 /* If writing the event failed, the queue was probably full.
441 * Store the old state in the property object where it can
442 * eventually be cleaned up sometime later (not ideal, but
443 * better than blocking or leaking).
445 props->State = evt.u.mEffectState;
449 AtomicReplaceHead(context->FreeEffectslotProps, props);
451 else
452 state = slot->Params.EffectState;
454 state->update(context, slot, &slot->Params.EffectProps);
455 return true;
459 static const struct ChanMap MonoMap[1] = {
460 { FrontCenter, 0.0f, 0.0f }
461 }, RearMap[2] = {
462 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
463 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
464 }, QuadMap[4] = {
465 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
466 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
467 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
468 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
469 }, X51Map[6] = {
470 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
471 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
472 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
473 { LFE, 0.0f, 0.0f },
474 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
475 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
476 }, X61Map[7] = {
477 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
478 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
479 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
480 { LFE, 0.0f, 0.0f },
481 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
482 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
483 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
484 }, X71Map[8] = {
485 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
486 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
487 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
488 { LFE, 0.0f, 0.0f },
489 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
490 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
491 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
492 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
495 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALfloat Elev,
496 const ALfloat Distance, const ALfloat Spread,
497 const ALfloat DryGain, const ALfloat DryGainHF,
498 const ALfloat DryGainLF, const ALfloat *WetGain,
499 const ALfloat *WetGainLF, const ALfloat *WetGainHF,
500 ALeffectslot **SendSlots, const ALbuffer *Buffer,
501 const struct ALvoiceProps *props, const ALlistener &Listener,
502 const ALCdevice *Device)
504 struct ChanMap StereoMap[2] = {
505 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
506 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
508 bool DirectChannels = props->DirectChannels;
509 const ALsizei NumSends = Device->NumAuxSends;
510 const ALuint Frequency = Device->Frequency;
511 const struct ChanMap *chans = NULL;
512 ALsizei num_channels = 0;
513 bool isbformat = false;
514 ALfloat downmix_gain = 1.0f;
515 ALsizei c, i;
517 switch(Buffer->FmtChannels)
519 case FmtMono:
520 chans = MonoMap;
521 num_channels = 1;
522 /* Mono buffers are never played direct. */
523 DirectChannels = false;
524 break;
526 case FmtStereo:
527 /* Convert counter-clockwise to clockwise. */
528 StereoMap[0].angle = -props->StereoPan[0];
529 StereoMap[1].angle = -props->StereoPan[1];
531 chans = StereoMap;
532 num_channels = 2;
533 downmix_gain = 1.0f / 2.0f;
534 break;
536 case FmtRear:
537 chans = RearMap;
538 num_channels = 2;
539 downmix_gain = 1.0f / 2.0f;
540 break;
542 case FmtQuad:
543 chans = QuadMap;
544 num_channels = 4;
545 downmix_gain = 1.0f / 4.0f;
546 break;
548 case FmtX51:
549 chans = X51Map;
550 num_channels = 6;
551 /* NOTE: Excludes LFE. */
552 downmix_gain = 1.0f / 5.0f;
553 break;
555 case FmtX61:
556 chans = X61Map;
557 num_channels = 7;
558 /* NOTE: Excludes LFE. */
559 downmix_gain = 1.0f / 6.0f;
560 break;
562 case FmtX71:
563 chans = X71Map;
564 num_channels = 8;
565 /* NOTE: Excludes LFE. */
566 downmix_gain = 1.0f / 7.0f;
567 break;
569 case FmtBFormat2D:
570 num_channels = 3;
571 isbformat = true;
572 DirectChannels = false;
573 break;
575 case FmtBFormat3D:
576 num_channels = 4;
577 isbformat = true;
578 DirectChannels = false;
579 break;
582 for(c = 0;c < num_channels;c++)
584 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
585 sizeof(voice->Direct.Params[c].Hrtf.Target));
586 ClearArray(voice->Direct.Params[c].Gains.Target);
588 for(i = 0;i < NumSends;i++)
590 for(c = 0;c < num_channels;c++)
591 ClearArray(voice->Send[i].Params[c].Gains.Target);
594 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
595 if(isbformat)
597 /* Special handling for B-Format sources. */
599 if(Distance > FLT_EPSILON)
601 /* Panning a B-Format sound toward some direction is easy. Just pan
602 * the first (W) channel as a normal mono sound and silence the
603 * others.
605 ALfloat coeffs[MAX_AMBI_COEFFS];
607 if(Device->AvgSpeakerDist > 0.0f)
609 ALfloat mdist = Distance * Listener.Params.MetersPerUnit;
610 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
611 (mdist * (ALfloat)Device->Frequency);
612 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
613 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
614 /* Clamp w0 for really close distances, to prevent excessive
615 * bass.
617 w0 = minf(w0, w1*4.0f);
619 /* Only need to adjust the first channel of a B-Format source. */
620 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
622 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
623 voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
624 voice->Flags |= VOICE_HAS_NFC;
627 /* A scalar of 1.5 for plain stereo results in +/-60 degrees being
628 * moved to +/-90 degrees for direct right and left speaker
629 * responses.
631 CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
632 Elev, Spread, coeffs);
634 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
635 ComputePanGains(&Device->Dry, coeffs, DryGain*SQRTF_2,
636 voice->Direct.Params[0].Gains.Target);
637 for(i = 0;i < NumSends;i++)
639 const ALeffectslot *Slot = SendSlots[i];
640 if(Slot)
641 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
642 WetGain[i]*SQRTF_2, voice->Send[i].Params[0].Gains.Target
646 else
648 /* Local B-Format sources have their XYZ channels rotated according
649 * to the orientation.
651 ALfloat N[3], V[3], U[3];
652 aluMatrixf matrix;
654 if(Device->AvgSpeakerDist > 0.0f)
656 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
657 * is what we want for FOA input. The first channel may have
658 * been previously re-adjusted if panned, so reset it.
660 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, 0.0f);
662 voice->Direct.ChannelsPerOrder[0] = 1;
663 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
664 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
665 voice->Direct.ChannelsPerOrder[i] = 0;
666 voice->Flags |= VOICE_HAS_NFC;
669 /* AT then UP */
670 N[0] = props->Orientation[0][0];
671 N[1] = props->Orientation[0][1];
672 N[2] = props->Orientation[0][2];
673 aluNormalize(N);
674 V[0] = props->Orientation[1][0];
675 V[1] = props->Orientation[1][1];
676 V[2] = props->Orientation[1][2];
677 aluNormalize(V);
678 if(!props->HeadRelative)
680 const aluMatrixf *lmatrix = &Listener.Params.Matrix;
681 aluMatrixfFloat3(N, 0.0f, lmatrix);
682 aluMatrixfFloat3(V, 0.0f, lmatrix);
684 /* Build and normalize right-vector */
685 aluCrossproduct(N, V, U);
686 aluNormalize(U);
688 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). NOTE: This
689 * matrix is transposed, for the inputs to align on the rows and
690 * outputs on the columns.
692 aluMatrixfSet(&matrix,
693 // ACN0 ACN1 ACN2 ACN3
694 SQRTF_2, 0.0f, 0.0f, 0.0f, // Ambi W
695 0.0f, -N[0]*SQRTF_3, N[1]*SQRTF_3, -N[2]*SQRTF_3, // Ambi X
696 0.0f, U[0]*SQRTF_3, -U[1]*SQRTF_3, U[2]*SQRTF_3, // Ambi Y
697 0.0f, -V[0]*SQRTF_3, V[1]*SQRTF_3, -V[2]*SQRTF_3 // Ambi Z
700 voice->Direct.Buffer = Device->FOAOut.Buffer;
701 voice->Direct.Channels = Device->FOAOut.NumChannels;
702 for(c = 0;c < num_channels;c++)
703 ComputePanGains(&Device->FOAOut, matrix.m[c], DryGain,
704 voice->Direct.Params[c].Gains.Target);
705 for(i = 0;i < NumSends;i++)
707 const ALeffectslot *Slot = SendSlots[i];
708 if(Slot)
710 for(c = 0;c < num_channels;c++)
711 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
712 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
718 else if(DirectChannels)
720 /* Direct source channels always play local. Skip the virtual channels
721 * and write inputs to the matching real outputs.
723 voice->Direct.Buffer = Device->RealOut.Buffer;
724 voice->Direct.Channels = Device->RealOut.NumChannels;
726 for(c = 0;c < num_channels;c++)
728 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
729 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
732 /* Auxiliary sends still use normal channel panning since they mix to
733 * B-Format, which can't channel-match.
735 for(c = 0;c < num_channels;c++)
737 ALfloat coeffs[MAX_AMBI_COEFFS];
738 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
740 for(i = 0;i < NumSends;i++)
742 const ALeffectslot *Slot = SendSlots[i];
743 if(Slot)
744 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
745 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
750 else if(Device->Render_Mode == HrtfRender)
752 /* Full HRTF rendering. Skip the virtual channels and render to the
753 * real outputs.
755 voice->Direct.Buffer = Device->RealOut.Buffer;
756 voice->Direct.Channels = Device->RealOut.NumChannels;
758 if(Distance > FLT_EPSILON)
760 ALfloat coeffs[MAX_AMBI_COEFFS];
762 /* Get the HRIR coefficients and delays just once, for the given
763 * source direction.
765 GetHrtfCoeffs(Device->HrtfHandle, Elev, Azi, Spread,
766 voice->Direct.Params[0].Hrtf.Target.Coeffs,
767 voice->Direct.Params[0].Hrtf.Target.Delay);
768 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
770 /* Remaining channels use the same results as the first. */
771 for(c = 1;c < num_channels;c++)
773 /* Skip LFE */
774 if(chans[c].channel != LFE)
775 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
778 /* Calculate the directional coefficients once, which apply to all
779 * input channels of the source sends.
781 CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
783 for(i = 0;i < NumSends;i++)
785 const ALeffectslot *Slot = SendSlots[i];
786 if(Slot)
787 for(c = 0;c < num_channels;c++)
789 /* Skip LFE */
790 if(chans[c].channel != LFE)
791 ComputePanningGainsBF(Slot->ChanMap,
792 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
793 voice->Send[i].Params[c].Gains.Target
798 else
800 /* Local sources on HRTF play with each channel panned to its
801 * relative location around the listener, providing "virtual
802 * speaker" responses.
804 for(c = 0;c < num_channels;c++)
806 ALfloat coeffs[MAX_AMBI_COEFFS];
808 if(chans[c].channel == LFE)
810 /* Skip LFE */
811 continue;
814 /* Get the HRIR coefficients and delays for this channel
815 * position.
817 GetHrtfCoeffs(Device->HrtfHandle,
818 chans[c].elevation, chans[c].angle, Spread,
819 voice->Direct.Params[c].Hrtf.Target.Coeffs,
820 voice->Direct.Params[c].Hrtf.Target.Delay
822 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
824 /* Normal panning for auxiliary sends. */
825 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
827 for(i = 0;i < NumSends;i++)
829 const ALeffectslot *Slot = SendSlots[i];
830 if(Slot)
831 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
832 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
838 voice->Flags |= VOICE_HAS_HRTF;
840 else
842 /* Non-HRTF rendering. Use normal panning to the output. */
844 if(Distance > FLT_EPSILON)
846 ALfloat coeffs[MAX_AMBI_COEFFS];
847 ALfloat w0 = 0.0f;
849 /* Calculate NFC filter coefficient if needed. */
850 if(Device->AvgSpeakerDist > 0.0f)
852 ALfloat mdist = Distance * Listener.Params.MetersPerUnit;
853 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
854 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
855 w0 = SPEEDOFSOUNDMETRESPERSEC /
856 (mdist * (ALfloat)Device->Frequency);
857 /* Clamp w0 for really close distances, to prevent excessive
858 * bass.
860 w0 = minf(w0, w1*4.0f);
862 /* Adjust NFC filters. */
863 for(c = 0;c < num_channels;c++)
864 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
866 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
867 voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
868 voice->Flags |= VOICE_HAS_NFC;
871 /* Calculate the directional coefficients once, which apply to all
872 * input channels.
874 CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
875 Elev, Spread, coeffs);
877 for(c = 0;c < num_channels;c++)
879 /* Special-case LFE */
880 if(chans[c].channel == LFE)
882 if(Device->Dry.Buffer == Device->RealOut.Buffer)
884 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
885 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
887 continue;
890 ComputePanGains(&Device->Dry, coeffs, DryGain * downmix_gain,
891 voice->Direct.Params[c].Gains.Target);
894 for(i = 0;i < NumSends;i++)
896 const ALeffectslot *Slot = SendSlots[i];
897 if(Slot)
898 for(c = 0;c < num_channels;c++)
900 /* Skip LFE */
901 if(chans[c].channel != LFE)
902 ComputePanningGainsBF(Slot->ChanMap,
903 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
904 voice->Send[i].Params[c].Gains.Target
909 else
911 ALfloat w0 = 0.0f;
913 if(Device->AvgSpeakerDist > 0.0f)
915 /* If the source distance is 0, set w0 to w1 to act as a pass-
916 * through. We still want to pass the signal through the
917 * filters so they keep an appropriate history, in case the
918 * source moves away from the listener.
920 w0 = SPEEDOFSOUNDMETRESPERSEC /
921 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
923 for(c = 0;c < num_channels;c++)
924 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
926 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
927 voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
928 voice->Flags |= VOICE_HAS_NFC;
931 for(c = 0;c < num_channels;c++)
933 ALfloat coeffs[MAX_AMBI_COEFFS];
935 /* Special-case LFE */
936 if(chans[c].channel == LFE)
938 if(Device->Dry.Buffer == Device->RealOut.Buffer)
940 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
941 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
943 continue;
946 CalcAngleCoeffs(
947 (Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(chans[c].angle, 3.0f)
948 : chans[c].angle,
949 chans[c].elevation, Spread, coeffs
952 ComputePanGains(&Device->Dry, coeffs, DryGain,
953 voice->Direct.Params[c].Gains.Target);
954 for(i = 0;i < NumSends;i++)
956 const ALeffectslot *Slot = SendSlots[i];
957 if(Slot)
958 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
959 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
967 ALfloat hfScale = props->Direct.HFReference / Frequency;
968 ALfloat lfScale = props->Direct.LFReference / Frequency;
969 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
970 ALfloat gainLF = maxf(DryGainLF, 0.001f);
972 voice->Direct.FilterType = AF_None;
973 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
974 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
975 BiquadFilter_setParams(
976 &voice->Direct.Params[0].LowPass, BiquadType::HighShelf,
977 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
979 BiquadFilter_setParams(
980 &voice->Direct.Params[0].HighPass, BiquadType::LowShelf,
981 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
983 for(c = 1;c < num_channels;c++)
985 BiquadFilter_copyParams(&voice->Direct.Params[c].LowPass,
986 &voice->Direct.Params[0].LowPass);
987 BiquadFilter_copyParams(&voice->Direct.Params[c].HighPass,
988 &voice->Direct.Params[0].HighPass);
991 for(i = 0;i < NumSends;i++)
993 ALfloat hfScale = props->Send[i].HFReference / Frequency;
994 ALfloat lfScale = props->Send[i].LFReference / Frequency;
995 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
996 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
998 voice->Send[i].FilterType = AF_None;
999 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1000 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1001 BiquadFilter_setParams(
1002 &voice->Send[i].Params[0].LowPass, BiquadType::HighShelf,
1003 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1005 BiquadFilter_setParams(
1006 &voice->Send[i].Params[0].HighPass, BiquadType::LowShelf,
1007 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1009 for(c = 1;c < num_channels;c++)
1011 BiquadFilter_copyParams(&voice->Send[i].Params[c].LowPass,
1012 &voice->Send[i].Params[0].LowPass);
1013 BiquadFilter_copyParams(&voice->Send[i].Params[c].HighPass,
1014 &voice->Send[i].Params[0].HighPass);
1019 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1021 const ALCdevice *Device = ALContext->Device;
1022 const ALlistener &Listener = ALContext->Listener;
1023 ALfloat DryGain, DryGainHF, DryGainLF;
1024 ALfloat WetGain[MAX_SENDS];
1025 ALfloat WetGainHF[MAX_SENDS];
1026 ALfloat WetGainLF[MAX_SENDS];
1027 ALeffectslot *SendSlots[MAX_SENDS];
1028 ALfloat Pitch;
1029 ALsizei i;
1031 voice->Direct.Buffer = Device->Dry.Buffer;
1032 voice->Direct.Channels = Device->Dry.NumChannels;
1033 for(i = 0;i < Device->NumAuxSends;i++)
1035 SendSlots[i] = props->Send[i].Slot;
1036 if(!SendSlots[i] && i == 0)
1037 SendSlots[i] = ALContext->DefaultSlot;
1038 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1040 SendSlots[i] = NULL;
1041 voice->Send[i].Buffer = NULL;
1042 voice->Send[i].Channels = 0;
1044 else
1046 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1047 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1051 /* Calculate the stepping value */
1052 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1053 if(Pitch > (ALfloat)MAX_PITCH)
1054 voice->Step = MAX_PITCH<<FRACTIONBITS;
1055 else
1056 voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
1057 if(props->Resampler == BSinc24Resampler)
1058 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1059 else if(props->Resampler == BSinc12Resampler)
1060 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1061 voice->Resampler = SelectResampler(props->Resampler);
1063 /* Calculate gains */
1064 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1065 DryGain *= props->Direct.Gain * Listener.Params.Gain;
1066 DryGain = minf(DryGain, GAIN_MIX_MAX);
1067 DryGainHF = props->Direct.GainHF;
1068 DryGainLF = props->Direct.GainLF;
1069 for(i = 0;i < Device->NumAuxSends;i++)
1071 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1072 WetGain[i] *= props->Send[i].Gain * Listener.Params.Gain;
1073 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1074 WetGainHF[i] = props->Send[i].GainHF;
1075 WetGainLF[i] = props->Send[i].GainLF;
1078 CalcPanningAndFilters(voice, 0.0f, 0.0f, 0.0f, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1079 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1082 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1084 const ALCdevice *Device = ALContext->Device;
1085 const ALlistener &Listener = ALContext->Listener;
1086 const ALsizei NumSends = Device->NumAuxSends;
1087 aluVector Position, Velocity, Direction, SourceToListener;
1088 ALfloat Distance, ClampedDist, DopplerFactor;
1089 ALeffectslot *SendSlots[MAX_SENDS];
1090 ALfloat RoomRolloff[MAX_SENDS];
1091 ALfloat DecayDistance[MAX_SENDS];
1092 ALfloat DecayLFDistance[MAX_SENDS];
1093 ALfloat DecayHFDistance[MAX_SENDS];
1094 ALfloat DryGain, DryGainHF, DryGainLF;
1095 ALfloat WetGain[MAX_SENDS];
1096 ALfloat WetGainHF[MAX_SENDS];
1097 ALfloat WetGainLF[MAX_SENDS];
1098 bool directional;
1099 ALfloat ev, az;
1100 ALfloat spread;
1101 ALfloat Pitch;
1102 ALint i;
1104 /* Set mixing buffers and get send parameters. */
1105 voice->Direct.Buffer = Device->Dry.Buffer;
1106 voice->Direct.Channels = Device->Dry.NumChannels;
1107 for(i = 0;i < NumSends;i++)
1109 SendSlots[i] = props->Send[i].Slot;
1110 if(!SendSlots[i] && i == 0)
1111 SendSlots[i] = ALContext->DefaultSlot;
1112 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1114 SendSlots[i] = NULL;
1115 RoomRolloff[i] = 0.0f;
1116 DecayDistance[i] = 0.0f;
1117 DecayLFDistance[i] = 0.0f;
1118 DecayHFDistance[i] = 0.0f;
1120 else if(SendSlots[i]->Params.AuxSendAuto)
1122 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1123 /* Calculate the distances to where this effect's decay reaches
1124 * -60dB.
1126 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1127 Listener.Params.ReverbSpeedOfSound;
1128 DecayLFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayLFRatio;
1129 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1130 if(SendSlots[i]->Params.DecayHFLimit)
1132 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1133 if(airAbsorption < 1.0f)
1135 /* Calculate the distance to where this effect's air
1136 * absorption reaches -60dB, and limit the effect's HF
1137 * decay distance (so it doesn't take any longer to decay
1138 * than the air would allow).
1140 ALfloat absorb_dist = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1141 DecayHFDistance[i] = minf(absorb_dist, DecayHFDistance[i]);
1145 else
1147 /* If the slot's auxiliary send auto is off, the data sent to the
1148 * effect slot is the same as the dry path, sans filter effects */
1149 RoomRolloff[i] = props->RolloffFactor;
1150 DecayDistance[i] = 0.0f;
1151 DecayLFDistance[i] = 0.0f;
1152 DecayHFDistance[i] = 0.0f;
1155 if(!SendSlots[i])
1157 voice->Send[i].Buffer = NULL;
1158 voice->Send[i].Channels = 0;
1160 else
1162 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1163 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1167 /* Transform source to listener space (convert to head relative) */
1168 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1169 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1170 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1171 if(props->HeadRelative == AL_FALSE)
1173 const aluMatrixf *Matrix = &Listener.Params.Matrix;
1174 /* Transform source vectors */
1175 Position = aluMatrixfVector(Matrix, &Position);
1176 Velocity = aluMatrixfVector(Matrix, &Velocity);
1177 Direction = aluMatrixfVector(Matrix, &Direction);
1179 else
1181 const aluVector *lvelocity = &Listener.Params.Velocity;
1182 /* Offset the source velocity to be relative of the listener velocity */
1183 Velocity.v[0] += lvelocity->v[0];
1184 Velocity.v[1] += lvelocity->v[1];
1185 Velocity.v[2] += lvelocity->v[2];
1188 directional = aluNormalize(Direction.v) > 0.0f;
1189 SourceToListener.v[0] = -Position.v[0];
1190 SourceToListener.v[1] = -Position.v[1];
1191 SourceToListener.v[2] = -Position.v[2];
1192 SourceToListener.v[3] = 0.0f;
1193 Distance = aluNormalize(SourceToListener.v);
1195 /* Initial source gain */
1196 DryGain = props->Gain;
1197 DryGainHF = 1.0f;
1198 DryGainLF = 1.0f;
1199 for(i = 0;i < NumSends;i++)
1201 WetGain[i] = props->Gain;
1202 WetGainHF[i] = 1.0f;
1203 WetGainLF[i] = 1.0f;
1206 /* Calculate distance attenuation */
1207 ClampedDist = Distance;
1209 switch(Listener.Params.SourceDistanceModel ?
1210 props->mDistanceModel : Listener.Params.mDistanceModel)
1212 case DistanceModel::InverseClamped:
1213 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1214 if(props->MaxDistance < props->RefDistance)
1215 break;
1216 /*fall-through*/
1217 case DistanceModel::Inverse:
1218 if(!(props->RefDistance > 0.0f))
1219 ClampedDist = props->RefDistance;
1220 else
1222 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1223 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1224 for(i = 0;i < NumSends;i++)
1226 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1227 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1230 break;
1232 case DistanceModel::LinearClamped:
1233 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1234 if(props->MaxDistance < props->RefDistance)
1235 break;
1236 /*fall-through*/
1237 case DistanceModel::Linear:
1238 if(!(props->MaxDistance != props->RefDistance))
1239 ClampedDist = props->RefDistance;
1240 else
1242 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1243 (props->MaxDistance-props->RefDistance);
1244 DryGain *= maxf(1.0f - attn, 0.0f);
1245 for(i = 0;i < NumSends;i++)
1247 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1248 (props->MaxDistance-props->RefDistance);
1249 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1252 break;
1254 case DistanceModel::ExponentClamped:
1255 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1256 if(props->MaxDistance < props->RefDistance)
1257 break;
1258 /*fall-through*/
1259 case DistanceModel::Exponent:
1260 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1261 ClampedDist = props->RefDistance;
1262 else
1264 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1265 for(i = 0;i < NumSends;i++)
1266 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1268 break;
1270 case DistanceModel::Disable:
1271 ClampedDist = props->RefDistance;
1272 break;
1275 /* Calculate directional soundcones */
1276 if(directional && props->InnerAngle < 360.0f)
1278 ALfloat ConeVolume;
1279 ALfloat ConeHF;
1280 ALfloat Angle;
1282 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1283 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1284 if(!(Angle > props->InnerAngle))
1286 ConeVolume = 1.0f;
1287 ConeHF = 1.0f;
1289 else if(Angle < props->OuterAngle)
1291 ALfloat scale = ( Angle-props->InnerAngle) /
1292 (props->OuterAngle-props->InnerAngle);
1293 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1294 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1296 else
1298 ConeVolume = props->OuterGain;
1299 ConeHF = props->OuterGainHF;
1302 DryGain *= ConeVolume;
1303 if(props->DryGainHFAuto)
1304 DryGainHF *= ConeHF;
1305 if(props->WetGainAuto)
1307 for(i = 0;i < NumSends;i++)
1308 WetGain[i] *= ConeVolume;
1310 if(props->WetGainHFAuto)
1312 for(i = 0;i < NumSends;i++)
1313 WetGainHF[i] *= ConeHF;
1317 /* Apply gain and frequency filters */
1318 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1319 DryGain = minf(DryGain*props->Direct.Gain*Listener.Params.Gain, GAIN_MIX_MAX);
1320 DryGainHF *= props->Direct.GainHF;
1321 DryGainLF *= props->Direct.GainLF;
1322 for(i = 0;i < NumSends;i++)
1324 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1325 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener.Params.Gain, GAIN_MIX_MAX);
1326 WetGainHF[i] *= props->Send[i].GainHF;
1327 WetGainLF[i] *= props->Send[i].GainLF;
1330 /* Distance-based air absorption and initial send decay. */
1331 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1333 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1334 Listener.Params.MetersPerUnit;
1335 if(props->AirAbsorptionFactor > 0.0f)
1337 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1338 DryGainHF *= hfattn;
1339 for(i = 0;i < NumSends;i++)
1340 WetGainHF[i] *= hfattn;
1343 if(props->WetGainAuto)
1345 /* Apply a decay-time transformation to the wet path, based on the
1346 * source distance in meters. The initial decay of the reverb
1347 * effect is calculated and applied to the wet path.
1349 for(i = 0;i < NumSends;i++)
1351 ALfloat gain, gainhf, gainlf;
1353 if(!(DecayDistance[i] > 0.0f))
1354 continue;
1356 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1357 WetGain[i] *= gain;
1358 /* Yes, the wet path's air absorption is applied with
1359 * WetGainAuto on, rather than WetGainHFAuto.
1361 if(gain > 0.0f)
1363 gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1364 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1365 gainlf = powf(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i]);
1366 WetGainLF[i] *= minf(gainlf / gain, 1.0f);
1373 /* Initial source pitch */
1374 Pitch = props->Pitch;
1376 /* Calculate velocity-based doppler effect */
1377 DopplerFactor = props->DopplerFactor * Listener.Params.DopplerFactor;
1378 if(DopplerFactor > 0.0f)
1380 const aluVector *lvelocity = &Listener.Params.Velocity;
1381 const ALfloat SpeedOfSound = Listener.Params.SpeedOfSound;
1382 ALfloat vss, vls;
1384 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1385 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1387 if(!(vls < SpeedOfSound))
1389 /* Listener moving away from the source at the speed of sound.
1390 * Sound waves can't catch it.
1392 Pitch = 0.0f;
1394 else if(!(vss < SpeedOfSound))
1396 /* Source moving toward the listener at the speed of sound. Sound
1397 * waves bunch up to extreme frequencies.
1399 Pitch = HUGE_VALF;
1401 else
1403 /* Source and listener movement is nominal. Calculate the proper
1404 * doppler shift.
1406 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1410 /* Adjust pitch based on the buffer and output frequencies, and calculate
1411 * fixed-point stepping value.
1413 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1414 if(Pitch > (ALfloat)MAX_PITCH)
1415 voice->Step = MAX_PITCH<<FRACTIONBITS;
1416 else
1417 voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
1418 if(props->Resampler == BSinc24Resampler)
1419 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1420 else if(props->Resampler == BSinc12Resampler)
1421 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1422 voice->Resampler = SelectResampler(props->Resampler);
1424 if(Distance > 0.0f)
1426 /* Clamp Y, in case rounding errors caused it to end up outside of
1427 * -1...+1.
1429 ev = asinf(clampf(-SourceToListener.v[1], -1.0f, 1.0f));
1430 /* Double negation on Z cancels out; negate once for changing source-
1431 * to-listener to listener-to-source, and again for right-handed coords
1432 * with -Z in front.
1434 az = atan2f(-SourceToListener.v[0], SourceToListener.v[2]*ZScale);
1436 else
1437 ev = az = 0.0f;
1439 if(props->Radius > Distance)
1440 spread = F_TAU - Distance/props->Radius*F_PI;
1441 else if(Distance > 0.0f)
1442 spread = asinf(props->Radius / Distance) * 2.0f;
1443 else
1444 spread = 0.0f;
1446 CalcPanningAndFilters(voice, az, ev, Distance, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1447 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1450 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1452 ALbufferlistitem *BufferListItem;
1453 struct ALvoiceProps *props;
1455 props = voice->Update.exchange(nullptr, std::memory_order_acq_rel);
1456 if(!props && !force) return;
1458 if(props)
1460 memcpy(voice->Props, props,
1461 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1464 AtomicReplaceHead(context->FreeVoiceProps, props);
1466 props = voice->Props;
1468 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1469 while(BufferListItem != NULL)
1471 const ALbuffer *buffer = NULL;
1472 ALsizei i = 0;
1473 while(!buffer && i < BufferListItem->num_buffers)
1474 buffer = BufferListItem->buffers[i];
1475 if(LIKELY(buffer))
1477 if(props->SpatializeMode == SpatializeOn ||
1478 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1479 CalcAttnSourceParams(voice, props, buffer, context);
1480 else
1481 CalcNonAttnSourceParams(voice, props, buffer, context);
1482 break;
1484 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1489 static void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1491 ALvoice **voice, **voice_end;
1492 ALsource *source;
1493 ALsizei i;
1495 IncrementRef(&ctx->UpdateCount);
1496 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1498 bool cforce = CalcContextParams(ctx);
1499 bool force = CalcListenerParams(ctx) | cforce;
1500 for(i = 0;i < slots->count;i++)
1501 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1503 voice = ctx->Voices;
1504 voice_end = voice + ctx->VoiceCount;
1505 for(;voice != voice_end;++voice)
1507 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1508 if(source) CalcSourceParams(*voice, ctx, force);
1511 IncrementRef(&ctx->UpdateCount);
1515 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*RESTRICT Buffer)[BUFFERSIZE],
1516 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1517 ALsizei NumChannels)
1519 ALfloat (*RESTRICT lsplit)[BUFFERSIZE] = Stablizer->LSplit;
1520 ALfloat (*RESTRICT rsplit)[BUFFERSIZE] = Stablizer->RSplit;
1521 ALsizei i;
1523 /* Apply an all-pass to all channels, except the front-left and front-
1524 * right, so they maintain the same relative phase.
1526 for(i = 0;i < NumChannels;i++)
1528 if(i == lidx || i == ridx)
1529 continue;
1530 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1533 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1534 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1536 for(i = 0;i < SamplesToDo;i++)
1538 ALfloat lfsum, hfsum;
1539 ALfloat m, s, c;
1541 lfsum = lsplit[0][i] + rsplit[0][i];
1542 hfsum = lsplit[1][i] + rsplit[1][i];
1543 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1545 /* This pans the separate low- and high-frequency sums between being on
1546 * the center channel and the left/right channels. The low-frequency
1547 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1548 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1549 * values can be tweaked.
1551 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1552 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1554 /* The generated center channel signal adds to the existing signal,
1555 * while the modified left and right channels replace.
1557 Buffer[lidx][i] = (m + s) * 0.5f;
1558 Buffer[ridx][i] = (m - s) * 0.5f;
1559 Buffer[cidx][i] += c * 0.5f;
1563 static void ApplyDistanceComp(ALfloat (*RESTRICT Samples)[BUFFERSIZE], DistanceComp *distcomp,
1564 ALfloat *RESTRICT Values, ALsizei SamplesToDo, ALsizei numchans)
1566 ALsizei i, c;
1568 for(c = 0;c < numchans;c++)
1570 ALfloat *RESTRICT inout = Samples[c];
1571 const ALfloat gain = distcomp[c].Gain;
1572 const ALsizei base = distcomp[c].Length;
1573 ALfloat *RESTRICT distbuf = distcomp[c].Buffer;
1575 if(base == 0)
1577 if(gain < 1.0f)
1579 for(i = 0;i < SamplesToDo;i++)
1580 inout[i] *= gain;
1582 continue;
1585 if(LIKELY(SamplesToDo >= base))
1587 for(i = 0;i < base;i++)
1588 Values[i] = distbuf[i];
1589 for(;i < SamplesToDo;i++)
1590 Values[i] = inout[i-base];
1591 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1593 else
1595 for(i = 0;i < SamplesToDo;i++)
1596 Values[i] = distbuf[i];
1597 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1598 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1600 for(i = 0;i < SamplesToDo;i++)
1601 inout[i] = Values[i]*gain;
1605 static void ApplyDither(ALfloat (*RESTRICT Samples)[BUFFERSIZE], ALuint *dither_seed,
1606 const ALfloat quant_scale, const ALsizei SamplesToDo,
1607 const ALsizei numchans)
1609 const ALfloat invscale = 1.0f / quant_scale;
1610 ALuint seed = *dither_seed;
1611 ALsizei c, i;
1613 ASSUME(numchans > 0);
1614 ASSUME(SamplesToDo > 0);
1616 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1617 * values between -1 and +1). Step 2 is to add the noise to the samples,
1618 * before rounding and after scaling up to the desired quantization depth.
1620 for(c = 0;c < numchans;c++)
1622 ALfloat *RESTRICT samples = Samples[c];
1623 for(i = 0;i < SamplesToDo;i++)
1625 ALfloat val = samples[i] * quant_scale;
1626 ALuint rng0 = dither_rng(&seed);
1627 ALuint rng1 = dither_rng(&seed);
1628 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1629 samples[i] = fast_roundf(val) * invscale;
1632 *dither_seed = seed;
1636 static inline ALfloat Conv_ALfloat(ALfloat val)
1637 { return val; }
1638 static inline ALint Conv_ALint(ALfloat val)
1640 /* Floats have a 23-bit mantissa. There is an implied 1 bit in the mantissa
1641 * along with the sign bit, giving 25 bits total, so [-16777216, +16777216]
1642 * is the max value a normalized float can be scaled to before losing
1643 * precision.
1645 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1647 static inline ALshort Conv_ALshort(ALfloat val)
1648 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1649 static inline ALbyte Conv_ALbyte(ALfloat val)
1650 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1652 /* Define unsigned output variations. */
1653 #define DECL_TEMPLATE(T, func, O) \
1654 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1656 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1657 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1658 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1660 #undef DECL_TEMPLATE
1662 #define DECL_TEMPLATE(T, A) \
1663 static void Write##A(const ALfloat (*RESTRICT InBuffer)[BUFFERSIZE], \
1664 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1665 ALsizei numchans) \
1667 ALsizei i, j; \
1669 ASSUME(numchans > 0); \
1670 ASSUME(SamplesToDo > 0); \
1672 for(j = 0;j < numchans;j++) \
1674 const ALfloat *RESTRICT in = InBuffer[j]; \
1675 T *RESTRICT out = (T*)OutBuffer + Offset*numchans + j; \
1677 for(i = 0;i < SamplesToDo;i++) \
1678 out[i*numchans] = Conv_##T(in[i]); \
1682 DECL_TEMPLATE(ALfloat, F32)
1683 DECL_TEMPLATE(ALuint, UI32)
1684 DECL_TEMPLATE(ALint, I32)
1685 DECL_TEMPLATE(ALushort, UI16)
1686 DECL_TEMPLATE(ALshort, I16)
1687 DECL_TEMPLATE(ALubyte, UI8)
1688 DECL_TEMPLATE(ALbyte, I8)
1690 #undef DECL_TEMPLATE
1693 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1695 ALsizei SamplesToDo;
1696 ALsizei SamplesDone;
1697 ALCcontext *ctx;
1698 ALsizei i, c;
1700 START_MIXER_MODE();
1701 for(SamplesDone = 0;SamplesDone < NumSamples;)
1703 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1704 for(c = 0;c < device->Dry.NumChannels;c++)
1705 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1706 if(device->Dry.Buffer != device->FOAOut.Buffer)
1707 for(c = 0;c < device->FOAOut.NumChannels;c++)
1708 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1709 if(device->Dry.Buffer != device->RealOut.Buffer)
1710 for(c = 0;c < device->RealOut.NumChannels;c++)
1711 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1713 IncrementRef(&device->MixCount);
1715 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1716 while(ctx)
1718 const struct ALeffectslotArray *auxslots;
1720 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1721 ProcessParamUpdates(ctx, auxslots);
1723 for(i = 0;i < auxslots->count;i++)
1725 ALeffectslot *slot = auxslots->slot[i];
1726 for(c = 0;c < slot->NumChannels;c++)
1727 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1730 /* source processing */
1731 for(i = 0;i < ctx->VoiceCount;i++)
1733 ALvoice *voice = ctx->Voices[i];
1734 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1735 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1736 voice->Step > 0)
1738 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1740 ATOMIC_STORE(&voice->Source, static_cast<ALsource*>(nullptr),
1741 almemory_order_relaxed);
1742 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1743 SendSourceStoppedEvent(ctx, source->id);
1748 /* effect slot processing */
1749 for(i = 0;i < auxslots->count;i++)
1751 const ALeffectslot *slot = auxslots->slot[i];
1752 EffectState *state = slot->Params.EffectState;
1753 state->process(SamplesToDo, slot->WetBuffer, state->mOutBuffer,
1754 state->mOutChannels);
1757 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
1760 /* Increment the clock time. Every second's worth of samples is
1761 * converted and added to clock base so that large sample counts don't
1762 * overflow during conversion. This also guarantees an exact, stable
1763 * conversion. */
1764 device->SamplesDone += SamplesToDo;
1765 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1766 device->SamplesDone %= device->Frequency;
1767 IncrementRef(&device->MixCount);
1769 /* Apply post-process for finalizing the Dry mix to the RealOut
1770 * (Ambisonic decode, UHJ encode, etc).
1772 if(LIKELY(device->PostProcess))
1773 device->PostProcess(device, SamplesToDo);
1775 if(device->Stablizer)
1777 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1778 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1779 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1780 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1782 ApplyStablizer(device->Stablizer, device->RealOut.Buffer, lidx, ridx, cidx,
1783 SamplesToDo, device->RealOut.NumChannels);
1786 ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
1787 SamplesToDo, device->RealOut.NumChannels);
1789 if(device->Limiter)
1790 ApplyCompression(device->Limiter, SamplesToDo, device->RealOut.Buffer);
1792 if(device->DitherDepth > 0.0f)
1793 ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
1794 SamplesToDo, device->RealOut.NumChannels);
1796 if(LIKELY(OutBuffer))
1798 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1799 ALsizei Channels = device->RealOut.NumChannels;
1801 switch(device->FmtType)
1803 #define HANDLE_WRITE(T, S) case T: \
1804 Write##S(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break;
1805 HANDLE_WRITE(DevFmtByte, I8)
1806 HANDLE_WRITE(DevFmtUByte, UI8)
1807 HANDLE_WRITE(DevFmtShort, I16)
1808 HANDLE_WRITE(DevFmtUShort, UI16)
1809 HANDLE_WRITE(DevFmtInt, I32)
1810 HANDLE_WRITE(DevFmtUInt, UI32)
1811 HANDLE_WRITE(DevFmtFloat, F32)
1812 #undef HANDLE_WRITE
1816 SamplesDone += SamplesToDo;
1818 END_MIXER_MODE();
1822 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1824 AsyncEvent evt = ASYNC_EVENT(EventType_Disconnected);
1825 ALCcontext *ctx;
1826 va_list args;
1827 int msglen;
1829 if(!device->Connected.exchange(AL_FALSE, std::memory_order_acq_rel))
1830 return;
1832 evt.u.user.type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1833 evt.u.user.id = 0;
1834 evt.u.user.param = 0;
1836 va_start(args, msg);
1837 msglen = vsnprintf(evt.u.user.msg, sizeof(evt.u.user.msg), msg, args);
1838 va_end(args);
1840 if(msglen < 0 || (size_t)msglen >= sizeof(evt.u.user.msg))
1841 evt.u.user.msg[sizeof(evt.u.user.msg)-1] = 0;
1843 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1844 while(ctx)
1846 ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire);
1847 ALsizei i;
1849 if((enabledevt&EventType_Disconnected) &&
1850 ll_ringbuffer_write(ctx->AsyncEvents, &evt, 1) == 1)
1851 alsem_post(&ctx->EventSem);
1853 for(i = 0;i < ctx->VoiceCount;i++)
1855 ALvoice *voice = ctx->Voices[i];
1856 ALsource *source = voice->Source.exchange(nullptr, std::memory_order_relaxed);
1857 if(source && voice->Playing.load(std::memory_order_relaxed))
1859 /* If the source's voice was playing, it's now effectively
1860 * stopped (the source state will be updated the next time it's
1861 * checked).
1863 SendSourceStoppedEvent(ctx, source->id);
1865 voice->Playing.store(false, std::memory_order_release);
1868 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);