Rename BiquadState to BiquadFilter
[openal-soft.git] / Alc / ALu.c
blob8eda30948b71c8267a7bb1aa3d511379099c07e8
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"
43 #include "mixer/defs.h"
44 #include "fpu_modes.h"
45 #include "cpu_caps.h"
46 #include "bsinc_inc.h"
48 #include "backends/base.h"
51 extern inline ALfloat minf(ALfloat a, ALfloat b);
52 extern inline ALfloat maxf(ALfloat a, ALfloat b);
53 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
55 extern inline ALdouble mind(ALdouble a, ALdouble b);
56 extern inline ALdouble maxd(ALdouble a, ALdouble b);
57 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
59 extern inline ALuint minu(ALuint a, ALuint b);
60 extern inline ALuint maxu(ALuint a, ALuint b);
61 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
63 extern inline ALint mini(ALint a, ALint b);
64 extern inline ALint maxi(ALint a, ALint b);
65 extern inline ALint clampi(ALint val, ALint min, ALint max);
67 extern inline ALint64 mini64(ALint64 a, ALint64 b);
68 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
69 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
71 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
72 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
73 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
75 extern inline size_t minz(size_t a, size_t b);
76 extern inline size_t maxz(size_t a, size_t b);
77 extern inline size_t clampz(size_t val, size_t min, size_t max);
79 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
80 extern inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu);
82 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
84 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
85 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
86 extern inline void aluMatrixfSet(aluMatrixf *matrix,
87 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
88 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
89 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
90 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
93 /* Cone scalar */
94 ALfloat ConeScale = 1.0f;
96 /* Localized Z scalar for mono sources */
97 ALfloat ZScale = 1.0f;
99 /* Force default speed of sound for distance-related reverb decay. */
100 ALboolean OverrideReverbSpeedOfSound = AL_FALSE;
102 const aluMatrixf IdentityMatrixf = {{
103 { 1.0f, 0.0f, 0.0f, 0.0f },
104 { 0.0f, 1.0f, 0.0f, 0.0f },
105 { 0.0f, 0.0f, 1.0f, 0.0f },
106 { 0.0f, 0.0f, 0.0f, 1.0f },
110 static void ClearArray(ALfloat f[MAX_OUTPUT_CHANNELS])
112 size_t i;
113 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
114 f[i] = 0.0f;
117 struct ChanMap {
118 enum Channel channel;
119 ALfloat angle;
120 ALfloat elevation;
123 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
126 void DeinitVoice(ALvoice *voice)
128 al_free(ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL));
132 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
134 #ifdef HAVE_NEON
135 if((CPUCapFlags&CPU_CAP_NEON))
136 return MixDirectHrtf_Neon;
137 #endif
138 #ifdef HAVE_SSE
139 if((CPUCapFlags&CPU_CAP_SSE))
140 return MixDirectHrtf_SSE;
141 #endif
143 return MixDirectHrtf_C;
147 /* Prior to VS2013, MSVC lacks the round() family of functions. */
148 #if defined(_MSC_VER) && _MSC_VER < 1800
149 static float roundf(float val)
151 if(val < 0.0f)
152 return ceilf(val-0.5f);
153 return floorf(val+0.5f);
155 #endif
157 /* This RNG method was created based on the math found in opusdec. It's quick,
158 * and starting with a seed value of 22222, is suitable for generating
159 * whitenoise.
161 static inline ALuint dither_rng(ALuint *seed)
163 *seed = (*seed * 96314165) + 907633515;
164 return *seed;
168 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
170 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
171 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
172 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
175 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
177 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
180 static ALfloat aluNormalize(ALfloat *vec)
182 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
183 if(length > FLT_EPSILON)
185 ALfloat inv_length = 1.0f/length;
186 vec[0] *= inv_length;
187 vec[1] *= inv_length;
188 vec[2] *= inv_length;
189 return length;
191 vec[0] = vec[1] = vec[2] = 0.0f;
192 return 0.0f;
195 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
197 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
199 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];
200 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];
201 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];
204 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
206 aluVector v;
207 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];
208 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];
209 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];
210 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];
211 return v;
215 void aluInit(void)
217 MixDirectHrtf = SelectHrtfMixer();
221 static void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
223 ALbitfieldSOFT enabledevt;
224 AsyncEvent evt;
225 size_t strpos;
226 ALuint scale;
228 enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
229 if(!(enabledevt&EventType_SourceStateChange)) return;
231 evt.EnumType = EventType_SourceStateChange;
232 evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
233 evt.ObjectId = id;
234 evt.Param = AL_STOPPED;
236 /* Normally snprintf would be used, but this is called from the mixer and
237 * that function's not real-time safe, so we have to construct it manually.
239 strcpy(evt.Message, "Source ID "); strpos = 10;
240 scale = 1000000000;
241 while(scale > 0 && scale > id)
242 scale /= 10;
243 while(scale > 0)
245 evt.Message[strpos++] = '0' + ((id/scale)%10);
246 scale /= 10;
248 strcpy(evt.Message+strpos, " state changed to AL_STOPPED");
250 if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
251 alsem_post(&context->EventSem);
255 static void ProcessHrtf(ALCdevice *device, ALsizei SamplesToDo)
257 DirectHrtfState *state;
258 int lidx, ridx;
259 ALsizei c;
261 if(device->AmbiUp)
262 ambiup_process(device->AmbiUp,
263 device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer,
264 SamplesToDo
267 lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
268 ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
269 assert(lidx != -1 && ridx != -1);
271 state = device->Hrtf;
272 for(c = 0;c < device->Dry.NumChannels;c++)
274 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
275 device->Dry.Buffer[c], state->Offset, state->IrSize,
276 state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo
279 state->Offset += SamplesToDo;
282 static void ProcessAmbiDec(ALCdevice *device, ALsizei SamplesToDo)
284 if(device->Dry.Buffer != device->FOAOut.Buffer)
285 bformatdec_upSample(device->AmbiDecoder,
286 device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
287 SamplesToDo
289 bformatdec_process(device->AmbiDecoder,
290 device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
291 SamplesToDo
295 static void ProcessAmbiUp(ALCdevice *device, ALsizei SamplesToDo)
297 ambiup_process(device->AmbiUp,
298 device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer,
299 SamplesToDo
303 static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
305 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
306 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
307 if(LIKELY(lidx != -1 && ridx != -1))
309 /* Encode to stereo-compatible 2-channel UHJ output. */
310 EncodeUhj2(device->Uhj_Encoder,
311 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
312 device->Dry.Buffer, SamplesToDo
317 static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
319 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
320 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
321 if(LIKELY(lidx != -1 && ridx != -1))
323 /* Apply binaural/crossfeed filter */
324 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
325 device->RealOut.Buffer[ridx], SamplesToDo);
329 void aluSelectPostProcess(ALCdevice *device)
331 if(device->HrtfHandle)
332 device->PostProcess = ProcessHrtf;
333 else if(device->AmbiDecoder)
334 device->PostProcess = ProcessAmbiDec;
335 else if(device->AmbiUp)
336 device->PostProcess = ProcessAmbiUp;
337 else if(device->Uhj_Encoder)
338 device->PostProcess = ProcessUhj;
339 else if(device->Bs2b)
340 device->PostProcess = ProcessBs2b;
341 else
342 device->PostProcess = NULL;
346 /* Prepares the interpolator for a given rate (determined by increment). A
347 * result of AL_FALSE indicates that the filter output will completely cut
348 * the input signal.
350 * With a bit of work, and a trade of memory for CPU cost, this could be
351 * modified for use with an interpolated increment for buttery-smooth pitch
352 * changes.
354 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
356 ALfloat sf;
357 ALsizei si;
359 if(increment > FRACTIONONE)
361 sf = (ALfloat)FRACTIONONE / increment;
362 sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
363 si = fastf2i(sf);
364 /* The interpolation factor is fit to this diagonally-symmetric curve
365 * to reduce the transition ripple caused by interpolating different
366 * scales of the sinc function.
368 sf = 1.0f - cosf(asinf(sf - si));
370 else
372 sf = 0.0f;
373 si = BSINC_SCALE_COUNT - 1;
376 state->sf = sf;
377 state->m = table->m[si];
378 state->l = -((state->m/2) - 1);
379 state->filter = table->Tab + table->filterOffset[si];
383 static bool CalcContextParams(ALCcontext *Context)
385 ALlistener *Listener = Context->Listener;
386 struct ALcontextProps *props;
388 props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel);
389 if(!props) return false;
391 Listener->Params.MetersPerUnit = props->MetersPerUnit;
393 Listener->Params.DopplerFactor = props->DopplerFactor;
394 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
395 if(!OverrideReverbSpeedOfSound)
396 Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
397 Listener->Params.MetersPerUnit;
399 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
400 Listener->Params.DistanceModel = props->DistanceModel;
402 ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props);
403 return true;
406 static bool CalcListenerParams(ALCcontext *Context)
408 ALlistener *Listener = Context->Listener;
409 ALfloat N[3], V[3], U[3], P[3];
410 struct ALlistenerProps *props;
411 aluVector vel;
413 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
414 if(!props) return false;
416 /* AT then UP */
417 N[0] = props->Forward[0];
418 N[1] = props->Forward[1];
419 N[2] = props->Forward[2];
420 aluNormalize(N);
421 V[0] = props->Up[0];
422 V[1] = props->Up[1];
423 V[2] = props->Up[2];
424 aluNormalize(V);
425 /* Build and normalize right-vector */
426 aluCrossproduct(N, V, U);
427 aluNormalize(U);
429 aluMatrixfSet(&Listener->Params.Matrix,
430 U[0], V[0], -N[0], 0.0,
431 U[1], V[1], -N[1], 0.0,
432 U[2], V[2], -N[2], 0.0,
433 0.0, 0.0, 0.0, 1.0
436 P[0] = props->Position[0];
437 P[1] = props->Position[1];
438 P[2] = props->Position[2];
439 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
440 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
442 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
443 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
445 Listener->Params.Gain = props->Gain * Context->GainBoost;
447 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props);
448 return true;
451 static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
453 struct ALeffectslotProps *props;
454 ALeffectState *state;
456 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
457 if(!props && !force) return false;
459 if(props)
461 slot->Params.Gain = props->Gain;
462 slot->Params.AuxSendAuto = props->AuxSendAuto;
463 slot->Params.EffectType = props->Type;
464 slot->Params.EffectProps = props->Props;
465 if(IsReverbEffect(props->Type))
467 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
468 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
469 slot->Params.DecayLFRatio = props->Props.Reverb.DecayLFRatio;
470 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
471 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
472 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
474 else
476 slot->Params.RoomRolloff = 0.0f;
477 slot->Params.DecayTime = 0.0f;
478 slot->Params.DecayLFRatio = 0.0f;
479 slot->Params.DecayHFRatio = 0.0f;
480 slot->Params.DecayHFLimit = AL_FALSE;
481 slot->Params.AirAbsorptionGainHF = 1.0f;
484 /* Swap effect states. No need to play with the ref counts since they
485 * keep the same number of refs.
487 state = props->State;
488 props->State = slot->Params.EffectState;
489 slot->Params.EffectState = state;
491 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
493 else
494 state = slot->Params.EffectState;
496 V(state,update)(context, slot, &slot->Params.EffectProps);
497 return true;
501 static const struct ChanMap MonoMap[1] = {
502 { FrontCenter, 0.0f, 0.0f }
503 }, RearMap[2] = {
504 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
505 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
506 }, QuadMap[4] = {
507 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
508 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
509 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
510 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
511 }, X51Map[6] = {
512 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
513 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
514 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
515 { LFE, 0.0f, 0.0f },
516 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
517 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
518 }, X61Map[7] = {
519 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
520 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
521 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
522 { LFE, 0.0f, 0.0f },
523 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
524 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
525 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
526 }, X71Map[8] = {
527 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
528 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
529 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
530 { LFE, 0.0f, 0.0f },
531 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
532 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
533 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
534 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
537 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALfloat Elev,
538 const ALfloat Distance, const ALfloat Spread,
539 const ALfloat DryGain, const ALfloat DryGainHF,
540 const ALfloat DryGainLF, const ALfloat *WetGain,
541 const ALfloat *WetGainLF, const ALfloat *WetGainHF,
542 ALeffectslot **SendSlots, const ALbuffer *Buffer,
543 const struct ALvoiceProps *props, const ALlistener *Listener,
544 const ALCdevice *Device)
546 struct ChanMap StereoMap[2] = {
547 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
548 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
550 bool DirectChannels = props->DirectChannels;
551 const ALsizei NumSends = Device->NumAuxSends;
552 const ALuint Frequency = Device->Frequency;
553 const struct ChanMap *chans = NULL;
554 ALsizei num_channels = 0;
555 bool isbformat = false;
556 ALfloat downmix_gain = 1.0f;
557 ALsizei c, i;
559 switch(Buffer->FmtChannels)
561 case FmtMono:
562 chans = MonoMap;
563 num_channels = 1;
564 /* Mono buffers are never played direct. */
565 DirectChannels = false;
566 break;
568 case FmtStereo:
569 /* Convert counter-clockwise to clockwise. */
570 StereoMap[0].angle = -props->StereoPan[0];
571 StereoMap[1].angle = -props->StereoPan[1];
573 chans = StereoMap;
574 num_channels = 2;
575 downmix_gain = 1.0f / 2.0f;
576 break;
578 case FmtRear:
579 chans = RearMap;
580 num_channels = 2;
581 downmix_gain = 1.0f / 2.0f;
582 break;
584 case FmtQuad:
585 chans = QuadMap;
586 num_channels = 4;
587 downmix_gain = 1.0f / 4.0f;
588 break;
590 case FmtX51:
591 chans = X51Map;
592 num_channels = 6;
593 /* NOTE: Excludes LFE. */
594 downmix_gain = 1.0f / 5.0f;
595 break;
597 case FmtX61:
598 chans = X61Map;
599 num_channels = 7;
600 /* NOTE: Excludes LFE. */
601 downmix_gain = 1.0f / 6.0f;
602 break;
604 case FmtX71:
605 chans = X71Map;
606 num_channels = 8;
607 /* NOTE: Excludes LFE. */
608 downmix_gain = 1.0f / 7.0f;
609 break;
611 case FmtBFormat2D:
612 num_channels = 3;
613 isbformat = true;
614 DirectChannels = false;
615 break;
617 case FmtBFormat3D:
618 num_channels = 4;
619 isbformat = true;
620 DirectChannels = false;
621 break;
624 for(c = 0;c < num_channels;c++)
626 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
627 sizeof(voice->Direct.Params[c].Hrtf.Target));
628 ClearArray(voice->Direct.Params[c].Gains.Target);
630 for(i = 0;i < NumSends;i++)
632 for(c = 0;c < num_channels;c++)
633 ClearArray(voice->Send[i].Params[c].Gains.Target);
636 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
637 if(isbformat)
639 /* Special handling for B-Format sources. */
641 if(Distance > FLT_EPSILON)
643 /* Panning a B-Format sound toward some direction is easy. Just pan
644 * the first (W) channel as a normal mono sound and silence the
645 * others.
647 ALfloat coeffs[MAX_AMBI_COEFFS];
649 if(Device->AvgSpeakerDist > 0.0f)
651 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
652 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
653 (mdist * (ALfloat)Device->Frequency);
654 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
655 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
656 /* Clamp w0 for really close distances, to prevent excessive
657 * bass.
659 w0 = minf(w0, w1*4.0f);
661 /* Only need to adjust the first channel of a B-Format source. */
662 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
664 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
665 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
666 voice->Flags |= VOICE_HAS_NFC;
669 if(Device->Render_Mode == StereoPair)
670 CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
671 else
672 CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
674 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
675 ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
676 voice->Direct.Params[0].Gains.Target);
677 for(i = 0;i < NumSends;i++)
679 const ALeffectslot *Slot = SendSlots[i];
680 if(Slot)
681 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
682 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
686 else
688 /* Local B-Format sources have their XYZ channels rotated according
689 * to the orientation.
691 const ALfloat sqrt_2 = sqrtf(2.0f);
692 const ALfloat sqrt_3 = sqrtf(3.0f);
693 ALfloat N[3], V[3], U[3];
694 aluMatrixf matrix;
696 if(Device->AvgSpeakerDist > 0.0f)
698 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
699 * is what we want for FOA input. The first channel may have
700 * been previously re-adjusted if panned, so reset it.
702 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, 0.0f);
704 voice->Direct.ChannelsPerOrder[0] = 1;
705 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
706 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
707 voice->Direct.ChannelsPerOrder[i] = 0;
708 voice->Flags |= VOICE_HAS_NFC;
711 /* AT then UP */
712 N[0] = props->Orientation[0][0];
713 N[1] = props->Orientation[0][1];
714 N[2] = props->Orientation[0][2];
715 aluNormalize(N);
716 V[0] = props->Orientation[1][0];
717 V[1] = props->Orientation[1][1];
718 V[2] = props->Orientation[1][2];
719 aluNormalize(V);
720 if(!props->HeadRelative)
722 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
723 aluMatrixfFloat3(N, 0.0f, lmatrix);
724 aluMatrixfFloat3(V, 0.0f, lmatrix);
726 /* Build and normalize right-vector */
727 aluCrossproduct(N, V, U);
728 aluNormalize(U);
730 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). NOTE: This
731 * matrix is transposed, for the inputs to align on the rows and
732 * outputs on the columns.
734 aluMatrixfSet(&matrix,
735 // ACN0 ACN1 ACN2 ACN3
736 sqrt_2, 0.0f, 0.0f, 0.0f, // Ambi W
737 0.0f, -N[0]*sqrt_3, N[1]*sqrt_3, -N[2]*sqrt_3, // Ambi X
738 0.0f, U[0]*sqrt_3, -U[1]*sqrt_3, U[2]*sqrt_3, // Ambi Y
739 0.0f, -V[0]*sqrt_3, V[1]*sqrt_3, -V[2]*sqrt_3 // Ambi Z
742 voice->Direct.Buffer = Device->FOAOut.Buffer;
743 voice->Direct.Channels = Device->FOAOut.NumChannels;
744 for(c = 0;c < num_channels;c++)
745 ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
746 voice->Direct.Params[c].Gains.Target);
747 for(i = 0;i < NumSends;i++)
749 const ALeffectslot *Slot = SendSlots[i];
750 if(Slot)
752 for(c = 0;c < num_channels;c++)
753 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
754 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
760 else if(DirectChannels)
762 /* Direct source channels always play local. Skip the virtual channels
763 * and write inputs to the matching real outputs.
765 voice->Direct.Buffer = Device->RealOut.Buffer;
766 voice->Direct.Channels = Device->RealOut.NumChannels;
768 for(c = 0;c < num_channels;c++)
770 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
771 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
774 /* Auxiliary sends still use normal channel panning since they mix to
775 * B-Format, which can't channel-match.
777 for(c = 0;c < num_channels;c++)
779 ALfloat coeffs[MAX_AMBI_COEFFS];
780 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
782 for(i = 0;i < NumSends;i++)
784 const ALeffectslot *Slot = SendSlots[i];
785 if(Slot)
786 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
787 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
792 else if(Device->Render_Mode == HrtfRender)
794 /* Full HRTF rendering. Skip the virtual channels and render to the
795 * real outputs.
797 voice->Direct.Buffer = Device->RealOut.Buffer;
798 voice->Direct.Channels = Device->RealOut.NumChannels;
800 if(Distance > FLT_EPSILON)
802 ALfloat coeffs[MAX_AMBI_COEFFS];
804 /* Get the HRIR coefficients and delays just once, for the given
805 * source direction.
807 GetHrtfCoeffs(Device->HrtfHandle, Elev, Azi, Spread,
808 voice->Direct.Params[0].Hrtf.Target.Coeffs,
809 voice->Direct.Params[0].Hrtf.Target.Delay);
810 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
812 /* Remaining channels use the same results as the first. */
813 for(c = 1;c < num_channels;c++)
815 /* Skip LFE */
816 if(chans[c].channel != LFE)
817 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
820 /* Calculate the directional coefficients once, which apply to all
821 * input channels of the source sends.
823 CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
825 for(i = 0;i < NumSends;i++)
827 const ALeffectslot *Slot = SendSlots[i];
828 if(Slot)
829 for(c = 0;c < num_channels;c++)
831 /* Skip LFE */
832 if(chans[c].channel != LFE)
833 ComputePanningGainsBF(Slot->ChanMap,
834 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
835 voice->Send[i].Params[c].Gains.Target
840 else
842 /* Local sources on HRTF play with each channel panned to its
843 * relative location around the listener, providing "virtual
844 * speaker" responses.
846 for(c = 0;c < num_channels;c++)
848 ALfloat coeffs[MAX_AMBI_COEFFS];
850 if(chans[c].channel == LFE)
852 /* Skip LFE */
853 continue;
856 /* Get the HRIR coefficients and delays for this channel
857 * position.
859 GetHrtfCoeffs(Device->HrtfHandle,
860 chans[c].elevation, chans[c].angle, Spread,
861 voice->Direct.Params[c].Hrtf.Target.Coeffs,
862 voice->Direct.Params[c].Hrtf.Target.Delay
864 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
866 /* Normal panning for auxiliary sends. */
867 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
869 for(i = 0;i < NumSends;i++)
871 const ALeffectslot *Slot = SendSlots[i];
872 if(Slot)
873 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
874 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
880 voice->Flags |= VOICE_HAS_HRTF;
882 else
884 /* Non-HRTF rendering. Use normal panning to the output. */
886 if(Distance > FLT_EPSILON)
888 ALfloat coeffs[MAX_AMBI_COEFFS];
889 ALfloat w0 = 0.0f;
891 /* Calculate NFC filter coefficient if needed. */
892 if(Device->AvgSpeakerDist > 0.0f)
894 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
895 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
896 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
897 w0 = SPEEDOFSOUNDMETRESPERSEC /
898 (mdist * (ALfloat)Device->Frequency);
899 /* Clamp w0 for really close distances, to prevent excessive
900 * bass.
902 w0 = minf(w0, w1*4.0f);
904 /* Adjust NFC filters. */
905 for(c = 0;c < num_channels;c++)
906 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
908 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
909 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
910 voice->Flags |= VOICE_HAS_NFC;
913 /* Calculate the directional coefficients once, which apply to all
914 * input channels.
916 if(Device->Render_Mode == StereoPair)
917 CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
918 else
919 CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
921 for(c = 0;c < num_channels;c++)
923 /* Special-case LFE */
924 if(chans[c].channel == LFE)
926 if(Device->Dry.Buffer == Device->RealOut.Buffer)
928 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
929 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
931 continue;
934 ComputeDryPanGains(&Device->Dry,
935 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
939 for(i = 0;i < NumSends;i++)
941 const ALeffectslot *Slot = SendSlots[i];
942 if(Slot)
943 for(c = 0;c < num_channels;c++)
945 /* Skip LFE */
946 if(chans[c].channel != LFE)
947 ComputePanningGainsBF(Slot->ChanMap,
948 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
949 voice->Send[i].Params[c].Gains.Target
954 else
956 ALfloat w0 = 0.0f;
958 if(Device->AvgSpeakerDist > 0.0f)
960 /* If the source distance is 0, set w0 to w1 to act as a pass-
961 * through. We still want to pass the signal through the
962 * filters so they keep an appropriate history, in case the
963 * source moves away from the listener.
965 w0 = SPEEDOFSOUNDMETRESPERSEC /
966 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
968 for(c = 0;c < num_channels;c++)
969 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
971 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
972 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
973 voice->Flags |= VOICE_HAS_NFC;
976 for(c = 0;c < num_channels;c++)
978 ALfloat coeffs[MAX_AMBI_COEFFS];
980 /* Special-case LFE */
981 if(chans[c].channel == LFE)
983 if(Device->Dry.Buffer == Device->RealOut.Buffer)
985 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
986 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
988 continue;
991 if(Device->Render_Mode == StereoPair)
992 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
993 else
994 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
995 ComputeDryPanGains(&Device->Dry,
996 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
999 for(i = 0;i < NumSends;i++)
1001 const ALeffectslot *Slot = SendSlots[i];
1002 if(Slot)
1003 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1004 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
1012 ALfloat hfScale = props->Direct.HFReference / Frequency;
1013 ALfloat lfScale = props->Direct.LFReference / Frequency;
1014 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
1015 ALfloat gainLF = maxf(DryGainLF, 0.001f);
1017 voice->Direct.FilterType = AF_None;
1018 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
1019 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
1020 BiquadFilter_setParams(
1021 &voice->Direct.Params[0].LowPass, BiquadType_HighShelf,
1022 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1024 BiquadFilter_setParams(
1025 &voice->Direct.Params[0].HighPass, BiquadType_LowShelf,
1026 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1028 for(c = 1;c < num_channels;c++)
1030 BiquadFilter_copyParams(&voice->Direct.Params[c].LowPass,
1031 &voice->Direct.Params[0].LowPass);
1032 BiquadFilter_copyParams(&voice->Direct.Params[c].HighPass,
1033 &voice->Direct.Params[0].HighPass);
1036 for(i = 0;i < NumSends;i++)
1038 ALfloat hfScale = props->Send[i].HFReference / Frequency;
1039 ALfloat lfScale = props->Send[i].LFReference / Frequency;
1040 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
1041 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
1043 voice->Send[i].FilterType = AF_None;
1044 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1045 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1046 BiquadFilter_setParams(
1047 &voice->Send[i].Params[0].LowPass, BiquadType_HighShelf,
1048 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1050 BiquadFilter_setParams(
1051 &voice->Send[i].Params[0].HighPass, BiquadType_LowShelf,
1052 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1054 for(c = 1;c < num_channels;c++)
1056 BiquadFilter_copyParams(&voice->Send[i].Params[c].LowPass,
1057 &voice->Send[i].Params[0].LowPass);
1058 BiquadFilter_copyParams(&voice->Send[i].Params[c].HighPass,
1059 &voice->Send[i].Params[0].HighPass);
1064 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1066 const ALCdevice *Device = ALContext->Device;
1067 const ALlistener *Listener = ALContext->Listener;
1068 ALfloat DryGain, DryGainHF, DryGainLF;
1069 ALfloat WetGain[MAX_SENDS];
1070 ALfloat WetGainHF[MAX_SENDS];
1071 ALfloat WetGainLF[MAX_SENDS];
1072 ALeffectslot *SendSlots[MAX_SENDS];
1073 ALfloat Pitch;
1074 ALsizei i;
1076 voice->Direct.Buffer = Device->Dry.Buffer;
1077 voice->Direct.Channels = Device->Dry.NumChannels;
1078 for(i = 0;i < Device->NumAuxSends;i++)
1080 SendSlots[i] = props->Send[i].Slot;
1081 if(!SendSlots[i] && i == 0)
1082 SendSlots[i] = ALContext->DefaultSlot;
1083 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1085 SendSlots[i] = NULL;
1086 voice->Send[i].Buffer = NULL;
1087 voice->Send[i].Channels = 0;
1089 else
1091 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1092 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1096 /* Calculate the stepping value */
1097 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1098 if(Pitch > (ALfloat)MAX_PITCH)
1099 voice->Step = MAX_PITCH<<FRACTIONBITS;
1100 else
1101 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1102 if(props->Resampler == BSinc24Resampler)
1103 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1104 else if(props->Resampler == BSinc12Resampler)
1105 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1106 voice->Resampler = SelectResampler(props->Resampler);
1108 /* Calculate gains */
1109 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1110 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1111 DryGain = minf(DryGain, GAIN_MIX_MAX);
1112 DryGainHF = props->Direct.GainHF;
1113 DryGainLF = props->Direct.GainLF;
1114 for(i = 0;i < Device->NumAuxSends;i++)
1116 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1117 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1118 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1119 WetGainHF[i] = props->Send[i].GainHF;
1120 WetGainLF[i] = props->Send[i].GainLF;
1123 CalcPanningAndFilters(voice, 0.0f, 0.0f, 0.0f, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1124 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1127 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1129 const ALCdevice *Device = ALContext->Device;
1130 const ALlistener *Listener = ALContext->Listener;
1131 const ALsizei NumSends = Device->NumAuxSends;
1132 aluVector Position, Velocity, Direction, SourceToListener;
1133 ALfloat Distance, ClampedDist, DopplerFactor;
1134 ALeffectslot *SendSlots[MAX_SENDS];
1135 ALfloat RoomRolloff[MAX_SENDS];
1136 ALfloat DecayDistance[MAX_SENDS];
1137 ALfloat DecayLFDistance[MAX_SENDS];
1138 ALfloat DecayHFDistance[MAX_SENDS];
1139 ALfloat DryGain, DryGainHF, DryGainLF;
1140 ALfloat WetGain[MAX_SENDS];
1141 ALfloat WetGainHF[MAX_SENDS];
1142 ALfloat WetGainLF[MAX_SENDS];
1143 bool directional;
1144 ALfloat ev, az;
1145 ALfloat spread;
1146 ALfloat Pitch;
1147 ALint i;
1149 /* Set mixing buffers and get send parameters. */
1150 voice->Direct.Buffer = Device->Dry.Buffer;
1151 voice->Direct.Channels = Device->Dry.NumChannels;
1152 for(i = 0;i < NumSends;i++)
1154 SendSlots[i] = props->Send[i].Slot;
1155 if(!SendSlots[i] && i == 0)
1156 SendSlots[i] = ALContext->DefaultSlot;
1157 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1159 SendSlots[i] = NULL;
1160 RoomRolloff[i] = 0.0f;
1161 DecayDistance[i] = 0.0f;
1162 DecayLFDistance[i] = 0.0f;
1163 DecayHFDistance[i] = 0.0f;
1165 else if(SendSlots[i]->Params.AuxSendAuto)
1167 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1168 /* Calculate the distances to where this effect's decay reaches
1169 * -60dB.
1171 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1172 Listener->Params.ReverbSpeedOfSound;
1173 DecayLFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayLFRatio;
1174 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1175 if(SendSlots[i]->Params.DecayHFLimit)
1177 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1178 if(airAbsorption < 1.0f)
1180 /* Calculate the distance to where this effect's air
1181 * absorption reaches -60dB, and limit the effect's HF
1182 * decay distance (so it doesn't take any longer to decay
1183 * than the air would allow).
1185 ALfloat absorb_dist = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1186 DecayHFDistance[i] = minf(absorb_dist, DecayHFDistance[i]);
1190 else
1192 /* If the slot's auxiliary send auto is off, the data sent to the
1193 * effect slot is the same as the dry path, sans filter effects */
1194 RoomRolloff[i] = props->RolloffFactor;
1195 DecayDistance[i] = 0.0f;
1196 DecayLFDistance[i] = 0.0f;
1197 DecayHFDistance[i] = 0.0f;
1200 if(!SendSlots[i])
1202 voice->Send[i].Buffer = NULL;
1203 voice->Send[i].Channels = 0;
1205 else
1207 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1208 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1212 /* Transform source to listener space (convert to head relative) */
1213 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1214 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1215 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1216 if(props->HeadRelative == AL_FALSE)
1218 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1219 /* Transform source vectors */
1220 Position = aluMatrixfVector(Matrix, &Position);
1221 Velocity = aluMatrixfVector(Matrix, &Velocity);
1222 Direction = aluMatrixfVector(Matrix, &Direction);
1224 else
1226 const aluVector *lvelocity = &Listener->Params.Velocity;
1227 /* Offset the source velocity to be relative of the listener velocity */
1228 Velocity.v[0] += lvelocity->v[0];
1229 Velocity.v[1] += lvelocity->v[1];
1230 Velocity.v[2] += lvelocity->v[2];
1233 directional = aluNormalize(Direction.v) > 0.0f;
1234 SourceToListener.v[0] = -Position.v[0];
1235 SourceToListener.v[1] = -Position.v[1];
1236 SourceToListener.v[2] = -Position.v[2];
1237 SourceToListener.v[3] = 0.0f;
1238 Distance = aluNormalize(SourceToListener.v);
1240 /* Initial source gain */
1241 DryGain = props->Gain;
1242 DryGainHF = 1.0f;
1243 DryGainLF = 1.0f;
1244 for(i = 0;i < NumSends;i++)
1246 WetGain[i] = props->Gain;
1247 WetGainHF[i] = 1.0f;
1248 WetGainLF[i] = 1.0f;
1251 /* Calculate distance attenuation */
1252 ClampedDist = Distance;
1254 switch(Listener->Params.SourceDistanceModel ?
1255 props->DistanceModel : Listener->Params.DistanceModel)
1257 case InverseDistanceClamped:
1258 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1259 if(props->MaxDistance < props->RefDistance)
1260 break;
1261 /*fall-through*/
1262 case InverseDistance:
1263 if(!(props->RefDistance > 0.0f))
1264 ClampedDist = props->RefDistance;
1265 else
1267 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1268 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1269 for(i = 0;i < NumSends;i++)
1271 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1272 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1275 break;
1277 case LinearDistanceClamped:
1278 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1279 if(props->MaxDistance < props->RefDistance)
1280 break;
1281 /*fall-through*/
1282 case LinearDistance:
1283 if(!(props->MaxDistance != props->RefDistance))
1284 ClampedDist = props->RefDistance;
1285 else
1287 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1288 (props->MaxDistance-props->RefDistance);
1289 DryGain *= maxf(1.0f - attn, 0.0f);
1290 for(i = 0;i < NumSends;i++)
1292 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1293 (props->MaxDistance-props->RefDistance);
1294 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1297 break;
1299 case ExponentDistanceClamped:
1300 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1301 if(props->MaxDistance < props->RefDistance)
1302 break;
1303 /*fall-through*/
1304 case ExponentDistance:
1305 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1306 ClampedDist = props->RefDistance;
1307 else
1309 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1310 for(i = 0;i < NumSends;i++)
1311 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1313 break;
1315 case DisableDistance:
1316 ClampedDist = props->RefDistance;
1317 break;
1320 /* Calculate directional soundcones */
1321 if(directional && props->InnerAngle < 360.0f)
1323 ALfloat ConeVolume;
1324 ALfloat ConeHF;
1325 ALfloat Angle;
1327 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1328 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1329 if(!(Angle > props->InnerAngle))
1331 ConeVolume = 1.0f;
1332 ConeHF = 1.0f;
1334 else if(Angle < props->OuterAngle)
1336 ALfloat scale = ( Angle-props->InnerAngle) /
1337 (props->OuterAngle-props->InnerAngle);
1338 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1339 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1341 else
1343 ConeVolume = props->OuterGain;
1344 ConeHF = props->OuterGainHF;
1347 DryGain *= ConeVolume;
1348 if(props->DryGainHFAuto)
1349 DryGainHF *= ConeHF;
1350 if(props->WetGainAuto)
1352 for(i = 0;i < NumSends;i++)
1353 WetGain[i] *= ConeVolume;
1355 if(props->WetGainHFAuto)
1357 for(i = 0;i < NumSends;i++)
1358 WetGainHF[i] *= ConeHF;
1362 /* Apply gain and frequency filters */
1363 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1364 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1365 DryGainHF *= props->Direct.GainHF;
1366 DryGainLF *= props->Direct.GainLF;
1367 for(i = 0;i < NumSends;i++)
1369 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1370 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1371 WetGainHF[i] *= props->Send[i].GainHF;
1372 WetGainLF[i] *= props->Send[i].GainLF;
1375 /* Distance-based air absorption and initial send decay. */
1376 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1378 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1379 Listener->Params.MetersPerUnit;
1380 if(props->AirAbsorptionFactor > 0.0f)
1382 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1383 DryGainHF *= hfattn;
1384 for(i = 0;i < NumSends;i++)
1385 WetGainHF[i] *= hfattn;
1388 if(props->WetGainAuto)
1390 /* Apply a decay-time transformation to the wet path, based on the
1391 * source distance in meters. The initial decay of the reverb
1392 * effect is calculated and applied to the wet path.
1394 for(i = 0;i < NumSends;i++)
1396 ALfloat gain, gainhf, gainlf;
1398 if(!(DecayDistance[i] > 0.0f))
1399 continue;
1401 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1402 WetGain[i] *= gain;
1403 /* Yes, the wet path's air absorption is applied with
1404 * WetGainAuto on, rather than WetGainHFAuto.
1406 if(gain > 0.0f)
1408 gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1409 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1410 gainlf = powf(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i]);
1411 WetGainLF[i] *= minf(gainlf / gain, 1.0f);
1418 /* Initial source pitch */
1419 Pitch = props->Pitch;
1421 /* Calculate velocity-based doppler effect */
1422 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1423 if(DopplerFactor > 0.0f)
1425 const aluVector *lvelocity = &Listener->Params.Velocity;
1426 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1427 ALfloat vss, vls;
1429 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1430 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1432 if(!(vls < SpeedOfSound))
1434 /* Listener moving away from the source at the speed of sound.
1435 * Sound waves can't catch it.
1437 Pitch = 0.0f;
1439 else if(!(vss < SpeedOfSound))
1441 /* Source moving toward the listener at the speed of sound. Sound
1442 * waves bunch up to extreme frequencies.
1444 Pitch = HUGE_VALF;
1446 else
1448 /* Source and listener movement is nominal. Calculate the proper
1449 * doppler shift.
1451 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1455 /* Adjust pitch based on the buffer and output frequencies, and calculate
1456 * fixed-point stepping value.
1458 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1459 if(Pitch > (ALfloat)MAX_PITCH)
1460 voice->Step = MAX_PITCH<<FRACTIONBITS;
1461 else
1462 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1463 if(props->Resampler == BSinc24Resampler)
1464 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1465 else if(props->Resampler == BSinc12Resampler)
1466 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1467 voice->Resampler = SelectResampler(props->Resampler);
1469 if(Distance > 0.0f)
1471 /* Clamp Y, in case rounding errors caused it to end up outside of
1472 * -1...+1.
1474 ev = asinf(clampf(-SourceToListener.v[1], -1.0f, 1.0f));
1475 /* Double negation on Z cancels out; negate once for changing source-
1476 * to-listener to listener-to-source, and again for right-handed coords
1477 * with -Z in front.
1479 az = atan2f(-SourceToListener.v[0], SourceToListener.v[2]*ZScale);
1481 else
1482 ev = az = 0.0f;
1484 if(props->Radius > Distance)
1485 spread = F_TAU - Distance/props->Radius*F_PI;
1486 else if(Distance > 0.0f)
1487 spread = asinf(props->Radius / Distance) * 2.0f;
1488 else
1489 spread = 0.0f;
1491 CalcPanningAndFilters(voice, az, ev, Distance, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1492 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1495 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1497 ALbufferlistitem *BufferListItem;
1498 struct ALvoiceProps *props;
1500 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1501 if(!props && !force) return;
1503 if(props)
1505 memcpy(voice->Props, props,
1506 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1509 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1511 props = voice->Props;
1513 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1514 while(BufferListItem != NULL)
1516 const ALbuffer *buffer = NULL;
1517 ALsizei i = 0;
1518 while(!buffer && i < BufferListItem->num_buffers)
1519 buffer = BufferListItem->buffers[i];
1520 if(LIKELY(buffer))
1522 if(props->SpatializeMode == SpatializeOn ||
1523 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1524 CalcAttnSourceParams(voice, props, buffer, context);
1525 else
1526 CalcNonAttnSourceParams(voice, props, buffer, context);
1527 break;
1529 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1534 static void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1536 ALvoice **voice, **voice_end;
1537 ALsource *source;
1538 ALsizei i;
1540 IncrementRef(&ctx->UpdateCount);
1541 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1543 bool cforce = CalcContextParams(ctx);
1544 bool force = CalcListenerParams(ctx) | cforce;
1545 for(i = 0;i < slots->count;i++)
1546 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1548 voice = ctx->Voices;
1549 voice_end = voice + ctx->VoiceCount;
1550 for(;voice != voice_end;++voice)
1552 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1553 if(source) CalcSourceParams(*voice, ctx, force);
1556 IncrementRef(&ctx->UpdateCount);
1560 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1561 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1562 ALsizei NumChannels)
1564 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1565 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1566 ALsizei i;
1568 /* Apply an all-pass to all channels, except the front-left and front-
1569 * right, so they maintain the same relative phase.
1571 for(i = 0;i < NumChannels;i++)
1573 if(i == lidx || i == ridx)
1574 continue;
1575 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1578 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1579 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1581 for(i = 0;i < SamplesToDo;i++)
1583 ALfloat lfsum, hfsum;
1584 ALfloat m, s, c;
1586 lfsum = lsplit[0][i] + rsplit[0][i];
1587 hfsum = lsplit[1][i] + rsplit[1][i];
1588 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1590 /* This pans the separate low- and high-frequency sums between being on
1591 * the center channel and the left/right channels. The low-frequency
1592 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1593 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1594 * values can be tweaked.
1596 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1597 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1599 /* The generated center channel signal adds to the existing signal,
1600 * while the modified left and right channels replace.
1602 Buffer[lidx][i] = (m + s) * 0.5f;
1603 Buffer[ridx][i] = (m - s) * 0.5f;
1604 Buffer[cidx][i] += c * 0.5f;
1608 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1609 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1611 ALsizei i, c;
1613 Values = ASSUME_ALIGNED(Values, 16);
1614 for(c = 0;c < numchans;c++)
1616 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1617 const ALfloat gain = distcomp[c].Gain;
1618 const ALsizei base = distcomp[c].Length;
1619 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1621 if(base == 0)
1623 if(gain < 1.0f)
1625 for(i = 0;i < SamplesToDo;i++)
1626 inout[i] *= gain;
1628 continue;
1631 if(SamplesToDo >= base)
1633 for(i = 0;i < base;i++)
1634 Values[i] = distbuf[i];
1635 for(;i < SamplesToDo;i++)
1636 Values[i] = inout[i-base];
1637 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1639 else
1641 for(i = 0;i < SamplesToDo;i++)
1642 Values[i] = distbuf[i];
1643 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1644 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1646 for(i = 0;i < SamplesToDo;i++)
1647 inout[i] = Values[i]*gain;
1651 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1652 const ALfloat quant_scale, const ALsizei SamplesToDo,
1653 const ALsizei numchans)
1655 const ALfloat invscale = 1.0f / quant_scale;
1656 ALuint seed = *dither_seed;
1657 ALsizei c, i;
1659 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1660 * values between -1 and +1). Step 2 is to add the noise to the samples,
1661 * before rounding and after scaling up to the desired quantization depth.
1663 for(c = 0;c < numchans;c++)
1665 ALfloat *restrict samples = Samples[c];
1666 for(i = 0;i < SamplesToDo;i++)
1668 ALfloat val = samples[i] * quant_scale;
1669 ALuint rng0 = dither_rng(&seed);
1670 ALuint rng1 = dither_rng(&seed);
1671 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1672 samples[i] = roundf(val) * invscale;
1675 *dither_seed = seed;
1679 static inline ALfloat Conv_ALfloat(ALfloat val)
1680 { return val; }
1681 static inline ALint Conv_ALint(ALfloat val)
1683 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1684 * integer range normalized floats can be safely converted to (a bit of the
1685 * exponent helps out, effectively giving 25 bits).
1687 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1689 static inline ALshort Conv_ALshort(ALfloat val)
1690 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1691 static inline ALbyte Conv_ALbyte(ALfloat val)
1692 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1694 /* Define unsigned output variations. */
1695 #define DECL_TEMPLATE(T, func, O) \
1696 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1698 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1699 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1700 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1702 #undef DECL_TEMPLATE
1704 #define DECL_TEMPLATE(T, A) \
1705 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1706 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1707 ALsizei numchans) \
1709 ALsizei i, j; \
1710 for(j = 0;j < numchans;j++) \
1712 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1713 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1715 for(i = 0;i < SamplesToDo;i++) \
1716 out[i*numchans] = Conv_##T(in[i]); \
1720 DECL_TEMPLATE(ALfloat, F32)
1721 DECL_TEMPLATE(ALuint, UI32)
1722 DECL_TEMPLATE(ALint, I32)
1723 DECL_TEMPLATE(ALushort, UI16)
1724 DECL_TEMPLATE(ALshort, I16)
1725 DECL_TEMPLATE(ALubyte, UI8)
1726 DECL_TEMPLATE(ALbyte, I8)
1728 #undef DECL_TEMPLATE
1731 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1733 ALsizei SamplesToDo;
1734 ALsizei SamplesDone;
1735 ALCcontext *ctx;
1736 ALsizei i, c;
1738 START_MIXER_MODE();
1739 for(SamplesDone = 0;SamplesDone < NumSamples;)
1741 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1742 for(c = 0;c < device->Dry.NumChannels;c++)
1743 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1744 if(device->Dry.Buffer != device->FOAOut.Buffer)
1745 for(c = 0;c < device->FOAOut.NumChannels;c++)
1746 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1747 if(device->Dry.Buffer != device->RealOut.Buffer)
1748 for(c = 0;c < device->RealOut.NumChannels;c++)
1749 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1751 IncrementRef(&device->MixCount);
1753 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1754 while(ctx)
1756 const struct ALeffectslotArray *auxslots;
1758 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1759 ProcessParamUpdates(ctx, auxslots);
1761 for(i = 0;i < auxslots->count;i++)
1763 ALeffectslot *slot = auxslots->slot[i];
1764 for(c = 0;c < slot->NumChannels;c++)
1765 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1768 /* source processing */
1769 for(i = 0;i < ctx->VoiceCount;i++)
1771 ALvoice *voice = ctx->Voices[i];
1772 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1773 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1774 voice->Step > 0)
1776 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1778 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1779 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1780 SendSourceStoppedEvent(ctx, source->id);
1785 /* effect slot processing */
1786 for(i = 0;i < auxslots->count;i++)
1788 const ALeffectslot *slot = auxslots->slot[i];
1789 ALeffectState *state = slot->Params.EffectState;
1790 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1791 state->OutChannels);
1794 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
1797 /* Increment the clock time. Every second's worth of samples is
1798 * converted and added to clock base so that large sample counts don't
1799 * overflow during conversion. This also guarantees an exact, stable
1800 * conversion. */
1801 device->SamplesDone += SamplesToDo;
1802 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1803 device->SamplesDone %= device->Frequency;
1804 IncrementRef(&device->MixCount);
1806 /* Apply post-process for finalizing the Dry mix to the RealOut
1807 * (Ambisonic decode, UHJ encode, etc).
1809 if(LIKELY(device->PostProcess))
1810 device->PostProcess(device, SamplesToDo);
1812 if(device->Stablizer)
1814 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1815 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1816 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1817 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1819 ApplyStablizer(device->Stablizer, device->RealOut.Buffer, lidx, ridx, cidx,
1820 SamplesToDo, device->RealOut.NumChannels);
1823 ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
1824 SamplesToDo, device->RealOut.NumChannels);
1826 if(device->Limiter)
1827 ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
1828 device->RealOut.Buffer);
1830 if(device->DitherDepth > 0.0f)
1831 ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
1832 SamplesToDo, device->RealOut.NumChannels);
1834 if(OutBuffer)
1836 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1837 ALsizei Channels = device->RealOut.NumChannels;
1839 switch(device->FmtType)
1841 case DevFmtByte:
1842 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1843 break;
1844 case DevFmtUByte:
1845 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1846 break;
1847 case DevFmtShort:
1848 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1849 break;
1850 case DevFmtUShort:
1851 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1852 break;
1853 case DevFmtInt:
1854 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1855 break;
1856 case DevFmtUInt:
1857 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1858 break;
1859 case DevFmtFloat:
1860 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1861 break;
1865 SamplesDone += SamplesToDo;
1867 END_MIXER_MODE();
1871 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1873 ALCcontext *ctx;
1874 AsyncEvent evt;
1875 va_list args;
1876 int msglen;
1878 if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
1879 return;
1881 evt.EnumType = EventType_Disconnected;
1882 evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1883 evt.ObjectId = 0;
1884 evt.Param = 0;
1886 va_start(args, msg);
1887 msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
1888 va_end(args);
1890 if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
1892 evt.Message[sizeof(evt.Message)-1] = 0;
1893 msglen = (int)strlen(evt.Message);
1895 if(msglen > 0)
1896 msg = evt.Message;
1897 else
1899 msg = "<internal error constructing message>";
1900 msglen = (int)strlen(msg);
1903 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1904 while(ctx)
1906 ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire);
1907 ALsizei i;
1909 if((enabledevt&EventType_Disconnected) &&
1910 ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1) == 1)
1911 alsem_post(&ctx->EventSem);
1913 for(i = 0;i < ctx->VoiceCount;i++)
1915 ALvoice *voice = ctx->Voices[i];
1916 ALsource *source;
1918 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_relaxed);
1919 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed))
1921 /* If the source's voice was playing, it's now effectively
1922 * stopped (the source state will be updated the next time it's
1923 * checked).
1925 SendSourceStoppedEvent(ctx, source->id);
1927 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1930 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);