Combine nearly-duplicate structures
[openal-soft.git] / Alc / ALu.c
blob4d966ba3a5f63271599773119b02f8956566c785
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->NumChannelsPerOrder[i];
648 voice->Flags |= VOICE_HAS_NFC;
651 /* A scalar of 1.5 for plain stereo results in +/-60 degrees being
652 * moved to +/-90 degrees for direct right and left speaker
653 * responses.
655 CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
656 Elev, Spread, coeffs);
658 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
659 ComputeDryPanGains(&Device->Dry, coeffs, DryGain*SQRTF_2,
660 voice->Direct.Params[0].Gains.Target);
661 for(i = 0;i < NumSends;i++)
663 const ALeffectslot *Slot = SendSlots[i];
664 if(Slot)
665 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
666 WetGain[i]*SQRTF_2, voice->Send[i].Params[0].Gains.Target
670 else
672 /* Local B-Format sources have their XYZ channels rotated according
673 * to the orientation.
675 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 SQRTF_2, 0.0f, 0.0f, 0.0f, // Ambi W
719 0.0f, -N[0]*SQRTF_3, N[1]*SQRTF_3, -N[2]*SQRTF_3, // Ambi X
720 0.0f, U[0]*SQRTF_3, -U[1]*SQRTF_3, U[2]*SQRTF_3, // Ambi Y
721 0.0f, -V[0]*SQRTF_3, V[1]*SQRTF_3, -V[2]*SQRTF_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 ComputePanningGainsBF(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->NumChannelsPerOrder[i];
892 voice->Flags |= VOICE_HAS_NFC;
895 /* Calculate the directional coefficients once, which apply to all
896 * input channels.
898 CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
899 Elev, Spread, coeffs);
901 for(c = 0;c < num_channels;c++)
903 /* Special-case LFE */
904 if(chans[c].channel == LFE)
906 if(Device->Dry.Buffer == Device->RealOut.Buffer)
908 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
909 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
911 continue;
914 ComputeDryPanGains(&Device->Dry,
915 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
919 for(i = 0;i < NumSends;i++)
921 const ALeffectslot *Slot = SendSlots[i];
922 if(Slot)
923 for(c = 0;c < num_channels;c++)
925 /* Skip LFE */
926 if(chans[c].channel != LFE)
927 ComputePanningGainsBF(Slot->ChanMap,
928 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
929 voice->Send[i].Params[c].Gains.Target
934 else
936 ALfloat w0 = 0.0f;
938 if(Device->AvgSpeakerDist > 0.0f)
940 /* If the source distance is 0, set w0 to w1 to act as a pass-
941 * through. We still want to pass the signal through the
942 * filters so they keep an appropriate history, in case the
943 * source moves away from the listener.
945 w0 = SPEEDOFSOUNDMETRESPERSEC /
946 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
948 for(c = 0;c < num_channels;c++)
949 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
951 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
952 voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
953 voice->Flags |= VOICE_HAS_NFC;
956 for(c = 0;c < num_channels;c++)
958 ALfloat coeffs[MAX_AMBI_COEFFS];
960 /* Special-case LFE */
961 if(chans[c].channel == LFE)
963 if(Device->Dry.Buffer == Device->RealOut.Buffer)
965 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
966 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
968 continue;
971 CalcAngleCoeffs(
972 (Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(chans[c].angle, 3.0f)
973 : chans[c].angle,
974 chans[c].elevation, Spread, coeffs
977 ComputeDryPanGains(&Device->Dry,
978 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
980 for(i = 0;i < NumSends;i++)
982 const ALeffectslot *Slot = SendSlots[i];
983 if(Slot)
984 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
985 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
993 ALfloat hfScale = props->Direct.HFReference / Frequency;
994 ALfloat lfScale = props->Direct.LFReference / Frequency;
995 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
996 ALfloat gainLF = maxf(DryGainLF, 0.001f);
998 voice->Direct.FilterType = AF_None;
999 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
1000 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
1001 BiquadFilter_setParams(
1002 &voice->Direct.Params[0].LowPass, BiquadType_HighShelf,
1003 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1005 BiquadFilter_setParams(
1006 &voice->Direct.Params[0].HighPass, BiquadType_LowShelf,
1007 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1009 for(c = 1;c < num_channels;c++)
1011 BiquadFilter_copyParams(&voice->Direct.Params[c].LowPass,
1012 &voice->Direct.Params[0].LowPass);
1013 BiquadFilter_copyParams(&voice->Direct.Params[c].HighPass,
1014 &voice->Direct.Params[0].HighPass);
1017 for(i = 0;i < NumSends;i++)
1019 ALfloat hfScale = props->Send[i].HFReference / Frequency;
1020 ALfloat lfScale = props->Send[i].LFReference / Frequency;
1021 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
1022 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
1024 voice->Send[i].FilterType = AF_None;
1025 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1026 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1027 BiquadFilter_setParams(
1028 &voice->Send[i].Params[0].LowPass, BiquadType_HighShelf,
1029 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1031 BiquadFilter_setParams(
1032 &voice->Send[i].Params[0].HighPass, BiquadType_LowShelf,
1033 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1035 for(c = 1;c < num_channels;c++)
1037 BiquadFilter_copyParams(&voice->Send[i].Params[c].LowPass,
1038 &voice->Send[i].Params[0].LowPass);
1039 BiquadFilter_copyParams(&voice->Send[i].Params[c].HighPass,
1040 &voice->Send[i].Params[0].HighPass);
1045 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1047 const ALCdevice *Device = ALContext->Device;
1048 const ALlistener *Listener = ALContext->Listener;
1049 ALfloat DryGain, DryGainHF, DryGainLF;
1050 ALfloat WetGain[MAX_SENDS];
1051 ALfloat WetGainHF[MAX_SENDS];
1052 ALfloat WetGainLF[MAX_SENDS];
1053 ALeffectslot *SendSlots[MAX_SENDS];
1054 ALfloat Pitch;
1055 ALsizei i;
1057 voice->Direct.Buffer = Device->Dry.Buffer;
1058 voice->Direct.Channels = Device->Dry.NumChannels;
1059 for(i = 0;i < Device->NumAuxSends;i++)
1061 SendSlots[i] = props->Send[i].Slot;
1062 if(!SendSlots[i] && i == 0)
1063 SendSlots[i] = ALContext->DefaultSlot;
1064 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1066 SendSlots[i] = NULL;
1067 voice->Send[i].Buffer = NULL;
1068 voice->Send[i].Channels = 0;
1070 else
1072 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1073 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1077 /* Calculate the stepping value */
1078 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1079 if(Pitch > (ALfloat)MAX_PITCH)
1080 voice->Step = MAX_PITCH<<FRACTIONBITS;
1081 else
1082 voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
1083 if(props->Resampler == BSinc24Resampler)
1084 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1085 else if(props->Resampler == BSinc12Resampler)
1086 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1087 voice->Resampler = SelectResampler(props->Resampler);
1089 /* Calculate gains */
1090 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1091 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1092 DryGain = minf(DryGain, GAIN_MIX_MAX);
1093 DryGainHF = props->Direct.GainHF;
1094 DryGainLF = props->Direct.GainLF;
1095 for(i = 0;i < Device->NumAuxSends;i++)
1097 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1098 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1099 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1100 WetGainHF[i] = props->Send[i].GainHF;
1101 WetGainLF[i] = props->Send[i].GainLF;
1104 CalcPanningAndFilters(voice, 0.0f, 0.0f, 0.0f, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1105 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1108 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1110 const ALCdevice *Device = ALContext->Device;
1111 const ALlistener *Listener = ALContext->Listener;
1112 const ALsizei NumSends = Device->NumAuxSends;
1113 aluVector Position, Velocity, Direction, SourceToListener;
1114 ALfloat Distance, ClampedDist, DopplerFactor;
1115 ALeffectslot *SendSlots[MAX_SENDS];
1116 ALfloat RoomRolloff[MAX_SENDS];
1117 ALfloat DecayDistance[MAX_SENDS];
1118 ALfloat DecayLFDistance[MAX_SENDS];
1119 ALfloat DecayHFDistance[MAX_SENDS];
1120 ALfloat DryGain, DryGainHF, DryGainLF;
1121 ALfloat WetGain[MAX_SENDS];
1122 ALfloat WetGainHF[MAX_SENDS];
1123 ALfloat WetGainLF[MAX_SENDS];
1124 bool directional;
1125 ALfloat ev, az;
1126 ALfloat spread;
1127 ALfloat Pitch;
1128 ALint i;
1130 /* Set mixing buffers and get send parameters. */
1131 voice->Direct.Buffer = Device->Dry.Buffer;
1132 voice->Direct.Channels = Device->Dry.NumChannels;
1133 for(i = 0;i < NumSends;i++)
1135 SendSlots[i] = props->Send[i].Slot;
1136 if(!SendSlots[i] && i == 0)
1137 SendSlots[i] = ALContext->DefaultSlot;
1138 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1140 SendSlots[i] = NULL;
1141 RoomRolloff[i] = 0.0f;
1142 DecayDistance[i] = 0.0f;
1143 DecayLFDistance[i] = 0.0f;
1144 DecayHFDistance[i] = 0.0f;
1146 else if(SendSlots[i]->Params.AuxSendAuto)
1148 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1149 /* Calculate the distances to where this effect's decay reaches
1150 * -60dB.
1152 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1153 Listener->Params.ReverbSpeedOfSound;
1154 DecayLFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayLFRatio;
1155 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1156 if(SendSlots[i]->Params.DecayHFLimit)
1158 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1159 if(airAbsorption < 1.0f)
1161 /* Calculate the distance to where this effect's air
1162 * absorption reaches -60dB, and limit the effect's HF
1163 * decay distance (so it doesn't take any longer to decay
1164 * than the air would allow).
1166 ALfloat absorb_dist = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1167 DecayHFDistance[i] = minf(absorb_dist, DecayHFDistance[i]);
1171 else
1173 /* If the slot's auxiliary send auto is off, the data sent to the
1174 * effect slot is the same as the dry path, sans filter effects */
1175 RoomRolloff[i] = props->RolloffFactor;
1176 DecayDistance[i] = 0.0f;
1177 DecayLFDistance[i] = 0.0f;
1178 DecayHFDistance[i] = 0.0f;
1181 if(!SendSlots[i])
1183 voice->Send[i].Buffer = NULL;
1184 voice->Send[i].Channels = 0;
1186 else
1188 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1189 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1193 /* Transform source to listener space (convert to head relative) */
1194 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1195 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1196 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1197 if(props->HeadRelative == AL_FALSE)
1199 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1200 /* Transform source vectors */
1201 Position = aluMatrixfVector(Matrix, &Position);
1202 Velocity = aluMatrixfVector(Matrix, &Velocity);
1203 Direction = aluMatrixfVector(Matrix, &Direction);
1205 else
1207 const aluVector *lvelocity = &Listener->Params.Velocity;
1208 /* Offset the source velocity to be relative of the listener velocity */
1209 Velocity.v[0] += lvelocity->v[0];
1210 Velocity.v[1] += lvelocity->v[1];
1211 Velocity.v[2] += lvelocity->v[2];
1214 directional = aluNormalize(Direction.v) > 0.0f;
1215 SourceToListener.v[0] = -Position.v[0];
1216 SourceToListener.v[1] = -Position.v[1];
1217 SourceToListener.v[2] = -Position.v[2];
1218 SourceToListener.v[3] = 0.0f;
1219 Distance = aluNormalize(SourceToListener.v);
1221 /* Initial source gain */
1222 DryGain = props->Gain;
1223 DryGainHF = 1.0f;
1224 DryGainLF = 1.0f;
1225 for(i = 0;i < NumSends;i++)
1227 WetGain[i] = props->Gain;
1228 WetGainHF[i] = 1.0f;
1229 WetGainLF[i] = 1.0f;
1232 /* Calculate distance attenuation */
1233 ClampedDist = Distance;
1235 switch(Listener->Params.SourceDistanceModel ?
1236 props->DistanceModel : Listener->Params.DistanceModel)
1238 case InverseDistanceClamped:
1239 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1240 if(props->MaxDistance < props->RefDistance)
1241 break;
1242 /*fall-through*/
1243 case InverseDistance:
1244 if(!(props->RefDistance > 0.0f))
1245 ClampedDist = props->RefDistance;
1246 else
1248 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1249 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1250 for(i = 0;i < NumSends;i++)
1252 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1253 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1256 break;
1258 case LinearDistanceClamped:
1259 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1260 if(props->MaxDistance < props->RefDistance)
1261 break;
1262 /*fall-through*/
1263 case LinearDistance:
1264 if(!(props->MaxDistance != props->RefDistance))
1265 ClampedDist = props->RefDistance;
1266 else
1268 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1269 (props->MaxDistance-props->RefDistance);
1270 DryGain *= maxf(1.0f - attn, 0.0f);
1271 for(i = 0;i < NumSends;i++)
1273 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1274 (props->MaxDistance-props->RefDistance);
1275 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1278 break;
1280 case ExponentDistanceClamped:
1281 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1282 if(props->MaxDistance < props->RefDistance)
1283 break;
1284 /*fall-through*/
1285 case ExponentDistance:
1286 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1287 ClampedDist = props->RefDistance;
1288 else
1290 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1291 for(i = 0;i < NumSends;i++)
1292 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1294 break;
1296 case DisableDistance:
1297 ClampedDist = props->RefDistance;
1298 break;
1301 /* Calculate directional soundcones */
1302 if(directional && props->InnerAngle < 360.0f)
1304 ALfloat ConeVolume;
1305 ALfloat ConeHF;
1306 ALfloat Angle;
1308 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1309 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1310 if(!(Angle > props->InnerAngle))
1312 ConeVolume = 1.0f;
1313 ConeHF = 1.0f;
1315 else if(Angle < props->OuterAngle)
1317 ALfloat scale = ( Angle-props->InnerAngle) /
1318 (props->OuterAngle-props->InnerAngle);
1319 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1320 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1322 else
1324 ConeVolume = props->OuterGain;
1325 ConeHF = props->OuterGainHF;
1328 DryGain *= ConeVolume;
1329 if(props->DryGainHFAuto)
1330 DryGainHF *= ConeHF;
1331 if(props->WetGainAuto)
1333 for(i = 0;i < NumSends;i++)
1334 WetGain[i] *= ConeVolume;
1336 if(props->WetGainHFAuto)
1338 for(i = 0;i < NumSends;i++)
1339 WetGainHF[i] *= ConeHF;
1343 /* Apply gain and frequency filters */
1344 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1345 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1346 DryGainHF *= props->Direct.GainHF;
1347 DryGainLF *= props->Direct.GainLF;
1348 for(i = 0;i < NumSends;i++)
1350 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1351 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1352 WetGainHF[i] *= props->Send[i].GainHF;
1353 WetGainLF[i] *= props->Send[i].GainLF;
1356 /* Distance-based air absorption and initial send decay. */
1357 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1359 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1360 Listener->Params.MetersPerUnit;
1361 if(props->AirAbsorptionFactor > 0.0f)
1363 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1364 DryGainHF *= hfattn;
1365 for(i = 0;i < NumSends;i++)
1366 WetGainHF[i] *= hfattn;
1369 if(props->WetGainAuto)
1371 /* Apply a decay-time transformation to the wet path, based on the
1372 * source distance in meters. The initial decay of the reverb
1373 * effect is calculated and applied to the wet path.
1375 for(i = 0;i < NumSends;i++)
1377 ALfloat gain, gainhf, gainlf;
1379 if(!(DecayDistance[i] > 0.0f))
1380 continue;
1382 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1383 WetGain[i] *= gain;
1384 /* Yes, the wet path's air absorption is applied with
1385 * WetGainAuto on, rather than WetGainHFAuto.
1387 if(gain > 0.0f)
1389 gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1390 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1391 gainlf = powf(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i]);
1392 WetGainLF[i] *= minf(gainlf / gain, 1.0f);
1399 /* Initial source pitch */
1400 Pitch = props->Pitch;
1402 /* Calculate velocity-based doppler effect */
1403 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1404 if(DopplerFactor > 0.0f)
1406 const aluVector *lvelocity = &Listener->Params.Velocity;
1407 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1408 ALfloat vss, vls;
1410 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1411 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1413 if(!(vls < SpeedOfSound))
1415 /* Listener moving away from the source at the speed of sound.
1416 * Sound waves can't catch it.
1418 Pitch = 0.0f;
1420 else if(!(vss < SpeedOfSound))
1422 /* Source moving toward the listener at the speed of sound. Sound
1423 * waves bunch up to extreme frequencies.
1425 Pitch = HUGE_VALF;
1427 else
1429 /* Source and listener movement is nominal. Calculate the proper
1430 * doppler shift.
1432 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1436 /* Adjust pitch based on the buffer and output frequencies, and calculate
1437 * fixed-point stepping value.
1439 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1440 if(Pitch > (ALfloat)MAX_PITCH)
1441 voice->Step = MAX_PITCH<<FRACTIONBITS;
1442 else
1443 voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
1444 if(props->Resampler == BSinc24Resampler)
1445 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1446 else if(props->Resampler == BSinc12Resampler)
1447 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1448 voice->Resampler = SelectResampler(props->Resampler);
1450 if(Distance > 0.0f)
1452 /* Clamp Y, in case rounding errors caused it to end up outside of
1453 * -1...+1.
1455 ev = asinf(clampf(-SourceToListener.v[1], -1.0f, 1.0f));
1456 /* Double negation on Z cancels out; negate once for changing source-
1457 * to-listener to listener-to-source, and again for right-handed coords
1458 * with -Z in front.
1460 az = atan2f(-SourceToListener.v[0], SourceToListener.v[2]*ZScale);
1462 else
1463 ev = az = 0.0f;
1465 if(props->Radius > Distance)
1466 spread = F_TAU - Distance/props->Radius*F_PI;
1467 else if(Distance > 0.0f)
1468 spread = asinf(props->Radius / Distance) * 2.0f;
1469 else
1470 spread = 0.0f;
1472 CalcPanningAndFilters(voice, az, ev, Distance, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1473 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1476 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1478 ALbufferlistitem *BufferListItem;
1479 struct ALvoiceProps *props;
1481 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1482 if(!props && !force) return;
1484 if(props)
1486 memcpy(voice->Props, props,
1487 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1490 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1492 props = voice->Props;
1494 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1495 while(BufferListItem != NULL)
1497 const ALbuffer *buffer = NULL;
1498 ALsizei i = 0;
1499 while(!buffer && i < BufferListItem->num_buffers)
1500 buffer = BufferListItem->buffers[i];
1501 if(LIKELY(buffer))
1503 if(props->SpatializeMode == SpatializeOn ||
1504 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1505 CalcAttnSourceParams(voice, props, buffer, context);
1506 else
1507 CalcNonAttnSourceParams(voice, props, buffer, context);
1508 break;
1510 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1515 static void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1517 ALvoice **voice, **voice_end;
1518 ALsource *source;
1519 ALsizei i;
1521 IncrementRef(&ctx->UpdateCount);
1522 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1524 bool cforce = CalcContextParams(ctx);
1525 bool force = CalcListenerParams(ctx) | cforce;
1526 for(i = 0;i < slots->count;i++)
1527 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1529 voice = ctx->Voices;
1530 voice_end = voice + ctx->VoiceCount;
1531 for(;voice != voice_end;++voice)
1533 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1534 if(source) CalcSourceParams(*voice, ctx, force);
1537 IncrementRef(&ctx->UpdateCount);
1541 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1542 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1543 ALsizei NumChannels)
1545 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1546 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1547 ALsizei i;
1549 /* Apply an all-pass to all channels, except the front-left and front-
1550 * right, so they maintain the same relative phase.
1552 for(i = 0;i < NumChannels;i++)
1554 if(i == lidx || i == ridx)
1555 continue;
1556 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1559 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1560 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1562 for(i = 0;i < SamplesToDo;i++)
1564 ALfloat lfsum, hfsum;
1565 ALfloat m, s, c;
1567 lfsum = lsplit[0][i] + rsplit[0][i];
1568 hfsum = lsplit[1][i] + rsplit[1][i];
1569 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1571 /* This pans the separate low- and high-frequency sums between being on
1572 * the center channel and the left/right channels. The low-frequency
1573 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1574 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1575 * values can be tweaked.
1577 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1578 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1580 /* The generated center channel signal adds to the existing signal,
1581 * while the modified left and right channels replace.
1583 Buffer[lidx][i] = (m + s) * 0.5f;
1584 Buffer[ridx][i] = (m - s) * 0.5f;
1585 Buffer[cidx][i] += c * 0.5f;
1589 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1590 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1592 ALsizei i, c;
1594 Values = ASSUME_ALIGNED(Values, 16);
1595 for(c = 0;c < numchans;c++)
1597 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1598 const ALfloat gain = distcomp[c].Gain;
1599 const ALsizei base = distcomp[c].Length;
1600 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1602 if(base == 0)
1604 if(gain < 1.0f)
1606 for(i = 0;i < SamplesToDo;i++)
1607 inout[i] *= gain;
1609 continue;
1612 if(LIKELY(SamplesToDo >= base))
1614 for(i = 0;i < base;i++)
1615 Values[i] = distbuf[i];
1616 for(;i < SamplesToDo;i++)
1617 Values[i] = inout[i-base];
1618 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1620 else
1622 for(i = 0;i < SamplesToDo;i++)
1623 Values[i] = distbuf[i];
1624 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1625 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1627 for(i = 0;i < SamplesToDo;i++)
1628 inout[i] = Values[i]*gain;
1632 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1633 const ALfloat quant_scale, const ALsizei SamplesToDo,
1634 const ALsizei numchans)
1636 const ALfloat invscale = 1.0f / quant_scale;
1637 ALuint seed = *dither_seed;
1638 ALsizei c, i;
1640 ASSUME(numchans > 0);
1641 ASSUME(SamplesToDo > 0);
1643 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1644 * values between -1 and +1). Step 2 is to add the noise to the samples,
1645 * before rounding and after scaling up to the desired quantization depth.
1647 for(c = 0;c < numchans;c++)
1649 ALfloat *restrict samples = Samples[c];
1650 for(i = 0;i < SamplesToDo;i++)
1652 ALfloat val = samples[i] * quant_scale;
1653 ALuint rng0 = dither_rng(&seed);
1654 ALuint rng1 = dither_rng(&seed);
1655 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1656 samples[i] = fast_roundf(val) * invscale;
1659 *dither_seed = seed;
1663 static inline ALfloat Conv_ALfloat(ALfloat val)
1664 { return val; }
1665 static inline ALint Conv_ALint(ALfloat val)
1667 /* Floats have a 23-bit mantissa. There is an implied 1 bit in the mantissa
1668 * along with the sign bit, giving 25 bits total, so [-16777216, +16777216]
1669 * is the max value a normalized float can be scaled to before losing
1670 * precision.
1672 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1674 static inline ALshort Conv_ALshort(ALfloat val)
1675 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1676 static inline ALbyte Conv_ALbyte(ALfloat val)
1677 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1679 /* Define unsigned output variations. */
1680 #define DECL_TEMPLATE(T, func, O) \
1681 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1683 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1684 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1685 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1687 #undef DECL_TEMPLATE
1689 #define DECL_TEMPLATE(T, A) \
1690 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1691 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1692 ALsizei numchans) \
1694 ALsizei i, j; \
1696 ASSUME(numchans > 0); \
1697 ASSUME(SamplesToDo > 0); \
1699 for(j = 0;j < numchans;j++) \
1701 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1702 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1704 for(i = 0;i < SamplesToDo;i++) \
1705 out[i*numchans] = Conv_##T(in[i]); \
1709 DECL_TEMPLATE(ALfloat, F32)
1710 DECL_TEMPLATE(ALuint, UI32)
1711 DECL_TEMPLATE(ALint, I32)
1712 DECL_TEMPLATE(ALushort, UI16)
1713 DECL_TEMPLATE(ALshort, I16)
1714 DECL_TEMPLATE(ALubyte, UI8)
1715 DECL_TEMPLATE(ALbyte, I8)
1717 #undef DECL_TEMPLATE
1720 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1722 ALsizei SamplesToDo;
1723 ALsizei SamplesDone;
1724 ALCcontext *ctx;
1725 ALsizei i, c;
1727 START_MIXER_MODE();
1728 for(SamplesDone = 0;SamplesDone < NumSamples;)
1730 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1731 for(c = 0;c < device->Dry.NumChannels;c++)
1732 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1733 if(device->Dry.Buffer != device->FOAOut.Buffer)
1734 for(c = 0;c < device->FOAOut.NumChannels;c++)
1735 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1736 if(device->Dry.Buffer != device->RealOut.Buffer)
1737 for(c = 0;c < device->RealOut.NumChannels;c++)
1738 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1740 IncrementRef(&device->MixCount);
1742 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1743 while(ctx)
1745 const struct ALeffectslotArray *auxslots;
1747 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1748 ProcessParamUpdates(ctx, auxslots);
1750 for(i = 0;i < auxslots->count;i++)
1752 ALeffectslot *slot = auxslots->slot[i];
1753 for(c = 0;c < slot->NumChannels;c++)
1754 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1757 /* source processing */
1758 for(i = 0;i < ctx->VoiceCount;i++)
1760 ALvoice *voice = ctx->Voices[i];
1761 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1762 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1763 voice->Step > 0)
1765 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1767 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1768 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1769 SendSourceStoppedEvent(ctx, source->id);
1774 /* effect slot processing */
1775 for(i = 0;i < auxslots->count;i++)
1777 const ALeffectslot *slot = auxslots->slot[i];
1778 ALeffectState *state = slot->Params.EffectState;
1779 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1780 state->OutChannels);
1783 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
1786 /* Increment the clock time. Every second's worth of samples is
1787 * converted and added to clock base so that large sample counts don't
1788 * overflow during conversion. This also guarantees an exact, stable
1789 * conversion. */
1790 device->SamplesDone += SamplesToDo;
1791 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1792 device->SamplesDone %= device->Frequency;
1793 IncrementRef(&device->MixCount);
1795 /* Apply post-process for finalizing the Dry mix to the RealOut
1796 * (Ambisonic decode, UHJ encode, etc).
1798 if(LIKELY(device->PostProcess))
1799 device->PostProcess(device, SamplesToDo);
1801 if(device->Stablizer)
1803 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1804 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1805 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1806 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1808 ApplyStablizer(device->Stablizer, device->RealOut.Buffer, lidx, ridx, cidx,
1809 SamplesToDo, device->RealOut.NumChannels);
1812 ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
1813 SamplesToDo, device->RealOut.NumChannels);
1815 if(device->Limiter)
1816 ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
1817 device->RealOut.Buffer);
1819 if(device->DitherDepth > 0.0f)
1820 ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
1821 SamplesToDo, device->RealOut.NumChannels);
1823 if(LIKELY(OutBuffer))
1825 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1826 ALsizei Channels = device->RealOut.NumChannels;
1828 switch(device->FmtType)
1830 #define HANDLE_WRITE(T, S) case T: \
1831 Write##S(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break;
1832 HANDLE_WRITE(DevFmtByte, I8)
1833 HANDLE_WRITE(DevFmtUByte, UI8)
1834 HANDLE_WRITE(DevFmtShort, I16)
1835 HANDLE_WRITE(DevFmtUShort, UI16)
1836 HANDLE_WRITE(DevFmtInt, I32)
1837 HANDLE_WRITE(DevFmtUInt, UI32)
1838 HANDLE_WRITE(DevFmtFloat, F32)
1839 #undef HANDLE_WRITE
1843 SamplesDone += SamplesToDo;
1845 END_MIXER_MODE();
1849 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1851 ALCcontext *ctx;
1852 AsyncEvent evt;
1853 va_list args;
1854 int msglen;
1856 if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
1857 return;
1859 evt.EnumType = EventType_Disconnected;
1860 evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1861 evt.ObjectId = 0;
1862 evt.Param = 0;
1864 va_start(args, msg);
1865 msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
1866 va_end(args);
1868 if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
1869 evt.Message[sizeof(evt.Message)-1] = 0;
1871 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1872 while(ctx)
1874 ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire);
1875 ALsizei i;
1877 if((enabledevt&EventType_Disconnected) &&
1878 ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1) == 1)
1879 alsem_post(&ctx->EventSem);
1881 for(i = 0;i < ctx->VoiceCount;i++)
1883 ALvoice *voice = ctx->Voices[i];
1884 ALsource *source;
1886 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_relaxed);
1887 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed))
1889 /* If the source's voice was playing, it's now effectively
1890 * stopped (the source state will be updated the next time it's
1891 * checked).
1893 SendSourceStoppedEvent(ctx, source->id);
1895 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1898 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);