Use a function pointer for applying the dry mix post-process
[openal-soft.git] / Alc / ALu.c
bloba3de456b357f93beb99038c25a991256e6404e17
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 ProcessHrtf(ALCdevice *device, ALsizei SamplesToDo)
214 DirectHrtfState *state;
215 int lidx, ridx;
216 ALsizei c;
218 if(device->AmbiUp)
219 ambiup_process(device->AmbiUp,
220 device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer,
221 SamplesToDo
224 lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
225 ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
226 assert(lidx != -1 && ridx != -1);
228 state = device->Hrtf;
229 for(c = 0;c < device->Dry.NumChannels;c++)
231 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
232 device->Dry.Buffer[c], state->Offset, state->IrSize,
233 state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo
236 state->Offset += SamplesToDo;
239 static void ProcessAmbiDec(ALCdevice *device, ALsizei SamplesToDo)
241 if(device->Dry.Buffer != device->FOAOut.Buffer)
242 bformatdec_upSample(device->AmbiDecoder,
243 device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
244 SamplesToDo
246 bformatdec_process(device->AmbiDecoder,
247 device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
248 SamplesToDo
252 static void ProcessAmbiUp(ALCdevice *device, ALsizei SamplesToDo)
254 ambiup_process(device->AmbiUp,
255 device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer,
256 SamplesToDo
260 static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
262 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
263 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
264 if(LIKELY(lidx != -1 && ridx != -1))
266 /* Encode to stereo-compatible 2-channel UHJ output. */
267 EncodeUhj2(device->Uhj_Encoder,
268 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
269 device->Dry.Buffer, SamplesToDo
274 static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
276 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
277 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
278 if(LIKELY(lidx != -1 && ridx != -1))
280 /* Apply binaural/crossfeed filter */
281 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
282 device->RealOut.Buffer[ridx], SamplesToDo);
286 void aluSelectPostProcess(ALCdevice *device)
288 if(device->HrtfHandle)
289 device->PostProcess = ProcessHrtf;
290 else if(device->AmbiDecoder)
291 device->PostProcess = ProcessAmbiDec;
292 else if(device->AmbiUp)
293 device->PostProcess = ProcessAmbiUp;
294 else if(device->Uhj_Encoder)
295 device->PostProcess = ProcessUhj;
296 else if(device->Bs2b)
297 device->PostProcess = ProcessBs2b;
298 else
299 device->PostProcess = NULL;
303 /* Prepares the interpolator for a given rate (determined by increment). A
304 * result of AL_FALSE indicates that the filter output will completely cut
305 * the input signal.
307 * With a bit of work, and a trade of memory for CPU cost, this could be
308 * modified for use with an interpolated increment for buttery-smooth pitch
309 * changes.
311 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
313 ALfloat sf;
314 ALsizei si;
316 if(increment > FRACTIONONE)
318 sf = (ALfloat)FRACTIONONE / increment;
319 sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
320 si = fastf2i(sf);
321 /* The interpolation factor is fit to this diagonally-symmetric curve
322 * to reduce the transition ripple caused by interpolating different
323 * scales of the sinc function.
325 sf = 1.0f - cosf(asinf(sf - si));
327 else
329 sf = 0.0f;
330 si = BSINC_SCALE_COUNT - 1;
333 state->sf = sf;
334 state->m = table->m[si];
335 state->l = -((state->m/2) - 1);
336 state->filter = table->Tab + table->filterOffset[si];
340 static bool CalcContextParams(ALCcontext *Context)
342 ALlistener *Listener = Context->Listener;
343 struct ALcontextProps *props;
345 props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel);
346 if(!props) return false;
348 Listener->Params.MetersPerUnit = props->MetersPerUnit;
350 Listener->Params.DopplerFactor = props->DopplerFactor;
351 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
352 if(!OverrideReverbSpeedOfSound)
353 Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
354 Listener->Params.MetersPerUnit;
356 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
357 Listener->Params.DistanceModel = props->DistanceModel;
359 ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props);
360 return true;
363 static bool CalcListenerParams(ALCcontext *Context)
365 ALlistener *Listener = Context->Listener;
366 ALfloat N[3], V[3], U[3], P[3];
367 struct ALlistenerProps *props;
368 aluVector vel;
370 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
371 if(!props) return false;
373 /* AT then UP */
374 N[0] = props->Forward[0];
375 N[1] = props->Forward[1];
376 N[2] = props->Forward[2];
377 aluNormalize(N);
378 V[0] = props->Up[0];
379 V[1] = props->Up[1];
380 V[2] = props->Up[2];
381 aluNormalize(V);
382 /* Build and normalize right-vector */
383 aluCrossproduct(N, V, U);
384 aluNormalize(U);
386 aluMatrixfSet(&Listener->Params.Matrix,
387 U[0], V[0], -N[0], 0.0,
388 U[1], V[1], -N[1], 0.0,
389 U[2], V[2], -N[2], 0.0,
390 0.0, 0.0, 0.0, 1.0
393 P[0] = props->Position[0];
394 P[1] = props->Position[1];
395 P[2] = props->Position[2];
396 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
397 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
399 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
400 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
402 Listener->Params.Gain = props->Gain * Context->GainBoost;
404 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props);
405 return true;
408 static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
410 struct ALeffectslotProps *props;
411 ALeffectState *state;
413 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
414 if(!props && !force) return false;
416 if(props)
418 slot->Params.Gain = props->Gain;
419 slot->Params.AuxSendAuto = props->AuxSendAuto;
420 slot->Params.EffectType = props->Type;
421 slot->Params.EffectProps = props->Props;
422 if(IsReverbEffect(props->Type))
424 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
425 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
426 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
427 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
428 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
430 else
432 slot->Params.RoomRolloff = 0.0f;
433 slot->Params.DecayTime = 0.0f;
434 slot->Params.DecayHFRatio = 0.0f;
435 slot->Params.DecayHFLimit = AL_FALSE;
436 slot->Params.AirAbsorptionGainHF = 1.0f;
439 /* Swap effect states. No need to play with the ref counts since they
440 * keep the same number of refs.
442 state = props->State;
443 props->State = slot->Params.EffectState;
444 slot->Params.EffectState = state;
446 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
448 else
449 state = slot->Params.EffectState;
451 V(state,update)(context, slot, &slot->Params.EffectProps);
452 return true;
456 static const struct ChanMap MonoMap[1] = {
457 { FrontCenter, 0.0f, 0.0f }
458 }, RearMap[2] = {
459 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
460 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
461 }, QuadMap[4] = {
462 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
463 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
464 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
465 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
466 }, X51Map[6] = {
467 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
468 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
469 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
470 { LFE, 0.0f, 0.0f },
471 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
472 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
473 }, X61Map[7] = {
474 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
475 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
476 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
477 { LFE, 0.0f, 0.0f },
478 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
479 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
480 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
481 }, X71Map[8] = {
482 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
483 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
484 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
485 { LFE, 0.0f, 0.0f },
486 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
487 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
488 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
489 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
492 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
493 const ALfloat Spread, const ALfloat DryGain,
494 const ALfloat DryGainHF, const ALfloat DryGainLF,
495 const ALfloat *WetGain, const ALfloat *WetGainLF,
496 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
497 const ALbuffer *Buffer, const struct ALvoiceProps *props,
498 const ALlistener *Listener, const ALCdevice *Device)
500 struct ChanMap StereoMap[2] = {
501 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
502 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
504 bool DirectChannels = props->DirectChannels;
505 const ALsizei NumSends = Device->NumAuxSends;
506 const ALuint Frequency = Device->Frequency;
507 const struct ChanMap *chans = NULL;
508 ALsizei num_channels = 0;
509 bool isbformat = false;
510 ALfloat downmix_gain = 1.0f;
511 ALsizei c, i, j;
513 switch(Buffer->FmtChannels)
515 case FmtMono:
516 chans = MonoMap;
517 num_channels = 1;
518 /* Mono buffers are never played direct. */
519 DirectChannels = false;
520 break;
522 case FmtStereo:
523 /* Convert counter-clockwise to clockwise. */
524 StereoMap[0].angle = -props->StereoPan[0];
525 StereoMap[1].angle = -props->StereoPan[1];
527 chans = StereoMap;
528 num_channels = 2;
529 downmix_gain = 1.0f / 2.0f;
530 break;
532 case FmtRear:
533 chans = RearMap;
534 num_channels = 2;
535 downmix_gain = 1.0f / 2.0f;
536 break;
538 case FmtQuad:
539 chans = QuadMap;
540 num_channels = 4;
541 downmix_gain = 1.0f / 4.0f;
542 break;
544 case FmtX51:
545 chans = X51Map;
546 num_channels = 6;
547 /* NOTE: Excludes LFE. */
548 downmix_gain = 1.0f / 5.0f;
549 break;
551 case FmtX61:
552 chans = X61Map;
553 num_channels = 7;
554 /* NOTE: Excludes LFE. */
555 downmix_gain = 1.0f / 6.0f;
556 break;
558 case FmtX71:
559 chans = X71Map;
560 num_channels = 8;
561 /* NOTE: Excludes LFE. */
562 downmix_gain = 1.0f / 7.0f;
563 break;
565 case FmtBFormat2D:
566 num_channels = 3;
567 isbformat = true;
568 DirectChannels = false;
569 break;
571 case FmtBFormat3D:
572 num_channels = 4;
573 isbformat = true;
574 DirectChannels = false;
575 break;
578 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
579 if(isbformat)
581 /* Special handling for B-Format sources. */
583 if(Distance > FLT_EPSILON)
585 /* Panning a B-Format sound toward some direction is easy. Just pan
586 * the first (W) channel as a normal mono sound and silence the
587 * others.
589 ALfloat coeffs[MAX_AMBI_COEFFS];
591 if(Device->AvgSpeakerDist > 0.0f)
593 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
594 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
595 (mdist * (ALfloat)Device->Frequency);
596 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
597 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
598 /* Clamp w0 for really close distances, to prevent excessive
599 * bass.
601 w0 = minf(w0, w1*4.0f);
603 /* Only need to adjust the first channel of a B-Format source. */
604 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
605 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
606 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
608 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
609 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
610 voice->Flags |= VOICE_HAS_NFC;
613 if(Device->Render_Mode == StereoPair)
615 ALfloat ev = asinf(Dir[1]);
616 ALfloat az = atan2f(Dir[0], -Dir[2]);
617 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
619 else
620 CalcDirectionCoeffs(Dir, Spread, coeffs);
622 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
623 ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
624 voice->Direct.Params[0].Gains.Target);
625 for(c = 1;c < num_channels;c++)
627 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
628 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
631 for(i = 0;i < NumSends;i++)
633 const ALeffectslot *Slot = SendSlots[i];
634 if(Slot)
635 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
636 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
638 else
639 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
640 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
641 for(c = 1;c < num_channels;c++)
643 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
644 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
648 else
650 /* Local B-Format sources have their XYZ channels rotated according
651 * to the orientation.
653 ALfloat N[3], V[3], U[3];
654 aluMatrixf matrix;
655 ALfloat scale;
657 if(Device->AvgSpeakerDist > 0.0f)
659 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
660 * is what we want for FOA input. The first channel may have
661 * been previously re-adjusted if panned, so reset it.
663 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f);
664 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f);
665 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 0.0f);
667 voice->Direct.ChannelsPerOrder[0] = 1;
668 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
669 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
670 voice->Direct.ChannelsPerOrder[i] = 0;
671 voice->Flags |= VOICE_HAS_NFC;
674 /* AT then UP */
675 N[0] = props->Orientation[0][0];
676 N[1] = props->Orientation[0][1];
677 N[2] = props->Orientation[0][2];
678 aluNormalize(N);
679 V[0] = props->Orientation[1][0];
680 V[1] = props->Orientation[1][1];
681 V[2] = props->Orientation[1][2];
682 aluNormalize(V);
683 if(!props->HeadRelative)
685 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
686 aluMatrixfFloat3(N, 0.0f, lmatrix);
687 aluMatrixfFloat3(V, 0.0f, lmatrix);
689 /* Build and normalize right-vector */
690 aluCrossproduct(N, V, U);
691 aluNormalize(U);
693 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
694 scale = 1.732050808f;
695 aluMatrixfSet(&matrix,
696 1.414213562f, 0.0f, 0.0f, 0.0f,
697 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
698 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
699 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
702 voice->Direct.Buffer = Device->FOAOut.Buffer;
703 voice->Direct.Channels = Device->FOAOut.NumChannels;
704 for(c = 0;c < num_channels;c++)
705 ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
706 voice->Direct.Params[c].Gains.Target);
707 for(i = 0;i < NumSends;i++)
709 const ALeffectslot *Slot = SendSlots[i];
710 if(Slot)
712 for(c = 0;c < num_channels;c++)
713 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
714 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
717 else
719 for(c = 0;c < num_channels;c++)
720 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
721 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
726 else if(DirectChannels)
728 /* Direct source channels always play local. Skip the virtual channels
729 * and write inputs to the matching real outputs.
731 voice->Direct.Buffer = Device->RealOut.Buffer;
732 voice->Direct.Channels = Device->RealOut.NumChannels;
734 for(c = 0;c < num_channels;c++)
736 int idx;
737 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
738 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
739 if((idx=GetChannelIdxByName(&Device->RealOut, chans[c].channel)) != -1)
740 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
743 /* Auxiliary sends still use normal channel panning since they mix to
744 * B-Format, which can't channel-match.
746 for(c = 0;c < num_channels;c++)
748 ALfloat coeffs[MAX_AMBI_COEFFS];
749 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
751 for(i = 0;i < NumSends;i++)
753 const ALeffectslot *Slot = SendSlots[i];
754 if(Slot)
755 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
756 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
758 else
759 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
760 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
764 else if(Device->Render_Mode == HrtfRender)
766 /* Full HRTF rendering. Skip the virtual channels and render to the
767 * real outputs.
769 voice->Direct.Buffer = Device->RealOut.Buffer;
770 voice->Direct.Channels = Device->RealOut.NumChannels;
772 if(Distance > FLT_EPSILON)
774 ALfloat coeffs[MAX_AMBI_COEFFS];
775 ALfloat ev, az;
777 ev = asinf(Dir[1]);
778 az = atan2f(Dir[0], -Dir[2]);
780 /* Get the HRIR coefficients and delays just once, for the given
781 * source direction.
783 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
784 voice->Direct.Params[0].Hrtf.Target.Coeffs,
785 voice->Direct.Params[0].Hrtf.Target.Delay);
786 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
788 /* Remaining channels use the same results as the first. */
789 for(c = 1;c < num_channels;c++)
791 /* Skip LFE */
792 if(chans[c].channel == LFE)
793 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
794 sizeof(voice->Direct.Params[c].Hrtf.Target));
795 else
796 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
799 /* Calculate the directional coefficients once, which apply to all
800 * input channels of the source sends.
802 CalcDirectionCoeffs(Dir, Spread, coeffs);
804 for(i = 0;i < NumSends;i++)
806 const ALeffectslot *Slot = SendSlots[i];
807 if(Slot)
808 for(c = 0;c < num_channels;c++)
810 /* Skip LFE */
811 if(chans[c].channel == LFE)
812 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
813 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
814 else
815 ComputePanningGainsBF(Slot->ChanMap,
816 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
817 voice->Send[i].Params[c].Gains.Target
820 else
821 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
822 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
825 else
827 /* Local sources on HRTF play with each channel panned to its
828 * relative location around the listener, providing "virtual
829 * speaker" responses.
831 for(c = 0;c < num_channels;c++)
833 ALfloat coeffs[MAX_AMBI_COEFFS];
835 if(chans[c].channel == LFE)
837 /* Skip LFE */
838 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
839 sizeof(voice->Direct.Params[c].Hrtf.Target));
840 for(i = 0;i < NumSends;i++)
842 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
843 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
845 continue;
848 /* Get the HRIR coefficients and delays for this channel
849 * position.
851 GetHrtfCoeffs(Device->HrtfHandle,
852 chans[c].elevation, chans[c].angle, Spread,
853 voice->Direct.Params[c].Hrtf.Target.Coeffs,
854 voice->Direct.Params[c].Hrtf.Target.Delay
856 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
858 /* Normal panning for auxiliary sends. */
859 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
861 for(i = 0;i < NumSends;i++)
863 const ALeffectslot *Slot = SendSlots[i];
864 if(Slot)
865 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
866 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
868 else
869 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
870 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
875 voice->Flags |= VOICE_HAS_HRTF;
877 else
879 /* Non-HRTF rendering. Use normal panning to the output. */
881 if(Distance > FLT_EPSILON)
883 ALfloat coeffs[MAX_AMBI_COEFFS];
884 ALfloat w0 = 0.0f;
886 /* Calculate NFC filter coefficient if needed. */
887 if(Device->AvgSpeakerDist > 0.0f)
889 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
890 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
891 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
892 w0 = SPEEDOFSOUNDMETRESPERSEC /
893 (mdist * (ALfloat)Device->Frequency);
894 /* Clamp w0 for really close distances, to prevent excessive
895 * bass.
897 w0 = minf(w0, w1*4.0f);
899 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
900 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
901 voice->Flags |= VOICE_HAS_NFC;
904 /* Calculate the directional coefficients once, which apply to all
905 * input channels.
907 if(Device->Render_Mode == StereoPair)
909 ALfloat ev = asinf(Dir[1]);
910 ALfloat az = atan2f(Dir[0], -Dir[2]);
911 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
913 else
914 CalcDirectionCoeffs(Dir, Spread, coeffs);
916 for(c = 0;c < num_channels;c++)
918 /* Adjust NFC filters if needed. */
919 if((voice->Flags&VOICE_HAS_NFC))
921 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
922 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
923 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
926 /* Special-case LFE */
927 if(chans[c].channel == LFE)
929 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
930 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
931 if(Device->Dry.Buffer == Device->RealOut.Buffer)
933 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
934 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
936 continue;
939 ComputeDryPanGains(&Device->Dry,
940 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
944 for(i = 0;i < NumSends;i++)
946 const ALeffectslot *Slot = SendSlots[i];
947 if(Slot)
948 for(c = 0;c < num_channels;c++)
950 /* Skip LFE */
951 if(chans[c].channel == LFE)
952 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
953 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
954 else
955 ComputePanningGainsBF(Slot->ChanMap,
956 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
957 voice->Send[i].Params[c].Gains.Target
960 else
961 for(c = 0;c < num_channels;c++)
963 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
964 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
968 else
970 ALfloat w0 = 0.0f;
972 if(Device->AvgSpeakerDist > 0.0f)
974 /* If the source distance is 0, set w0 to w1 to act as a pass-
975 * through. We still want to pass the signal through the
976 * filters so they keep an appropriate history, in case the
977 * source moves away from the listener.
979 w0 = SPEEDOFSOUNDMETRESPERSEC /
980 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
982 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
983 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
984 voice->Flags |= VOICE_HAS_NFC;
987 for(c = 0;c < num_channels;c++)
989 ALfloat coeffs[MAX_AMBI_COEFFS];
991 if((voice->Flags&VOICE_HAS_NFC))
993 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
994 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
995 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
998 /* Special-case LFE */
999 if(chans[c].channel == LFE)
1001 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
1002 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
1003 if(Device->Dry.Buffer == Device->RealOut.Buffer)
1005 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
1006 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
1009 for(i = 0;i < NumSends;i++)
1011 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1012 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
1014 continue;
1017 if(Device->Render_Mode == StereoPair)
1018 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
1019 else
1020 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
1021 ComputeDryPanGains(&Device->Dry,
1022 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
1025 for(i = 0;i < NumSends;i++)
1027 const ALeffectslot *Slot = SendSlots[i];
1028 if(Slot)
1029 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1030 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
1032 else
1033 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1034 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
1041 ALfloat hfScale = props->Direct.HFReference / Frequency;
1042 ALfloat lfScale = props->Direct.LFReference / Frequency;
1043 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
1044 ALfloat gainLF = maxf(DryGainLF, 0.001f);
1046 voice->Direct.FilterType = AF_None;
1047 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
1048 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
1049 ALfilterState_setParams(
1050 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
1051 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1053 ALfilterState_setParams(
1054 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
1055 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1057 for(c = 1;c < num_channels;c++)
1059 ALfilterState_copyParams(&voice->Direct.Params[c].LowPass,
1060 &voice->Direct.Params[0].LowPass);
1061 ALfilterState_copyParams(&voice->Direct.Params[c].HighPass,
1062 &voice->Direct.Params[0].HighPass);
1065 for(i = 0;i < NumSends;i++)
1067 ALfloat hfScale = props->Send[i].HFReference / Frequency;
1068 ALfloat lfScale = props->Send[i].LFReference / Frequency;
1069 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
1070 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
1072 voice->Send[i].FilterType = AF_None;
1073 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1074 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1075 ALfilterState_setParams(
1076 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
1077 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1079 ALfilterState_setParams(
1080 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
1081 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1083 for(c = 1;c < num_channels;c++)
1085 ALfilterState_copyParams(&voice->Send[i].Params[c].LowPass,
1086 &voice->Send[i].Params[0].LowPass);
1087 ALfilterState_copyParams(&voice->Send[i].Params[c].HighPass,
1088 &voice->Send[i].Params[0].HighPass);
1093 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1095 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1096 const ALCdevice *Device = ALContext->Device;
1097 const ALlistener *Listener = ALContext->Listener;
1098 ALfloat DryGain, DryGainHF, DryGainLF;
1099 ALfloat WetGain[MAX_SENDS];
1100 ALfloat WetGainHF[MAX_SENDS];
1101 ALfloat WetGainLF[MAX_SENDS];
1102 ALeffectslot *SendSlots[MAX_SENDS];
1103 ALfloat Pitch;
1104 ALsizei i;
1106 voice->Direct.Buffer = Device->Dry.Buffer;
1107 voice->Direct.Channels = Device->Dry.NumChannels;
1108 for(i = 0;i < Device->NumAuxSends;i++)
1110 SendSlots[i] = props->Send[i].Slot;
1111 if(!SendSlots[i] && i == 0)
1112 SendSlots[i] = ALContext->DefaultSlot;
1113 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1115 SendSlots[i] = NULL;
1116 voice->Send[i].Buffer = NULL;
1117 voice->Send[i].Channels = 0;
1119 else
1121 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1122 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1126 /* Calculate the stepping value */
1127 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1128 if(Pitch > (ALfloat)MAX_PITCH)
1129 voice->Step = MAX_PITCH<<FRACTIONBITS;
1130 else
1131 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1132 if(props->Resampler == BSinc24Resampler)
1133 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1134 else if(props->Resampler == BSinc12Resampler)
1135 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1136 voice->Resampler = SelectResampler(props->Resampler);
1138 /* Calculate gains */
1139 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1140 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1141 DryGain = minf(DryGain, GAIN_MIX_MAX);
1142 DryGainHF = props->Direct.GainHF;
1143 DryGainLF = props->Direct.GainLF;
1144 for(i = 0;i < Device->NumAuxSends;i++)
1146 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1147 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1148 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1149 WetGainHF[i] = props->Send[i].GainHF;
1150 WetGainLF[i] = props->Send[i].GainLF;
1153 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1154 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1157 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1159 const ALCdevice *Device = ALContext->Device;
1160 const ALlistener *Listener = ALContext->Listener;
1161 const ALsizei NumSends = Device->NumAuxSends;
1162 aluVector Position, Velocity, Direction, SourceToListener;
1163 ALfloat Distance, ClampedDist, DopplerFactor;
1164 ALeffectslot *SendSlots[MAX_SENDS];
1165 ALfloat RoomRolloff[MAX_SENDS];
1166 ALfloat DecayDistance[MAX_SENDS];
1167 ALfloat DecayHFDistance[MAX_SENDS];
1168 ALfloat DryGain, DryGainHF, DryGainLF;
1169 ALfloat WetGain[MAX_SENDS];
1170 ALfloat WetGainHF[MAX_SENDS];
1171 ALfloat WetGainLF[MAX_SENDS];
1172 bool directional;
1173 ALfloat dir[3];
1174 ALfloat spread;
1175 ALfloat Pitch;
1176 ALint i;
1178 /* Set mixing buffers and get send parameters. */
1179 voice->Direct.Buffer = Device->Dry.Buffer;
1180 voice->Direct.Channels = Device->Dry.NumChannels;
1181 for(i = 0;i < NumSends;i++)
1183 SendSlots[i] = props->Send[i].Slot;
1184 if(!SendSlots[i] && i == 0)
1185 SendSlots[i] = ALContext->DefaultSlot;
1186 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1188 SendSlots[i] = NULL;
1189 RoomRolloff[i] = 0.0f;
1190 DecayDistance[i] = 0.0f;
1191 DecayHFDistance[i] = 0.0f;
1193 else if(SendSlots[i]->Params.AuxSendAuto)
1195 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1196 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1197 Listener->Params.ReverbSpeedOfSound;
1198 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1199 if(SendSlots[i]->Params.DecayHFLimit)
1201 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1202 if(airAbsorption < 1.0f)
1204 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1205 DecayHFDistance[i] = minf(limitRatio, DecayHFDistance[i]);
1209 else
1211 /* If the slot's auxiliary send auto is off, the data sent to the
1212 * effect slot is the same as the dry path, sans filter effects */
1213 RoomRolloff[i] = props->RolloffFactor;
1214 DecayDistance[i] = 0.0f;
1215 DecayHFDistance[i] = 0.0f;
1218 if(!SendSlots[i])
1220 voice->Send[i].Buffer = NULL;
1221 voice->Send[i].Channels = 0;
1223 else
1225 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1226 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1230 /* Transform source to listener space (convert to head relative) */
1231 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1232 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1233 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1234 if(props->HeadRelative == AL_FALSE)
1236 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1237 /* Transform source vectors */
1238 Position = aluMatrixfVector(Matrix, &Position);
1239 Velocity = aluMatrixfVector(Matrix, &Velocity);
1240 Direction = aluMatrixfVector(Matrix, &Direction);
1242 else
1244 const aluVector *lvelocity = &Listener->Params.Velocity;
1245 /* Offset the source velocity to be relative of the listener velocity */
1246 Velocity.v[0] += lvelocity->v[0];
1247 Velocity.v[1] += lvelocity->v[1];
1248 Velocity.v[2] += lvelocity->v[2];
1251 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1252 SourceToListener.v[0] = -Position.v[0];
1253 SourceToListener.v[1] = -Position.v[1];
1254 SourceToListener.v[2] = -Position.v[2];
1255 SourceToListener.v[3] = 0.0f;
1256 Distance = aluNormalize(SourceToListener.v);
1258 /* Initial source gain */
1259 DryGain = props->Gain;
1260 DryGainHF = 1.0f;
1261 DryGainLF = 1.0f;
1262 for(i = 0;i < NumSends;i++)
1264 WetGain[i] = props->Gain;
1265 WetGainHF[i] = 1.0f;
1266 WetGainLF[i] = 1.0f;
1269 /* Calculate distance attenuation */
1270 ClampedDist = Distance;
1272 switch(Listener->Params.SourceDistanceModel ?
1273 props->DistanceModel : Listener->Params.DistanceModel)
1275 case InverseDistanceClamped:
1276 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1277 if(props->MaxDistance < props->RefDistance)
1278 break;
1279 /*fall-through*/
1280 case InverseDistance:
1281 if(!(props->RefDistance > 0.0f))
1282 ClampedDist = props->RefDistance;
1283 else
1285 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1286 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1287 for(i = 0;i < NumSends;i++)
1289 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1290 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1293 break;
1295 case LinearDistanceClamped:
1296 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1297 if(props->MaxDistance < props->RefDistance)
1298 break;
1299 /*fall-through*/
1300 case LinearDistance:
1301 if(!(props->MaxDistance != props->RefDistance))
1302 ClampedDist = props->RefDistance;
1303 else
1305 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1306 (props->MaxDistance-props->RefDistance);
1307 DryGain *= maxf(1.0f - attn, 0.0f);
1308 for(i = 0;i < NumSends;i++)
1310 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1311 (props->MaxDistance-props->RefDistance);
1312 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1315 break;
1317 case ExponentDistanceClamped:
1318 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1319 if(props->MaxDistance < props->RefDistance)
1320 break;
1321 /*fall-through*/
1322 case ExponentDistance:
1323 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1324 ClampedDist = props->RefDistance;
1325 else
1327 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1328 for(i = 0;i < NumSends;i++)
1329 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1331 break;
1333 case DisableDistance:
1334 ClampedDist = props->RefDistance;
1335 break;
1338 /* Distance-based air absorption */
1339 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1341 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1342 Listener->Params.MetersPerUnit;
1343 if(props->AirAbsorptionFactor > 0.0f)
1345 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1346 DryGainHF *= hfattn;
1347 for(i = 0;i < NumSends;i++)
1348 WetGainHF[i] *= hfattn;
1351 if(props->WetGainAuto)
1353 /* Apply a decay-time transformation to the wet path, based on the
1354 * source distance in meters. The initial decay of the reverb
1355 * effect is calculated and applied to the wet path.
1357 for(i = 0;i < NumSends;i++)
1359 ALfloat gain;
1361 if(!(DecayDistance[i] > 0.0f))
1362 continue;
1364 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1365 WetGain[i] *= gain;
1366 /* Yes, the wet path's air absorption is applied with
1367 * WetGainAuto on, rather than WetGainHFAuto.
1369 if(gain > 0.0f)
1371 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1372 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1378 /* Calculate directional soundcones */
1379 if(directional && props->InnerAngle < 360.0f)
1381 ALfloat ConeVolume;
1382 ALfloat ConeHF;
1383 ALfloat Angle;
1385 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1386 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1387 if(!(Angle > props->InnerAngle))
1389 ConeVolume = 1.0f;
1390 ConeHF = 1.0f;
1392 else if(Angle < props->OuterAngle)
1394 ALfloat scale = ( Angle-props->InnerAngle) /
1395 (props->OuterAngle-props->InnerAngle);
1396 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1397 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1399 else
1401 ConeVolume = props->OuterGain;
1402 ConeHF = props->OuterGainHF;
1405 DryGain *= ConeVolume;
1406 if(props->DryGainHFAuto)
1407 DryGainHF *= ConeHF;
1408 if(props->WetGainAuto)
1410 for(i = 0;i < NumSends;i++)
1411 WetGain[i] *= ConeVolume;
1413 if(props->WetGainHFAuto)
1415 for(i = 0;i < NumSends;i++)
1416 WetGainHF[i] *= ConeHF;
1420 /* Apply gain and frequency filters */
1421 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1422 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1423 DryGainHF *= props->Direct.GainHF;
1424 DryGainLF *= props->Direct.GainLF;
1425 for(i = 0;i < NumSends;i++)
1427 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1428 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1429 WetGainHF[i] *= props->Send[i].GainHF;
1430 WetGainLF[i] *= props->Send[i].GainLF;
1434 /* Initial source pitch */
1435 Pitch = props->Pitch;
1437 /* Calculate velocity-based doppler effect */
1438 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1439 if(DopplerFactor > 0.0f)
1441 const aluVector *lvelocity = &Listener->Params.Velocity;
1442 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1443 ALfloat vss, vls;
1445 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1446 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1448 if(!(vls < SpeedOfSound))
1450 /* Listener moving away from the source at the speed of sound.
1451 * Sound waves can't catch it.
1453 Pitch = 0.0f;
1455 else if(!(vss < SpeedOfSound))
1457 /* Source moving toward the listener at the speed of sound. Sound
1458 * waves bunch up to extreme frequencies.
1460 Pitch = HUGE_VALF;
1462 else
1464 /* Source and listener movement is nominal. Calculate the proper
1465 * doppler shift.
1467 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1471 /* Adjust pitch based on the buffer and output frequencies, and calculate
1472 * fixed-point stepping value.
1474 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1475 if(Pitch > (ALfloat)MAX_PITCH)
1476 voice->Step = MAX_PITCH<<FRACTIONBITS;
1477 else
1478 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1479 if(props->Resampler == BSinc24Resampler)
1480 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1481 else if(props->Resampler == BSinc12Resampler)
1482 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1483 voice->Resampler = SelectResampler(props->Resampler);
1485 if(Distance > FLT_EPSILON)
1487 dir[0] = -SourceToListener.v[0];
1488 /* Clamp Y, in case rounding errors caused it to end up outside of
1489 * -1...+1.
1491 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1492 dir[2] = -SourceToListener.v[2] * ZScale;
1494 else
1496 dir[0] = 0.0f;
1497 dir[1] = 0.0f;
1498 dir[2] = -1.0f;
1500 if(props->Radius > Distance)
1501 spread = F_TAU - Distance/props->Radius*F_PI;
1502 else if(Distance > FLT_EPSILON)
1503 spread = asinf(props->Radius / Distance) * 2.0f;
1504 else
1505 spread = 0.0f;
1507 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1508 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1511 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1513 ALbufferlistitem *BufferListItem;
1514 struct ALvoiceProps *props;
1516 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1517 if(!props && !force) return;
1519 if(props)
1521 memcpy(voice->Props, props,
1522 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1525 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1527 props = voice->Props;
1529 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1530 while(BufferListItem != NULL)
1532 const ALbuffer *buffer;
1533 if(BufferListItem->num_buffers >= 1 && (buffer=BufferListItem->buffers[0]) != NULL)
1535 if(props->SpatializeMode == SpatializeOn ||
1536 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1537 CalcAttnSourceParams(voice, props, buffer, context);
1538 else
1539 CalcNonAttnSourceParams(voice, props, buffer, context);
1540 break;
1542 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1547 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1549 ALvoice **voice, **voice_end;
1550 ALsource *source;
1551 ALsizei i;
1553 IncrementRef(&ctx->UpdateCount);
1554 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1556 bool cforce = CalcContextParams(ctx);
1557 bool force = CalcListenerParams(ctx) | cforce;
1558 for(i = 0;i < slots->count;i++)
1559 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1561 voice = ctx->Voices;
1562 voice_end = voice + ctx->VoiceCount;
1563 for(;voice != voice_end;++voice)
1565 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1566 if(source) CalcSourceParams(*voice, ctx, force);
1569 IncrementRef(&ctx->UpdateCount);
1573 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1574 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1575 ALsizei NumChannels)
1577 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1578 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1579 ALsizei i;
1581 /* Apply an all-pass to all channels, except the front-left and front-
1582 * right, so they maintain the same relative phase.
1584 for(i = 0;i < NumChannels;i++)
1586 if(i == lidx || i == ridx)
1587 continue;
1588 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1591 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1592 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1594 for(i = 0;i < SamplesToDo;i++)
1596 ALfloat lfsum, hfsum;
1597 ALfloat m, s, c;
1599 lfsum = lsplit[0][i] + rsplit[0][i];
1600 hfsum = lsplit[1][i] + rsplit[1][i];
1601 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1603 /* This pans the separate low- and high-frequency sums between being on
1604 * the center channel and the left/right channels. The low-frequency
1605 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1606 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1607 * values can be tweaked.
1609 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1610 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1612 /* The generated center channel signal adds to the existing signal,
1613 * while the modified left and right channels replace.
1615 Buffer[lidx][i] = (m + s) * 0.5f;
1616 Buffer[ridx][i] = (m - s) * 0.5f;
1617 Buffer[cidx][i] += c * 0.5f;
1621 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1622 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1624 ALsizei i, c;
1626 Values = ASSUME_ALIGNED(Values, 16);
1627 for(c = 0;c < numchans;c++)
1629 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1630 const ALfloat gain = distcomp[c].Gain;
1631 const ALsizei base = distcomp[c].Length;
1632 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1634 if(base == 0)
1636 if(gain < 1.0f)
1638 for(i = 0;i < SamplesToDo;i++)
1639 inout[i] *= gain;
1641 continue;
1644 if(SamplesToDo >= base)
1646 for(i = 0;i < base;i++)
1647 Values[i] = distbuf[i];
1648 for(;i < SamplesToDo;i++)
1649 Values[i] = inout[i-base];
1650 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1652 else
1654 for(i = 0;i < SamplesToDo;i++)
1655 Values[i] = distbuf[i];
1656 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1657 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1659 for(i = 0;i < SamplesToDo;i++)
1660 inout[i] = Values[i]*gain;
1664 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1665 const ALfloat quant_scale, const ALsizei SamplesToDo,
1666 const ALsizei numchans)
1668 const ALfloat invscale = 1.0f / quant_scale;
1669 ALuint seed = *dither_seed;
1670 ALsizei c, i;
1672 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1673 * values between -1 and +1). Step 2 is to add the noise to the samples,
1674 * before rounding and after scaling up to the desired quantization depth.
1676 for(c = 0;c < numchans;c++)
1678 ALfloat *restrict samples = Samples[c];
1679 for(i = 0;i < SamplesToDo;i++)
1681 ALfloat val = samples[i] * quant_scale;
1682 ALuint rng0 = dither_rng(&seed);
1683 ALuint rng1 = dither_rng(&seed);
1684 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1685 samples[i] = roundf(val) * invscale;
1688 *dither_seed = seed;
1692 static inline ALfloat Conv_ALfloat(ALfloat val)
1693 { return val; }
1694 static inline ALint Conv_ALint(ALfloat val)
1696 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1697 * integer range normalized floats can be safely converted to (a bit of the
1698 * exponent helps out, effectively giving 25 bits).
1700 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1702 static inline ALshort Conv_ALshort(ALfloat val)
1703 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1704 static inline ALbyte Conv_ALbyte(ALfloat val)
1705 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1707 /* Define unsigned output variations. */
1708 #define DECL_TEMPLATE(T, func, O) \
1709 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1711 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1712 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1713 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1715 #undef DECL_TEMPLATE
1717 #define DECL_TEMPLATE(T, A) \
1718 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1719 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1720 ALsizei numchans) \
1722 ALsizei i, j; \
1723 for(j = 0;j < numchans;j++) \
1725 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1726 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1728 for(i = 0;i < SamplesToDo;i++) \
1729 out[i*numchans] = Conv_##T(in[i]); \
1733 DECL_TEMPLATE(ALfloat, F32)
1734 DECL_TEMPLATE(ALuint, UI32)
1735 DECL_TEMPLATE(ALint, I32)
1736 DECL_TEMPLATE(ALushort, UI16)
1737 DECL_TEMPLATE(ALshort, I16)
1738 DECL_TEMPLATE(ALubyte, UI8)
1739 DECL_TEMPLATE(ALbyte, I8)
1741 #undef DECL_TEMPLATE
1744 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1746 ALsizei SamplesToDo;
1747 ALsizei SamplesDone;
1748 ALCcontext *ctx;
1749 ALsizei i, c;
1751 START_MIXER_MODE();
1752 for(SamplesDone = 0;SamplesDone < NumSamples;)
1754 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1755 for(c = 0;c < device->Dry.NumChannels;c++)
1756 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1757 if(device->Dry.Buffer != device->FOAOut.Buffer)
1758 for(c = 0;c < device->FOAOut.NumChannels;c++)
1759 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1760 if(device->Dry.Buffer != device->RealOut.Buffer)
1761 for(c = 0;c < device->RealOut.NumChannels;c++)
1762 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1764 IncrementRef(&device->MixCount);
1766 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1767 while(ctx)
1769 const struct ALeffectslotArray *auxslots;
1771 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1772 UpdateContextSources(ctx, auxslots);
1774 for(i = 0;i < auxslots->count;i++)
1776 ALeffectslot *slot = auxslots->slot[i];
1777 for(c = 0;c < slot->NumChannels;c++)
1778 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1781 /* source processing */
1782 for(i = 0;i < ctx->VoiceCount;i++)
1784 ALvoice *voice = ctx->Voices[i];
1785 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1786 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1787 voice->Step > 0)
1789 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1791 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1792 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1797 /* effect slot processing */
1798 for(i = 0;i < auxslots->count;i++)
1800 const ALeffectslot *slot = auxslots->slot[i];
1801 ALeffectState *state = slot->Params.EffectState;
1802 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1803 state->OutChannels);
1806 ctx = ctx->next;
1809 /* Increment the clock time. Every second's worth of samples is
1810 * converted and added to clock base so that large sample counts don't
1811 * overflow during conversion. This also guarantees an exact, stable
1812 * conversion. */
1813 device->SamplesDone += SamplesToDo;
1814 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1815 device->SamplesDone %= device->Frequency;
1816 IncrementRef(&device->MixCount);
1818 /* Apply post-process for finalizing the Dry mix to the RealOut
1819 * (Ambisonic decode, UHJ encode, etc).
1821 if(LIKELY(device->PostProcess))
1822 device->PostProcess(device, SamplesToDo);
1824 if(OutBuffer)
1826 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1827 ALsizei Channels = device->RealOut.NumChannels;
1829 if(device->Stablizer)
1831 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1832 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1833 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1834 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1836 ApplyStablizer(device->Stablizer, Buffer, lidx, ridx, cidx,
1837 SamplesToDo, Channels);
1840 ApplyDistanceComp(Buffer, device->ChannelDelay, device->TempBuffer[0],
1841 SamplesToDo, Channels);
1843 if(device->Limiter)
1844 ApplyCompression(device->Limiter, Channels, SamplesToDo, Buffer);
1846 if(device->DitherDepth > 0.0f)
1847 ApplyDither(Buffer, &device->DitherSeed, device->DitherDepth, SamplesToDo,
1848 Channels);
1850 switch(device->FmtType)
1852 case DevFmtByte:
1853 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1854 break;
1855 case DevFmtUByte:
1856 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1857 break;
1858 case DevFmtShort:
1859 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1860 break;
1861 case DevFmtUShort:
1862 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1863 break;
1864 case DevFmtInt:
1865 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1866 break;
1867 case DevFmtUInt:
1868 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1869 break;
1870 case DevFmtFloat:
1871 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1872 break;
1876 SamplesDone += SamplesToDo;
1878 END_MIXER_MODE();
1882 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1884 ALCcontext *ctx;
1885 AsyncEvent evt;
1886 va_list args;
1887 int msglen;
1889 if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
1890 return;
1892 evt.EnumType = EventType_Disconnected;
1893 evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1894 evt.ObjectId = 0;
1895 evt.Param = 0;
1897 va_start(args, msg);
1898 msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
1899 va_end(args);
1901 if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
1903 evt.Message[sizeof(evt.Message)-1] = 0;
1904 msglen = (int)strlen(evt.Message);
1906 if(msglen > 0)
1907 msg = evt.Message;
1908 else
1910 msg = "<internal error constructing message>";
1911 msglen = (int)strlen(msg);
1914 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1915 while(ctx)
1917 ALsizei i;
1919 if((ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire)&EventType_Disconnected) &&
1920 ll_ringbuffer_write_space(ctx->AsyncEvents) > 0)
1922 ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1);
1923 alsem_post(&ctx->EventSem);
1926 for(i = 0;i < ctx->VoiceCount;i++)
1928 ALvoice *voice = ctx->Voices[i];
1929 ALsource *source;
1931 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_acq_rel);
1932 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1934 if(source)
1936 ALenum playing = AL_PLAYING;
1937 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1940 ctx->VoiceCount = 0;
1942 ctx = ctx->next;