Store effects in an array of lists
[openal-soft.git] / Alc / ALu.c
blobd190a1be578205ae3882b92e0211124a12c611bb
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"
42 #include "fpu_modes.h"
43 #include "cpu_caps.h"
44 #include "mixer_defs.h"
45 #include "bsinc_inc.h"
47 #include "backends/base.h"
50 extern inline ALfloat minf(ALfloat a, ALfloat b);
51 extern inline ALfloat maxf(ALfloat a, ALfloat b);
52 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
54 extern inline ALdouble mind(ALdouble a, ALdouble b);
55 extern inline ALdouble maxd(ALdouble a, ALdouble b);
56 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
58 extern inline ALuint minu(ALuint a, ALuint b);
59 extern inline ALuint maxu(ALuint a, ALuint b);
60 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
62 extern inline ALint mini(ALint a, ALint b);
63 extern inline ALint maxi(ALint a, ALint b);
64 extern inline ALint clampi(ALint val, ALint min, ALint max);
66 extern inline ALint64 mini64(ALint64 a, ALint64 b);
67 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
68 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
70 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
71 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
72 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
74 extern inline size_t minz(size_t a, size_t b);
75 extern inline size_t maxz(size_t a, size_t b);
76 extern inline size_t clampz(size_t val, size_t min, size_t max);
78 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
79 extern inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu);
81 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
83 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
84 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
85 extern inline void aluMatrixfSet(aluMatrixf *matrix,
86 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
87 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
88 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
89 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
92 /* Cone scalar */
93 ALfloat ConeScale = 1.0f;
95 /* Localized Z scalar for mono sources */
96 ALfloat ZScale = 1.0f;
98 /* Force default speed of sound for distance-related reverb decay. */
99 ALboolean OverrideReverbSpeedOfSound = AL_FALSE;
101 const aluMatrixf IdentityMatrixf = {{
102 { 1.0f, 0.0f, 0.0f, 0.0f },
103 { 0.0f, 1.0f, 0.0f, 0.0f },
104 { 0.0f, 0.0f, 1.0f, 0.0f },
105 { 0.0f, 0.0f, 0.0f, 1.0f },
109 struct ChanMap {
110 enum Channel channel;
111 ALfloat angle;
112 ALfloat elevation;
115 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
118 void DeinitVoice(ALvoice *voice)
120 al_free(ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL));
124 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
126 #ifdef HAVE_NEON
127 if((CPUCapFlags&CPU_CAP_NEON))
128 return MixDirectHrtf_Neon;
129 #endif
130 #ifdef HAVE_SSE
131 if((CPUCapFlags&CPU_CAP_SSE))
132 return MixDirectHrtf_SSE;
133 #endif
135 return MixDirectHrtf_C;
139 /* Prior to VS2013, MSVC lacks the round() family of functions. */
140 #if defined(_MSC_VER) && _MSC_VER < 1800
141 static float roundf(float val)
143 if(val < 0.0f)
144 return ceilf(val-0.5f);
145 return floorf(val+0.5f);
147 #endif
149 /* This RNG method was created based on the math found in opusdec. It's quick,
150 * and starting with a seed value of 22222, is suitable for generating
151 * whitenoise.
153 static inline ALuint dither_rng(ALuint *seed)
155 *seed = (*seed * 96314165) + 907633515;
156 return *seed;
160 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
162 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
163 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
164 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
167 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
169 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
172 static ALfloat aluNormalize(ALfloat *vec)
174 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
175 if(length > 0.0f)
177 ALfloat inv_length = 1.0f/length;
178 vec[0] *= inv_length;
179 vec[1] *= inv_length;
180 vec[2] *= inv_length;
182 return length;
185 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
187 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
189 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];
190 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];
191 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];
194 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
196 aluVector v;
197 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];
198 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];
199 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];
200 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];
201 return v;
205 void aluInit(void)
207 MixDirectHrtf = SelectHrtfMixer();
210 /* Prepares the interpolator for a given rate (determined by increment). A
211 * result of AL_FALSE indicates that the filter output will completely cut
212 * the input signal.
214 * With a bit of work, and a trade of memory for CPU cost, this could be
215 * modified for use with an interpolated increment for buttery-smooth pitch
216 * changes.
218 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
220 ALfloat sf;
221 ALsizei si;
223 if(increment > FRACTIONONE)
225 sf = (ALfloat)FRACTIONONE / increment;
226 sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
227 si = fastf2i(sf);
228 /* The interpolation factor is fit to this diagonally-symmetric curve
229 * to reduce the transition ripple caused by interpolating different
230 * scales of the sinc function.
232 sf = 1.0f - cosf(asinf(sf - si));
234 else
236 sf = 0.0f;
237 si = BSINC_SCALE_COUNT - 1;
240 state->sf = sf;
241 state->m = table->m[si];
242 state->l = -((state->m/2) - 1);
243 state->filter = table->Tab + table->filterOffset[si];
247 static bool CalcContextParams(ALCcontext *Context)
249 ALlistener *Listener = Context->Listener;
250 struct ALcontextProps *props;
252 props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel);
253 if(!props) return false;
255 Listener->Params.MetersPerUnit = props->MetersPerUnit;
257 Listener->Params.DopplerFactor = props->DopplerFactor;
258 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
259 if(!OverrideReverbSpeedOfSound)
260 Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
261 Listener->Params.MetersPerUnit;
263 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
264 Listener->Params.DistanceModel = props->DistanceModel;
266 ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props);
267 return true;
270 static bool CalcListenerParams(ALCcontext *Context)
272 ALlistener *Listener = Context->Listener;
273 ALfloat N[3], V[3], U[3], P[3];
274 struct ALlistenerProps *props;
275 aluVector vel;
277 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
278 if(!props) return false;
280 /* AT then UP */
281 N[0] = props->Forward[0];
282 N[1] = props->Forward[1];
283 N[2] = props->Forward[2];
284 aluNormalize(N);
285 V[0] = props->Up[0];
286 V[1] = props->Up[1];
287 V[2] = props->Up[2];
288 aluNormalize(V);
289 /* Build and normalize right-vector */
290 aluCrossproduct(N, V, U);
291 aluNormalize(U);
293 aluMatrixfSet(&Listener->Params.Matrix,
294 U[0], V[0], -N[0], 0.0,
295 U[1], V[1], -N[1], 0.0,
296 U[2], V[2], -N[2], 0.0,
297 0.0, 0.0, 0.0, 1.0
300 P[0] = props->Position[0];
301 P[1] = props->Position[1];
302 P[2] = props->Position[2];
303 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
304 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
306 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
307 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
309 Listener->Params.Gain = props->Gain * Context->GainBoost;
311 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props);
312 return true;
315 static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
317 struct ALeffectslotProps *props;
318 ALeffectState *state;
320 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
321 if(!props && !force) return false;
323 if(props)
325 slot->Params.Gain = props->Gain;
326 slot->Params.AuxSendAuto = props->AuxSendAuto;
327 slot->Params.EffectType = props->Type;
328 slot->Params.EffectProps = props->Props;
329 if(IsReverbEffect(props->Type))
331 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
332 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
333 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
334 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
335 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
337 else
339 slot->Params.RoomRolloff = 0.0f;
340 slot->Params.DecayTime = 0.0f;
341 slot->Params.DecayHFRatio = 0.0f;
342 slot->Params.DecayHFLimit = AL_FALSE;
343 slot->Params.AirAbsorptionGainHF = 1.0f;
346 /* Swap effect states. No need to play with the ref counts since they
347 * keep the same number of refs.
349 state = props->State;
350 props->State = slot->Params.EffectState;
351 slot->Params.EffectState = state;
353 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
355 else
356 state = slot->Params.EffectState;
358 V(state,update)(context, slot, &slot->Params.EffectProps);
359 return true;
363 static const struct ChanMap MonoMap[1] = {
364 { FrontCenter, 0.0f, 0.0f }
365 }, RearMap[2] = {
366 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
367 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
368 }, QuadMap[4] = {
369 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
370 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
371 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
372 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
373 }, X51Map[6] = {
374 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
375 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
376 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
377 { LFE, 0.0f, 0.0f },
378 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
379 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
380 }, X61Map[7] = {
381 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
382 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
383 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
384 { LFE, 0.0f, 0.0f },
385 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
386 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
387 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
388 }, X71Map[8] = {
389 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
390 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
391 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
392 { LFE, 0.0f, 0.0f },
393 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
394 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
395 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
396 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
399 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
400 const ALfloat Spread, const ALfloat DryGain,
401 const ALfloat DryGainHF, const ALfloat DryGainLF,
402 const ALfloat *WetGain, const ALfloat *WetGainLF,
403 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
404 const ALbuffer *Buffer, const struct ALvoiceProps *props,
405 const ALlistener *Listener, const ALCdevice *Device)
407 struct ChanMap StereoMap[2] = {
408 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
409 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
411 bool DirectChannels = props->DirectChannels;
412 const ALsizei NumSends = Device->NumAuxSends;
413 const ALuint Frequency = Device->Frequency;
414 const struct ChanMap *chans = NULL;
415 ALsizei num_channels = 0;
416 bool isbformat = false;
417 ALfloat downmix_gain = 1.0f;
418 ALsizei c, i, j;
420 switch(Buffer->FmtChannels)
422 case FmtMono:
423 chans = MonoMap;
424 num_channels = 1;
425 /* Mono buffers are never played direct. */
426 DirectChannels = false;
427 break;
429 case FmtStereo:
430 /* Convert counter-clockwise to clockwise. */
431 StereoMap[0].angle = -props->StereoPan[0];
432 StereoMap[1].angle = -props->StereoPan[1];
434 chans = StereoMap;
435 num_channels = 2;
436 downmix_gain = 1.0f / 2.0f;
437 break;
439 case FmtRear:
440 chans = RearMap;
441 num_channels = 2;
442 downmix_gain = 1.0f / 2.0f;
443 break;
445 case FmtQuad:
446 chans = QuadMap;
447 num_channels = 4;
448 downmix_gain = 1.0f / 4.0f;
449 break;
451 case FmtX51:
452 chans = X51Map;
453 num_channels = 6;
454 /* NOTE: Excludes LFE. */
455 downmix_gain = 1.0f / 5.0f;
456 break;
458 case FmtX61:
459 chans = X61Map;
460 num_channels = 7;
461 /* NOTE: Excludes LFE. */
462 downmix_gain = 1.0f / 6.0f;
463 break;
465 case FmtX71:
466 chans = X71Map;
467 num_channels = 8;
468 /* NOTE: Excludes LFE. */
469 downmix_gain = 1.0f / 7.0f;
470 break;
472 case FmtBFormat2D:
473 num_channels = 3;
474 isbformat = true;
475 DirectChannels = false;
476 break;
478 case FmtBFormat3D:
479 num_channels = 4;
480 isbformat = true;
481 DirectChannels = false;
482 break;
485 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
486 if(isbformat)
488 /* Special handling for B-Format sources. */
490 if(Distance > FLT_EPSILON)
492 /* Panning a B-Format sound toward some direction is easy. Just pan
493 * the first (W) channel as a normal mono sound and silence the
494 * others.
496 ALfloat coeffs[MAX_AMBI_COEFFS];
498 if(Device->AvgSpeakerDist > 0.0f)
500 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
501 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
502 (mdist * (ALfloat)Device->Frequency);
503 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
504 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
505 /* Clamp w0 for really close distances, to prevent excessive
506 * bass.
508 w0 = minf(w0, w1*4.0f);
510 /* Only need to adjust the first channel of a B-Format source. */
511 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
512 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
513 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
515 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
516 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
517 voice->Flags |= VOICE_HAS_NFC;
520 if(Device->Render_Mode == StereoPair)
522 ALfloat ev = asinf(Dir[1]);
523 ALfloat az = atan2f(Dir[0], -Dir[2]);
524 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
526 else
527 CalcDirectionCoeffs(Dir, Spread, coeffs);
529 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
530 ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
531 voice->Direct.Params[0].Gains.Target);
532 for(c = 1;c < num_channels;c++)
534 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
535 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
538 for(i = 0;i < NumSends;i++)
540 const ALeffectslot *Slot = SendSlots[i];
541 if(Slot)
542 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
543 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
545 else
546 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
547 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
548 for(c = 1;c < num_channels;c++)
550 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
551 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
555 else
557 /* Local B-Format sources have their XYZ channels rotated according
558 * to the orientation.
560 ALfloat N[3], V[3], U[3];
561 aluMatrixf matrix;
562 ALfloat scale;
564 if(Device->AvgSpeakerDist > 0.0f)
566 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
567 * is what we want for FOA input. The first channel may have
568 * been previously re-adjusted if panned, so reset it.
570 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f);
571 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f);
572 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 0.0f);
574 voice->Direct.ChannelsPerOrder[0] = 1;
575 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
576 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
577 voice->Direct.ChannelsPerOrder[i] = 0;
578 voice->Flags |= VOICE_HAS_NFC;
581 /* AT then UP */
582 N[0] = props->Orientation[0][0];
583 N[1] = props->Orientation[0][1];
584 N[2] = props->Orientation[0][2];
585 aluNormalize(N);
586 V[0] = props->Orientation[1][0];
587 V[1] = props->Orientation[1][1];
588 V[2] = props->Orientation[1][2];
589 aluNormalize(V);
590 if(!props->HeadRelative)
592 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
593 aluMatrixfFloat3(N, 0.0f, lmatrix);
594 aluMatrixfFloat3(V, 0.0f, lmatrix);
596 /* Build and normalize right-vector */
597 aluCrossproduct(N, V, U);
598 aluNormalize(U);
600 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
601 scale = 1.732050808f;
602 aluMatrixfSet(&matrix,
603 1.414213562f, 0.0f, 0.0f, 0.0f,
604 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
605 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
606 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
609 voice->Direct.Buffer = Device->FOAOut.Buffer;
610 voice->Direct.Channels = Device->FOAOut.NumChannels;
611 for(c = 0;c < num_channels;c++)
612 ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
613 voice->Direct.Params[c].Gains.Target);
614 for(i = 0;i < NumSends;i++)
616 const ALeffectslot *Slot = SendSlots[i];
617 if(Slot)
619 for(c = 0;c < num_channels;c++)
620 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
621 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
624 else
626 for(c = 0;c < num_channels;c++)
627 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
628 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
633 else if(DirectChannels)
635 /* Direct source channels always play local. Skip the virtual channels
636 * and write inputs to the matching real outputs.
638 voice->Direct.Buffer = Device->RealOut.Buffer;
639 voice->Direct.Channels = Device->RealOut.NumChannels;
641 for(c = 0;c < num_channels;c++)
643 int idx;
644 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
645 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
646 if((idx=GetChannelIdxByName(&Device->RealOut, chans[c].channel)) != -1)
647 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
650 /* Auxiliary sends still use normal channel panning since they mix to
651 * B-Format, which can't channel-match.
653 for(c = 0;c < num_channels;c++)
655 ALfloat coeffs[MAX_AMBI_COEFFS];
656 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
658 for(i = 0;i < NumSends;i++)
660 const ALeffectslot *Slot = SendSlots[i];
661 if(Slot)
662 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
663 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
665 else
666 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
667 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
671 else if(Device->Render_Mode == HrtfRender)
673 /* Full HRTF rendering. Skip the virtual channels and render to the
674 * real outputs.
676 voice->Direct.Buffer = Device->RealOut.Buffer;
677 voice->Direct.Channels = Device->RealOut.NumChannels;
679 if(Distance > FLT_EPSILON)
681 ALfloat coeffs[MAX_AMBI_COEFFS];
682 ALfloat ev, az;
684 ev = asinf(Dir[1]);
685 az = atan2f(Dir[0], -Dir[2]);
687 /* Get the HRIR coefficients and delays just once, for the given
688 * source direction.
690 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
691 voice->Direct.Params[0].Hrtf.Target.Coeffs,
692 voice->Direct.Params[0].Hrtf.Target.Delay);
693 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
695 /* Remaining channels use the same results as the first. */
696 for(c = 1;c < num_channels;c++)
698 /* Skip LFE */
699 if(chans[c].channel == LFE)
700 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
701 sizeof(voice->Direct.Params[c].Hrtf.Target));
702 else
703 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
706 /* Calculate the directional coefficients once, which apply to all
707 * input channels of the source sends.
709 CalcDirectionCoeffs(Dir, Spread, coeffs);
711 for(i = 0;i < NumSends;i++)
713 const ALeffectslot *Slot = SendSlots[i];
714 if(Slot)
715 for(c = 0;c < num_channels;c++)
717 /* Skip LFE */
718 if(chans[c].channel == LFE)
719 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
720 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
721 else
722 ComputePanningGainsBF(Slot->ChanMap,
723 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
724 voice->Send[i].Params[c].Gains.Target
727 else
728 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
729 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
732 else
734 /* Local sources on HRTF play with each channel panned to its
735 * relative location around the listener, providing "virtual
736 * speaker" responses.
738 for(c = 0;c < num_channels;c++)
740 ALfloat coeffs[MAX_AMBI_COEFFS];
742 if(chans[c].channel == LFE)
744 /* Skip LFE */
745 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
746 sizeof(voice->Direct.Params[c].Hrtf.Target));
747 for(i = 0;i < NumSends;i++)
749 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
750 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
752 continue;
755 /* Get the HRIR coefficients and delays for this channel
756 * position.
758 GetHrtfCoeffs(Device->HrtfHandle,
759 chans[c].elevation, chans[c].angle, Spread,
760 voice->Direct.Params[c].Hrtf.Target.Coeffs,
761 voice->Direct.Params[c].Hrtf.Target.Delay
763 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
765 /* Normal panning for auxiliary sends. */
766 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
768 for(i = 0;i < NumSends;i++)
770 const ALeffectslot *Slot = SendSlots[i];
771 if(Slot)
772 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
773 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
775 else
776 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
777 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
782 voice->Flags |= VOICE_HAS_HRTF;
784 else
786 /* Non-HRTF rendering. Use normal panning to the output. */
788 if(Distance > FLT_EPSILON)
790 ALfloat coeffs[MAX_AMBI_COEFFS];
791 ALfloat w0 = 0.0f;
793 /* Calculate NFC filter coefficient if needed. */
794 if(Device->AvgSpeakerDist > 0.0f)
796 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
797 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
798 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
799 w0 = SPEEDOFSOUNDMETRESPERSEC /
800 (mdist * (ALfloat)Device->Frequency);
801 /* Clamp w0 for really close distances, to prevent excessive
802 * bass.
804 w0 = minf(w0, w1*4.0f);
806 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
807 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
808 voice->Flags |= VOICE_HAS_NFC;
811 /* Calculate the directional coefficients once, which apply to all
812 * input channels.
814 if(Device->Render_Mode == StereoPair)
816 ALfloat ev = asinf(Dir[1]);
817 ALfloat az = atan2f(Dir[0], -Dir[2]);
818 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
820 else
821 CalcDirectionCoeffs(Dir, Spread, coeffs);
823 for(c = 0;c < num_channels;c++)
825 /* Adjust NFC filters if needed. */
826 if((voice->Flags&VOICE_HAS_NFC))
828 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
829 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
830 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
833 /* Special-case LFE */
834 if(chans[c].channel == LFE)
836 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
837 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
838 if(Device->Dry.Buffer == Device->RealOut.Buffer)
840 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
841 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
843 continue;
846 ComputeDryPanGains(&Device->Dry,
847 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
851 for(i = 0;i < NumSends;i++)
853 const ALeffectslot *Slot = SendSlots[i];
854 if(Slot)
855 for(c = 0;c < num_channels;c++)
857 /* Skip LFE */
858 if(chans[c].channel == LFE)
859 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
860 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
861 else
862 ComputePanningGainsBF(Slot->ChanMap,
863 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
864 voice->Send[i].Params[c].Gains.Target
867 else
868 for(c = 0;c < num_channels;c++)
870 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
871 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
875 else
877 ALfloat w0 = 0.0f;
879 if(Device->AvgSpeakerDist > 0.0f)
881 /* If the source distance is 0, set w0 to w1 to act as a pass-
882 * through. We still want to pass the signal through the
883 * filters so they keep an appropriate history, in case the
884 * source moves away from the listener.
886 w0 = SPEEDOFSOUNDMETRESPERSEC /
887 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
889 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
890 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
891 voice->Flags |= VOICE_HAS_NFC;
894 for(c = 0;c < num_channels;c++)
896 ALfloat coeffs[MAX_AMBI_COEFFS];
898 if((voice->Flags&VOICE_HAS_NFC))
900 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
901 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
902 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
905 /* Special-case LFE */
906 if(chans[c].channel == LFE)
908 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
909 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
910 if(Device->Dry.Buffer == Device->RealOut.Buffer)
912 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
913 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
916 for(i = 0;i < NumSends;i++)
918 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
919 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
921 continue;
924 if(Device->Render_Mode == StereoPair)
925 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
926 else
927 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
928 ComputeDryPanGains(&Device->Dry,
929 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
932 for(i = 0;i < NumSends;i++)
934 const ALeffectslot *Slot = SendSlots[i];
935 if(Slot)
936 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
937 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
939 else
940 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
941 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
948 ALfloat hfScale = props->Direct.HFReference / Frequency;
949 ALfloat lfScale = props->Direct.LFReference / Frequency;
950 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
951 ALfloat gainLF = maxf(DryGainLF, 0.001f);
953 voice->Direct.FilterType = AF_None;
954 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
955 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
956 ALfilterState_setParams(
957 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
958 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
960 ALfilterState_setParams(
961 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
962 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
964 for(c = 1;c < num_channels;c++)
966 ALfilterState_copyParams(&voice->Direct.Params[c].LowPass,
967 &voice->Direct.Params[0].LowPass);
968 ALfilterState_copyParams(&voice->Direct.Params[c].HighPass,
969 &voice->Direct.Params[0].HighPass);
972 for(i = 0;i < NumSends;i++)
974 ALfloat hfScale = props->Send[i].HFReference / Frequency;
975 ALfloat lfScale = props->Send[i].LFReference / Frequency;
976 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
977 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
979 voice->Send[i].FilterType = AF_None;
980 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
981 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
982 ALfilterState_setParams(
983 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
984 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
986 ALfilterState_setParams(
987 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
988 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
990 for(c = 1;c < num_channels;c++)
992 ALfilterState_copyParams(&voice->Send[i].Params[c].LowPass,
993 &voice->Send[i].Params[0].LowPass);
994 ALfilterState_copyParams(&voice->Send[i].Params[c].HighPass,
995 &voice->Send[i].Params[0].HighPass);
1000 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1002 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1003 const ALCdevice *Device = ALContext->Device;
1004 const ALlistener *Listener = ALContext->Listener;
1005 ALfloat DryGain, DryGainHF, DryGainLF;
1006 ALfloat WetGain[MAX_SENDS];
1007 ALfloat WetGainHF[MAX_SENDS];
1008 ALfloat WetGainLF[MAX_SENDS];
1009 ALeffectslot *SendSlots[MAX_SENDS];
1010 ALfloat Pitch;
1011 ALsizei i;
1013 voice->Direct.Buffer = Device->Dry.Buffer;
1014 voice->Direct.Channels = Device->Dry.NumChannels;
1015 for(i = 0;i < Device->NumAuxSends;i++)
1017 SendSlots[i] = props->Send[i].Slot;
1018 if(!SendSlots[i] && i == 0)
1019 SendSlots[i] = ALContext->DefaultSlot;
1020 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1022 SendSlots[i] = NULL;
1023 voice->Send[i].Buffer = NULL;
1024 voice->Send[i].Channels = 0;
1026 else
1028 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1029 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1033 /* Calculate the stepping value */
1034 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1035 if(Pitch > (ALfloat)MAX_PITCH)
1036 voice->Step = MAX_PITCH<<FRACTIONBITS;
1037 else
1038 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1039 if(props->Resampler == BSinc24Resampler)
1040 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1041 else if(props->Resampler == BSinc12Resampler)
1042 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1043 voice->Resampler = SelectResampler(props->Resampler);
1045 /* Calculate gains */
1046 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1047 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1048 DryGain = minf(DryGain, GAIN_MIX_MAX);
1049 DryGainHF = props->Direct.GainHF;
1050 DryGainLF = props->Direct.GainLF;
1051 for(i = 0;i < Device->NumAuxSends;i++)
1053 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1054 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1055 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1056 WetGainHF[i] = props->Send[i].GainHF;
1057 WetGainLF[i] = props->Send[i].GainLF;
1060 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1061 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1064 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1066 const ALCdevice *Device = ALContext->Device;
1067 const ALlistener *Listener = ALContext->Listener;
1068 const ALsizei NumSends = Device->NumAuxSends;
1069 aluVector Position, Velocity, Direction, SourceToListener;
1070 ALfloat Distance, ClampedDist, DopplerFactor;
1071 ALeffectslot *SendSlots[MAX_SENDS];
1072 ALfloat RoomRolloff[MAX_SENDS];
1073 ALfloat DecayDistance[MAX_SENDS];
1074 ALfloat DecayHFDistance[MAX_SENDS];
1075 ALfloat DryGain, DryGainHF, DryGainLF;
1076 ALfloat WetGain[MAX_SENDS];
1077 ALfloat WetGainHF[MAX_SENDS];
1078 ALfloat WetGainLF[MAX_SENDS];
1079 bool directional;
1080 ALfloat dir[3];
1081 ALfloat spread;
1082 ALfloat Pitch;
1083 ALint i;
1085 /* Set mixing buffers and get send parameters. */
1086 voice->Direct.Buffer = Device->Dry.Buffer;
1087 voice->Direct.Channels = Device->Dry.NumChannels;
1088 for(i = 0;i < NumSends;i++)
1090 SendSlots[i] = props->Send[i].Slot;
1091 if(!SendSlots[i] && i == 0)
1092 SendSlots[i] = ALContext->DefaultSlot;
1093 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1095 SendSlots[i] = NULL;
1096 RoomRolloff[i] = 0.0f;
1097 DecayDistance[i] = 0.0f;
1098 DecayHFDistance[i] = 0.0f;
1100 else if(SendSlots[i]->Params.AuxSendAuto)
1102 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1103 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1104 Listener->Params.ReverbSpeedOfSound;
1105 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1106 if(SendSlots[i]->Params.DecayHFLimit)
1108 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1109 if(airAbsorption < 1.0f)
1111 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1112 DecayHFDistance[i] = minf(limitRatio, DecayHFDistance[i]);
1116 else
1118 /* If the slot's auxiliary send auto is off, the data sent to the
1119 * effect slot is the same as the dry path, sans filter effects */
1120 RoomRolloff[i] = props->RolloffFactor;
1121 DecayDistance[i] = 0.0f;
1122 DecayHFDistance[i] = 0.0f;
1125 if(!SendSlots[i])
1127 voice->Send[i].Buffer = NULL;
1128 voice->Send[i].Channels = 0;
1130 else
1132 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1133 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1137 /* Transform source to listener space (convert to head relative) */
1138 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1139 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1140 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1141 if(props->HeadRelative == AL_FALSE)
1143 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1144 /* Transform source vectors */
1145 Position = aluMatrixfVector(Matrix, &Position);
1146 Velocity = aluMatrixfVector(Matrix, &Velocity);
1147 Direction = aluMatrixfVector(Matrix, &Direction);
1149 else
1151 const aluVector *lvelocity = &Listener->Params.Velocity;
1152 /* Offset the source velocity to be relative of the listener velocity */
1153 Velocity.v[0] += lvelocity->v[0];
1154 Velocity.v[1] += lvelocity->v[1];
1155 Velocity.v[2] += lvelocity->v[2];
1158 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1159 SourceToListener.v[0] = -Position.v[0];
1160 SourceToListener.v[1] = -Position.v[1];
1161 SourceToListener.v[2] = -Position.v[2];
1162 SourceToListener.v[3] = 0.0f;
1163 Distance = aluNormalize(SourceToListener.v);
1165 /* Initial source gain */
1166 DryGain = props->Gain;
1167 DryGainHF = 1.0f;
1168 DryGainLF = 1.0f;
1169 for(i = 0;i < NumSends;i++)
1171 WetGain[i] = props->Gain;
1172 WetGainHF[i] = 1.0f;
1173 WetGainLF[i] = 1.0f;
1176 /* Calculate distance attenuation */
1177 ClampedDist = Distance;
1179 switch(Listener->Params.SourceDistanceModel ?
1180 props->DistanceModel : Listener->Params.DistanceModel)
1182 case InverseDistanceClamped:
1183 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1184 if(props->MaxDistance < props->RefDistance)
1185 break;
1186 /*fall-through*/
1187 case InverseDistance:
1188 if(!(props->RefDistance > 0.0f))
1189 ClampedDist = props->RefDistance;
1190 else
1192 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1193 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1194 for(i = 0;i < NumSends;i++)
1196 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1197 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1200 break;
1202 case LinearDistanceClamped:
1203 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1204 if(props->MaxDistance < props->RefDistance)
1205 break;
1206 /*fall-through*/
1207 case LinearDistance:
1208 if(!(props->MaxDistance != props->RefDistance))
1209 ClampedDist = props->RefDistance;
1210 else
1212 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1213 (props->MaxDistance-props->RefDistance);
1214 DryGain *= maxf(1.0f - attn, 0.0f);
1215 for(i = 0;i < NumSends;i++)
1217 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1218 (props->MaxDistance-props->RefDistance);
1219 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1222 break;
1224 case ExponentDistanceClamped:
1225 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1226 if(props->MaxDistance < props->RefDistance)
1227 break;
1228 /*fall-through*/
1229 case ExponentDistance:
1230 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1231 ClampedDist = props->RefDistance;
1232 else
1234 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1235 for(i = 0;i < NumSends;i++)
1236 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1238 break;
1240 case DisableDistance:
1241 ClampedDist = props->RefDistance;
1242 break;
1245 /* Distance-based air absorption */
1246 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1248 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1249 Listener->Params.MetersPerUnit;
1250 if(props->AirAbsorptionFactor > 0.0f)
1252 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1253 DryGainHF *= hfattn;
1254 for(i = 0;i < NumSends;i++)
1255 WetGainHF[i] *= hfattn;
1258 if(props->WetGainAuto)
1260 /* Apply a decay-time transformation to the wet path, based on the
1261 * source distance in meters. The initial decay of the reverb
1262 * effect is calculated and applied to the wet path.
1264 for(i = 0;i < NumSends;i++)
1266 ALfloat gain;
1268 if(!(DecayDistance[i] > 0.0f))
1269 continue;
1271 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1272 WetGain[i] *= gain;
1273 /* Yes, the wet path's air absorption is applied with
1274 * WetGainAuto on, rather than WetGainHFAuto.
1276 if(gain > 0.0f)
1278 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1279 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1285 /* Calculate directional soundcones */
1286 if(directional && props->InnerAngle < 360.0f)
1288 ALfloat ConeVolume;
1289 ALfloat ConeHF;
1290 ALfloat Angle;
1292 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1293 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1294 if(!(Angle > props->InnerAngle))
1296 ConeVolume = 1.0f;
1297 ConeHF = 1.0f;
1299 else if(Angle < props->OuterAngle)
1301 ALfloat scale = ( Angle-props->InnerAngle) /
1302 (props->OuterAngle-props->InnerAngle);
1303 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1304 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1306 else
1308 ConeVolume = props->OuterGain;
1309 ConeHF = props->OuterGainHF;
1312 DryGain *= ConeVolume;
1313 if(props->DryGainHFAuto)
1314 DryGainHF *= ConeHF;
1315 if(props->WetGainAuto)
1317 for(i = 0;i < NumSends;i++)
1318 WetGain[i] *= ConeVolume;
1320 if(props->WetGainHFAuto)
1322 for(i = 0;i < NumSends;i++)
1323 WetGainHF[i] *= ConeHF;
1327 /* Apply gain and frequency filters */
1328 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1329 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1330 DryGainHF *= props->Direct.GainHF;
1331 DryGainLF *= props->Direct.GainLF;
1332 for(i = 0;i < NumSends;i++)
1334 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1335 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1336 WetGainHF[i] *= props->Send[i].GainHF;
1337 WetGainLF[i] *= props->Send[i].GainLF;
1341 /* Initial source pitch */
1342 Pitch = props->Pitch;
1344 /* Calculate velocity-based doppler effect */
1345 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1346 if(DopplerFactor > 0.0f)
1348 const aluVector *lvelocity = &Listener->Params.Velocity;
1349 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1350 ALfloat vss, vls;
1352 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1353 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1355 if(!(vls < SpeedOfSound))
1357 /* Listener moving away from the source at the speed of sound.
1358 * Sound waves can't catch it.
1360 Pitch = 0.0f;
1362 else if(!(vss < SpeedOfSound))
1364 /* Source moving toward the listener at the speed of sound. Sound
1365 * waves bunch up to extreme frequencies.
1367 Pitch = HUGE_VALF;
1369 else
1371 /* Source and listener movement is nominal. Calculate the proper
1372 * doppler shift.
1374 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1378 /* Adjust pitch based on the buffer and output frequencies, and calculate
1379 * fixed-point stepping value.
1381 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1382 if(Pitch > (ALfloat)MAX_PITCH)
1383 voice->Step = MAX_PITCH<<FRACTIONBITS;
1384 else
1385 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1386 if(props->Resampler == BSinc24Resampler)
1387 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1388 else if(props->Resampler == BSinc12Resampler)
1389 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1390 voice->Resampler = SelectResampler(props->Resampler);
1392 if(Distance > FLT_EPSILON)
1394 dir[0] = -SourceToListener.v[0];
1395 /* Clamp Y, in case rounding errors caused it to end up outside of
1396 * -1...+1.
1398 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1399 dir[2] = -SourceToListener.v[2] * ZScale;
1401 else
1403 dir[0] = 0.0f;
1404 dir[1] = 0.0f;
1405 dir[2] = -1.0f;
1407 if(props->Radius > Distance)
1408 spread = F_TAU - Distance/props->Radius*F_PI;
1409 else if(Distance > FLT_EPSILON)
1410 spread = asinf(props->Radius / Distance) * 2.0f;
1411 else
1412 spread = 0.0f;
1414 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1415 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1418 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1420 ALbufferlistitem *BufferListItem;
1421 struct ALvoiceProps *props;
1423 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1424 if(!props && !force) return;
1426 if(props)
1428 memcpy(voice->Props, props,
1429 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1432 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1434 props = voice->Props;
1436 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1437 while(BufferListItem != NULL)
1439 const ALbuffer *buffer;
1440 if(BufferListItem->num_buffers >= 1 && (buffer=BufferListItem->buffers[0]) != NULL)
1442 if(props->SpatializeMode == SpatializeOn ||
1443 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1444 CalcAttnSourceParams(voice, props, buffer, context);
1445 else
1446 CalcNonAttnSourceParams(voice, props, buffer, context);
1447 break;
1449 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1454 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1456 ALvoice **voice, **voice_end;
1457 ALsource *source;
1458 ALsizei i;
1460 IncrementRef(&ctx->UpdateCount);
1461 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1463 bool cforce = CalcContextParams(ctx);
1464 bool force = CalcListenerParams(ctx) | cforce;
1465 for(i = 0;i < slots->count;i++)
1466 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1468 voice = ctx->Voices;
1469 voice_end = voice + ctx->VoiceCount;
1470 for(;voice != voice_end;++voice)
1472 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1473 if(source) CalcSourceParams(*voice, ctx, force);
1476 IncrementRef(&ctx->UpdateCount);
1480 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1481 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1482 ALsizei NumChannels)
1484 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1485 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1486 ALsizei i;
1488 /* Apply an all-pass to all channels, except the front-left and front-
1489 * right, so they maintain the same relative phase.
1491 for(i = 0;i < NumChannels;i++)
1493 if(i == lidx || i == ridx)
1494 continue;
1495 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1498 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1499 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1501 for(i = 0;i < SamplesToDo;i++)
1503 ALfloat lfsum, hfsum;
1504 ALfloat m, s, c;
1506 lfsum = lsplit[0][i] + rsplit[0][i];
1507 hfsum = lsplit[1][i] + rsplit[1][i];
1508 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1510 /* This pans the separate low- and high-frequency sums between being on
1511 * the center channel and the left/right channels. The low-frequency
1512 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1513 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1514 * values can be tweaked.
1516 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1517 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1519 /* The generated center channel signal adds to the existing signal,
1520 * while the modified left and right channels replace.
1522 Buffer[lidx][i] = (m + s) * 0.5f;
1523 Buffer[ridx][i] = (m - s) * 0.5f;
1524 Buffer[cidx][i] += c * 0.5f;
1528 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1529 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1531 ALsizei i, c;
1533 Values = ASSUME_ALIGNED(Values, 16);
1534 for(c = 0;c < numchans;c++)
1536 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1537 const ALfloat gain = distcomp[c].Gain;
1538 const ALsizei base = distcomp[c].Length;
1539 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1541 if(base == 0)
1543 if(gain < 1.0f)
1545 for(i = 0;i < SamplesToDo;i++)
1546 inout[i] *= gain;
1548 continue;
1551 if(SamplesToDo >= base)
1553 for(i = 0;i < base;i++)
1554 Values[i] = distbuf[i];
1555 for(;i < SamplesToDo;i++)
1556 Values[i] = inout[i-base];
1557 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1559 else
1561 for(i = 0;i < SamplesToDo;i++)
1562 Values[i] = distbuf[i];
1563 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1564 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1566 for(i = 0;i < SamplesToDo;i++)
1567 inout[i] = Values[i]*gain;
1571 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1572 const ALfloat quant_scale, const ALsizei SamplesToDo,
1573 const ALsizei numchans)
1575 const ALfloat invscale = 1.0f / quant_scale;
1576 ALuint seed = *dither_seed;
1577 ALsizei c, i;
1579 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1580 * values between -1 and +1). Step 2 is to add the noise to the samples,
1581 * before rounding and after scaling up to the desired quantization depth.
1583 for(c = 0;c < numchans;c++)
1585 ALfloat *restrict samples = Samples[c];
1586 for(i = 0;i < SamplesToDo;i++)
1588 ALfloat val = samples[i] * quant_scale;
1589 ALuint rng0 = dither_rng(&seed);
1590 ALuint rng1 = dither_rng(&seed);
1591 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1592 samples[i] = roundf(val) * invscale;
1595 *dither_seed = seed;
1599 static inline ALfloat Conv_ALfloat(ALfloat val)
1600 { return val; }
1601 static inline ALint Conv_ALint(ALfloat val)
1603 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1604 * integer range normalized floats can be safely converted to (a bit of the
1605 * exponent helps out, effectively giving 25 bits).
1607 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1609 static inline ALshort Conv_ALshort(ALfloat val)
1610 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1611 static inline ALbyte Conv_ALbyte(ALfloat val)
1612 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1614 /* Define unsigned output variations. */
1615 #define DECL_TEMPLATE(T, func, O) \
1616 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1618 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1619 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1620 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1622 #undef DECL_TEMPLATE
1624 #define DECL_TEMPLATE(T, A) \
1625 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1626 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1627 ALsizei numchans) \
1629 ALsizei i, j; \
1630 for(j = 0;j < numchans;j++) \
1632 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1633 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1635 for(i = 0;i < SamplesToDo;i++) \
1636 out[i*numchans] = Conv_##T(in[i]); \
1640 DECL_TEMPLATE(ALfloat, F32)
1641 DECL_TEMPLATE(ALuint, UI32)
1642 DECL_TEMPLATE(ALint, I32)
1643 DECL_TEMPLATE(ALushort, UI16)
1644 DECL_TEMPLATE(ALshort, I16)
1645 DECL_TEMPLATE(ALubyte, UI8)
1646 DECL_TEMPLATE(ALbyte, I8)
1648 #undef DECL_TEMPLATE
1651 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1653 ALsizei SamplesToDo;
1654 ALsizei SamplesDone;
1655 ALCcontext *ctx;
1656 ALsizei i, c;
1658 START_MIXER_MODE();
1659 for(SamplesDone = 0;SamplesDone < NumSamples;)
1661 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1662 for(c = 0;c < device->Dry.NumChannels;c++)
1663 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1664 if(device->Dry.Buffer != device->FOAOut.Buffer)
1665 for(c = 0;c < device->FOAOut.NumChannels;c++)
1666 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1667 if(device->Dry.Buffer != device->RealOut.Buffer)
1668 for(c = 0;c < device->RealOut.NumChannels;c++)
1669 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1671 IncrementRef(&device->MixCount);
1673 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1674 while(ctx)
1676 const struct ALeffectslotArray *auxslots;
1678 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1679 UpdateContextSources(ctx, auxslots);
1681 for(i = 0;i < auxslots->count;i++)
1683 ALeffectslot *slot = auxslots->slot[i];
1684 for(c = 0;c < slot->NumChannels;c++)
1685 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1688 /* source processing */
1689 for(i = 0;i < ctx->VoiceCount;i++)
1691 ALvoice *voice = ctx->Voices[i];
1692 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1693 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1694 voice->Step > 0)
1696 if(!MixSource(voice, device, SamplesToDo))
1698 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1699 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1704 /* effect slot processing */
1705 for(i = 0;i < auxslots->count;i++)
1707 const ALeffectslot *slot = auxslots->slot[i];
1708 ALeffectState *state = slot->Params.EffectState;
1709 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1710 state->OutChannels);
1713 ctx = ctx->next;
1716 /* Increment the clock time. Every second's worth of samples is
1717 * converted and added to clock base so that large sample counts don't
1718 * overflow during conversion. This also guarantees an exact, stable
1719 * conversion. */
1720 device->SamplesDone += SamplesToDo;
1721 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1722 device->SamplesDone %= device->Frequency;
1723 IncrementRef(&device->MixCount);
1725 if(device->HrtfHandle)
1727 DirectHrtfState *state;
1728 int lidx, ridx;
1730 if(device->AmbiUp)
1731 ambiup_process(device->AmbiUp,
1732 device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer,
1733 SamplesToDo
1736 lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1737 ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1738 assert(lidx != -1 && ridx != -1);
1740 state = device->Hrtf;
1741 for(c = 0;c < device->Dry.NumChannels;c++)
1743 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1744 device->Dry.Buffer[c], state->Offset, state->IrSize,
1745 state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo
1748 state->Offset += SamplesToDo;
1750 else if(device->AmbiDecoder)
1752 if(device->Dry.Buffer != device->FOAOut.Buffer)
1753 bformatdec_upSample(device->AmbiDecoder,
1754 device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
1755 SamplesToDo
1757 bformatdec_process(device->AmbiDecoder,
1758 device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
1759 SamplesToDo
1762 else if(device->AmbiUp)
1764 ambiup_process(device->AmbiUp,
1765 device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer,
1766 SamplesToDo
1769 else if(device->Uhj_Encoder)
1771 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1772 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1773 if(lidx != -1 && ridx != -1)
1775 /* Encode to stereo-compatible 2-channel UHJ output. */
1776 EncodeUhj2(device->Uhj_Encoder,
1777 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1778 device->Dry.Buffer, SamplesToDo
1782 else if(device->Bs2b)
1784 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1785 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1786 if(lidx != -1 && ridx != -1)
1788 /* Apply binaural/crossfeed filter */
1789 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1790 device->RealOut.Buffer[ridx], SamplesToDo);
1794 if(OutBuffer)
1796 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1797 ALsizei Channels = device->RealOut.NumChannels;
1799 if(device->Stablizer)
1801 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1802 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1803 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1804 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1806 ApplyStablizer(device->Stablizer, Buffer, lidx, ridx, cidx,
1807 SamplesToDo, Channels);
1810 ApplyDistanceComp(Buffer, device->ChannelDelay, device->TempBuffer[0],
1811 SamplesToDo, Channels);
1813 if(device->Limiter)
1814 ApplyCompression(device->Limiter, Channels, SamplesToDo, Buffer);
1816 if(device->DitherDepth > 0.0f)
1817 ApplyDither(Buffer, &device->DitherSeed, device->DitherDepth, SamplesToDo,
1818 Channels);
1820 switch(device->FmtType)
1822 case DevFmtByte:
1823 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1824 break;
1825 case DevFmtUByte:
1826 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1827 break;
1828 case DevFmtShort:
1829 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1830 break;
1831 case DevFmtUShort:
1832 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1833 break;
1834 case DevFmtInt:
1835 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1836 break;
1837 case DevFmtUInt:
1838 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1839 break;
1840 case DevFmtFloat:
1841 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1842 break;
1846 SamplesDone += SamplesToDo;
1848 END_MIXER_MODE();
1852 void aluHandleDisconnect(ALCdevice *device)
1854 ALCcontext *ctx;
1856 device->Connected = ALC_FALSE;
1858 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1859 while(ctx)
1861 ALsizei i;
1862 for(i = 0;i < ctx->VoiceCount;i++)
1864 ALvoice *voice = ctx->Voices[i];
1865 ALsource *source;
1867 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_acq_rel);
1868 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1870 if(source)
1872 ALenum playing = AL_PLAYING;
1873 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1876 ctx->VoiceCount = 0;
1878 ctx = ctx->next;