Rename UpdateContextSources for clarity
[openal-soft.git] / Alc / ALu.c
blob7461dcbf0b0c8517e8a5c01c3c9e4471a961a3a1
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 "fpu_modes.h"
44 #include "cpu_caps.h"
45 #include "mixer_defs.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 struct ChanMap {
111 enum Channel channel;
112 ALfloat angle;
113 ALfloat elevation;
116 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
119 void DeinitVoice(ALvoice *voice)
121 al_free(ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL));
125 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
127 #ifdef HAVE_NEON
128 if((CPUCapFlags&CPU_CAP_NEON))
129 return MixDirectHrtf_Neon;
130 #endif
131 #ifdef HAVE_SSE
132 if((CPUCapFlags&CPU_CAP_SSE))
133 return MixDirectHrtf_SSE;
134 #endif
136 return MixDirectHrtf_C;
140 /* Prior to VS2013, MSVC lacks the round() family of functions. */
141 #if defined(_MSC_VER) && _MSC_VER < 1800
142 static float roundf(float val)
144 if(val < 0.0f)
145 return ceilf(val-0.5f);
146 return floorf(val+0.5f);
148 #endif
150 /* This RNG method was created based on the math found in opusdec. It's quick,
151 * and starting with a seed value of 22222, is suitable for generating
152 * whitenoise.
154 static inline ALuint dither_rng(ALuint *seed)
156 *seed = (*seed * 96314165) + 907633515;
157 return *seed;
161 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
163 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
164 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
165 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
168 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
170 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
173 static ALfloat aluNormalize(ALfloat *vec)
175 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
176 if(length > 0.0f)
178 ALfloat inv_length = 1.0f/length;
179 vec[0] *= inv_length;
180 vec[1] *= inv_length;
181 vec[2] *= inv_length;
183 return length;
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 if(LIKELY(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
308 static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
310 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
311 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
312 if(LIKELY(lidx != -1 && ridx != -1))
314 /* Apply binaural/crossfeed filter */
315 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
316 device->RealOut.Buffer[ridx], SamplesToDo);
320 void aluSelectPostProcess(ALCdevice *device)
322 if(device->HrtfHandle)
323 device->PostProcess = ProcessHrtf;
324 else if(device->AmbiDecoder)
325 device->PostProcess = ProcessAmbiDec;
326 else if(device->AmbiUp)
327 device->PostProcess = ProcessAmbiUp;
328 else if(device->Uhj_Encoder)
329 device->PostProcess = ProcessUhj;
330 else if(device->Bs2b)
331 device->PostProcess = ProcessBs2b;
332 else
333 device->PostProcess = NULL;
337 /* Prepares the interpolator for a given rate (determined by increment). A
338 * result of AL_FALSE indicates that the filter output will completely cut
339 * the input signal.
341 * With a bit of work, and a trade of memory for CPU cost, this could be
342 * modified for use with an interpolated increment for buttery-smooth pitch
343 * changes.
345 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
347 ALfloat sf;
348 ALsizei si;
350 if(increment > FRACTIONONE)
352 sf = (ALfloat)FRACTIONONE / increment;
353 sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
354 si = fastf2i(sf);
355 /* The interpolation factor is fit to this diagonally-symmetric curve
356 * to reduce the transition ripple caused by interpolating different
357 * scales of the sinc function.
359 sf = 1.0f - cosf(asinf(sf - si));
361 else
363 sf = 0.0f;
364 si = BSINC_SCALE_COUNT - 1;
367 state->sf = sf;
368 state->m = table->m[si];
369 state->l = -((state->m/2) - 1);
370 state->filter = table->Tab + table->filterOffset[si];
374 static bool CalcContextParams(ALCcontext *Context)
376 ALlistener *Listener = Context->Listener;
377 struct ALcontextProps *props;
379 props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel);
380 if(!props) return false;
382 Listener->Params.MetersPerUnit = props->MetersPerUnit;
384 Listener->Params.DopplerFactor = props->DopplerFactor;
385 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
386 if(!OverrideReverbSpeedOfSound)
387 Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
388 Listener->Params.MetersPerUnit;
390 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
391 Listener->Params.DistanceModel = props->DistanceModel;
393 ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props);
394 return true;
397 static bool CalcListenerParams(ALCcontext *Context)
399 ALlistener *Listener = Context->Listener;
400 ALfloat N[3], V[3], U[3], P[3];
401 struct ALlistenerProps *props;
402 aluVector vel;
404 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
405 if(!props) return false;
407 /* AT then UP */
408 N[0] = props->Forward[0];
409 N[1] = props->Forward[1];
410 N[2] = props->Forward[2];
411 aluNormalize(N);
412 V[0] = props->Up[0];
413 V[1] = props->Up[1];
414 V[2] = props->Up[2];
415 aluNormalize(V);
416 /* Build and normalize right-vector */
417 aluCrossproduct(N, V, U);
418 aluNormalize(U);
420 aluMatrixfSet(&Listener->Params.Matrix,
421 U[0], V[0], -N[0], 0.0,
422 U[1], V[1], -N[1], 0.0,
423 U[2], V[2], -N[2], 0.0,
424 0.0, 0.0, 0.0, 1.0
427 P[0] = props->Position[0];
428 P[1] = props->Position[1];
429 P[2] = props->Position[2];
430 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
431 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
433 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
434 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
436 Listener->Params.Gain = props->Gain * Context->GainBoost;
438 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props);
439 return true;
442 static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
444 struct ALeffectslotProps *props;
445 ALeffectState *state;
447 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
448 if(!props && !force) return false;
450 if(props)
452 slot->Params.Gain = props->Gain;
453 slot->Params.AuxSendAuto = props->AuxSendAuto;
454 slot->Params.EffectType = props->Type;
455 slot->Params.EffectProps = props->Props;
456 if(IsReverbEffect(props->Type))
458 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
459 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
460 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
461 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
462 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
464 else
466 slot->Params.RoomRolloff = 0.0f;
467 slot->Params.DecayTime = 0.0f;
468 slot->Params.DecayHFRatio = 0.0f;
469 slot->Params.DecayHFLimit = AL_FALSE;
470 slot->Params.AirAbsorptionGainHF = 1.0f;
473 /* Swap effect states. No need to play with the ref counts since they
474 * keep the same number of refs.
476 state = props->State;
477 props->State = slot->Params.EffectState;
478 slot->Params.EffectState = state;
480 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
482 else
483 state = slot->Params.EffectState;
485 V(state,update)(context, slot, &slot->Params.EffectProps);
486 return true;
490 static const struct ChanMap MonoMap[1] = {
491 { FrontCenter, 0.0f, 0.0f }
492 }, RearMap[2] = {
493 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
494 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
495 }, QuadMap[4] = {
496 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
497 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
498 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
499 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
500 }, X51Map[6] = {
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 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
506 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
507 }, X61Map[7] = {
508 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
509 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
510 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
511 { LFE, 0.0f, 0.0f },
512 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
513 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
514 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
515 }, X71Map[8] = {
516 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
517 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
518 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
519 { LFE, 0.0f, 0.0f },
520 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
521 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
522 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
523 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
526 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
527 const ALfloat Spread, const ALfloat DryGain,
528 const ALfloat DryGainHF, const ALfloat DryGainLF,
529 const ALfloat *WetGain, const ALfloat *WetGainLF,
530 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
531 const ALbuffer *Buffer, const struct ALvoiceProps *props,
532 const ALlistener *Listener, const ALCdevice *Device)
534 struct ChanMap StereoMap[2] = {
535 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
536 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
538 bool DirectChannels = props->DirectChannels;
539 const ALsizei NumSends = Device->NumAuxSends;
540 const ALuint Frequency = Device->Frequency;
541 const struct ChanMap *chans = NULL;
542 ALsizei num_channels = 0;
543 bool isbformat = false;
544 ALfloat downmix_gain = 1.0f;
545 ALsizei c, i, j;
547 switch(Buffer->FmtChannels)
549 case FmtMono:
550 chans = MonoMap;
551 num_channels = 1;
552 /* Mono buffers are never played direct. */
553 DirectChannels = false;
554 break;
556 case FmtStereo:
557 /* Convert counter-clockwise to clockwise. */
558 StereoMap[0].angle = -props->StereoPan[0];
559 StereoMap[1].angle = -props->StereoPan[1];
561 chans = StereoMap;
562 num_channels = 2;
563 downmix_gain = 1.0f / 2.0f;
564 break;
566 case FmtRear:
567 chans = RearMap;
568 num_channels = 2;
569 downmix_gain = 1.0f / 2.0f;
570 break;
572 case FmtQuad:
573 chans = QuadMap;
574 num_channels = 4;
575 downmix_gain = 1.0f / 4.0f;
576 break;
578 case FmtX51:
579 chans = X51Map;
580 num_channels = 6;
581 /* NOTE: Excludes LFE. */
582 downmix_gain = 1.0f / 5.0f;
583 break;
585 case FmtX61:
586 chans = X61Map;
587 num_channels = 7;
588 /* NOTE: Excludes LFE. */
589 downmix_gain = 1.0f / 6.0f;
590 break;
592 case FmtX71:
593 chans = X71Map;
594 num_channels = 8;
595 /* NOTE: Excludes LFE. */
596 downmix_gain = 1.0f / 7.0f;
597 break;
599 case FmtBFormat2D:
600 num_channels = 3;
601 isbformat = true;
602 DirectChannels = false;
603 break;
605 case FmtBFormat3D:
606 num_channels = 4;
607 isbformat = true;
608 DirectChannels = false;
609 break;
612 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
613 if(isbformat)
615 /* Special handling for B-Format sources. */
617 if(Distance > FLT_EPSILON)
619 /* Panning a B-Format sound toward some direction is easy. Just pan
620 * the first (W) channel as a normal mono sound and silence the
621 * others.
623 ALfloat coeffs[MAX_AMBI_COEFFS];
625 if(Device->AvgSpeakerDist > 0.0f)
627 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
628 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
629 (mdist * (ALfloat)Device->Frequency);
630 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
631 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
632 /* Clamp w0 for really close distances, to prevent excessive
633 * bass.
635 w0 = minf(w0, w1*4.0f);
637 /* Only need to adjust the first channel of a B-Format source. */
638 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
640 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
641 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
642 voice->Flags |= VOICE_HAS_NFC;
645 if(Device->Render_Mode == StereoPair)
647 ALfloat ev = asinf(Dir[1]);
648 ALfloat az = atan2f(Dir[0], -Dir[2]);
649 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
651 else
652 CalcDirectionCoeffs(Dir, Spread, coeffs);
654 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
655 ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
656 voice->Direct.Params[0].Gains.Target);
657 for(c = 1;c < num_channels;c++)
659 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
660 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
663 for(i = 0;i < NumSends;i++)
665 const ALeffectslot *Slot = SendSlots[i];
666 if(Slot)
667 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
668 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
670 else
671 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
672 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
673 for(c = 1;c < num_channels;c++)
675 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
676 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
680 else
682 /* Local B-Format sources have their XYZ channels rotated according
683 * to the orientation.
685 ALfloat N[3], V[3], U[3];
686 aluMatrixf matrix;
687 ALfloat scale;
689 if(Device->AvgSpeakerDist > 0.0f)
691 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
692 * is what we want for FOA input. The first channel may have
693 * been previously re-adjusted if panned, so reset it.
695 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, 0.0f);
697 voice->Direct.ChannelsPerOrder[0] = 1;
698 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
699 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
700 voice->Direct.ChannelsPerOrder[i] = 0;
701 voice->Flags |= VOICE_HAS_NFC;
704 /* AT then UP */
705 N[0] = props->Orientation[0][0];
706 N[1] = props->Orientation[0][1];
707 N[2] = props->Orientation[0][2];
708 aluNormalize(N);
709 V[0] = props->Orientation[1][0];
710 V[1] = props->Orientation[1][1];
711 V[2] = props->Orientation[1][2];
712 aluNormalize(V);
713 if(!props->HeadRelative)
715 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
716 aluMatrixfFloat3(N, 0.0f, lmatrix);
717 aluMatrixfFloat3(V, 0.0f, lmatrix);
719 /* Build and normalize right-vector */
720 aluCrossproduct(N, V, U);
721 aluNormalize(U);
723 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
724 scale = 1.732050808f;
725 aluMatrixfSet(&matrix,
726 1.414213562f, 0.0f, 0.0f, 0.0f,
727 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
728 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
729 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
732 voice->Direct.Buffer = Device->FOAOut.Buffer;
733 voice->Direct.Channels = Device->FOAOut.NumChannels;
734 for(c = 0;c < num_channels;c++)
735 ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
736 voice->Direct.Params[c].Gains.Target);
737 for(i = 0;i < NumSends;i++)
739 const ALeffectslot *Slot = SendSlots[i];
740 if(Slot)
742 for(c = 0;c < num_channels;c++)
743 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
744 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
747 else
749 for(c = 0;c < num_channels;c++)
750 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
751 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
756 else if(DirectChannels)
758 /* Direct source channels always play local. Skip the virtual channels
759 * and write inputs to the matching real outputs.
761 voice->Direct.Buffer = Device->RealOut.Buffer;
762 voice->Direct.Channels = Device->RealOut.NumChannels;
764 for(c = 0;c < num_channels;c++)
766 int idx;
767 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
768 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
769 if((idx=GetChannelIdxByName(&Device->RealOut, chans[c].channel)) != -1)
770 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
773 /* Auxiliary sends still use normal channel panning since they mix to
774 * B-Format, which can't channel-match.
776 for(c = 0;c < num_channels;c++)
778 ALfloat coeffs[MAX_AMBI_COEFFS];
779 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
781 for(i = 0;i < NumSends;i++)
783 const ALeffectslot *Slot = SendSlots[i];
784 if(Slot)
785 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
786 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
788 else
789 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
790 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
794 else if(Device->Render_Mode == HrtfRender)
796 /* Full HRTF rendering. Skip the virtual channels and render to the
797 * real outputs.
799 voice->Direct.Buffer = Device->RealOut.Buffer;
800 voice->Direct.Channels = Device->RealOut.NumChannels;
802 if(Distance > FLT_EPSILON)
804 ALfloat coeffs[MAX_AMBI_COEFFS];
805 ALfloat ev, az;
807 ev = asinf(Dir[1]);
808 az = atan2f(Dir[0], -Dir[2]);
810 /* Get the HRIR coefficients and delays just once, for the given
811 * source direction.
813 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
814 voice->Direct.Params[0].Hrtf.Target.Coeffs,
815 voice->Direct.Params[0].Hrtf.Target.Delay);
816 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
818 /* Remaining channels use the same results as the first. */
819 for(c = 1;c < num_channels;c++)
821 /* Skip LFE */
822 if(chans[c].channel == LFE)
823 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
824 sizeof(voice->Direct.Params[c].Hrtf.Target));
825 else
826 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
829 /* Calculate the directional coefficients once, which apply to all
830 * input channels of the source sends.
832 CalcDirectionCoeffs(Dir, Spread, coeffs);
834 for(i = 0;i < NumSends;i++)
836 const ALeffectslot *Slot = SendSlots[i];
837 if(Slot)
838 for(c = 0;c < num_channels;c++)
840 /* Skip LFE */
841 if(chans[c].channel == LFE)
842 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
843 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
844 else
845 ComputePanningGainsBF(Slot->ChanMap,
846 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
847 voice->Send[i].Params[c].Gains.Target
850 else
851 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
852 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
855 else
857 /* Local sources on HRTF play with each channel panned to its
858 * relative location around the listener, providing "virtual
859 * speaker" responses.
861 for(c = 0;c < num_channels;c++)
863 ALfloat coeffs[MAX_AMBI_COEFFS];
865 if(chans[c].channel == LFE)
867 /* Skip LFE */
868 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
869 sizeof(voice->Direct.Params[c].Hrtf.Target));
870 for(i = 0;i < NumSends;i++)
872 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
873 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
875 continue;
878 /* Get the HRIR coefficients and delays for this channel
879 * position.
881 GetHrtfCoeffs(Device->HrtfHandle,
882 chans[c].elevation, chans[c].angle, Spread,
883 voice->Direct.Params[c].Hrtf.Target.Coeffs,
884 voice->Direct.Params[c].Hrtf.Target.Delay
886 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
888 /* Normal panning for auxiliary sends. */
889 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
891 for(i = 0;i < NumSends;i++)
893 const ALeffectslot *Slot = SendSlots[i];
894 if(Slot)
895 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
896 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
898 else
899 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
900 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
905 voice->Flags |= VOICE_HAS_HRTF;
907 else
909 /* Non-HRTF rendering. Use normal panning to the output. */
911 if(Distance > FLT_EPSILON)
913 ALfloat coeffs[MAX_AMBI_COEFFS];
914 ALfloat w0 = 0.0f;
916 /* Calculate NFC filter coefficient if needed. */
917 if(Device->AvgSpeakerDist > 0.0f)
919 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
920 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
921 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
922 w0 = SPEEDOFSOUNDMETRESPERSEC /
923 (mdist * (ALfloat)Device->Frequency);
924 /* Clamp w0 for really close distances, to prevent excessive
925 * bass.
927 w0 = minf(w0, w1*4.0f);
929 /* Adjust NFC filters. */
930 for(c = 0;c < num_channels;c++)
931 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
933 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
934 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
935 voice->Flags |= VOICE_HAS_NFC;
938 /* Calculate the directional coefficients once, which apply to all
939 * input channels.
941 if(Device->Render_Mode == StereoPair)
943 ALfloat ev = asinf(Dir[1]);
944 ALfloat az = atan2f(Dir[0], -Dir[2]);
945 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
947 else
948 CalcDirectionCoeffs(Dir, Spread, coeffs);
950 for(c = 0;c < num_channels;c++)
952 /* Special-case LFE */
953 if(chans[c].channel == LFE)
955 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
956 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
957 if(Device->Dry.Buffer == Device->RealOut.Buffer)
959 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
960 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
962 continue;
965 ComputeDryPanGains(&Device->Dry,
966 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
970 for(i = 0;i < NumSends;i++)
972 const ALeffectslot *Slot = SendSlots[i];
973 if(Slot)
974 for(c = 0;c < num_channels;c++)
976 /* Skip LFE */
977 if(chans[c].channel == LFE)
978 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
979 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
980 else
981 ComputePanningGainsBF(Slot->ChanMap,
982 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
983 voice->Send[i].Params[c].Gains.Target
986 else
987 for(c = 0;c < num_channels;c++)
989 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
990 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
994 else
996 ALfloat w0 = 0.0f;
998 if(Device->AvgSpeakerDist > 0.0f)
1000 /* If the source distance is 0, set w0 to w1 to act as a pass-
1001 * through. We still want to pass the signal through the
1002 * filters so they keep an appropriate history, in case the
1003 * source moves away from the listener.
1005 w0 = SPEEDOFSOUNDMETRESPERSEC /
1006 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
1008 for(c = 0;c < num_channels;c++)
1009 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
1011 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
1012 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
1013 voice->Flags |= VOICE_HAS_NFC;
1016 for(c = 0;c < num_channels;c++)
1018 ALfloat coeffs[MAX_AMBI_COEFFS];
1020 /* Special-case LFE */
1021 if(chans[c].channel == LFE)
1023 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
1024 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
1025 if(Device->Dry.Buffer == Device->RealOut.Buffer)
1027 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
1028 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
1031 for(i = 0;i < NumSends;i++)
1033 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1034 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
1036 continue;
1039 if(Device->Render_Mode == StereoPair)
1040 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
1041 else
1042 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
1043 ComputeDryPanGains(&Device->Dry,
1044 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
1047 for(i = 0;i < NumSends;i++)
1049 const ALeffectslot *Slot = SendSlots[i];
1050 if(Slot)
1051 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1052 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
1054 else
1055 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1056 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
1063 ALfloat hfScale = props->Direct.HFReference / Frequency;
1064 ALfloat lfScale = props->Direct.LFReference / Frequency;
1065 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
1066 ALfloat gainLF = maxf(DryGainLF, 0.001f);
1068 voice->Direct.FilterType = AF_None;
1069 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
1070 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
1071 ALfilterState_setParams(
1072 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
1073 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1075 ALfilterState_setParams(
1076 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
1077 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1079 for(c = 1;c < num_channels;c++)
1081 ALfilterState_copyParams(&voice->Direct.Params[c].LowPass,
1082 &voice->Direct.Params[0].LowPass);
1083 ALfilterState_copyParams(&voice->Direct.Params[c].HighPass,
1084 &voice->Direct.Params[0].HighPass);
1087 for(i = 0;i < NumSends;i++)
1089 ALfloat hfScale = props->Send[i].HFReference / Frequency;
1090 ALfloat lfScale = props->Send[i].LFReference / Frequency;
1091 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
1092 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
1094 voice->Send[i].FilterType = AF_None;
1095 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1096 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1097 ALfilterState_setParams(
1098 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
1099 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1101 ALfilterState_setParams(
1102 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
1103 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1105 for(c = 1;c < num_channels;c++)
1107 ALfilterState_copyParams(&voice->Send[i].Params[c].LowPass,
1108 &voice->Send[i].Params[0].LowPass);
1109 ALfilterState_copyParams(&voice->Send[i].Params[c].HighPass,
1110 &voice->Send[i].Params[0].HighPass);
1115 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1117 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1118 const ALCdevice *Device = ALContext->Device;
1119 const ALlistener *Listener = ALContext->Listener;
1120 ALfloat DryGain, DryGainHF, DryGainLF;
1121 ALfloat WetGain[MAX_SENDS];
1122 ALfloat WetGainHF[MAX_SENDS];
1123 ALfloat WetGainLF[MAX_SENDS];
1124 ALeffectslot *SendSlots[MAX_SENDS];
1125 ALfloat Pitch;
1126 ALsizei i;
1128 voice->Direct.Buffer = Device->Dry.Buffer;
1129 voice->Direct.Channels = Device->Dry.NumChannels;
1130 for(i = 0;i < Device->NumAuxSends;i++)
1132 SendSlots[i] = props->Send[i].Slot;
1133 if(!SendSlots[i] && i == 0)
1134 SendSlots[i] = ALContext->DefaultSlot;
1135 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1137 SendSlots[i] = NULL;
1138 voice->Send[i].Buffer = NULL;
1139 voice->Send[i].Channels = 0;
1141 else
1143 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1144 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1148 /* Calculate the stepping value */
1149 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1150 if(Pitch > (ALfloat)MAX_PITCH)
1151 voice->Step = MAX_PITCH<<FRACTIONBITS;
1152 else
1153 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1154 if(props->Resampler == BSinc24Resampler)
1155 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1156 else if(props->Resampler == BSinc12Resampler)
1157 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1158 voice->Resampler = SelectResampler(props->Resampler);
1160 /* Calculate gains */
1161 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1162 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1163 DryGain = minf(DryGain, GAIN_MIX_MAX);
1164 DryGainHF = props->Direct.GainHF;
1165 DryGainLF = props->Direct.GainLF;
1166 for(i = 0;i < Device->NumAuxSends;i++)
1168 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1169 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1170 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1171 WetGainHF[i] = props->Send[i].GainHF;
1172 WetGainLF[i] = props->Send[i].GainLF;
1175 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1176 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1179 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1181 const ALCdevice *Device = ALContext->Device;
1182 const ALlistener *Listener = ALContext->Listener;
1183 const ALsizei NumSends = Device->NumAuxSends;
1184 aluVector Position, Velocity, Direction, SourceToListener;
1185 ALfloat Distance, ClampedDist, DopplerFactor;
1186 ALeffectslot *SendSlots[MAX_SENDS];
1187 ALfloat RoomRolloff[MAX_SENDS];
1188 ALfloat DecayDistance[MAX_SENDS];
1189 ALfloat DecayHFDistance[MAX_SENDS];
1190 ALfloat DryGain, DryGainHF, DryGainLF;
1191 ALfloat WetGain[MAX_SENDS];
1192 ALfloat WetGainHF[MAX_SENDS];
1193 ALfloat WetGainLF[MAX_SENDS];
1194 bool directional;
1195 ALfloat dir[3];
1196 ALfloat spread;
1197 ALfloat Pitch;
1198 ALint i;
1200 /* Set mixing buffers and get send parameters. */
1201 voice->Direct.Buffer = Device->Dry.Buffer;
1202 voice->Direct.Channels = Device->Dry.NumChannels;
1203 for(i = 0;i < NumSends;i++)
1205 SendSlots[i] = props->Send[i].Slot;
1206 if(!SendSlots[i] && i == 0)
1207 SendSlots[i] = ALContext->DefaultSlot;
1208 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1210 SendSlots[i] = NULL;
1211 RoomRolloff[i] = 0.0f;
1212 DecayDistance[i] = 0.0f;
1213 DecayHFDistance[i] = 0.0f;
1215 else if(SendSlots[i]->Params.AuxSendAuto)
1217 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1218 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1219 Listener->Params.ReverbSpeedOfSound;
1220 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1221 if(SendSlots[i]->Params.DecayHFLimit)
1223 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1224 if(airAbsorption < 1.0f)
1226 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1227 DecayHFDistance[i] = minf(limitRatio, DecayHFDistance[i]);
1231 else
1233 /* If the slot's auxiliary send auto is off, the data sent to the
1234 * effect slot is the same as the dry path, sans filter effects */
1235 RoomRolloff[i] = props->RolloffFactor;
1236 DecayDistance[i] = 0.0f;
1237 DecayHFDistance[i] = 0.0f;
1240 if(!SendSlots[i])
1242 voice->Send[i].Buffer = NULL;
1243 voice->Send[i].Channels = 0;
1245 else
1247 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1248 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1252 /* Transform source to listener space (convert to head relative) */
1253 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1254 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1255 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1256 if(props->HeadRelative == AL_FALSE)
1258 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1259 /* Transform source vectors */
1260 Position = aluMatrixfVector(Matrix, &Position);
1261 Velocity = aluMatrixfVector(Matrix, &Velocity);
1262 Direction = aluMatrixfVector(Matrix, &Direction);
1264 else
1266 const aluVector *lvelocity = &Listener->Params.Velocity;
1267 /* Offset the source velocity to be relative of the listener velocity */
1268 Velocity.v[0] += lvelocity->v[0];
1269 Velocity.v[1] += lvelocity->v[1];
1270 Velocity.v[2] += lvelocity->v[2];
1273 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1274 SourceToListener.v[0] = -Position.v[0];
1275 SourceToListener.v[1] = -Position.v[1];
1276 SourceToListener.v[2] = -Position.v[2];
1277 SourceToListener.v[3] = 0.0f;
1278 Distance = aluNormalize(SourceToListener.v);
1280 /* Initial source gain */
1281 DryGain = props->Gain;
1282 DryGainHF = 1.0f;
1283 DryGainLF = 1.0f;
1284 for(i = 0;i < NumSends;i++)
1286 WetGain[i] = props->Gain;
1287 WetGainHF[i] = 1.0f;
1288 WetGainLF[i] = 1.0f;
1291 /* Calculate distance attenuation */
1292 ClampedDist = Distance;
1294 switch(Listener->Params.SourceDistanceModel ?
1295 props->DistanceModel : Listener->Params.DistanceModel)
1297 case InverseDistanceClamped:
1298 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1299 if(props->MaxDistance < props->RefDistance)
1300 break;
1301 /*fall-through*/
1302 case InverseDistance:
1303 if(!(props->RefDistance > 0.0f))
1304 ClampedDist = props->RefDistance;
1305 else
1307 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1308 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1309 for(i = 0;i < NumSends;i++)
1311 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1312 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1315 break;
1317 case LinearDistanceClamped:
1318 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1319 if(props->MaxDistance < props->RefDistance)
1320 break;
1321 /*fall-through*/
1322 case LinearDistance:
1323 if(!(props->MaxDistance != props->RefDistance))
1324 ClampedDist = props->RefDistance;
1325 else
1327 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1328 (props->MaxDistance-props->RefDistance);
1329 DryGain *= maxf(1.0f - attn, 0.0f);
1330 for(i = 0;i < NumSends;i++)
1332 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1333 (props->MaxDistance-props->RefDistance);
1334 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1337 break;
1339 case ExponentDistanceClamped:
1340 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1341 if(props->MaxDistance < props->RefDistance)
1342 break;
1343 /*fall-through*/
1344 case ExponentDistance:
1345 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1346 ClampedDist = props->RefDistance;
1347 else
1349 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1350 for(i = 0;i < NumSends;i++)
1351 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1353 break;
1355 case DisableDistance:
1356 ClampedDist = props->RefDistance;
1357 break;
1360 /* Distance-based air absorption */
1361 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1363 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1364 Listener->Params.MetersPerUnit;
1365 if(props->AirAbsorptionFactor > 0.0f)
1367 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1368 DryGainHF *= hfattn;
1369 for(i = 0;i < NumSends;i++)
1370 WetGainHF[i] *= hfattn;
1373 if(props->WetGainAuto)
1375 /* Apply a decay-time transformation to the wet path, based on the
1376 * source distance in meters. The initial decay of the reverb
1377 * effect is calculated and applied to the wet path.
1379 for(i = 0;i < NumSends;i++)
1381 ALfloat gain;
1383 if(!(DecayDistance[i] > 0.0f))
1384 continue;
1386 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1387 WetGain[i] *= gain;
1388 /* Yes, the wet path's air absorption is applied with
1389 * WetGainAuto on, rather than WetGainHFAuto.
1391 if(gain > 0.0f)
1393 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1394 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1400 /* Calculate directional soundcones */
1401 if(directional && props->InnerAngle < 360.0f)
1403 ALfloat ConeVolume;
1404 ALfloat ConeHF;
1405 ALfloat Angle;
1407 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1408 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1409 if(!(Angle > props->InnerAngle))
1411 ConeVolume = 1.0f;
1412 ConeHF = 1.0f;
1414 else if(Angle < props->OuterAngle)
1416 ALfloat scale = ( Angle-props->InnerAngle) /
1417 (props->OuterAngle-props->InnerAngle);
1418 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1419 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1421 else
1423 ConeVolume = props->OuterGain;
1424 ConeHF = props->OuterGainHF;
1427 DryGain *= ConeVolume;
1428 if(props->DryGainHFAuto)
1429 DryGainHF *= ConeHF;
1430 if(props->WetGainAuto)
1432 for(i = 0;i < NumSends;i++)
1433 WetGain[i] *= ConeVolume;
1435 if(props->WetGainHFAuto)
1437 for(i = 0;i < NumSends;i++)
1438 WetGainHF[i] *= ConeHF;
1442 /* Apply gain and frequency filters */
1443 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1444 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1445 DryGainHF *= props->Direct.GainHF;
1446 DryGainLF *= props->Direct.GainLF;
1447 for(i = 0;i < NumSends;i++)
1449 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1450 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1451 WetGainHF[i] *= props->Send[i].GainHF;
1452 WetGainLF[i] *= props->Send[i].GainLF;
1456 /* Initial source pitch */
1457 Pitch = props->Pitch;
1459 /* Calculate velocity-based doppler effect */
1460 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1461 if(DopplerFactor > 0.0f)
1463 const aluVector *lvelocity = &Listener->Params.Velocity;
1464 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1465 ALfloat vss, vls;
1467 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1468 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1470 if(!(vls < SpeedOfSound))
1472 /* Listener moving away from the source at the speed of sound.
1473 * Sound waves can't catch it.
1475 Pitch = 0.0f;
1477 else if(!(vss < SpeedOfSound))
1479 /* Source moving toward the listener at the speed of sound. Sound
1480 * waves bunch up to extreme frequencies.
1482 Pitch = HUGE_VALF;
1484 else
1486 /* Source and listener movement is nominal. Calculate the proper
1487 * doppler shift.
1489 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1493 /* Adjust pitch based on the buffer and output frequencies, and calculate
1494 * fixed-point stepping value.
1496 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1497 if(Pitch > (ALfloat)MAX_PITCH)
1498 voice->Step = MAX_PITCH<<FRACTIONBITS;
1499 else
1500 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1501 if(props->Resampler == BSinc24Resampler)
1502 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1503 else if(props->Resampler == BSinc12Resampler)
1504 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1505 voice->Resampler = SelectResampler(props->Resampler);
1507 if(Distance > FLT_EPSILON)
1509 dir[0] = -SourceToListener.v[0];
1510 /* Clamp Y, in case rounding errors caused it to end up outside of
1511 * -1...+1.
1513 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1514 dir[2] = -SourceToListener.v[2] * ZScale;
1516 else
1518 dir[0] = 0.0f;
1519 dir[1] = 0.0f;
1520 dir[2] = -1.0f;
1522 if(props->Radius > Distance)
1523 spread = F_TAU - Distance/props->Radius*F_PI;
1524 else if(Distance > FLT_EPSILON)
1525 spread = asinf(props->Radius / Distance) * 2.0f;
1526 else
1527 spread = 0.0f;
1529 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1530 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1533 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1535 ALbufferlistitem *BufferListItem;
1536 struct ALvoiceProps *props;
1538 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1539 if(!props && !force) return;
1541 if(props)
1543 memcpy(voice->Props, props,
1544 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1547 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1549 props = voice->Props;
1551 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1552 while(BufferListItem != NULL)
1554 const ALbuffer *buffer;
1555 if(BufferListItem->num_buffers >= 1 && (buffer=BufferListItem->buffers[0]) != NULL)
1557 if(props->SpatializeMode == SpatializeOn ||
1558 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1559 CalcAttnSourceParams(voice, props, buffer, context);
1560 else
1561 CalcNonAttnSourceParams(voice, props, buffer, context);
1562 break;
1564 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1569 static void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1571 ALvoice **voice, **voice_end;
1572 ALsource *source;
1573 ALsizei i;
1575 IncrementRef(&ctx->UpdateCount);
1576 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1578 bool cforce = CalcContextParams(ctx);
1579 bool force = CalcListenerParams(ctx) | cforce;
1580 for(i = 0;i < slots->count;i++)
1581 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1583 voice = ctx->Voices;
1584 voice_end = voice + ctx->VoiceCount;
1585 for(;voice != voice_end;++voice)
1587 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1588 if(source) CalcSourceParams(*voice, ctx, force);
1591 IncrementRef(&ctx->UpdateCount);
1595 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1596 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1597 ALsizei NumChannels)
1599 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1600 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1601 ALsizei i;
1603 /* Apply an all-pass to all channels, except the front-left and front-
1604 * right, so they maintain the same relative phase.
1606 for(i = 0;i < NumChannels;i++)
1608 if(i == lidx || i == ridx)
1609 continue;
1610 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1613 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1614 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1616 for(i = 0;i < SamplesToDo;i++)
1618 ALfloat lfsum, hfsum;
1619 ALfloat m, s, c;
1621 lfsum = lsplit[0][i] + rsplit[0][i];
1622 hfsum = lsplit[1][i] + rsplit[1][i];
1623 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1625 /* This pans the separate low- and high-frequency sums between being on
1626 * the center channel and the left/right channels. The low-frequency
1627 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1628 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1629 * values can be tweaked.
1631 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1632 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1634 /* The generated center channel signal adds to the existing signal,
1635 * while the modified left and right channels replace.
1637 Buffer[lidx][i] = (m + s) * 0.5f;
1638 Buffer[ridx][i] = (m - s) * 0.5f;
1639 Buffer[cidx][i] += c * 0.5f;
1643 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1644 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1646 ALsizei i, c;
1648 Values = ASSUME_ALIGNED(Values, 16);
1649 for(c = 0;c < numchans;c++)
1651 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1652 const ALfloat gain = distcomp[c].Gain;
1653 const ALsizei base = distcomp[c].Length;
1654 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1656 if(base == 0)
1658 if(gain < 1.0f)
1660 for(i = 0;i < SamplesToDo;i++)
1661 inout[i] *= gain;
1663 continue;
1666 if(SamplesToDo >= base)
1668 for(i = 0;i < base;i++)
1669 Values[i] = distbuf[i];
1670 for(;i < SamplesToDo;i++)
1671 Values[i] = inout[i-base];
1672 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1674 else
1676 for(i = 0;i < SamplesToDo;i++)
1677 Values[i] = distbuf[i];
1678 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1679 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1681 for(i = 0;i < SamplesToDo;i++)
1682 inout[i] = Values[i]*gain;
1686 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1687 const ALfloat quant_scale, const ALsizei SamplesToDo,
1688 const ALsizei numchans)
1690 const ALfloat invscale = 1.0f / quant_scale;
1691 ALuint seed = *dither_seed;
1692 ALsizei c, i;
1694 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1695 * values between -1 and +1). Step 2 is to add the noise to the samples,
1696 * before rounding and after scaling up to the desired quantization depth.
1698 for(c = 0;c < numchans;c++)
1700 ALfloat *restrict samples = Samples[c];
1701 for(i = 0;i < SamplesToDo;i++)
1703 ALfloat val = samples[i] * quant_scale;
1704 ALuint rng0 = dither_rng(&seed);
1705 ALuint rng1 = dither_rng(&seed);
1706 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1707 samples[i] = roundf(val) * invscale;
1710 *dither_seed = seed;
1714 static inline ALfloat Conv_ALfloat(ALfloat val)
1715 { return val; }
1716 static inline ALint Conv_ALint(ALfloat val)
1718 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1719 * integer range normalized floats can be safely converted to (a bit of the
1720 * exponent helps out, effectively giving 25 bits).
1722 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1724 static inline ALshort Conv_ALshort(ALfloat val)
1725 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1726 static inline ALbyte Conv_ALbyte(ALfloat val)
1727 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1729 /* Define unsigned output variations. */
1730 #define DECL_TEMPLATE(T, func, O) \
1731 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1733 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1734 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1735 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1737 #undef DECL_TEMPLATE
1739 #define DECL_TEMPLATE(T, A) \
1740 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1741 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1742 ALsizei numchans) \
1744 ALsizei i, j; \
1745 for(j = 0;j < numchans;j++) \
1747 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1748 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1750 for(i = 0;i < SamplesToDo;i++) \
1751 out[i*numchans] = Conv_##T(in[i]); \
1755 DECL_TEMPLATE(ALfloat, F32)
1756 DECL_TEMPLATE(ALuint, UI32)
1757 DECL_TEMPLATE(ALint, I32)
1758 DECL_TEMPLATE(ALushort, UI16)
1759 DECL_TEMPLATE(ALshort, I16)
1760 DECL_TEMPLATE(ALubyte, UI8)
1761 DECL_TEMPLATE(ALbyte, I8)
1763 #undef DECL_TEMPLATE
1766 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1768 ALsizei SamplesToDo;
1769 ALsizei SamplesDone;
1770 ALCcontext *ctx;
1771 ALsizei i, c;
1773 START_MIXER_MODE();
1774 for(SamplesDone = 0;SamplesDone < NumSamples;)
1776 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1777 for(c = 0;c < device->Dry.NumChannels;c++)
1778 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1779 if(device->Dry.Buffer != device->FOAOut.Buffer)
1780 for(c = 0;c < device->FOAOut.NumChannels;c++)
1781 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1782 if(device->Dry.Buffer != device->RealOut.Buffer)
1783 for(c = 0;c < device->RealOut.NumChannels;c++)
1784 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1786 IncrementRef(&device->MixCount);
1788 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1789 while(ctx)
1791 const struct ALeffectslotArray *auxslots;
1793 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1794 ProcessParamUpdates(ctx, auxslots);
1796 for(i = 0;i < auxslots->count;i++)
1798 ALeffectslot *slot = auxslots->slot[i];
1799 for(c = 0;c < slot->NumChannels;c++)
1800 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1803 /* source processing */
1804 for(i = 0;i < ctx->VoiceCount;i++)
1806 ALvoice *voice = ctx->Voices[i];
1807 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1808 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1809 voice->Step > 0)
1811 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1813 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1814 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1815 SendSourceStoppedEvent(ctx, source->id);
1820 /* effect slot processing */
1821 for(i = 0;i < auxslots->count;i++)
1823 const ALeffectslot *slot = auxslots->slot[i];
1824 ALeffectState *state = slot->Params.EffectState;
1825 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1826 state->OutChannels);
1829 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
1832 /* Increment the clock time. Every second's worth of samples is
1833 * converted and added to clock base so that large sample counts don't
1834 * overflow during conversion. This also guarantees an exact, stable
1835 * conversion. */
1836 device->SamplesDone += SamplesToDo;
1837 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1838 device->SamplesDone %= device->Frequency;
1839 IncrementRef(&device->MixCount);
1841 /* Apply post-process for finalizing the Dry mix to the RealOut
1842 * (Ambisonic decode, UHJ encode, etc).
1844 if(LIKELY(device->PostProcess))
1845 device->PostProcess(device, SamplesToDo);
1847 if(device->Stablizer)
1849 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1850 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1851 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1852 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1854 ApplyStablizer(device->Stablizer, device->RealOut.Buffer, lidx, ridx, cidx,
1855 SamplesToDo, device->RealOut.NumChannels);
1858 ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
1859 SamplesToDo, device->RealOut.NumChannels);
1861 if(device->Limiter)
1862 ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
1863 device->RealOut.Buffer);
1865 if(device->DitherDepth > 0.0f)
1866 ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
1867 SamplesToDo, device->RealOut.NumChannels);
1869 if(OutBuffer)
1871 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1872 ALsizei Channels = device->RealOut.NumChannels;
1874 switch(device->FmtType)
1876 case DevFmtByte:
1877 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1878 break;
1879 case DevFmtUByte:
1880 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1881 break;
1882 case DevFmtShort:
1883 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1884 break;
1885 case DevFmtUShort:
1886 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1887 break;
1888 case DevFmtInt:
1889 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1890 break;
1891 case DevFmtUInt:
1892 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1893 break;
1894 case DevFmtFloat:
1895 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1896 break;
1900 SamplesDone += SamplesToDo;
1902 END_MIXER_MODE();
1906 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1908 ALCcontext *ctx;
1909 AsyncEvent evt;
1910 va_list args;
1911 int msglen;
1913 if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
1914 return;
1916 evt.EnumType = EventType_Disconnected;
1917 evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1918 evt.ObjectId = 0;
1919 evt.Param = 0;
1921 va_start(args, msg);
1922 msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
1923 va_end(args);
1925 if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
1927 evt.Message[sizeof(evt.Message)-1] = 0;
1928 msglen = (int)strlen(evt.Message);
1930 if(msglen > 0)
1931 msg = evt.Message;
1932 else
1934 msg = "<internal error constructing message>";
1935 msglen = (int)strlen(msg);
1938 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1939 while(ctx)
1941 ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire);
1942 ALsizei i;
1944 if((enabledevt&EventType_Disconnected) &&
1945 ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1) == 1)
1946 alsem_post(&ctx->EventSem);
1948 for(i = 0;i < ctx->VoiceCount;i++)
1950 ALvoice *voice = ctx->Voices[i];
1951 ALsource *source;
1953 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_relaxed);
1954 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed))
1956 /* If the source's voice was playing, it's now effectively
1957 * stopped (the source state will be updated the next time it's
1958 * checked).
1960 SendSourceStoppedEvent(ctx, source->id);
1962 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1965 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);