Merge pull request #204 from jhasse/android-byte-order
[openal-soft.git] / Alc / ALu.c
blobdc24755d13f7fba575d2085a3c69896326ae0354
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 if(Device->Render_Mode == StereoPair)
652 CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
653 else
654 CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
656 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
657 ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
658 voice->Direct.Params[0].Gains.Target);
659 for(i = 0;i < NumSends;i++)
661 const ALeffectslot *Slot = SendSlots[i];
662 if(Slot)
663 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
664 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
668 else
670 /* Local B-Format sources have their XYZ channels rotated according
671 * to the orientation.
673 const ALfloat sqrt_2 = sqrtf(2.0f);
674 const ALfloat sqrt_3 = sqrtf(3.0f);
675 ALfloat N[3], V[3], U[3];
676 aluMatrixf matrix;
678 if(Device->AvgSpeakerDist > 0.0f)
680 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
681 * is what we want for FOA input. The first channel may have
682 * been previously re-adjusted if panned, so reset it.
684 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, 0.0f);
686 voice->Direct.ChannelsPerOrder[0] = 1;
687 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
688 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
689 voice->Direct.ChannelsPerOrder[i] = 0;
690 voice->Flags |= VOICE_HAS_NFC;
693 /* AT then UP */
694 N[0] = props->Orientation[0][0];
695 N[1] = props->Orientation[0][1];
696 N[2] = props->Orientation[0][2];
697 aluNormalize(N);
698 V[0] = props->Orientation[1][0];
699 V[1] = props->Orientation[1][1];
700 V[2] = props->Orientation[1][2];
701 aluNormalize(V);
702 if(!props->HeadRelative)
704 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
705 aluMatrixfFloat3(N, 0.0f, lmatrix);
706 aluMatrixfFloat3(V, 0.0f, lmatrix);
708 /* Build and normalize right-vector */
709 aluCrossproduct(N, V, U);
710 aluNormalize(U);
712 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). NOTE: This
713 * matrix is transposed, for the inputs to align on the rows and
714 * outputs on the columns.
716 aluMatrixfSet(&matrix,
717 // ACN0 ACN1 ACN2 ACN3
718 sqrt_2, 0.0f, 0.0f, 0.0f, // Ambi W
719 0.0f, -N[0]*sqrt_3, N[1]*sqrt_3, -N[2]*sqrt_3, // Ambi X
720 0.0f, U[0]*sqrt_3, -U[1]*sqrt_3, U[2]*sqrt_3, // Ambi Y
721 0.0f, -V[0]*sqrt_3, V[1]*sqrt_3, -V[2]*sqrt_3 // Ambi Z
724 voice->Direct.Buffer = Device->FOAOut.Buffer;
725 voice->Direct.Channels = Device->FOAOut.NumChannels;
726 for(c = 0;c < num_channels;c++)
727 ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
728 voice->Direct.Params[c].Gains.Target);
729 for(i = 0;i < NumSends;i++)
731 const ALeffectslot *Slot = SendSlots[i];
732 if(Slot)
734 for(c = 0;c < num_channels;c++)
735 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
736 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
742 else if(DirectChannels)
744 /* Direct source channels always play local. Skip the virtual channels
745 * and write inputs to the matching real outputs.
747 voice->Direct.Buffer = Device->RealOut.Buffer;
748 voice->Direct.Channels = Device->RealOut.NumChannels;
750 for(c = 0;c < num_channels;c++)
752 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
753 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
756 /* Auxiliary sends still use normal channel panning since they mix to
757 * B-Format, which can't channel-match.
759 for(c = 0;c < num_channels;c++)
761 ALfloat coeffs[MAX_AMBI_COEFFS];
762 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
764 for(i = 0;i < NumSends;i++)
766 const ALeffectslot *Slot = SendSlots[i];
767 if(Slot)
768 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
769 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
774 else if(Device->Render_Mode == HrtfRender)
776 /* Full HRTF rendering. Skip the virtual channels and render to the
777 * real outputs.
779 voice->Direct.Buffer = Device->RealOut.Buffer;
780 voice->Direct.Channels = Device->RealOut.NumChannels;
782 if(Distance > FLT_EPSILON)
784 ALfloat coeffs[MAX_AMBI_COEFFS];
786 /* Get the HRIR coefficients and delays just once, for the given
787 * source direction.
789 GetHrtfCoeffs(Device->HrtfHandle, Elev, Azi, Spread,
790 voice->Direct.Params[0].Hrtf.Target.Coeffs,
791 voice->Direct.Params[0].Hrtf.Target.Delay);
792 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
794 /* Remaining channels use the same results as the first. */
795 for(c = 1;c < num_channels;c++)
797 /* Skip LFE */
798 if(chans[c].channel != LFE)
799 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
802 /* Calculate the directional coefficients once, which apply to all
803 * input channels of the source sends.
805 CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
807 for(i = 0;i < NumSends;i++)
809 const ALeffectslot *Slot = SendSlots[i];
810 if(Slot)
811 for(c = 0;c < num_channels;c++)
813 /* Skip LFE */
814 if(chans[c].channel != LFE)
815 ComputePanningGainsBF(Slot->ChanMap,
816 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
817 voice->Send[i].Params[c].Gains.Target
822 else
824 /* Local sources on HRTF play with each channel panned to its
825 * relative location around the listener, providing "virtual
826 * speaker" responses.
828 for(c = 0;c < num_channels;c++)
830 ALfloat coeffs[MAX_AMBI_COEFFS];
832 if(chans[c].channel == LFE)
834 /* Skip LFE */
835 continue;
838 /* Get the HRIR coefficients and delays for this channel
839 * position.
841 GetHrtfCoeffs(Device->HrtfHandle,
842 chans[c].elevation, chans[c].angle, Spread,
843 voice->Direct.Params[c].Hrtf.Target.Coeffs,
844 voice->Direct.Params[c].Hrtf.Target.Delay
846 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
848 /* Normal panning for auxiliary sends. */
849 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
851 for(i = 0;i < NumSends;i++)
853 const ALeffectslot *Slot = SendSlots[i];
854 if(Slot)
855 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
856 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
862 voice->Flags |= VOICE_HAS_HRTF;
864 else
866 /* Non-HRTF rendering. Use normal panning to the output. */
868 if(Distance > FLT_EPSILON)
870 ALfloat coeffs[MAX_AMBI_COEFFS];
871 ALfloat w0 = 0.0f;
873 /* Calculate NFC filter coefficient if needed. */
874 if(Device->AvgSpeakerDist > 0.0f)
876 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
877 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
878 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
879 w0 = SPEEDOFSOUNDMETRESPERSEC /
880 (mdist * (ALfloat)Device->Frequency);
881 /* Clamp w0 for really close distances, to prevent excessive
882 * bass.
884 w0 = minf(w0, w1*4.0f);
886 /* Adjust NFC filters. */
887 for(c = 0;c < num_channels;c++)
888 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
890 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
891 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
892 voice->Flags |= VOICE_HAS_NFC;
895 /* Calculate the directional coefficients once, which apply to all
896 * input channels.
898 if(Device->Render_Mode == StereoPair)
899 CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
900 else
901 CalcAngleCoeffs(Azi, 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 if(Device->Render_Mode == StereoPair)
974 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
975 else
976 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
977 ComputeDryPanGains(&Device->Dry,
978 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
981 for(i = 0;i < NumSends;i++)
983 const ALeffectslot *Slot = SendSlots[i];
984 if(Slot)
985 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
986 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
994 ALfloat hfScale = props->Direct.HFReference / Frequency;
995 ALfloat lfScale = props->Direct.LFReference / Frequency;
996 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
997 ALfloat gainLF = maxf(DryGainLF, 0.001f);
999 voice->Direct.FilterType = AF_None;
1000 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
1001 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
1002 BiquadFilter_setParams(
1003 &voice->Direct.Params[0].LowPass, BiquadType_HighShelf,
1004 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1006 BiquadFilter_setParams(
1007 &voice->Direct.Params[0].HighPass, BiquadType_LowShelf,
1008 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1010 for(c = 1;c < num_channels;c++)
1012 BiquadFilter_copyParams(&voice->Direct.Params[c].LowPass,
1013 &voice->Direct.Params[0].LowPass);
1014 BiquadFilter_copyParams(&voice->Direct.Params[c].HighPass,
1015 &voice->Direct.Params[0].HighPass);
1018 for(i = 0;i < NumSends;i++)
1020 ALfloat hfScale = props->Send[i].HFReference / Frequency;
1021 ALfloat lfScale = props->Send[i].LFReference / Frequency;
1022 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
1023 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
1025 voice->Send[i].FilterType = AF_None;
1026 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1027 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1028 BiquadFilter_setParams(
1029 &voice->Send[i].Params[0].LowPass, BiquadType_HighShelf,
1030 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1032 BiquadFilter_setParams(
1033 &voice->Send[i].Params[0].HighPass, BiquadType_LowShelf,
1034 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1036 for(c = 1;c < num_channels;c++)
1038 BiquadFilter_copyParams(&voice->Send[i].Params[c].LowPass,
1039 &voice->Send[i].Params[0].LowPass);
1040 BiquadFilter_copyParams(&voice->Send[i].Params[c].HighPass,
1041 &voice->Send[i].Params[0].HighPass);
1046 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1048 const ALCdevice *Device = ALContext->Device;
1049 const ALlistener *Listener = ALContext->Listener;
1050 ALfloat DryGain, DryGainHF, DryGainLF;
1051 ALfloat WetGain[MAX_SENDS];
1052 ALfloat WetGainHF[MAX_SENDS];
1053 ALfloat WetGainLF[MAX_SENDS];
1054 ALeffectslot *SendSlots[MAX_SENDS];
1055 ALfloat Pitch;
1056 ALsizei i;
1058 voice->Direct.Buffer = Device->Dry.Buffer;
1059 voice->Direct.Channels = Device->Dry.NumChannels;
1060 for(i = 0;i < Device->NumAuxSends;i++)
1062 SendSlots[i] = props->Send[i].Slot;
1063 if(!SendSlots[i] && i == 0)
1064 SendSlots[i] = ALContext->DefaultSlot;
1065 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1067 SendSlots[i] = NULL;
1068 voice->Send[i].Buffer = NULL;
1069 voice->Send[i].Channels = 0;
1071 else
1073 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1074 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1078 /* Calculate the stepping value */
1079 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1080 if(Pitch > (ALfloat)MAX_PITCH)
1081 voice->Step = MAX_PITCH<<FRACTIONBITS;
1082 else
1083 voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
1084 if(props->Resampler == BSinc24Resampler)
1085 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1086 else if(props->Resampler == BSinc12Resampler)
1087 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1088 voice->Resampler = SelectResampler(props->Resampler);
1090 /* Calculate gains */
1091 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1092 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1093 DryGain = minf(DryGain, GAIN_MIX_MAX);
1094 DryGainHF = props->Direct.GainHF;
1095 DryGainLF = props->Direct.GainLF;
1096 for(i = 0;i < Device->NumAuxSends;i++)
1098 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1099 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1100 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1101 WetGainHF[i] = props->Send[i].GainHF;
1102 WetGainLF[i] = props->Send[i].GainLF;
1105 CalcPanningAndFilters(voice, 0.0f, 0.0f, 0.0f, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1106 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1109 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1111 const ALCdevice *Device = ALContext->Device;
1112 const ALlistener *Listener = ALContext->Listener;
1113 const ALsizei NumSends = Device->NumAuxSends;
1114 aluVector Position, Velocity, Direction, SourceToListener;
1115 ALfloat Distance, ClampedDist, DopplerFactor;
1116 ALeffectslot *SendSlots[MAX_SENDS];
1117 ALfloat RoomRolloff[MAX_SENDS];
1118 ALfloat DecayDistance[MAX_SENDS];
1119 ALfloat DecayLFDistance[MAX_SENDS];
1120 ALfloat DecayHFDistance[MAX_SENDS];
1121 ALfloat DryGain, DryGainHF, DryGainLF;
1122 ALfloat WetGain[MAX_SENDS];
1123 ALfloat WetGainHF[MAX_SENDS];
1124 ALfloat WetGainLF[MAX_SENDS];
1125 bool directional;
1126 ALfloat ev, az;
1127 ALfloat spread;
1128 ALfloat Pitch;
1129 ALint i;
1131 /* Set mixing buffers and get send parameters. */
1132 voice->Direct.Buffer = Device->Dry.Buffer;
1133 voice->Direct.Channels = Device->Dry.NumChannels;
1134 for(i = 0;i < NumSends;i++)
1136 SendSlots[i] = props->Send[i].Slot;
1137 if(!SendSlots[i] && i == 0)
1138 SendSlots[i] = ALContext->DefaultSlot;
1139 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1141 SendSlots[i] = NULL;
1142 RoomRolloff[i] = 0.0f;
1143 DecayDistance[i] = 0.0f;
1144 DecayLFDistance[i] = 0.0f;
1145 DecayHFDistance[i] = 0.0f;
1147 else if(SendSlots[i]->Params.AuxSendAuto)
1149 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1150 /* Calculate the distances to where this effect's decay reaches
1151 * -60dB.
1153 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1154 Listener->Params.ReverbSpeedOfSound;
1155 DecayLFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayLFRatio;
1156 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1157 if(SendSlots[i]->Params.DecayHFLimit)
1159 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1160 if(airAbsorption < 1.0f)
1162 /* Calculate the distance to where this effect's air
1163 * absorption reaches -60dB, and limit the effect's HF
1164 * decay distance (so it doesn't take any longer to decay
1165 * than the air would allow).
1167 ALfloat absorb_dist = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1168 DecayHFDistance[i] = minf(absorb_dist, DecayHFDistance[i]);
1172 else
1174 /* If the slot's auxiliary send auto is off, the data sent to the
1175 * effect slot is the same as the dry path, sans filter effects */
1176 RoomRolloff[i] = props->RolloffFactor;
1177 DecayDistance[i] = 0.0f;
1178 DecayLFDistance[i] = 0.0f;
1179 DecayHFDistance[i] = 0.0f;
1182 if(!SendSlots[i])
1184 voice->Send[i].Buffer = NULL;
1185 voice->Send[i].Channels = 0;
1187 else
1189 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1190 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1194 /* Transform source to listener space (convert to head relative) */
1195 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1196 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1197 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1198 if(props->HeadRelative == AL_FALSE)
1200 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1201 /* Transform source vectors */
1202 Position = aluMatrixfVector(Matrix, &Position);
1203 Velocity = aluMatrixfVector(Matrix, &Velocity);
1204 Direction = aluMatrixfVector(Matrix, &Direction);
1206 else
1208 const aluVector *lvelocity = &Listener->Params.Velocity;
1209 /* Offset the source velocity to be relative of the listener velocity */
1210 Velocity.v[0] += lvelocity->v[0];
1211 Velocity.v[1] += lvelocity->v[1];
1212 Velocity.v[2] += lvelocity->v[2];
1215 directional = aluNormalize(Direction.v) > 0.0f;
1216 SourceToListener.v[0] = -Position.v[0];
1217 SourceToListener.v[1] = -Position.v[1];
1218 SourceToListener.v[2] = -Position.v[2];
1219 SourceToListener.v[3] = 0.0f;
1220 Distance = aluNormalize(SourceToListener.v);
1222 /* Initial source gain */
1223 DryGain = props->Gain;
1224 DryGainHF = 1.0f;
1225 DryGainLF = 1.0f;
1226 for(i = 0;i < NumSends;i++)
1228 WetGain[i] = props->Gain;
1229 WetGainHF[i] = 1.0f;
1230 WetGainLF[i] = 1.0f;
1233 /* Calculate distance attenuation */
1234 ClampedDist = Distance;
1236 switch(Listener->Params.SourceDistanceModel ?
1237 props->DistanceModel : Listener->Params.DistanceModel)
1239 case InverseDistanceClamped:
1240 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1241 if(props->MaxDistance < props->RefDistance)
1242 break;
1243 /*fall-through*/
1244 case InverseDistance:
1245 if(!(props->RefDistance > 0.0f))
1246 ClampedDist = props->RefDistance;
1247 else
1249 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1250 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1251 for(i = 0;i < NumSends;i++)
1253 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1254 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1257 break;
1259 case LinearDistanceClamped:
1260 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1261 if(props->MaxDistance < props->RefDistance)
1262 break;
1263 /*fall-through*/
1264 case LinearDistance:
1265 if(!(props->MaxDistance != props->RefDistance))
1266 ClampedDist = props->RefDistance;
1267 else
1269 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1270 (props->MaxDistance-props->RefDistance);
1271 DryGain *= maxf(1.0f - attn, 0.0f);
1272 for(i = 0;i < NumSends;i++)
1274 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1275 (props->MaxDistance-props->RefDistance);
1276 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1279 break;
1281 case ExponentDistanceClamped:
1282 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1283 if(props->MaxDistance < props->RefDistance)
1284 break;
1285 /*fall-through*/
1286 case ExponentDistance:
1287 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1288 ClampedDist = props->RefDistance;
1289 else
1291 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1292 for(i = 0;i < NumSends;i++)
1293 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1295 break;
1297 case DisableDistance:
1298 ClampedDist = props->RefDistance;
1299 break;
1302 /* Calculate directional soundcones */
1303 if(directional && props->InnerAngle < 360.0f)
1305 ALfloat ConeVolume;
1306 ALfloat ConeHF;
1307 ALfloat Angle;
1309 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1310 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1311 if(!(Angle > props->InnerAngle))
1313 ConeVolume = 1.0f;
1314 ConeHF = 1.0f;
1316 else if(Angle < props->OuterAngle)
1318 ALfloat scale = ( Angle-props->InnerAngle) /
1319 (props->OuterAngle-props->InnerAngle);
1320 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1321 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1323 else
1325 ConeVolume = props->OuterGain;
1326 ConeHF = props->OuterGainHF;
1329 DryGain *= ConeVolume;
1330 if(props->DryGainHFAuto)
1331 DryGainHF *= ConeHF;
1332 if(props->WetGainAuto)
1334 for(i = 0;i < NumSends;i++)
1335 WetGain[i] *= ConeVolume;
1337 if(props->WetGainHFAuto)
1339 for(i = 0;i < NumSends;i++)
1340 WetGainHF[i] *= ConeHF;
1344 /* Apply gain and frequency filters */
1345 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1346 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1347 DryGainHF *= props->Direct.GainHF;
1348 DryGainLF *= props->Direct.GainLF;
1349 for(i = 0;i < NumSends;i++)
1351 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1352 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1353 WetGainHF[i] *= props->Send[i].GainHF;
1354 WetGainLF[i] *= props->Send[i].GainLF;
1357 /* Distance-based air absorption and initial send decay. */
1358 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1360 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1361 Listener->Params.MetersPerUnit;
1362 if(props->AirAbsorptionFactor > 0.0f)
1364 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1365 DryGainHF *= hfattn;
1366 for(i = 0;i < NumSends;i++)
1367 WetGainHF[i] *= hfattn;
1370 if(props->WetGainAuto)
1372 /* Apply a decay-time transformation to the wet path, based on the
1373 * source distance in meters. The initial decay of the reverb
1374 * effect is calculated and applied to the wet path.
1376 for(i = 0;i < NumSends;i++)
1378 ALfloat gain, gainhf, gainlf;
1380 if(!(DecayDistance[i] > 0.0f))
1381 continue;
1383 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1384 WetGain[i] *= gain;
1385 /* Yes, the wet path's air absorption is applied with
1386 * WetGainAuto on, rather than WetGainHFAuto.
1388 if(gain > 0.0f)
1390 gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1391 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1392 gainlf = powf(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i]);
1393 WetGainLF[i] *= minf(gainlf / gain, 1.0f);
1400 /* Initial source pitch */
1401 Pitch = props->Pitch;
1403 /* Calculate velocity-based doppler effect */
1404 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1405 if(DopplerFactor > 0.0f)
1407 const aluVector *lvelocity = &Listener->Params.Velocity;
1408 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1409 ALfloat vss, vls;
1411 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1412 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1414 if(!(vls < SpeedOfSound))
1416 /* Listener moving away from the source at the speed of sound.
1417 * Sound waves can't catch it.
1419 Pitch = 0.0f;
1421 else if(!(vss < SpeedOfSound))
1423 /* Source moving toward the listener at the speed of sound. Sound
1424 * waves bunch up to extreme frequencies.
1426 Pitch = HUGE_VALF;
1428 else
1430 /* Source and listener movement is nominal. Calculate the proper
1431 * doppler shift.
1433 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1437 /* Adjust pitch based on the buffer and output frequencies, and calculate
1438 * fixed-point stepping value.
1440 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1441 if(Pitch > (ALfloat)MAX_PITCH)
1442 voice->Step = MAX_PITCH<<FRACTIONBITS;
1443 else
1444 voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
1445 if(props->Resampler == BSinc24Resampler)
1446 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1447 else if(props->Resampler == BSinc12Resampler)
1448 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1449 voice->Resampler = SelectResampler(props->Resampler);
1451 if(Distance > 0.0f)
1453 /* Clamp Y, in case rounding errors caused it to end up outside of
1454 * -1...+1.
1456 ev = asinf(clampf(-SourceToListener.v[1], -1.0f, 1.0f));
1457 /* Double negation on Z cancels out; negate once for changing source-
1458 * to-listener to listener-to-source, and again for right-handed coords
1459 * with -Z in front.
1461 az = atan2f(-SourceToListener.v[0], SourceToListener.v[2]*ZScale);
1463 else
1464 ev = az = 0.0f;
1466 if(props->Radius > Distance)
1467 spread = F_TAU - Distance/props->Radius*F_PI;
1468 else if(Distance > 0.0f)
1469 spread = asinf(props->Radius / Distance) * 2.0f;
1470 else
1471 spread = 0.0f;
1473 CalcPanningAndFilters(voice, az, ev, Distance, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1474 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1477 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1479 ALbufferlistitem *BufferListItem;
1480 struct ALvoiceProps *props;
1482 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1483 if(!props && !force) return;
1485 if(props)
1487 memcpy(voice->Props, props,
1488 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1491 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1493 props = voice->Props;
1495 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1496 while(BufferListItem != NULL)
1498 const ALbuffer *buffer = NULL;
1499 ALsizei i = 0;
1500 while(!buffer && i < BufferListItem->num_buffers)
1501 buffer = BufferListItem->buffers[i];
1502 if(LIKELY(buffer))
1504 if(props->SpatializeMode == SpatializeOn ||
1505 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1506 CalcAttnSourceParams(voice, props, buffer, context);
1507 else
1508 CalcNonAttnSourceParams(voice, props, buffer, context);
1509 break;
1511 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1516 static void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1518 ALvoice **voice, **voice_end;
1519 ALsource *source;
1520 ALsizei i;
1522 IncrementRef(&ctx->UpdateCount);
1523 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1525 bool cforce = CalcContextParams(ctx);
1526 bool force = CalcListenerParams(ctx) | cforce;
1527 for(i = 0;i < slots->count;i++)
1528 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1530 voice = ctx->Voices;
1531 voice_end = voice + ctx->VoiceCount;
1532 for(;voice != voice_end;++voice)
1534 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1535 if(source) CalcSourceParams(*voice, ctx, force);
1538 IncrementRef(&ctx->UpdateCount);
1542 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1543 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1544 ALsizei NumChannels)
1546 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1547 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1548 ALsizei i;
1550 /* Apply an all-pass to all channels, except the front-left and front-
1551 * right, so they maintain the same relative phase.
1553 for(i = 0;i < NumChannels;i++)
1555 if(i == lidx || i == ridx)
1556 continue;
1557 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1560 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1561 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1563 for(i = 0;i < SamplesToDo;i++)
1565 ALfloat lfsum, hfsum;
1566 ALfloat m, s, c;
1568 lfsum = lsplit[0][i] + rsplit[0][i];
1569 hfsum = lsplit[1][i] + rsplit[1][i];
1570 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1572 /* This pans the separate low- and high-frequency sums between being on
1573 * the center channel and the left/right channels. The low-frequency
1574 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1575 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1576 * values can be tweaked.
1578 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1579 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1581 /* The generated center channel signal adds to the existing signal,
1582 * while the modified left and right channels replace.
1584 Buffer[lidx][i] = (m + s) * 0.5f;
1585 Buffer[ridx][i] = (m - s) * 0.5f;
1586 Buffer[cidx][i] += c * 0.5f;
1590 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1591 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1593 ALsizei i, c;
1595 Values = ASSUME_ALIGNED(Values, 16);
1596 for(c = 0;c < numchans;c++)
1598 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1599 const ALfloat gain = distcomp[c].Gain;
1600 const ALsizei base = distcomp[c].Length;
1601 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1603 if(base == 0)
1605 if(gain < 1.0f)
1607 for(i = 0;i < SamplesToDo;i++)
1608 inout[i] *= gain;
1610 continue;
1613 if(LIKELY(SamplesToDo >= base))
1615 for(i = 0;i < base;i++)
1616 Values[i] = distbuf[i];
1617 for(;i < SamplesToDo;i++)
1618 Values[i] = inout[i-base];
1619 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1621 else
1623 for(i = 0;i < SamplesToDo;i++)
1624 Values[i] = distbuf[i];
1625 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1626 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1628 for(i = 0;i < SamplesToDo;i++)
1629 inout[i] = Values[i]*gain;
1633 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1634 const ALfloat quant_scale, const ALsizei SamplesToDo,
1635 const ALsizei numchans)
1637 const ALfloat invscale = 1.0f / quant_scale;
1638 ALuint seed = *dither_seed;
1639 ALsizei c, i;
1641 ASSUME(numchans > 0);
1642 ASSUME(SamplesToDo > 0);
1644 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1645 * values between -1 and +1). Step 2 is to add the noise to the samples,
1646 * before rounding and after scaling up to the desired quantization depth.
1648 for(c = 0;c < numchans;c++)
1650 ALfloat *restrict samples = Samples[c];
1651 for(i = 0;i < SamplesToDo;i++)
1653 ALfloat val = samples[i] * quant_scale;
1654 ALuint rng0 = dither_rng(&seed);
1655 ALuint rng1 = dither_rng(&seed);
1656 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1657 samples[i] = fast_roundf(val) * invscale;
1660 *dither_seed = seed;
1664 static inline ALfloat Conv_ALfloat(ALfloat val)
1665 { return val; }
1666 static inline ALint Conv_ALint(ALfloat val)
1668 /* Floats have a 23-bit mantissa. There is an implied 1 bit in the mantissa
1669 * along with the sign bit, giving 25 bits total, so [-16777216, +16777216]
1670 * is the max value a normalized float can be scaled to before losing
1671 * precision.
1673 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1675 static inline ALshort Conv_ALshort(ALfloat val)
1676 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1677 static inline ALbyte Conv_ALbyte(ALfloat val)
1678 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1680 /* Define unsigned output variations. */
1681 #define DECL_TEMPLATE(T, func, O) \
1682 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1684 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1685 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1686 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1688 #undef DECL_TEMPLATE
1690 #define DECL_TEMPLATE(T, A) \
1691 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1692 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1693 ALsizei numchans) \
1695 ALsizei i, j; \
1697 ASSUME(numchans > 0); \
1698 ASSUME(SamplesToDo > 0); \
1700 for(j = 0;j < numchans;j++) \
1702 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1703 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1705 for(i = 0;i < SamplesToDo;i++) \
1706 out[i*numchans] = Conv_##T(in[i]); \
1710 DECL_TEMPLATE(ALfloat, F32)
1711 DECL_TEMPLATE(ALuint, UI32)
1712 DECL_TEMPLATE(ALint, I32)
1713 DECL_TEMPLATE(ALushort, UI16)
1714 DECL_TEMPLATE(ALshort, I16)
1715 DECL_TEMPLATE(ALubyte, UI8)
1716 DECL_TEMPLATE(ALbyte, I8)
1718 #undef DECL_TEMPLATE
1721 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1723 ALsizei SamplesToDo;
1724 ALsizei SamplesDone;
1725 ALCcontext *ctx;
1726 ALsizei i, c;
1728 START_MIXER_MODE();
1729 for(SamplesDone = 0;SamplesDone < NumSamples;)
1731 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1732 for(c = 0;c < device->Dry.NumChannels;c++)
1733 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1734 if(device->Dry.Buffer != device->FOAOut.Buffer)
1735 for(c = 0;c < device->FOAOut.NumChannels;c++)
1736 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1737 if(device->Dry.Buffer != device->RealOut.Buffer)
1738 for(c = 0;c < device->RealOut.NumChannels;c++)
1739 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1741 IncrementRef(&device->MixCount);
1743 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1744 while(ctx)
1746 const struct ALeffectslotArray *auxslots;
1748 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1749 ProcessParamUpdates(ctx, auxslots);
1751 for(i = 0;i < auxslots->count;i++)
1753 ALeffectslot *slot = auxslots->slot[i];
1754 for(c = 0;c < slot->NumChannels;c++)
1755 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1758 /* source processing */
1759 for(i = 0;i < ctx->VoiceCount;i++)
1761 ALvoice *voice = ctx->Voices[i];
1762 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1763 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1764 voice->Step > 0)
1766 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1768 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1769 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1770 SendSourceStoppedEvent(ctx, source->id);
1775 /* effect slot processing */
1776 for(i = 0;i < auxslots->count;i++)
1778 const ALeffectslot *slot = auxslots->slot[i];
1779 ALeffectState *state = slot->Params.EffectState;
1780 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1781 state->OutChannels);
1784 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
1787 /* Increment the clock time. Every second's worth of samples is
1788 * converted and added to clock base so that large sample counts don't
1789 * overflow during conversion. This also guarantees an exact, stable
1790 * conversion. */
1791 device->SamplesDone += SamplesToDo;
1792 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1793 device->SamplesDone %= device->Frequency;
1794 IncrementRef(&device->MixCount);
1796 /* Apply post-process for finalizing the Dry mix to the RealOut
1797 * (Ambisonic decode, UHJ encode, etc).
1799 if(LIKELY(device->PostProcess))
1800 device->PostProcess(device, SamplesToDo);
1802 if(device->Stablizer)
1804 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1805 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1806 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1807 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1809 ApplyStablizer(device->Stablizer, device->RealOut.Buffer, lidx, ridx, cidx,
1810 SamplesToDo, device->RealOut.NumChannels);
1813 ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
1814 SamplesToDo, device->RealOut.NumChannels);
1816 if(device->Limiter)
1817 ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
1818 device->RealOut.Buffer);
1820 if(device->DitherDepth > 0.0f)
1821 ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
1822 SamplesToDo, device->RealOut.NumChannels);
1824 if(LIKELY(OutBuffer))
1826 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1827 ALsizei Channels = device->RealOut.NumChannels;
1829 switch(device->FmtType)
1831 #define HANDLE_WRITE(T, S) case T: \
1832 Write##S(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break;
1833 HANDLE_WRITE(DevFmtByte, I8)
1834 HANDLE_WRITE(DevFmtUByte, UI8)
1835 HANDLE_WRITE(DevFmtShort, I16)
1836 HANDLE_WRITE(DevFmtUShort, UI16)
1837 HANDLE_WRITE(DevFmtInt, I32)
1838 HANDLE_WRITE(DevFmtUInt, UI32)
1839 HANDLE_WRITE(DevFmtFloat, F32)
1840 #undef HANDLE_WRITE
1844 SamplesDone += SamplesToDo;
1846 END_MIXER_MODE();
1850 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1852 ALCcontext *ctx;
1853 AsyncEvent evt;
1854 va_list args;
1855 int msglen;
1857 if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
1858 return;
1860 evt.EnumType = EventType_Disconnected;
1861 evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1862 evt.ObjectId = 0;
1863 evt.Param = 0;
1865 va_start(args, msg);
1866 msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
1867 va_end(args);
1869 if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
1870 evt.Message[sizeof(evt.Message)-1] = 0;
1872 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1873 while(ctx)
1875 ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire);
1876 ALsizei i;
1878 if((enabledevt&EventType_Disconnected) &&
1879 ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1) == 1)
1880 alsem_post(&ctx->EventSem);
1882 for(i = 0;i < ctx->VoiceCount;i++)
1884 ALvoice *voice = ctx->Voices[i];
1885 ALsource *source;
1887 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_relaxed);
1888 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed))
1890 /* If the source's voice was playing, it's now effectively
1891 * stopped (the source state will be updated the next time it's
1892 * checked).
1894 SendSourceStoppedEvent(ctx, source->id);
1896 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1899 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);