Separate the delay line feeding from reading
[openal-soft.git] / Alc / ALu.c
blobe5203d6ea88192af4dbfc5e58e619eaccc0ab955
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 ALbitfieldSOFT enabledevt;
215 AsyncEvent evt;
216 size_t strpos;
217 ALuint scale;
219 enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
220 if(!(enabledevt&EventType_SourceStateChange)) return;
222 evt.EnumType = EventType_SourceStateChange;
223 evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
224 evt.ObjectId = id;
225 evt.Param = AL_STOPPED;
227 /* Normally snprintf would be used, but this is called from the mixer and
228 * that function's not real-time safe, so we have to construct it manually.
230 strcpy(evt.Message, "Source ID "); strpos = 10;
231 scale = 1000000000;
232 while(scale > 0 && scale > id)
233 scale /= 10;
234 while(scale > 0)
236 evt.Message[strpos++] = '0' + ((id/scale)%10);
237 scale /= 10;
239 strcpy(evt.Message+strpos, " state changed to AL_STOPPED");
241 if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
242 alsem_post(&context->EventSem);
246 static void ProcessHrtf(ALCdevice *device, ALsizei SamplesToDo)
248 DirectHrtfState *state;
249 int lidx, ridx;
250 ALsizei c;
252 if(device->AmbiUp)
253 ambiup_process(device->AmbiUp,
254 device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer,
255 SamplesToDo
258 lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
259 ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
260 assert(lidx != -1 && ridx != -1);
262 state = device->Hrtf;
263 for(c = 0;c < device->Dry.NumChannels;c++)
265 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
266 device->Dry.Buffer[c], state->Offset, state->IrSize,
267 state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo
270 state->Offset += SamplesToDo;
273 static void ProcessAmbiDec(ALCdevice *device, ALsizei SamplesToDo)
275 if(device->Dry.Buffer != device->FOAOut.Buffer)
276 bformatdec_upSample(device->AmbiDecoder,
277 device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
278 SamplesToDo
280 bformatdec_process(device->AmbiDecoder,
281 device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
282 SamplesToDo
286 static void ProcessAmbiUp(ALCdevice *device, ALsizei SamplesToDo)
288 ambiup_process(device->AmbiUp,
289 device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer,
290 SamplesToDo
294 static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
296 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
297 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
298 assert(lidx != -1 && ridx != -1);
300 /* Encode to stereo-compatible 2-channel UHJ output. */
301 EncodeUhj2(device->Uhj_Encoder,
302 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
303 device->Dry.Buffer, SamplesToDo
307 static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
309 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
310 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
311 assert(lidx != -1 && ridx != -1);
313 /* Apply binaural/crossfeed filter */
314 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
315 device->RealOut.Buffer[ridx], SamplesToDo);
318 void aluSelectPostProcess(ALCdevice *device)
320 if(device->HrtfHandle)
321 device->PostProcess = ProcessHrtf;
322 else if(device->AmbiDecoder)
323 device->PostProcess = ProcessAmbiDec;
324 else if(device->AmbiUp)
325 device->PostProcess = ProcessAmbiUp;
326 else if(device->Uhj_Encoder)
327 device->PostProcess = ProcessUhj;
328 else if(device->Bs2b)
329 device->PostProcess = ProcessBs2b;
330 else
331 device->PostProcess = NULL;
335 /* Prepares the interpolator for a given rate (determined by increment).
337 * With a bit of work, and a trade of memory for CPU cost, this could be
338 * modified for use with an interpolated increment for buttery-smooth pitch
339 * changes.
341 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
343 ALfloat sf = 0.0f;
344 ALsizei si = BSINC_SCALE_COUNT-1;
346 if(increment > FRACTIONONE)
348 sf = (ALfloat)FRACTIONONE / increment;
349 sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
350 si = float2int(sf);
351 /* The interpolation factor is fit to this diagonally-symmetric curve
352 * to reduce the transition ripple caused by interpolating different
353 * scales of the sinc function.
355 sf = 1.0f - cosf(asinf(sf - si));
358 state->sf = sf;
359 state->m = table->m[si];
360 state->l = -((state->m/2) - 1);
361 state->filter = table->Tab + table->filterOffset[si];
365 static bool CalcContextParams(ALCcontext *Context)
367 ALlistener *Listener = Context->Listener;
368 struct ALcontextProps *props;
370 props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel);
371 if(!props) return false;
373 Listener->Params.MetersPerUnit = props->MetersPerUnit;
375 Listener->Params.DopplerFactor = props->DopplerFactor;
376 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
377 if(!OverrideReverbSpeedOfSound)
378 Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
379 Listener->Params.MetersPerUnit;
381 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
382 Listener->Params.DistanceModel = props->DistanceModel;
384 ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props);
385 return true;
388 static bool CalcListenerParams(ALCcontext *Context)
390 ALlistener *Listener = Context->Listener;
391 ALfloat N[3], V[3], U[3], P[3];
392 struct ALlistenerProps *props;
393 aluVector vel;
395 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
396 if(!props) return false;
398 /* AT then UP */
399 N[0] = props->Forward[0];
400 N[1] = props->Forward[1];
401 N[2] = props->Forward[2];
402 aluNormalize(N);
403 V[0] = props->Up[0];
404 V[1] = props->Up[1];
405 V[2] = props->Up[2];
406 aluNormalize(V);
407 /* Build and normalize right-vector */
408 aluCrossproduct(N, V, U);
409 aluNormalize(U);
411 aluMatrixfSet(&Listener->Params.Matrix,
412 U[0], V[0], -N[0], 0.0,
413 U[1], V[1], -N[1], 0.0,
414 U[2], V[2], -N[2], 0.0,
415 0.0, 0.0, 0.0, 1.0
418 P[0] = props->Position[0];
419 P[1] = props->Position[1];
420 P[2] = props->Position[2];
421 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
422 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
424 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
425 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
427 Listener->Params.Gain = props->Gain * Context->GainBoost;
429 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props);
430 return true;
433 static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
435 struct ALeffectslotProps *props;
436 ALeffectState *state;
438 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
439 if(!props && !force) return false;
441 if(props)
443 slot->Params.Gain = props->Gain;
444 slot->Params.AuxSendAuto = props->AuxSendAuto;
445 slot->Params.EffectType = props->Type;
446 slot->Params.EffectProps = props->Props;
447 if(IsReverbEffect(props->Type))
449 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
450 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
451 slot->Params.DecayLFRatio = props->Props.Reverb.DecayLFRatio;
452 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
453 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
454 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
456 else
458 slot->Params.RoomRolloff = 0.0f;
459 slot->Params.DecayTime = 0.0f;
460 slot->Params.DecayLFRatio = 0.0f;
461 slot->Params.DecayHFRatio = 0.0f;
462 slot->Params.DecayHFLimit = AL_FALSE;
463 slot->Params.AirAbsorptionGainHF = 1.0f;
466 /* Swap effect states. No need to play with the ref counts since they
467 * keep the same number of refs.
469 state = props->State;
470 props->State = slot->Params.EffectState;
471 slot->Params.EffectState = state;
473 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
475 else
476 state = slot->Params.EffectState;
478 V(state,update)(context, slot, &slot->Params.EffectProps);
479 return true;
483 static const struct ChanMap MonoMap[1] = {
484 { FrontCenter, 0.0f, 0.0f }
485 }, RearMap[2] = {
486 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
487 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
488 }, QuadMap[4] = {
489 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
490 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
491 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
492 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
493 }, X51Map[6] = {
494 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
495 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
496 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
497 { LFE, 0.0f, 0.0f },
498 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
499 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
500 }, X61Map[7] = {
501 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
502 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
503 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
504 { LFE, 0.0f, 0.0f },
505 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
506 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
507 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
508 }, X71Map[8] = {
509 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
510 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
511 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
512 { LFE, 0.0f, 0.0f },
513 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
514 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
515 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
516 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
519 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALfloat Elev,
520 const ALfloat Distance, const ALfloat Spread,
521 const ALfloat DryGain, const ALfloat DryGainHF,
522 const ALfloat DryGainLF, const ALfloat *WetGain,
523 const ALfloat *WetGainLF, const ALfloat *WetGainHF,
524 ALeffectslot **SendSlots, const ALbuffer *Buffer,
525 const struct ALvoiceProps *props, const ALlistener *Listener,
526 const ALCdevice *Device)
528 struct ChanMap StereoMap[2] = {
529 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
530 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
532 bool DirectChannels = props->DirectChannels;
533 const ALsizei NumSends = Device->NumAuxSends;
534 const ALuint Frequency = Device->Frequency;
535 const struct ChanMap *chans = NULL;
536 ALsizei num_channels = 0;
537 bool isbformat = false;
538 ALfloat downmix_gain = 1.0f;
539 ALsizei c, i;
541 switch(Buffer->FmtChannels)
543 case FmtMono:
544 chans = MonoMap;
545 num_channels = 1;
546 /* Mono buffers are never played direct. */
547 DirectChannels = false;
548 break;
550 case FmtStereo:
551 /* Convert counter-clockwise to clockwise. */
552 StereoMap[0].angle = -props->StereoPan[0];
553 StereoMap[1].angle = -props->StereoPan[1];
555 chans = StereoMap;
556 num_channels = 2;
557 downmix_gain = 1.0f / 2.0f;
558 break;
560 case FmtRear:
561 chans = RearMap;
562 num_channels = 2;
563 downmix_gain = 1.0f / 2.0f;
564 break;
566 case FmtQuad:
567 chans = QuadMap;
568 num_channels = 4;
569 downmix_gain = 1.0f / 4.0f;
570 break;
572 case FmtX51:
573 chans = X51Map;
574 num_channels = 6;
575 /* NOTE: Excludes LFE. */
576 downmix_gain = 1.0f / 5.0f;
577 break;
579 case FmtX61:
580 chans = X61Map;
581 num_channels = 7;
582 /* NOTE: Excludes LFE. */
583 downmix_gain = 1.0f / 6.0f;
584 break;
586 case FmtX71:
587 chans = X71Map;
588 num_channels = 8;
589 /* NOTE: Excludes LFE. */
590 downmix_gain = 1.0f / 7.0f;
591 break;
593 case FmtBFormat2D:
594 num_channels = 3;
595 isbformat = true;
596 DirectChannels = false;
597 break;
599 case FmtBFormat3D:
600 num_channels = 4;
601 isbformat = true;
602 DirectChannels = false;
603 break;
606 for(c = 0;c < num_channels;c++)
608 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
609 sizeof(voice->Direct.Params[c].Hrtf.Target));
610 ClearArray(voice->Direct.Params[c].Gains.Target);
612 for(i = 0;i < NumSends;i++)
614 for(c = 0;c < num_channels;c++)
615 ClearArray(voice->Send[i].Params[c].Gains.Target);
618 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
619 if(isbformat)
621 /* Special handling for B-Format sources. */
623 if(Distance > FLT_EPSILON)
625 /* Panning a B-Format sound toward some direction is easy. Just pan
626 * the first (W) channel as a normal mono sound and silence the
627 * others.
629 ALfloat coeffs[MAX_AMBI_COEFFS];
631 if(Device->AvgSpeakerDist > 0.0f)
633 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
634 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
635 (mdist * (ALfloat)Device->Frequency);
636 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
637 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
638 /* Clamp w0 for really close distances, to prevent excessive
639 * bass.
641 w0 = minf(w0, w1*4.0f);
643 /* Only need to adjust the first channel of a B-Format source. */
644 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
646 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
647 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
648 voice->Flags |= VOICE_HAS_NFC;
651 /* A scalar of 1.5 for plain stereo results in +/-60 degrees being
652 * moved to +/-90 degrees for direct right and left speaker
653 * responses.
655 CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
656 Elev, Spread, coeffs);
658 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
659 ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
660 voice->Direct.Params[0].Gains.Target);
661 for(i = 0;i < NumSends;i++)
663 const ALeffectslot *Slot = SendSlots[i];
664 if(Slot)
665 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
666 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
670 else
672 /* Local B-Format sources have their XYZ channels rotated according
673 * to the orientation.
675 const ALfloat sqrt_2 = sqrtf(2.0f);
676 const ALfloat sqrt_3 = sqrtf(3.0f);
677 ALfloat N[3], V[3], U[3];
678 aluMatrixf matrix;
680 if(Device->AvgSpeakerDist > 0.0f)
682 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
683 * is what we want for FOA input. The first channel may have
684 * been previously re-adjusted if panned, so reset it.
686 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, 0.0f);
688 voice->Direct.ChannelsPerOrder[0] = 1;
689 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
690 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
691 voice->Direct.ChannelsPerOrder[i] = 0;
692 voice->Flags |= VOICE_HAS_NFC;
695 /* AT then UP */
696 N[0] = props->Orientation[0][0];
697 N[1] = props->Orientation[0][1];
698 N[2] = props->Orientation[0][2];
699 aluNormalize(N);
700 V[0] = props->Orientation[1][0];
701 V[1] = props->Orientation[1][1];
702 V[2] = props->Orientation[1][2];
703 aluNormalize(V);
704 if(!props->HeadRelative)
706 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
707 aluMatrixfFloat3(N, 0.0f, lmatrix);
708 aluMatrixfFloat3(V, 0.0f, lmatrix);
710 /* Build and normalize right-vector */
711 aluCrossproduct(N, V, U);
712 aluNormalize(U);
714 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). NOTE: This
715 * matrix is transposed, for the inputs to align on the rows and
716 * outputs on the columns.
718 aluMatrixfSet(&matrix,
719 // ACN0 ACN1 ACN2 ACN3
720 sqrt_2, 0.0f, 0.0f, 0.0f, // Ambi W
721 0.0f, -N[0]*sqrt_3, N[1]*sqrt_3, -N[2]*sqrt_3, // Ambi X
722 0.0f, U[0]*sqrt_3, -U[1]*sqrt_3, U[2]*sqrt_3, // Ambi Y
723 0.0f, -V[0]*sqrt_3, V[1]*sqrt_3, -V[2]*sqrt_3 // Ambi Z
726 voice->Direct.Buffer = Device->FOAOut.Buffer;
727 voice->Direct.Channels = Device->FOAOut.NumChannels;
728 for(c = 0;c < num_channels;c++)
729 ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
730 voice->Direct.Params[c].Gains.Target);
731 for(i = 0;i < NumSends;i++)
733 const ALeffectslot *Slot = SendSlots[i];
734 if(Slot)
736 for(c = 0;c < num_channels;c++)
737 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
738 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
744 else if(DirectChannels)
746 /* Direct source channels always play local. Skip the virtual channels
747 * and write inputs to the matching real outputs.
749 voice->Direct.Buffer = Device->RealOut.Buffer;
750 voice->Direct.Channels = Device->RealOut.NumChannels;
752 for(c = 0;c < num_channels;c++)
754 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
755 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
758 /* Auxiliary sends still use normal channel panning since they mix to
759 * B-Format, which can't channel-match.
761 for(c = 0;c < num_channels;c++)
763 ALfloat coeffs[MAX_AMBI_COEFFS];
764 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
766 for(i = 0;i < NumSends;i++)
768 const ALeffectslot *Slot = SendSlots[i];
769 if(Slot)
770 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
771 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
776 else if(Device->Render_Mode == HrtfRender)
778 /* Full HRTF rendering. Skip the virtual channels and render to the
779 * real outputs.
781 voice->Direct.Buffer = Device->RealOut.Buffer;
782 voice->Direct.Channels = Device->RealOut.NumChannels;
784 if(Distance > FLT_EPSILON)
786 ALfloat coeffs[MAX_AMBI_COEFFS];
788 /* Get the HRIR coefficients and delays just once, for the given
789 * source direction.
791 GetHrtfCoeffs(Device->HrtfHandle, Elev, Azi, Spread,
792 voice->Direct.Params[0].Hrtf.Target.Coeffs,
793 voice->Direct.Params[0].Hrtf.Target.Delay);
794 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
796 /* Remaining channels use the same results as the first. */
797 for(c = 1;c < num_channels;c++)
799 /* Skip LFE */
800 if(chans[c].channel != LFE)
801 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
804 /* Calculate the directional coefficients once, which apply to all
805 * input channels of the source sends.
807 CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
809 for(i = 0;i < NumSends;i++)
811 const ALeffectslot *Slot = SendSlots[i];
812 if(Slot)
813 for(c = 0;c < num_channels;c++)
815 /* Skip LFE */
816 if(chans[c].channel != LFE)
817 ComputePanningGainsBF(Slot->ChanMap,
818 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
819 voice->Send[i].Params[c].Gains.Target
824 else
826 /* Local sources on HRTF play with each channel panned to its
827 * relative location around the listener, providing "virtual
828 * speaker" responses.
830 for(c = 0;c < num_channels;c++)
832 ALfloat coeffs[MAX_AMBI_COEFFS];
834 if(chans[c].channel == LFE)
836 /* Skip LFE */
837 continue;
840 /* Get the HRIR coefficients and delays for this channel
841 * position.
843 GetHrtfCoeffs(Device->HrtfHandle,
844 chans[c].elevation, chans[c].angle, Spread,
845 voice->Direct.Params[c].Hrtf.Target.Coeffs,
846 voice->Direct.Params[c].Hrtf.Target.Delay
848 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
850 /* Normal panning for auxiliary sends. */
851 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
853 for(i = 0;i < NumSends;i++)
855 const ALeffectslot *Slot = SendSlots[i];
856 if(Slot)
857 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
858 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
864 voice->Flags |= VOICE_HAS_HRTF;
866 else
868 /* Non-HRTF rendering. Use normal panning to the output. */
870 if(Distance > FLT_EPSILON)
872 ALfloat coeffs[MAX_AMBI_COEFFS];
873 ALfloat w0 = 0.0f;
875 /* Calculate NFC filter coefficient if needed. */
876 if(Device->AvgSpeakerDist > 0.0f)
878 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
879 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
880 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
881 w0 = SPEEDOFSOUNDMETRESPERSEC /
882 (mdist * (ALfloat)Device->Frequency);
883 /* Clamp w0 for really close distances, to prevent excessive
884 * bass.
886 w0 = minf(w0, w1*4.0f);
888 /* Adjust NFC filters. */
889 for(c = 0;c < num_channels;c++)
890 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
892 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
893 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
894 voice->Flags |= VOICE_HAS_NFC;
897 /* Calculate the directional coefficients once, which apply to all
898 * input channels.
900 CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
901 Elev, Spread, coeffs);
903 for(c = 0;c < num_channels;c++)
905 /* Special-case LFE */
906 if(chans[c].channel == LFE)
908 if(Device->Dry.Buffer == Device->RealOut.Buffer)
910 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
911 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
913 continue;
916 ComputeDryPanGains(&Device->Dry,
917 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
921 for(i = 0;i < NumSends;i++)
923 const ALeffectslot *Slot = SendSlots[i];
924 if(Slot)
925 for(c = 0;c < num_channels;c++)
927 /* Skip LFE */
928 if(chans[c].channel != LFE)
929 ComputePanningGainsBF(Slot->ChanMap,
930 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
931 voice->Send[i].Params[c].Gains.Target
936 else
938 ALfloat w0 = 0.0f;
940 if(Device->AvgSpeakerDist > 0.0f)
942 /* If the source distance is 0, set w0 to w1 to act as a pass-
943 * through. We still want to pass the signal through the
944 * filters so they keep an appropriate history, in case the
945 * source moves away from the listener.
947 w0 = SPEEDOFSOUNDMETRESPERSEC /
948 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
950 for(c = 0;c < num_channels;c++)
951 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
953 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
954 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
955 voice->Flags |= VOICE_HAS_NFC;
958 for(c = 0;c < num_channels;c++)
960 ALfloat coeffs[MAX_AMBI_COEFFS];
962 /* Special-case LFE */
963 if(chans[c].channel == LFE)
965 if(Device->Dry.Buffer == Device->RealOut.Buffer)
967 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
968 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
970 continue;
973 CalcAngleCoeffs(
974 (Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(chans[c].angle, 3.0f)
975 : chans[c].angle,
976 chans[c].elevation, Spread, coeffs
979 ComputeDryPanGains(&Device->Dry,
980 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
982 for(i = 0;i < NumSends;i++)
984 const ALeffectslot *Slot = SendSlots[i];
985 if(Slot)
986 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
987 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
995 ALfloat hfScale = props->Direct.HFReference / Frequency;
996 ALfloat lfScale = props->Direct.LFReference / Frequency;
997 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
998 ALfloat gainLF = maxf(DryGainLF, 0.001f);
1000 voice->Direct.FilterType = AF_None;
1001 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
1002 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
1003 BiquadFilter_setParams(
1004 &voice->Direct.Params[0].LowPass, BiquadType_HighShelf,
1005 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1007 BiquadFilter_setParams(
1008 &voice->Direct.Params[0].HighPass, BiquadType_LowShelf,
1009 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1011 for(c = 1;c < num_channels;c++)
1013 BiquadFilter_copyParams(&voice->Direct.Params[c].LowPass,
1014 &voice->Direct.Params[0].LowPass);
1015 BiquadFilter_copyParams(&voice->Direct.Params[c].HighPass,
1016 &voice->Direct.Params[0].HighPass);
1019 for(i = 0;i < NumSends;i++)
1021 ALfloat hfScale = props->Send[i].HFReference / Frequency;
1022 ALfloat lfScale = props->Send[i].LFReference / Frequency;
1023 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
1024 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
1026 voice->Send[i].FilterType = AF_None;
1027 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1028 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1029 BiquadFilter_setParams(
1030 &voice->Send[i].Params[0].LowPass, BiquadType_HighShelf,
1031 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1033 BiquadFilter_setParams(
1034 &voice->Send[i].Params[0].HighPass, BiquadType_LowShelf,
1035 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1037 for(c = 1;c < num_channels;c++)
1039 BiquadFilter_copyParams(&voice->Send[i].Params[c].LowPass,
1040 &voice->Send[i].Params[0].LowPass);
1041 BiquadFilter_copyParams(&voice->Send[i].Params[c].HighPass,
1042 &voice->Send[i].Params[0].HighPass);
1047 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1049 const ALCdevice *Device = ALContext->Device;
1050 const ALlistener *Listener = ALContext->Listener;
1051 ALfloat DryGain, DryGainHF, DryGainLF;
1052 ALfloat WetGain[MAX_SENDS];
1053 ALfloat WetGainHF[MAX_SENDS];
1054 ALfloat WetGainLF[MAX_SENDS];
1055 ALeffectslot *SendSlots[MAX_SENDS];
1056 ALfloat Pitch;
1057 ALsizei i;
1059 voice->Direct.Buffer = Device->Dry.Buffer;
1060 voice->Direct.Channels = Device->Dry.NumChannels;
1061 for(i = 0;i < Device->NumAuxSends;i++)
1063 SendSlots[i] = props->Send[i].Slot;
1064 if(!SendSlots[i] && i == 0)
1065 SendSlots[i] = ALContext->DefaultSlot;
1066 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1068 SendSlots[i] = NULL;
1069 voice->Send[i].Buffer = NULL;
1070 voice->Send[i].Channels = 0;
1072 else
1074 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1075 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1079 /* Calculate the stepping value */
1080 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1081 if(Pitch > (ALfloat)MAX_PITCH)
1082 voice->Step = MAX_PITCH<<FRACTIONBITS;
1083 else
1084 voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
1085 if(props->Resampler == BSinc24Resampler)
1086 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1087 else if(props->Resampler == BSinc12Resampler)
1088 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1089 voice->Resampler = SelectResampler(props->Resampler);
1091 /* Calculate gains */
1092 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1093 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1094 DryGain = minf(DryGain, GAIN_MIX_MAX);
1095 DryGainHF = props->Direct.GainHF;
1096 DryGainLF = props->Direct.GainLF;
1097 for(i = 0;i < Device->NumAuxSends;i++)
1099 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1100 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1101 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1102 WetGainHF[i] = props->Send[i].GainHF;
1103 WetGainLF[i] = props->Send[i].GainLF;
1106 CalcPanningAndFilters(voice, 0.0f, 0.0f, 0.0f, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1107 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1110 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1112 const ALCdevice *Device = ALContext->Device;
1113 const ALlistener *Listener = ALContext->Listener;
1114 const ALsizei NumSends = Device->NumAuxSends;
1115 aluVector Position, Velocity, Direction, SourceToListener;
1116 ALfloat Distance, ClampedDist, DopplerFactor;
1117 ALeffectslot *SendSlots[MAX_SENDS];
1118 ALfloat RoomRolloff[MAX_SENDS];
1119 ALfloat DecayDistance[MAX_SENDS];
1120 ALfloat DecayLFDistance[MAX_SENDS];
1121 ALfloat DecayHFDistance[MAX_SENDS];
1122 ALfloat DryGain, DryGainHF, DryGainLF;
1123 ALfloat WetGain[MAX_SENDS];
1124 ALfloat WetGainHF[MAX_SENDS];
1125 ALfloat WetGainLF[MAX_SENDS];
1126 bool directional;
1127 ALfloat ev, az;
1128 ALfloat spread;
1129 ALfloat Pitch;
1130 ALint i;
1132 /* Set mixing buffers and get send parameters. */
1133 voice->Direct.Buffer = Device->Dry.Buffer;
1134 voice->Direct.Channels = Device->Dry.NumChannels;
1135 for(i = 0;i < NumSends;i++)
1137 SendSlots[i] = props->Send[i].Slot;
1138 if(!SendSlots[i] && i == 0)
1139 SendSlots[i] = ALContext->DefaultSlot;
1140 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1142 SendSlots[i] = NULL;
1143 RoomRolloff[i] = 0.0f;
1144 DecayDistance[i] = 0.0f;
1145 DecayLFDistance[i] = 0.0f;
1146 DecayHFDistance[i] = 0.0f;
1148 else if(SendSlots[i]->Params.AuxSendAuto)
1150 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1151 /* Calculate the distances to where this effect's decay reaches
1152 * -60dB.
1154 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1155 Listener->Params.ReverbSpeedOfSound;
1156 DecayLFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayLFRatio;
1157 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1158 if(SendSlots[i]->Params.DecayHFLimit)
1160 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1161 if(airAbsorption < 1.0f)
1163 /* Calculate the distance to where this effect's air
1164 * absorption reaches -60dB, and limit the effect's HF
1165 * decay distance (so it doesn't take any longer to decay
1166 * than the air would allow).
1168 ALfloat absorb_dist = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1169 DecayHFDistance[i] = minf(absorb_dist, DecayHFDistance[i]);
1173 else
1175 /* If the slot's auxiliary send auto is off, the data sent to the
1176 * effect slot is the same as the dry path, sans filter effects */
1177 RoomRolloff[i] = props->RolloffFactor;
1178 DecayDistance[i] = 0.0f;
1179 DecayLFDistance[i] = 0.0f;
1180 DecayHFDistance[i] = 0.0f;
1183 if(!SendSlots[i])
1185 voice->Send[i].Buffer = NULL;
1186 voice->Send[i].Channels = 0;
1188 else
1190 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1191 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1195 /* Transform source to listener space (convert to head relative) */
1196 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1197 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1198 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1199 if(props->HeadRelative == AL_FALSE)
1201 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1202 /* Transform source vectors */
1203 Position = aluMatrixfVector(Matrix, &Position);
1204 Velocity = aluMatrixfVector(Matrix, &Velocity);
1205 Direction = aluMatrixfVector(Matrix, &Direction);
1207 else
1209 const aluVector *lvelocity = &Listener->Params.Velocity;
1210 /* Offset the source velocity to be relative of the listener velocity */
1211 Velocity.v[0] += lvelocity->v[0];
1212 Velocity.v[1] += lvelocity->v[1];
1213 Velocity.v[2] += lvelocity->v[2];
1216 directional = aluNormalize(Direction.v) > 0.0f;
1217 SourceToListener.v[0] = -Position.v[0];
1218 SourceToListener.v[1] = -Position.v[1];
1219 SourceToListener.v[2] = -Position.v[2];
1220 SourceToListener.v[3] = 0.0f;
1221 Distance = aluNormalize(SourceToListener.v);
1223 /* Initial source gain */
1224 DryGain = props->Gain;
1225 DryGainHF = 1.0f;
1226 DryGainLF = 1.0f;
1227 for(i = 0;i < NumSends;i++)
1229 WetGain[i] = props->Gain;
1230 WetGainHF[i] = 1.0f;
1231 WetGainLF[i] = 1.0f;
1234 /* Calculate distance attenuation */
1235 ClampedDist = Distance;
1237 switch(Listener->Params.SourceDistanceModel ?
1238 props->DistanceModel : Listener->Params.DistanceModel)
1240 case InverseDistanceClamped:
1241 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1242 if(props->MaxDistance < props->RefDistance)
1243 break;
1244 /*fall-through*/
1245 case InverseDistance:
1246 if(!(props->RefDistance > 0.0f))
1247 ClampedDist = props->RefDistance;
1248 else
1250 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1251 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1252 for(i = 0;i < NumSends;i++)
1254 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1255 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1258 break;
1260 case LinearDistanceClamped:
1261 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1262 if(props->MaxDistance < props->RefDistance)
1263 break;
1264 /*fall-through*/
1265 case LinearDistance:
1266 if(!(props->MaxDistance != props->RefDistance))
1267 ClampedDist = props->RefDistance;
1268 else
1270 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1271 (props->MaxDistance-props->RefDistance);
1272 DryGain *= maxf(1.0f - attn, 0.0f);
1273 for(i = 0;i < NumSends;i++)
1275 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1276 (props->MaxDistance-props->RefDistance);
1277 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1280 break;
1282 case ExponentDistanceClamped:
1283 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1284 if(props->MaxDistance < props->RefDistance)
1285 break;
1286 /*fall-through*/
1287 case ExponentDistance:
1288 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1289 ClampedDist = props->RefDistance;
1290 else
1292 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1293 for(i = 0;i < NumSends;i++)
1294 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1296 break;
1298 case DisableDistance:
1299 ClampedDist = props->RefDistance;
1300 break;
1303 /* Calculate directional soundcones */
1304 if(directional && props->InnerAngle < 360.0f)
1306 ALfloat ConeVolume;
1307 ALfloat ConeHF;
1308 ALfloat Angle;
1310 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1311 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1312 if(!(Angle > props->InnerAngle))
1314 ConeVolume = 1.0f;
1315 ConeHF = 1.0f;
1317 else if(Angle < props->OuterAngle)
1319 ALfloat scale = ( Angle-props->InnerAngle) /
1320 (props->OuterAngle-props->InnerAngle);
1321 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1322 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1324 else
1326 ConeVolume = props->OuterGain;
1327 ConeHF = props->OuterGainHF;
1330 DryGain *= ConeVolume;
1331 if(props->DryGainHFAuto)
1332 DryGainHF *= ConeHF;
1333 if(props->WetGainAuto)
1335 for(i = 0;i < NumSends;i++)
1336 WetGain[i] *= ConeVolume;
1338 if(props->WetGainHFAuto)
1340 for(i = 0;i < NumSends;i++)
1341 WetGainHF[i] *= ConeHF;
1345 /* Apply gain and frequency filters */
1346 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1347 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1348 DryGainHF *= props->Direct.GainHF;
1349 DryGainLF *= props->Direct.GainLF;
1350 for(i = 0;i < NumSends;i++)
1352 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1353 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1354 WetGainHF[i] *= props->Send[i].GainHF;
1355 WetGainLF[i] *= props->Send[i].GainLF;
1358 /* Distance-based air absorption and initial send decay. */
1359 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1361 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1362 Listener->Params.MetersPerUnit;
1363 if(props->AirAbsorptionFactor > 0.0f)
1365 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1366 DryGainHF *= hfattn;
1367 for(i = 0;i < NumSends;i++)
1368 WetGainHF[i] *= hfattn;
1371 if(props->WetGainAuto)
1373 /* Apply a decay-time transformation to the wet path, based on the
1374 * source distance in meters. The initial decay of the reverb
1375 * effect is calculated and applied to the wet path.
1377 for(i = 0;i < NumSends;i++)
1379 ALfloat gain, gainhf, gainlf;
1381 if(!(DecayDistance[i] > 0.0f))
1382 continue;
1384 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1385 WetGain[i] *= gain;
1386 /* Yes, the wet path's air absorption is applied with
1387 * WetGainAuto on, rather than WetGainHFAuto.
1389 if(gain > 0.0f)
1391 gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1392 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1393 gainlf = powf(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i]);
1394 WetGainLF[i] *= minf(gainlf / gain, 1.0f);
1401 /* Initial source pitch */
1402 Pitch = props->Pitch;
1404 /* Calculate velocity-based doppler effect */
1405 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1406 if(DopplerFactor > 0.0f)
1408 const aluVector *lvelocity = &Listener->Params.Velocity;
1409 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1410 ALfloat vss, vls;
1412 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1413 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1415 if(!(vls < SpeedOfSound))
1417 /* Listener moving away from the source at the speed of sound.
1418 * Sound waves can't catch it.
1420 Pitch = 0.0f;
1422 else if(!(vss < SpeedOfSound))
1424 /* Source moving toward the listener at the speed of sound. Sound
1425 * waves bunch up to extreme frequencies.
1427 Pitch = HUGE_VALF;
1429 else
1431 /* Source and listener movement is nominal. Calculate the proper
1432 * doppler shift.
1434 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1438 /* Adjust pitch based on the buffer and output frequencies, and calculate
1439 * fixed-point stepping value.
1441 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1442 if(Pitch > (ALfloat)MAX_PITCH)
1443 voice->Step = MAX_PITCH<<FRACTIONBITS;
1444 else
1445 voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
1446 if(props->Resampler == BSinc24Resampler)
1447 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1448 else if(props->Resampler == BSinc12Resampler)
1449 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1450 voice->Resampler = SelectResampler(props->Resampler);
1452 if(Distance > 0.0f)
1454 /* Clamp Y, in case rounding errors caused it to end up outside of
1455 * -1...+1.
1457 ev = asinf(clampf(-SourceToListener.v[1], -1.0f, 1.0f));
1458 /* Double negation on Z cancels out; negate once for changing source-
1459 * to-listener to listener-to-source, and again for right-handed coords
1460 * with -Z in front.
1462 az = atan2f(-SourceToListener.v[0], SourceToListener.v[2]*ZScale);
1464 else
1465 ev = az = 0.0f;
1467 if(props->Radius > Distance)
1468 spread = F_TAU - Distance/props->Radius*F_PI;
1469 else if(Distance > 0.0f)
1470 spread = asinf(props->Radius / Distance) * 2.0f;
1471 else
1472 spread = 0.0f;
1474 CalcPanningAndFilters(voice, az, ev, Distance, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1475 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1478 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1480 ALbufferlistitem *BufferListItem;
1481 struct ALvoiceProps *props;
1483 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1484 if(!props && !force) return;
1486 if(props)
1488 memcpy(voice->Props, props,
1489 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1492 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1494 props = voice->Props;
1496 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1497 while(BufferListItem != NULL)
1499 const ALbuffer *buffer = NULL;
1500 ALsizei i = 0;
1501 while(!buffer && i < BufferListItem->num_buffers)
1502 buffer = BufferListItem->buffers[i];
1503 if(LIKELY(buffer))
1505 if(props->SpatializeMode == SpatializeOn ||
1506 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1507 CalcAttnSourceParams(voice, props, buffer, context);
1508 else
1509 CalcNonAttnSourceParams(voice, props, buffer, context);
1510 break;
1512 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1517 static void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1519 ALvoice **voice, **voice_end;
1520 ALsource *source;
1521 ALsizei i;
1523 IncrementRef(&ctx->UpdateCount);
1524 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1526 bool cforce = CalcContextParams(ctx);
1527 bool force = CalcListenerParams(ctx) | cforce;
1528 for(i = 0;i < slots->count;i++)
1529 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1531 voice = ctx->Voices;
1532 voice_end = voice + ctx->VoiceCount;
1533 for(;voice != voice_end;++voice)
1535 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1536 if(source) CalcSourceParams(*voice, ctx, force);
1539 IncrementRef(&ctx->UpdateCount);
1543 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1544 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1545 ALsizei NumChannels)
1547 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1548 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1549 ALsizei i;
1551 /* Apply an all-pass to all channels, except the front-left and front-
1552 * right, so they maintain the same relative phase.
1554 for(i = 0;i < NumChannels;i++)
1556 if(i == lidx || i == ridx)
1557 continue;
1558 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1561 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1562 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1564 for(i = 0;i < SamplesToDo;i++)
1566 ALfloat lfsum, hfsum;
1567 ALfloat m, s, c;
1569 lfsum = lsplit[0][i] + rsplit[0][i];
1570 hfsum = lsplit[1][i] + rsplit[1][i];
1571 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1573 /* This pans the separate low- and high-frequency sums between being on
1574 * the center channel and the left/right channels. The low-frequency
1575 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1576 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1577 * values can be tweaked.
1579 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1580 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1582 /* The generated center channel signal adds to the existing signal,
1583 * while the modified left and right channels replace.
1585 Buffer[lidx][i] = (m + s) * 0.5f;
1586 Buffer[ridx][i] = (m - s) * 0.5f;
1587 Buffer[cidx][i] += c * 0.5f;
1591 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1592 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1594 ALsizei i, c;
1596 Values = ASSUME_ALIGNED(Values, 16);
1597 for(c = 0;c < numchans;c++)
1599 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1600 const ALfloat gain = distcomp[c].Gain;
1601 const ALsizei base = distcomp[c].Length;
1602 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1604 if(base == 0)
1606 if(gain < 1.0f)
1608 for(i = 0;i < SamplesToDo;i++)
1609 inout[i] *= gain;
1611 continue;
1614 if(LIKELY(SamplesToDo >= base))
1616 for(i = 0;i < base;i++)
1617 Values[i] = distbuf[i];
1618 for(;i < SamplesToDo;i++)
1619 Values[i] = inout[i-base];
1620 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1622 else
1624 for(i = 0;i < SamplesToDo;i++)
1625 Values[i] = distbuf[i];
1626 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1627 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1629 for(i = 0;i < SamplesToDo;i++)
1630 inout[i] = Values[i]*gain;
1634 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1635 const ALfloat quant_scale, const ALsizei SamplesToDo,
1636 const ALsizei numchans)
1638 const ALfloat invscale = 1.0f / quant_scale;
1639 ALuint seed = *dither_seed;
1640 ALsizei c, i;
1642 ASSUME(numchans > 0);
1643 ASSUME(SamplesToDo > 0);
1645 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1646 * values between -1 and +1). Step 2 is to add the noise to the samples,
1647 * before rounding and after scaling up to the desired quantization depth.
1649 for(c = 0;c < numchans;c++)
1651 ALfloat *restrict samples = Samples[c];
1652 for(i = 0;i < SamplesToDo;i++)
1654 ALfloat val = samples[i] * quant_scale;
1655 ALuint rng0 = dither_rng(&seed);
1656 ALuint rng1 = dither_rng(&seed);
1657 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1658 samples[i] = fast_roundf(val) * invscale;
1661 *dither_seed = seed;
1665 static inline ALfloat Conv_ALfloat(ALfloat val)
1666 { return val; }
1667 static inline ALint Conv_ALint(ALfloat val)
1669 /* Floats have a 23-bit mantissa. There is an implied 1 bit in the mantissa
1670 * along with the sign bit, giving 25 bits total, so [-16777216, +16777216]
1671 * is the max value a normalized float can be scaled to before losing
1672 * precision.
1674 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1676 static inline ALshort Conv_ALshort(ALfloat val)
1677 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1678 static inline ALbyte Conv_ALbyte(ALfloat val)
1679 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1681 /* Define unsigned output variations. */
1682 #define DECL_TEMPLATE(T, func, O) \
1683 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1685 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1686 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1687 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1689 #undef DECL_TEMPLATE
1691 #define DECL_TEMPLATE(T, A) \
1692 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1693 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1694 ALsizei numchans) \
1696 ALsizei i, j; \
1698 ASSUME(numchans > 0); \
1699 ASSUME(SamplesToDo > 0); \
1701 for(j = 0;j < numchans;j++) \
1703 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1704 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1706 for(i = 0;i < SamplesToDo;i++) \
1707 out[i*numchans] = Conv_##T(in[i]); \
1711 DECL_TEMPLATE(ALfloat, F32)
1712 DECL_TEMPLATE(ALuint, UI32)
1713 DECL_TEMPLATE(ALint, I32)
1714 DECL_TEMPLATE(ALushort, UI16)
1715 DECL_TEMPLATE(ALshort, I16)
1716 DECL_TEMPLATE(ALubyte, UI8)
1717 DECL_TEMPLATE(ALbyte, I8)
1719 #undef DECL_TEMPLATE
1722 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1724 ALsizei SamplesToDo;
1725 ALsizei SamplesDone;
1726 ALCcontext *ctx;
1727 ALsizei i, c;
1729 START_MIXER_MODE();
1730 for(SamplesDone = 0;SamplesDone < NumSamples;)
1732 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1733 for(c = 0;c < device->Dry.NumChannels;c++)
1734 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1735 if(device->Dry.Buffer != device->FOAOut.Buffer)
1736 for(c = 0;c < device->FOAOut.NumChannels;c++)
1737 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1738 if(device->Dry.Buffer != device->RealOut.Buffer)
1739 for(c = 0;c < device->RealOut.NumChannels;c++)
1740 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1742 IncrementRef(&device->MixCount);
1744 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1745 while(ctx)
1747 const struct ALeffectslotArray *auxslots;
1749 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1750 ProcessParamUpdates(ctx, auxslots);
1752 for(i = 0;i < auxslots->count;i++)
1754 ALeffectslot *slot = auxslots->slot[i];
1755 for(c = 0;c < slot->NumChannels;c++)
1756 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1759 /* source processing */
1760 for(i = 0;i < ctx->VoiceCount;i++)
1762 ALvoice *voice = ctx->Voices[i];
1763 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1764 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1765 voice->Step > 0)
1767 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1769 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1770 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1771 SendSourceStoppedEvent(ctx, source->id);
1776 /* effect slot processing */
1777 for(i = 0;i < auxslots->count;i++)
1779 const ALeffectslot *slot = auxslots->slot[i];
1780 ALeffectState *state = slot->Params.EffectState;
1781 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1782 state->OutChannels);
1785 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
1788 /* Increment the clock time. Every second's worth of samples is
1789 * converted and added to clock base so that large sample counts don't
1790 * overflow during conversion. This also guarantees an exact, stable
1791 * conversion. */
1792 device->SamplesDone += SamplesToDo;
1793 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1794 device->SamplesDone %= device->Frequency;
1795 IncrementRef(&device->MixCount);
1797 /* Apply post-process for finalizing the Dry mix to the RealOut
1798 * (Ambisonic decode, UHJ encode, etc).
1800 if(LIKELY(device->PostProcess))
1801 device->PostProcess(device, SamplesToDo);
1803 if(device->Stablizer)
1805 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1806 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1807 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1808 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1810 ApplyStablizer(device->Stablizer, device->RealOut.Buffer, lidx, ridx, cidx,
1811 SamplesToDo, device->RealOut.NumChannels);
1814 ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
1815 SamplesToDo, device->RealOut.NumChannels);
1817 if(device->Limiter)
1818 ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
1819 device->RealOut.Buffer);
1821 if(device->DitherDepth > 0.0f)
1822 ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
1823 SamplesToDo, device->RealOut.NumChannels);
1825 if(LIKELY(OutBuffer))
1827 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1828 ALsizei Channels = device->RealOut.NumChannels;
1830 switch(device->FmtType)
1832 #define HANDLE_WRITE(T, S) case T: \
1833 Write##S(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break;
1834 HANDLE_WRITE(DevFmtByte, I8)
1835 HANDLE_WRITE(DevFmtUByte, UI8)
1836 HANDLE_WRITE(DevFmtShort, I16)
1837 HANDLE_WRITE(DevFmtUShort, UI16)
1838 HANDLE_WRITE(DevFmtInt, I32)
1839 HANDLE_WRITE(DevFmtUInt, UI32)
1840 HANDLE_WRITE(DevFmtFloat, F32)
1841 #undef HANDLE_WRITE
1845 SamplesDone += SamplesToDo;
1847 END_MIXER_MODE();
1851 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1853 ALCcontext *ctx;
1854 AsyncEvent evt;
1855 va_list args;
1856 int msglen;
1858 if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
1859 return;
1861 evt.EnumType = EventType_Disconnected;
1862 evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1863 evt.ObjectId = 0;
1864 evt.Param = 0;
1866 va_start(args, msg);
1867 msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
1868 va_end(args);
1870 if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
1871 evt.Message[sizeof(evt.Message)-1] = 0;
1873 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1874 while(ctx)
1876 ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire);
1877 ALsizei i;
1879 if((enabledevt&EventType_Disconnected) &&
1880 ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1) == 1)
1881 alsem_post(&ctx->EventSem);
1883 for(i = 0;i < ctx->VoiceCount;i++)
1885 ALvoice *voice = ctx->Voices[i];
1886 ALsource *source;
1888 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_relaxed);
1889 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed))
1891 /* If the source's voice was playing, it's now effectively
1892 * stopped (the source state will be updated the next time it's
1893 * checked).
1895 SendSourceStoppedEvent(ctx, source->id);
1897 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1900 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);