Add a comment about waiting to kill the event thread
[openal-soft.git] / Alc / ALu.c
blob03abb116b407245f1bdc97a3c15e41cc11116677
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 "alSource.h"
31 #include "alBuffer.h"
32 #include "alListener.h"
33 #include "alAuxEffectSlot.h"
34 #include "alu.h"
35 #include "bs2b.h"
36 #include "hrtf.h"
37 #include "mastering.h"
38 #include "uhjfilter.h"
39 #include "bformatdec.h"
40 #include "static_assert.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"
49 #include "backends/base.h"
52 extern inline ALfloat minf(ALfloat a, ALfloat b);
53 extern inline ALfloat maxf(ALfloat a, ALfloat b);
54 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
56 extern inline ALdouble mind(ALdouble a, ALdouble b);
57 extern inline ALdouble maxd(ALdouble a, ALdouble b);
58 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
60 extern inline ALuint minu(ALuint a, ALuint b);
61 extern inline ALuint maxu(ALuint a, ALuint b);
62 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
64 extern inline ALint mini(ALint a, ALint b);
65 extern inline ALint maxi(ALint a, ALint b);
66 extern inline ALint clampi(ALint val, ALint min, ALint max);
68 extern inline ALint64 mini64(ALint64 a, ALint64 b);
69 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
70 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
72 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
73 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
74 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
76 extern inline size_t minz(size_t a, size_t b);
77 extern inline size_t maxz(size_t a, size_t b);
78 extern inline size_t clampz(size_t val, size_t min, size_t max);
80 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
81 extern inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu);
83 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
85 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
86 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
87 extern inline void aluMatrixfSet(aluMatrixf *matrix,
88 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
89 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
90 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
91 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
94 /* Cone scalar */
95 ALfloat ConeScale = 1.0f;
97 /* Localized Z scalar for mono sources */
98 ALfloat ZScale = 1.0f;
100 /* Force default speed of sound for distance-related reverb decay. */
101 ALboolean OverrideReverbSpeedOfSound = AL_FALSE;
103 const aluMatrixf IdentityMatrixf = {{
104 { 1.0f, 0.0f, 0.0f, 0.0f },
105 { 0.0f, 1.0f, 0.0f, 0.0f },
106 { 0.0f, 0.0f, 1.0f, 0.0f },
107 { 0.0f, 0.0f, 0.0f, 1.0f },
111 static void ClearArray(ALfloat f[MAX_OUTPUT_CHANNELS])
113 size_t i;
114 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
115 f[i] = 0.0f;
118 struct ChanMap {
119 enum Channel channel;
120 ALfloat angle;
121 ALfloat elevation;
124 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
127 void DeinitVoice(ALvoice *voice)
129 al_free(ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL));
133 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
135 #ifdef HAVE_NEON
136 if((CPUCapFlags&CPU_CAP_NEON))
137 return MixDirectHrtf_Neon;
138 #endif
139 #ifdef HAVE_SSE
140 if((CPUCapFlags&CPU_CAP_SSE))
141 return MixDirectHrtf_SSE;
142 #endif
144 return MixDirectHrtf_C;
148 /* This RNG method was created based on the math found in opusdec. It's quick,
149 * and starting with a seed value of 22222, is suitable for generating
150 * whitenoise.
152 static inline ALuint dither_rng(ALuint *seed)
154 *seed = (*seed * 96314165) + 907633515;
155 return *seed;
159 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
161 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
162 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
163 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
166 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
168 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
171 static ALfloat aluNormalize(ALfloat *vec)
173 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
174 if(length > FLT_EPSILON)
176 ALfloat inv_length = 1.0f/length;
177 vec[0] *= inv_length;
178 vec[1] *= inv_length;
179 vec[2] *= inv_length;
180 return length;
182 vec[0] = vec[1] = vec[2] = 0.0f;
183 return 0.0f;
186 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
188 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
190 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];
191 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];
192 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];
195 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
197 aluVector v;
198 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];
199 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];
200 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];
201 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];
202 return v;
206 void aluInit(void)
208 MixDirectHrtf = SelectHrtfMixer();
212 static void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
214 AsyncEvent evt = ASYNC_EVENT(EventType_SourceStateChange);
215 ALbitfieldSOFT enabledevt;
216 size_t strpos;
217 ALuint scale;
219 enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
220 if(!(enabledevt&EventType_SourceStateChange)) return;
222 evt.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
223 evt.u.user.id = id;
224 evt.u.user.param = AL_STOPPED;
226 /* Normally snprintf would be used, but this is called from the mixer and
227 * that function's not real-time safe, so we have to construct it manually.
229 strcpy(evt.u.user.msg, "Source ID "); strpos = 10;
230 scale = 1000000000;
231 while(scale > 0 && scale > id)
232 scale /= 10;
233 while(scale > 0)
235 evt.u.user.msg[strpos++] = '0' + ((id/scale)%10);
236 scale /= 10;
238 strcpy(evt.u.user.msg+strpos, " state changed to AL_STOPPED");
240 if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
241 alsem_post(&context->EventSem);
245 static void ProcessHrtf(ALCdevice *device, ALsizei SamplesToDo)
247 DirectHrtfState *state;
248 int lidx, ridx;
249 ALsizei c;
251 if(device->AmbiUp)
252 ambiup_process(device->AmbiUp,
253 device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer,
254 SamplesToDo
257 lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
258 ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
259 assert(lidx != -1 && ridx != -1);
261 state = device->Hrtf;
262 for(c = 0;c < device->Dry.NumChannels;c++)
264 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
265 device->Dry.Buffer[c], state->Offset, state->IrSize,
266 state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo
269 state->Offset += SamplesToDo;
272 static void ProcessAmbiDec(ALCdevice *device, ALsizei SamplesToDo)
274 if(device->Dry.Buffer != device->FOAOut.Buffer)
275 bformatdec_upSample(device->AmbiDecoder,
276 device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
277 SamplesToDo
279 bformatdec_process(device->AmbiDecoder,
280 device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
281 SamplesToDo
285 static void ProcessAmbiUp(ALCdevice *device, ALsizei SamplesToDo)
287 ambiup_process(device->AmbiUp,
288 device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer,
289 SamplesToDo
293 static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
295 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
296 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
297 assert(lidx != -1 && ridx != -1);
299 /* Encode to stereo-compatible 2-channel UHJ output. */
300 EncodeUhj2(device->Uhj_Encoder,
301 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
302 device->Dry.Buffer, SamplesToDo
306 static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
308 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
309 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
310 assert(lidx != -1 && ridx != -1);
312 /* Apply binaural/crossfeed filter */
313 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
314 device->RealOut.Buffer[ridx], SamplesToDo);
317 void aluSelectPostProcess(ALCdevice *device)
319 if(device->HrtfHandle)
320 device->PostProcess = ProcessHrtf;
321 else if(device->AmbiDecoder)
322 device->PostProcess = ProcessAmbiDec;
323 else if(device->AmbiUp)
324 device->PostProcess = ProcessAmbiUp;
325 else if(device->Uhj_Encoder)
326 device->PostProcess = ProcessUhj;
327 else if(device->Bs2b)
328 device->PostProcess = ProcessBs2b;
329 else
330 device->PostProcess = NULL;
334 /* Prepares the interpolator for a given rate (determined by increment).
336 * With a bit of work, and a trade of memory for CPU cost, this could be
337 * modified for use with an interpolated increment for buttery-smooth pitch
338 * changes.
340 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
342 ALfloat sf = 0.0f;
343 ALsizei si = BSINC_SCALE_COUNT-1;
345 if(increment > FRACTIONONE)
347 sf = (ALfloat)FRACTIONONE / increment;
348 sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
349 si = float2int(sf);
350 /* The interpolation factor is fit to this diagonally-symmetric curve
351 * to reduce the transition ripple caused by interpolating different
352 * scales of the sinc function.
354 sf = 1.0f - cosf(asinf(sf - si));
357 state->sf = sf;
358 state->m = table->m[si];
359 state->l = (state->m/2) - 1;
360 state->filter = table->Tab + table->filterOffset[si];
364 static bool CalcContextParams(ALCcontext *Context)
366 ALlistener *Listener = Context->Listener;
367 struct ALcontextProps *props;
369 props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel);
370 if(!props) return false;
372 Listener->Params.MetersPerUnit = props->MetersPerUnit;
374 Listener->Params.DopplerFactor = props->DopplerFactor;
375 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
376 if(!OverrideReverbSpeedOfSound)
377 Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
378 Listener->Params.MetersPerUnit;
380 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
381 Listener->Params.DistanceModel = props->DistanceModel;
383 ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props);
384 return true;
387 static bool CalcListenerParams(ALCcontext *Context)
389 ALlistener *Listener = Context->Listener;
390 ALfloat N[3], V[3], U[3], P[3];
391 struct ALlistenerProps *props;
392 aluVector vel;
394 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
395 if(!props) return false;
397 /* AT then UP */
398 N[0] = props->Forward[0];
399 N[1] = props->Forward[1];
400 N[2] = props->Forward[2];
401 aluNormalize(N);
402 V[0] = props->Up[0];
403 V[1] = props->Up[1];
404 V[2] = props->Up[2];
405 aluNormalize(V);
406 /* Build and normalize right-vector */
407 aluCrossproduct(N, V, U);
408 aluNormalize(U);
410 aluMatrixfSet(&Listener->Params.Matrix,
411 U[0], V[0], -N[0], 0.0,
412 U[1], V[1], -N[1], 0.0,
413 U[2], V[2], -N[2], 0.0,
414 0.0, 0.0, 0.0, 1.0
417 P[0] = props->Position[0];
418 P[1] = props->Position[1];
419 P[2] = props->Position[2];
420 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
421 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
423 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
424 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
426 Listener->Params.Gain = props->Gain * Context->GainBoost;
428 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props);
429 return true;
432 static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
434 struct ALeffectslotProps *props;
435 ALeffectState *state;
437 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
438 if(!props && !force) return false;
440 if(props)
442 slot->Params.Gain = props->Gain;
443 slot->Params.AuxSendAuto = props->AuxSendAuto;
444 slot->Params.EffectType = props->Type;
445 slot->Params.EffectProps = props->Props;
446 if(IsReverbEffect(props->Type))
448 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
449 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
450 slot->Params.DecayLFRatio = props->Props.Reverb.DecayLFRatio;
451 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
452 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
453 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
455 else
457 slot->Params.RoomRolloff = 0.0f;
458 slot->Params.DecayTime = 0.0f;
459 slot->Params.DecayLFRatio = 0.0f;
460 slot->Params.DecayHFRatio = 0.0f;
461 slot->Params.DecayHFLimit = AL_FALSE;
462 slot->Params.AirAbsorptionGainHF = 1.0f;
465 state = props->State;
467 if(state == slot->Params.EffectState)
469 /* If the effect state is the same as current, we can decrement its
470 * count safely to remove it from the update object (it can't reach
471 * 0 refs since the current params also hold a reference).
473 DecrementRef(&state->Ref);
474 props->State = NULL;
476 else
478 /* Otherwise, replace it and send off the old one with a release
479 * event.
481 AsyncEvent evt = ASYNC_EVENT(EventType_ReleaseEffectState);
482 evt.u.EffectState = slot->Params.EffectState;
484 slot->Params.EffectState = state;
485 props->State = NULL;
487 if(LIKELY(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) != 0))
488 alsem_post(&context->EventSem);
489 else
491 /* If writing the event failed, the queue was probably full.
492 * Store the old state in the property object where it can
493 * eventually be cleaned up sometime later (not ideal, but
494 * better than blocking or leaking).
496 props->State = evt.u.EffectState;
500 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
502 else
503 state = slot->Params.EffectState;
505 V(state,update)(context, slot, &slot->Params.EffectProps);
506 return true;
510 static const struct ChanMap MonoMap[1] = {
511 { FrontCenter, 0.0f, 0.0f }
512 }, RearMap[2] = {
513 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
514 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
515 }, QuadMap[4] = {
516 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
517 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
518 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
519 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
520 }, X51Map[6] = {
521 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
522 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
523 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
524 { LFE, 0.0f, 0.0f },
525 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
526 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
527 }, X61Map[7] = {
528 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
529 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
530 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
531 { LFE, 0.0f, 0.0f },
532 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
533 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
534 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
535 }, X71Map[8] = {
536 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
537 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
538 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
539 { LFE, 0.0f, 0.0f },
540 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
541 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
542 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
543 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
546 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALfloat Elev,
547 const ALfloat Distance, const ALfloat Spread,
548 const ALfloat DryGain, const ALfloat DryGainHF,
549 const ALfloat DryGainLF, const ALfloat *WetGain,
550 const ALfloat *WetGainLF, const ALfloat *WetGainHF,
551 ALeffectslot **SendSlots, const ALbuffer *Buffer,
552 const struct ALvoiceProps *props, const ALlistener *Listener,
553 const ALCdevice *Device)
555 struct ChanMap StereoMap[2] = {
556 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
557 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
559 bool DirectChannels = props->DirectChannels;
560 const ALsizei NumSends = Device->NumAuxSends;
561 const ALuint Frequency = Device->Frequency;
562 const struct ChanMap *chans = NULL;
563 ALsizei num_channels = 0;
564 bool isbformat = false;
565 ALfloat downmix_gain = 1.0f;
566 ALsizei c, i;
568 switch(Buffer->FmtChannels)
570 case FmtMono:
571 chans = MonoMap;
572 num_channels = 1;
573 /* Mono buffers are never played direct. */
574 DirectChannels = false;
575 break;
577 case FmtStereo:
578 /* Convert counter-clockwise to clockwise. */
579 StereoMap[0].angle = -props->StereoPan[0];
580 StereoMap[1].angle = -props->StereoPan[1];
582 chans = StereoMap;
583 num_channels = 2;
584 downmix_gain = 1.0f / 2.0f;
585 break;
587 case FmtRear:
588 chans = RearMap;
589 num_channels = 2;
590 downmix_gain = 1.0f / 2.0f;
591 break;
593 case FmtQuad:
594 chans = QuadMap;
595 num_channels = 4;
596 downmix_gain = 1.0f / 4.0f;
597 break;
599 case FmtX51:
600 chans = X51Map;
601 num_channels = 6;
602 /* NOTE: Excludes LFE. */
603 downmix_gain = 1.0f / 5.0f;
604 break;
606 case FmtX61:
607 chans = X61Map;
608 num_channels = 7;
609 /* NOTE: Excludes LFE. */
610 downmix_gain = 1.0f / 6.0f;
611 break;
613 case FmtX71:
614 chans = X71Map;
615 num_channels = 8;
616 /* NOTE: Excludes LFE. */
617 downmix_gain = 1.0f / 7.0f;
618 break;
620 case FmtBFormat2D:
621 num_channels = 3;
622 isbformat = true;
623 DirectChannels = false;
624 break;
626 case FmtBFormat3D:
627 num_channels = 4;
628 isbformat = true;
629 DirectChannels = false;
630 break;
633 for(c = 0;c < num_channels;c++)
635 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
636 sizeof(voice->Direct.Params[c].Hrtf.Target));
637 ClearArray(voice->Direct.Params[c].Gains.Target);
639 for(i = 0;i < NumSends;i++)
641 for(c = 0;c < num_channels;c++)
642 ClearArray(voice->Send[i].Params[c].Gains.Target);
645 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
646 if(isbformat)
648 /* Special handling for B-Format sources. */
650 if(Distance > FLT_EPSILON)
652 /* Panning a B-Format sound toward some direction is easy. Just pan
653 * the first (W) channel as a normal mono sound and silence the
654 * others.
656 ALfloat coeffs[MAX_AMBI_COEFFS];
658 if(Device->AvgSpeakerDist > 0.0f)
660 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
661 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
662 (mdist * (ALfloat)Device->Frequency);
663 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
664 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
665 /* Clamp w0 for really close distances, to prevent excessive
666 * bass.
668 w0 = minf(w0, w1*4.0f);
670 /* Only need to adjust the first channel of a B-Format source. */
671 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
673 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
674 voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
675 voice->Flags |= VOICE_HAS_NFC;
678 /* A scalar of 1.5 for plain stereo results in +/-60 degrees being
679 * moved to +/-90 degrees for direct right and left speaker
680 * responses.
682 CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
683 Elev, Spread, coeffs);
685 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
686 ComputePanGains(&Device->Dry, coeffs, DryGain*SQRTF_2,
687 voice->Direct.Params[0].Gains.Target);
688 for(i = 0;i < NumSends;i++)
690 const ALeffectslot *Slot = SendSlots[i];
691 if(Slot)
692 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
693 WetGain[i]*SQRTF_2, voice->Send[i].Params[0].Gains.Target
697 else
699 /* Local B-Format sources have their XYZ channels rotated according
700 * to the orientation.
702 ALfloat N[3], V[3], U[3];
703 aluMatrixf matrix;
705 if(Device->AvgSpeakerDist > 0.0f)
707 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
708 * is what we want for FOA input. The first channel may have
709 * been previously re-adjusted if panned, so reset it.
711 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, 0.0f);
713 voice->Direct.ChannelsPerOrder[0] = 1;
714 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
715 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
716 voice->Direct.ChannelsPerOrder[i] = 0;
717 voice->Flags |= VOICE_HAS_NFC;
720 /* AT then UP */
721 N[0] = props->Orientation[0][0];
722 N[1] = props->Orientation[0][1];
723 N[2] = props->Orientation[0][2];
724 aluNormalize(N);
725 V[0] = props->Orientation[1][0];
726 V[1] = props->Orientation[1][1];
727 V[2] = props->Orientation[1][2];
728 aluNormalize(V);
729 if(!props->HeadRelative)
731 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
732 aluMatrixfFloat3(N, 0.0f, lmatrix);
733 aluMatrixfFloat3(V, 0.0f, lmatrix);
735 /* Build and normalize right-vector */
736 aluCrossproduct(N, V, U);
737 aluNormalize(U);
739 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). NOTE: This
740 * matrix is transposed, for the inputs to align on the rows and
741 * outputs on the columns.
743 aluMatrixfSet(&matrix,
744 // ACN0 ACN1 ACN2 ACN3
745 SQRTF_2, 0.0f, 0.0f, 0.0f, // Ambi W
746 0.0f, -N[0]*SQRTF_3, N[1]*SQRTF_3, -N[2]*SQRTF_3, // Ambi X
747 0.0f, U[0]*SQRTF_3, -U[1]*SQRTF_3, U[2]*SQRTF_3, // Ambi Y
748 0.0f, -V[0]*SQRTF_3, V[1]*SQRTF_3, -V[2]*SQRTF_3 // Ambi Z
751 voice->Direct.Buffer = Device->FOAOut.Buffer;
752 voice->Direct.Channels = Device->FOAOut.NumChannels;
753 for(c = 0;c < num_channels;c++)
754 ComputePanGains(&Device->FOAOut, matrix.m[c], DryGain,
755 voice->Direct.Params[c].Gains.Target);
756 for(i = 0;i < NumSends;i++)
758 const ALeffectslot *Slot = SendSlots[i];
759 if(Slot)
761 for(c = 0;c < num_channels;c++)
762 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
763 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
769 else if(DirectChannels)
771 /* Direct source channels always play local. Skip the virtual channels
772 * and write inputs to the matching real outputs.
774 voice->Direct.Buffer = Device->RealOut.Buffer;
775 voice->Direct.Channels = Device->RealOut.NumChannels;
777 for(c = 0;c < num_channels;c++)
779 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
780 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
783 /* Auxiliary sends still use normal channel panning since they mix to
784 * B-Format, which can't channel-match.
786 for(c = 0;c < num_channels;c++)
788 ALfloat coeffs[MAX_AMBI_COEFFS];
789 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
791 for(i = 0;i < NumSends;i++)
793 const ALeffectslot *Slot = SendSlots[i];
794 if(Slot)
795 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
796 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
801 else if(Device->Render_Mode == HrtfRender)
803 /* Full HRTF rendering. Skip the virtual channels and render to the
804 * real outputs.
806 voice->Direct.Buffer = Device->RealOut.Buffer;
807 voice->Direct.Channels = Device->RealOut.NumChannels;
809 if(Distance > FLT_EPSILON)
811 ALfloat coeffs[MAX_AMBI_COEFFS];
813 /* Get the HRIR coefficients and delays just once, for the given
814 * source direction.
816 GetHrtfCoeffs(Device->HrtfHandle, Elev, Azi, Spread,
817 voice->Direct.Params[0].Hrtf.Target.Coeffs,
818 voice->Direct.Params[0].Hrtf.Target.Delay);
819 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
821 /* Remaining channels use the same results as the first. */
822 for(c = 1;c < num_channels;c++)
824 /* Skip LFE */
825 if(chans[c].channel != LFE)
826 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
829 /* Calculate the directional coefficients once, which apply to all
830 * input channels of the source sends.
832 CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
834 for(i = 0;i < NumSends;i++)
836 const ALeffectslot *Slot = SendSlots[i];
837 if(Slot)
838 for(c = 0;c < num_channels;c++)
840 /* Skip LFE */
841 if(chans[c].channel != LFE)
842 ComputePanningGainsBF(Slot->ChanMap,
843 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
844 voice->Send[i].Params[c].Gains.Target
849 else
851 /* Local sources on HRTF play with each channel panned to its
852 * relative location around the listener, providing "virtual
853 * speaker" responses.
855 for(c = 0;c < num_channels;c++)
857 ALfloat coeffs[MAX_AMBI_COEFFS];
859 if(chans[c].channel == LFE)
861 /* Skip LFE */
862 continue;
865 /* Get the HRIR coefficients and delays for this channel
866 * position.
868 GetHrtfCoeffs(Device->HrtfHandle,
869 chans[c].elevation, chans[c].angle, Spread,
870 voice->Direct.Params[c].Hrtf.Target.Coeffs,
871 voice->Direct.Params[c].Hrtf.Target.Delay
873 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
875 /* Normal panning for auxiliary sends. */
876 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
878 for(i = 0;i < NumSends;i++)
880 const ALeffectslot *Slot = SendSlots[i];
881 if(Slot)
882 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
883 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
889 voice->Flags |= VOICE_HAS_HRTF;
891 else
893 /* Non-HRTF rendering. Use normal panning to the output. */
895 if(Distance > FLT_EPSILON)
897 ALfloat coeffs[MAX_AMBI_COEFFS];
898 ALfloat w0 = 0.0f;
900 /* Calculate NFC filter coefficient if needed. */
901 if(Device->AvgSpeakerDist > 0.0f)
903 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
904 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
905 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
906 w0 = SPEEDOFSOUNDMETRESPERSEC /
907 (mdist * (ALfloat)Device->Frequency);
908 /* Clamp w0 for really close distances, to prevent excessive
909 * bass.
911 w0 = minf(w0, w1*4.0f);
913 /* Adjust NFC filters. */
914 for(c = 0;c < num_channels;c++)
915 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
917 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
918 voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
919 voice->Flags |= VOICE_HAS_NFC;
922 /* Calculate the directional coefficients once, which apply to all
923 * input channels.
925 CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
926 Elev, Spread, coeffs);
928 for(c = 0;c < num_channels;c++)
930 /* Special-case LFE */
931 if(chans[c].channel == LFE)
933 if(Device->Dry.Buffer == Device->RealOut.Buffer)
935 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
936 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
938 continue;
941 ComputePanGains(&Device->Dry, coeffs, DryGain * downmix_gain,
942 voice->Direct.Params[c].Gains.Target);
945 for(i = 0;i < NumSends;i++)
947 const ALeffectslot *Slot = SendSlots[i];
948 if(Slot)
949 for(c = 0;c < num_channels;c++)
951 /* Skip LFE */
952 if(chans[c].channel != LFE)
953 ComputePanningGainsBF(Slot->ChanMap,
954 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
955 voice->Send[i].Params[c].Gains.Target
960 else
962 ALfloat w0 = 0.0f;
964 if(Device->AvgSpeakerDist > 0.0f)
966 /* If the source distance is 0, set w0 to w1 to act as a pass-
967 * through. We still want to pass the signal through the
968 * filters so they keep an appropriate history, in case the
969 * source moves away from the listener.
971 w0 = SPEEDOFSOUNDMETRESPERSEC /
972 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
974 for(c = 0;c < num_channels;c++)
975 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
977 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
978 voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
979 voice->Flags |= VOICE_HAS_NFC;
982 for(c = 0;c < num_channels;c++)
984 ALfloat coeffs[MAX_AMBI_COEFFS];
986 /* Special-case LFE */
987 if(chans[c].channel == LFE)
989 if(Device->Dry.Buffer == Device->RealOut.Buffer)
991 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
992 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
994 continue;
997 CalcAngleCoeffs(
998 (Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(chans[c].angle, 3.0f)
999 : chans[c].angle,
1000 chans[c].elevation, Spread, coeffs
1003 ComputePanGains(&Device->Dry, coeffs, DryGain,
1004 voice->Direct.Params[c].Gains.Target);
1005 for(i = 0;i < NumSends;i++)
1007 const ALeffectslot *Slot = SendSlots[i];
1008 if(Slot)
1009 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1010 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
1018 ALfloat hfScale = props->Direct.HFReference / Frequency;
1019 ALfloat lfScale = props->Direct.LFReference / Frequency;
1020 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
1021 ALfloat gainLF = maxf(DryGainLF, 0.001f);
1023 voice->Direct.FilterType = AF_None;
1024 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
1025 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
1026 BiquadFilter_setParams(
1027 &voice->Direct.Params[0].LowPass, BiquadType_HighShelf,
1028 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1030 BiquadFilter_setParams(
1031 &voice->Direct.Params[0].HighPass, BiquadType_LowShelf,
1032 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1034 for(c = 1;c < num_channels;c++)
1036 BiquadFilter_copyParams(&voice->Direct.Params[c].LowPass,
1037 &voice->Direct.Params[0].LowPass);
1038 BiquadFilter_copyParams(&voice->Direct.Params[c].HighPass,
1039 &voice->Direct.Params[0].HighPass);
1042 for(i = 0;i < NumSends;i++)
1044 ALfloat hfScale = props->Send[i].HFReference / Frequency;
1045 ALfloat lfScale = props->Send[i].LFReference / Frequency;
1046 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
1047 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
1049 voice->Send[i].FilterType = AF_None;
1050 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1051 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1052 BiquadFilter_setParams(
1053 &voice->Send[i].Params[0].LowPass, BiquadType_HighShelf,
1054 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1056 BiquadFilter_setParams(
1057 &voice->Send[i].Params[0].HighPass, BiquadType_LowShelf,
1058 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1060 for(c = 1;c < num_channels;c++)
1062 BiquadFilter_copyParams(&voice->Send[i].Params[c].LowPass,
1063 &voice->Send[i].Params[0].LowPass);
1064 BiquadFilter_copyParams(&voice->Send[i].Params[c].HighPass,
1065 &voice->Send[i].Params[0].HighPass);
1070 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1072 const ALCdevice *Device = ALContext->Device;
1073 const ALlistener *Listener = ALContext->Listener;
1074 ALfloat DryGain, DryGainHF, DryGainLF;
1075 ALfloat WetGain[MAX_SENDS];
1076 ALfloat WetGainHF[MAX_SENDS];
1077 ALfloat WetGainLF[MAX_SENDS];
1078 ALeffectslot *SendSlots[MAX_SENDS];
1079 ALfloat Pitch;
1080 ALsizei i;
1082 voice->Direct.Buffer = Device->Dry.Buffer;
1083 voice->Direct.Channels = Device->Dry.NumChannels;
1084 for(i = 0;i < Device->NumAuxSends;i++)
1086 SendSlots[i] = props->Send[i].Slot;
1087 if(!SendSlots[i] && i == 0)
1088 SendSlots[i] = ALContext->DefaultSlot;
1089 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1091 SendSlots[i] = NULL;
1092 voice->Send[i].Buffer = NULL;
1093 voice->Send[i].Channels = 0;
1095 else
1097 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1098 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1102 /* Calculate the stepping value */
1103 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1104 if(Pitch > (ALfloat)MAX_PITCH)
1105 voice->Step = MAX_PITCH<<FRACTIONBITS;
1106 else
1107 voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
1108 if(props->Resampler == BSinc24Resampler)
1109 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1110 else if(props->Resampler == BSinc12Resampler)
1111 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1112 voice->Resampler = SelectResampler(props->Resampler);
1114 /* Calculate gains */
1115 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1116 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1117 DryGain = minf(DryGain, GAIN_MIX_MAX);
1118 DryGainHF = props->Direct.GainHF;
1119 DryGainLF = props->Direct.GainLF;
1120 for(i = 0;i < Device->NumAuxSends;i++)
1122 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1123 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1124 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1125 WetGainHF[i] = props->Send[i].GainHF;
1126 WetGainLF[i] = props->Send[i].GainLF;
1129 CalcPanningAndFilters(voice, 0.0f, 0.0f, 0.0f, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1130 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1133 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1135 const ALCdevice *Device = ALContext->Device;
1136 const ALlistener *Listener = ALContext->Listener;
1137 const ALsizei NumSends = Device->NumAuxSends;
1138 aluVector Position, Velocity, Direction, SourceToListener;
1139 ALfloat Distance, ClampedDist, DopplerFactor;
1140 ALeffectslot *SendSlots[MAX_SENDS];
1141 ALfloat RoomRolloff[MAX_SENDS];
1142 ALfloat DecayDistance[MAX_SENDS];
1143 ALfloat DecayLFDistance[MAX_SENDS];
1144 ALfloat DecayHFDistance[MAX_SENDS];
1145 ALfloat DryGain, DryGainHF, DryGainLF;
1146 ALfloat WetGain[MAX_SENDS];
1147 ALfloat WetGainHF[MAX_SENDS];
1148 ALfloat WetGainLF[MAX_SENDS];
1149 bool directional;
1150 ALfloat ev, az;
1151 ALfloat spread;
1152 ALfloat Pitch;
1153 ALint i;
1155 /* Set mixing buffers and get send parameters. */
1156 voice->Direct.Buffer = Device->Dry.Buffer;
1157 voice->Direct.Channels = Device->Dry.NumChannels;
1158 for(i = 0;i < NumSends;i++)
1160 SendSlots[i] = props->Send[i].Slot;
1161 if(!SendSlots[i] && i == 0)
1162 SendSlots[i] = ALContext->DefaultSlot;
1163 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1165 SendSlots[i] = NULL;
1166 RoomRolloff[i] = 0.0f;
1167 DecayDistance[i] = 0.0f;
1168 DecayLFDistance[i] = 0.0f;
1169 DecayHFDistance[i] = 0.0f;
1171 else if(SendSlots[i]->Params.AuxSendAuto)
1173 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1174 /* Calculate the distances to where this effect's decay reaches
1175 * -60dB.
1177 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1178 Listener->Params.ReverbSpeedOfSound;
1179 DecayLFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayLFRatio;
1180 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1181 if(SendSlots[i]->Params.DecayHFLimit)
1183 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1184 if(airAbsorption < 1.0f)
1186 /* Calculate the distance to where this effect's air
1187 * absorption reaches -60dB, and limit the effect's HF
1188 * decay distance (so it doesn't take any longer to decay
1189 * than the air would allow).
1191 ALfloat absorb_dist = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1192 DecayHFDistance[i] = minf(absorb_dist, DecayHFDistance[i]);
1196 else
1198 /* If the slot's auxiliary send auto is off, the data sent to the
1199 * effect slot is the same as the dry path, sans filter effects */
1200 RoomRolloff[i] = props->RolloffFactor;
1201 DecayDistance[i] = 0.0f;
1202 DecayLFDistance[i] = 0.0f;
1203 DecayHFDistance[i] = 0.0f;
1206 if(!SendSlots[i])
1208 voice->Send[i].Buffer = NULL;
1209 voice->Send[i].Channels = 0;
1211 else
1213 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1214 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1218 /* Transform source to listener space (convert to head relative) */
1219 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1220 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1221 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1222 if(props->HeadRelative == AL_FALSE)
1224 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1225 /* Transform source vectors */
1226 Position = aluMatrixfVector(Matrix, &Position);
1227 Velocity = aluMatrixfVector(Matrix, &Velocity);
1228 Direction = aluMatrixfVector(Matrix, &Direction);
1230 else
1232 const aluVector *lvelocity = &Listener->Params.Velocity;
1233 /* Offset the source velocity to be relative of the listener velocity */
1234 Velocity.v[0] += lvelocity->v[0];
1235 Velocity.v[1] += lvelocity->v[1];
1236 Velocity.v[2] += lvelocity->v[2];
1239 directional = aluNormalize(Direction.v) > 0.0f;
1240 SourceToListener.v[0] = -Position.v[0];
1241 SourceToListener.v[1] = -Position.v[1];
1242 SourceToListener.v[2] = -Position.v[2];
1243 SourceToListener.v[3] = 0.0f;
1244 Distance = aluNormalize(SourceToListener.v);
1246 /* Initial source gain */
1247 DryGain = props->Gain;
1248 DryGainHF = 1.0f;
1249 DryGainLF = 1.0f;
1250 for(i = 0;i < NumSends;i++)
1252 WetGain[i] = props->Gain;
1253 WetGainHF[i] = 1.0f;
1254 WetGainLF[i] = 1.0f;
1257 /* Calculate distance attenuation */
1258 ClampedDist = Distance;
1260 switch(Listener->Params.SourceDistanceModel ?
1261 props->DistanceModel : Listener->Params.DistanceModel)
1263 case InverseDistanceClamped:
1264 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1265 if(props->MaxDistance < props->RefDistance)
1266 break;
1267 /*fall-through*/
1268 case InverseDistance:
1269 if(!(props->RefDistance > 0.0f))
1270 ClampedDist = props->RefDistance;
1271 else
1273 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1274 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1275 for(i = 0;i < NumSends;i++)
1277 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1278 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1281 break;
1283 case LinearDistanceClamped:
1284 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1285 if(props->MaxDistance < props->RefDistance)
1286 break;
1287 /*fall-through*/
1288 case LinearDistance:
1289 if(!(props->MaxDistance != props->RefDistance))
1290 ClampedDist = props->RefDistance;
1291 else
1293 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1294 (props->MaxDistance-props->RefDistance);
1295 DryGain *= maxf(1.0f - attn, 0.0f);
1296 for(i = 0;i < NumSends;i++)
1298 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1299 (props->MaxDistance-props->RefDistance);
1300 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1303 break;
1305 case ExponentDistanceClamped:
1306 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1307 if(props->MaxDistance < props->RefDistance)
1308 break;
1309 /*fall-through*/
1310 case ExponentDistance:
1311 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1312 ClampedDist = props->RefDistance;
1313 else
1315 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1316 for(i = 0;i < NumSends;i++)
1317 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1319 break;
1321 case DisableDistance:
1322 ClampedDist = props->RefDistance;
1323 break;
1326 /* Calculate directional soundcones */
1327 if(directional && props->InnerAngle < 360.0f)
1329 ALfloat ConeVolume;
1330 ALfloat ConeHF;
1331 ALfloat Angle;
1333 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1334 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1335 if(!(Angle > props->InnerAngle))
1337 ConeVolume = 1.0f;
1338 ConeHF = 1.0f;
1340 else if(Angle < props->OuterAngle)
1342 ALfloat scale = ( Angle-props->InnerAngle) /
1343 (props->OuterAngle-props->InnerAngle);
1344 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1345 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1347 else
1349 ConeVolume = props->OuterGain;
1350 ConeHF = props->OuterGainHF;
1353 DryGain *= ConeVolume;
1354 if(props->DryGainHFAuto)
1355 DryGainHF *= ConeHF;
1356 if(props->WetGainAuto)
1358 for(i = 0;i < NumSends;i++)
1359 WetGain[i] *= ConeVolume;
1361 if(props->WetGainHFAuto)
1363 for(i = 0;i < NumSends;i++)
1364 WetGainHF[i] *= ConeHF;
1368 /* Apply gain and frequency filters */
1369 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1370 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1371 DryGainHF *= props->Direct.GainHF;
1372 DryGainLF *= props->Direct.GainLF;
1373 for(i = 0;i < NumSends;i++)
1375 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1376 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1377 WetGainHF[i] *= props->Send[i].GainHF;
1378 WetGainLF[i] *= props->Send[i].GainLF;
1381 /* Distance-based air absorption and initial send decay. */
1382 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1384 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1385 Listener->Params.MetersPerUnit;
1386 if(props->AirAbsorptionFactor > 0.0f)
1388 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1389 DryGainHF *= hfattn;
1390 for(i = 0;i < NumSends;i++)
1391 WetGainHF[i] *= hfattn;
1394 if(props->WetGainAuto)
1396 /* Apply a decay-time transformation to the wet path, based on the
1397 * source distance in meters. The initial decay of the reverb
1398 * effect is calculated and applied to the wet path.
1400 for(i = 0;i < NumSends;i++)
1402 ALfloat gain, gainhf, gainlf;
1404 if(!(DecayDistance[i] > 0.0f))
1405 continue;
1407 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1408 WetGain[i] *= gain;
1409 /* Yes, the wet path's air absorption is applied with
1410 * WetGainAuto on, rather than WetGainHFAuto.
1412 if(gain > 0.0f)
1414 gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1415 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1416 gainlf = powf(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i]);
1417 WetGainLF[i] *= minf(gainlf / gain, 1.0f);
1424 /* Initial source pitch */
1425 Pitch = props->Pitch;
1427 /* Calculate velocity-based doppler effect */
1428 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1429 if(DopplerFactor > 0.0f)
1431 const aluVector *lvelocity = &Listener->Params.Velocity;
1432 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1433 ALfloat vss, vls;
1435 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1436 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1438 if(!(vls < SpeedOfSound))
1440 /* Listener moving away from the source at the speed of sound.
1441 * Sound waves can't catch it.
1443 Pitch = 0.0f;
1445 else if(!(vss < SpeedOfSound))
1447 /* Source moving toward the listener at the speed of sound. Sound
1448 * waves bunch up to extreme frequencies.
1450 Pitch = HUGE_VALF;
1452 else
1454 /* Source and listener movement is nominal. Calculate the proper
1455 * doppler shift.
1457 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1461 /* Adjust pitch based on the buffer and output frequencies, and calculate
1462 * fixed-point stepping value.
1464 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1465 if(Pitch > (ALfloat)MAX_PITCH)
1466 voice->Step = MAX_PITCH<<FRACTIONBITS;
1467 else
1468 voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
1469 if(props->Resampler == BSinc24Resampler)
1470 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1471 else if(props->Resampler == BSinc12Resampler)
1472 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1473 voice->Resampler = SelectResampler(props->Resampler);
1475 if(Distance > 0.0f)
1477 /* Clamp Y, in case rounding errors caused it to end up outside of
1478 * -1...+1.
1480 ev = asinf(clampf(-SourceToListener.v[1], -1.0f, 1.0f));
1481 /* Double negation on Z cancels out; negate once for changing source-
1482 * to-listener to listener-to-source, and again for right-handed coords
1483 * with -Z in front.
1485 az = atan2f(-SourceToListener.v[0], SourceToListener.v[2]*ZScale);
1487 else
1488 ev = az = 0.0f;
1490 if(props->Radius > Distance)
1491 spread = F_TAU - Distance/props->Radius*F_PI;
1492 else if(Distance > 0.0f)
1493 spread = asinf(props->Radius / Distance) * 2.0f;
1494 else
1495 spread = 0.0f;
1497 CalcPanningAndFilters(voice, az, ev, Distance, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1498 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1501 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1503 ALbufferlistitem *BufferListItem;
1504 struct ALvoiceProps *props;
1506 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1507 if(!props && !force) return;
1509 if(props)
1511 memcpy(voice->Props, props,
1512 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1515 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1517 props = voice->Props;
1519 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1520 while(BufferListItem != NULL)
1522 const ALbuffer *buffer = NULL;
1523 ALsizei i = 0;
1524 while(!buffer && i < BufferListItem->num_buffers)
1525 buffer = BufferListItem->buffers[i];
1526 if(LIKELY(buffer))
1528 if(props->SpatializeMode == SpatializeOn ||
1529 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1530 CalcAttnSourceParams(voice, props, buffer, context);
1531 else
1532 CalcNonAttnSourceParams(voice, props, buffer, context);
1533 break;
1535 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1540 static void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1542 ALvoice **voice, **voice_end;
1543 ALsource *source;
1544 ALsizei i;
1546 IncrementRef(&ctx->UpdateCount);
1547 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1549 bool cforce = CalcContextParams(ctx);
1550 bool force = CalcListenerParams(ctx) | cforce;
1551 for(i = 0;i < slots->count;i++)
1552 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1554 voice = ctx->Voices;
1555 voice_end = voice + ctx->VoiceCount;
1556 for(;voice != voice_end;++voice)
1558 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1559 if(source) CalcSourceParams(*voice, ctx, force);
1562 IncrementRef(&ctx->UpdateCount);
1566 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1567 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1568 ALsizei NumChannels)
1570 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1571 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1572 ALsizei i;
1574 /* Apply an all-pass to all channels, except the front-left and front-
1575 * right, so they maintain the same relative phase.
1577 for(i = 0;i < NumChannels;i++)
1579 if(i == lidx || i == ridx)
1580 continue;
1581 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1584 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1585 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1587 for(i = 0;i < SamplesToDo;i++)
1589 ALfloat lfsum, hfsum;
1590 ALfloat m, s, c;
1592 lfsum = lsplit[0][i] + rsplit[0][i];
1593 hfsum = lsplit[1][i] + rsplit[1][i];
1594 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1596 /* This pans the separate low- and high-frequency sums between being on
1597 * the center channel and the left/right channels. The low-frequency
1598 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1599 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1600 * values can be tweaked.
1602 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1603 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1605 /* The generated center channel signal adds to the existing signal,
1606 * while the modified left and right channels replace.
1608 Buffer[lidx][i] = (m + s) * 0.5f;
1609 Buffer[ridx][i] = (m - s) * 0.5f;
1610 Buffer[cidx][i] += c * 0.5f;
1614 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1615 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1617 ALsizei i, c;
1619 Values = ASSUME_ALIGNED(Values, 16);
1620 for(c = 0;c < numchans;c++)
1622 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1623 const ALfloat gain = distcomp[c].Gain;
1624 const ALsizei base = distcomp[c].Length;
1625 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1627 if(base == 0)
1629 if(gain < 1.0f)
1631 for(i = 0;i < SamplesToDo;i++)
1632 inout[i] *= gain;
1634 continue;
1637 if(LIKELY(SamplesToDo >= base))
1639 for(i = 0;i < base;i++)
1640 Values[i] = distbuf[i];
1641 for(;i < SamplesToDo;i++)
1642 Values[i] = inout[i-base];
1643 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1645 else
1647 for(i = 0;i < SamplesToDo;i++)
1648 Values[i] = distbuf[i];
1649 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1650 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1652 for(i = 0;i < SamplesToDo;i++)
1653 inout[i] = Values[i]*gain;
1657 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1658 const ALfloat quant_scale, const ALsizei SamplesToDo,
1659 const ALsizei numchans)
1661 const ALfloat invscale = 1.0f / quant_scale;
1662 ALuint seed = *dither_seed;
1663 ALsizei c, i;
1665 ASSUME(numchans > 0);
1666 ASSUME(SamplesToDo > 0);
1668 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1669 * values between -1 and +1). Step 2 is to add the noise to the samples,
1670 * before rounding and after scaling up to the desired quantization depth.
1672 for(c = 0;c < numchans;c++)
1674 ALfloat *restrict samples = Samples[c];
1675 for(i = 0;i < SamplesToDo;i++)
1677 ALfloat val = samples[i] * quant_scale;
1678 ALuint rng0 = dither_rng(&seed);
1679 ALuint rng1 = dither_rng(&seed);
1680 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1681 samples[i] = fast_roundf(val) * invscale;
1684 *dither_seed = seed;
1688 static inline ALfloat Conv_ALfloat(ALfloat val)
1689 { return val; }
1690 static inline ALint Conv_ALint(ALfloat val)
1692 /* Floats have a 23-bit mantissa. There is an implied 1 bit in the mantissa
1693 * along with the sign bit, giving 25 bits total, so [-16777216, +16777216]
1694 * is the max value a normalized float can be scaled to before losing
1695 * precision.
1697 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1699 static inline ALshort Conv_ALshort(ALfloat val)
1700 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1701 static inline ALbyte Conv_ALbyte(ALfloat val)
1702 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1704 /* Define unsigned output variations. */
1705 #define DECL_TEMPLATE(T, func, O) \
1706 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1708 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1709 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1710 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1712 #undef DECL_TEMPLATE
1714 #define DECL_TEMPLATE(T, A) \
1715 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1716 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1717 ALsizei numchans) \
1719 ALsizei i, j; \
1721 ASSUME(numchans > 0); \
1722 ASSUME(SamplesToDo > 0); \
1724 for(j = 0;j < numchans;j++) \
1726 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1727 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1729 for(i = 0;i < SamplesToDo;i++) \
1730 out[i*numchans] = Conv_##T(in[i]); \
1734 DECL_TEMPLATE(ALfloat, F32)
1735 DECL_TEMPLATE(ALuint, UI32)
1736 DECL_TEMPLATE(ALint, I32)
1737 DECL_TEMPLATE(ALushort, UI16)
1738 DECL_TEMPLATE(ALshort, I16)
1739 DECL_TEMPLATE(ALubyte, UI8)
1740 DECL_TEMPLATE(ALbyte, I8)
1742 #undef DECL_TEMPLATE
1745 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1747 ALsizei SamplesToDo;
1748 ALsizei SamplesDone;
1749 ALCcontext *ctx;
1750 ALsizei i, c;
1752 START_MIXER_MODE();
1753 for(SamplesDone = 0;SamplesDone < NumSamples;)
1755 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1756 for(c = 0;c < device->Dry.NumChannels;c++)
1757 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1758 if(device->Dry.Buffer != device->FOAOut.Buffer)
1759 for(c = 0;c < device->FOAOut.NumChannels;c++)
1760 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1761 if(device->Dry.Buffer != device->RealOut.Buffer)
1762 for(c = 0;c < device->RealOut.NumChannels;c++)
1763 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1765 IncrementRef(&device->MixCount);
1767 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1768 while(ctx)
1770 const struct ALeffectslotArray *auxslots;
1772 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1773 ProcessParamUpdates(ctx, auxslots);
1775 for(i = 0;i < auxslots->count;i++)
1777 ALeffectslot *slot = auxslots->slot[i];
1778 for(c = 0;c < slot->NumChannels;c++)
1779 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1782 /* source processing */
1783 for(i = 0;i < ctx->VoiceCount;i++)
1785 ALvoice *voice = ctx->Voices[i];
1786 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1787 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1788 voice->Step > 0)
1790 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1792 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1793 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1794 SendSourceStoppedEvent(ctx, source->id);
1799 /* effect slot processing */
1800 for(i = 0;i < auxslots->count;i++)
1802 const ALeffectslot *slot = auxslots->slot[i];
1803 ALeffectState *state = slot->Params.EffectState;
1804 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1805 state->OutChannels);
1808 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
1811 /* Increment the clock time. Every second's worth of samples is
1812 * converted and added to clock base so that large sample counts don't
1813 * overflow during conversion. This also guarantees an exact, stable
1814 * conversion. */
1815 device->SamplesDone += SamplesToDo;
1816 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1817 device->SamplesDone %= device->Frequency;
1818 IncrementRef(&device->MixCount);
1820 /* Apply post-process for finalizing the Dry mix to the RealOut
1821 * (Ambisonic decode, UHJ encode, etc).
1823 if(LIKELY(device->PostProcess))
1824 device->PostProcess(device, SamplesToDo);
1826 if(device->Stablizer)
1828 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1829 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1830 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1831 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1833 ApplyStablizer(device->Stablizer, device->RealOut.Buffer, lidx, ridx, cidx,
1834 SamplesToDo, device->RealOut.NumChannels);
1837 ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
1838 SamplesToDo, device->RealOut.NumChannels);
1840 if(device->Limiter)
1841 ApplyCompression(device->Limiter, SamplesToDo, device->RealOut.Buffer);
1843 if(device->DitherDepth > 0.0f)
1844 ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
1845 SamplesToDo, device->RealOut.NumChannels);
1847 if(LIKELY(OutBuffer))
1849 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1850 ALsizei Channels = device->RealOut.NumChannels;
1852 switch(device->FmtType)
1854 #define HANDLE_WRITE(T, S) case T: \
1855 Write##S(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break;
1856 HANDLE_WRITE(DevFmtByte, I8)
1857 HANDLE_WRITE(DevFmtUByte, UI8)
1858 HANDLE_WRITE(DevFmtShort, I16)
1859 HANDLE_WRITE(DevFmtUShort, UI16)
1860 HANDLE_WRITE(DevFmtInt, I32)
1861 HANDLE_WRITE(DevFmtUInt, UI32)
1862 HANDLE_WRITE(DevFmtFloat, F32)
1863 #undef HANDLE_WRITE
1867 SamplesDone += SamplesToDo;
1869 END_MIXER_MODE();
1873 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1875 AsyncEvent evt = ASYNC_EVENT(EventType_Disconnected);
1876 ALCcontext *ctx;
1877 va_list args;
1878 int msglen;
1880 if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
1881 return;
1883 evt.u.user.type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1884 evt.u.user.id = 0;
1885 evt.u.user.param = 0;
1887 va_start(args, msg);
1888 msglen = vsnprintf(evt.u.user.msg, sizeof(evt.u.user.msg), msg, args);
1889 va_end(args);
1891 if(msglen < 0 || (size_t)msglen >= sizeof(evt.u.user.msg))
1892 evt.u.user.msg[sizeof(evt.u.user.msg)-1] = 0;
1894 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1895 while(ctx)
1897 ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire);
1898 ALsizei i;
1900 if((enabledevt&EventType_Disconnected) &&
1901 ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1) == 1)
1902 alsem_post(&ctx->EventSem);
1904 for(i = 0;i < ctx->VoiceCount;i++)
1906 ALvoice *voice = ctx->Voices[i];
1907 ALsource *source;
1909 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_relaxed);
1910 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed))
1912 /* If the source's voice was playing, it's now effectively
1913 * stopped (the source state will be updated the next time it's
1914 * checked).
1916 SendSourceStoppedEvent(ctx, source->id);
1918 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1921 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);