Update ChangeLog
[openal-soft.git] / Alc / ALu.c
bloba2ea4811a2b1e9f5e820cdd52f62f1c27e5e3f7d
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 "uhjfilter.h"
38 #include "bformatdec.h"
39 #include "static_assert.h"
41 #include "mixer_defs.h"
42 #include "bsinc_inc.h"
44 #include "backends/base.h"
47 extern inline ALfloat minf(ALfloat a, ALfloat b);
48 extern inline ALfloat maxf(ALfloat a, ALfloat b);
49 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
51 extern inline ALdouble mind(ALdouble a, ALdouble b);
52 extern inline ALdouble maxd(ALdouble a, ALdouble b);
53 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
55 extern inline ALuint minu(ALuint a, ALuint b);
56 extern inline ALuint maxu(ALuint a, ALuint b);
57 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
59 extern inline ALint mini(ALint a, ALint b);
60 extern inline ALint maxi(ALint a, ALint b);
61 extern inline ALint clampi(ALint val, ALint min, ALint max);
63 extern inline ALint64 mini64(ALint64 a, ALint64 b);
64 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
65 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
67 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
68 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
69 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
71 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
72 extern inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3,
73 const ALfloat *restrict filter);
75 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
77 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
78 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
79 extern inline void aluMatrixfSet(aluMatrixf *matrix,
80 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
81 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
82 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
83 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
86 /* Cone scalar */
87 ALfloat ConeScale = 1.0f;
89 /* Localized Z scalar for mono sources */
90 ALfloat ZScale = 1.0f;
92 /* Force default speed of sound for distance-related reverb decay. */
93 ALboolean OverrideReverbSpeedOfSound = AL_FALSE;
95 const aluMatrixf IdentityMatrixf = {{
96 { 1.0f, 0.0f, 0.0f, 0.0f },
97 { 0.0f, 1.0f, 0.0f, 0.0f },
98 { 0.0f, 0.0f, 1.0f, 0.0f },
99 { 0.0f, 0.0f, 0.0f, 1.0f },
103 struct ChanMap {
104 enum Channel channel;
105 ALfloat angle;
106 ALfloat elevation;
109 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
112 void DeinitVoice(ALvoice *voice)
114 struct ALvoiceProps *props;
115 size_t count = 0;
117 props = ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL);
118 if(props) al_free(props);
120 props = ATOMIC_EXCHANGE_PTR(&voice->FreeList, NULL, almemory_order_relaxed);
121 while(props)
123 struct ALvoiceProps *next;
124 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
125 al_free(props);
126 props = next;
127 ++count;
129 /* This is excessively spammy if it traces every voice destruction, so just
130 * warn if it was unexpectedly large.
132 if(count > 3)
133 WARN("Freed "SZFMT" voice property objects\n", count);
137 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
139 #ifdef HAVE_NEON
140 if((CPUCapFlags&CPU_CAP_NEON))
141 return MixDirectHrtf_Neon;
142 #endif
143 #ifdef HAVE_SSE
144 if((CPUCapFlags&CPU_CAP_SSE))
145 return MixDirectHrtf_SSE;
146 #endif
148 return MixDirectHrtf_C;
152 /* Prior to VS2013, MSVC lacks the round() family of functions. */
153 #if defined(_MSC_VER) && _MSC_VER < 1800
154 static float roundf(float val)
156 if(val < 0.0f)
157 return ceilf(val-0.5f);
158 return floorf(val+0.5f);
160 #endif
162 /* This RNG method was created based on the math found in opusdec. It's quick,
163 * and starting with a seed value of 22222, is suitable for generating
164 * whitenoise.
166 static inline ALuint dither_rng(ALuint *seed)
168 *seed = (*seed * 96314165) + 907633515;
169 return *seed;
173 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
175 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
176 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
177 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
180 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
182 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
185 static ALfloat aluNormalize(ALfloat *vec)
187 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
188 if(length > 0.0f)
190 ALfloat inv_length = 1.0f/length;
191 vec[0] *= inv_length;
192 vec[1] *= inv_length;
193 vec[2] *= inv_length;
195 return length;
198 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
200 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
202 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];
203 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];
204 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];
207 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
209 aluVector v;
210 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];
211 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];
212 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];
213 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];
214 return v;
218 void aluInit(void)
220 MixDirectHrtf = SelectHrtfMixer();
223 /* Prepares the interpolator for a given rate (determined by increment). A
224 * result of AL_FALSE indicates that the filter output will completely cut
225 * the input signal.
227 * With a bit of work, and a trade of memory for CPU cost, this could be
228 * modified for use with an interpolated increment for buttery-smooth pitch
229 * changes.
231 ALboolean BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
233 ALboolean uncut = AL_TRUE;
234 ALfloat sf;
235 ALsizei si;
237 if(increment > FRACTIONONE)
239 sf = (ALfloat)FRACTIONONE / increment;
240 if(sf < table->scaleBase)
242 /* Signal has been completely cut. The return result can be used
243 * to skip the filter (and output zeros) as an optimization.
245 sf = 0.0f;
246 si = 0;
247 uncut = AL_FALSE;
249 else
251 sf = (BSINC_SCALE_COUNT - 1) * (sf - table->scaleBase) * table->scaleRange;
252 si = fastf2i(sf);
253 /* The interpolation factor is fit to this diagonally-symmetric
254 * curve to reduce the transition ripple caused by interpolating
255 * different scales of the sinc function.
257 sf = 1.0f - cosf(asinf(sf - si));
260 else
262 sf = 0.0f;
263 si = BSINC_SCALE_COUNT - 1;
266 state->sf = sf;
267 state->m = table->m[si];
268 state->l = -((state->m/2) - 1);
269 state->filter = table->Tab + table->filterOffset[si];
270 return uncut;
274 static ALboolean CalcListenerParams(ALCcontext *Context)
276 ALlistener *Listener = Context->Listener;
277 ALfloat N[3], V[3], U[3], P[3];
278 struct ALlistenerProps *props;
279 aluVector vel;
281 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
282 if(!props) return AL_FALSE;
284 /* AT then UP */
285 N[0] = props->Forward[0];
286 N[1] = props->Forward[1];
287 N[2] = props->Forward[2];
288 aluNormalize(N);
289 V[0] = props->Up[0];
290 V[1] = props->Up[1];
291 V[2] = props->Up[2];
292 aluNormalize(V);
293 /* Build and normalize right-vector */
294 aluCrossproduct(N, V, U);
295 aluNormalize(U);
297 aluMatrixfSet(&Listener->Params.Matrix,
298 U[0], V[0], -N[0], 0.0,
299 U[1], V[1], -N[1], 0.0,
300 U[2], V[2], -N[2], 0.0,
301 0.0, 0.0, 0.0, 1.0
304 P[0] = props->Position[0];
305 P[1] = props->Position[1];
306 P[2] = props->Position[2];
307 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
308 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
310 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
311 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
313 Listener->Params.Gain = props->Gain * Context->GainBoost;
314 Listener->Params.MetersPerUnit = props->MetersPerUnit;
316 Listener->Params.DopplerFactor = props->DopplerFactor;
317 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
318 if(OverrideReverbSpeedOfSound)
319 Listener->Params.ReverbSpeedOfSound = SPEEDOFSOUNDMETRESPERSEC;
320 else
321 Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
322 Listener->Params.MetersPerUnit;
324 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
325 Listener->Params.DistanceModel = props->DistanceModel;
327 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
328 return AL_TRUE;
331 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context)
333 struct ALeffectslotProps *props;
334 ALeffectState *state;
336 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
337 if(!props) return AL_FALSE;
339 slot->Params.Gain = props->Gain;
340 slot->Params.AuxSendAuto = props->AuxSendAuto;
341 slot->Params.EffectType = props->Type;
342 if(IsReverbEffect(slot->Params.EffectType))
344 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
345 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
346 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
347 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
348 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
350 else
352 slot->Params.RoomRolloff = 0.0f;
353 slot->Params.DecayTime = 0.0f;
354 slot->Params.DecayHFRatio = 0.0f;
355 slot->Params.DecayHFLimit = AL_FALSE;
356 slot->Params.AirAbsorptionGainHF = 1.0f;
359 /* Swap effect states. No need to play with the ref counts since they keep
360 * the same number of refs.
362 state = props->State;
363 props->State = slot->Params.EffectState;
364 slot->Params.EffectState = state;
366 V(state,update)(context, slot, &props->Props);
368 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
369 return AL_TRUE;
373 static const struct ChanMap MonoMap[1] = {
374 { FrontCenter, 0.0f, 0.0f }
375 }, RearMap[2] = {
376 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
377 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
378 }, QuadMap[4] = {
379 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
380 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
381 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
382 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
383 }, X51Map[6] = {
384 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
385 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
386 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
387 { LFE, 0.0f, 0.0f },
388 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
389 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
390 }, X61Map[7] = {
391 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
392 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
393 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
394 { LFE, 0.0f, 0.0f },
395 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
396 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
397 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
398 }, X71Map[8] = {
399 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
400 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
401 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
402 { LFE, 0.0f, 0.0f },
403 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
404 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
405 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
406 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
409 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
410 const ALfloat Spread, const ALfloat DryGain,
411 const ALfloat DryGainHF, const ALfloat DryGainLF,
412 const ALfloat *WetGain, const ALfloat *WetGainLF,
413 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
414 const ALbuffer *Buffer, const struct ALvoiceProps *props,
415 const ALlistener *Listener, const ALCdevice *Device)
417 struct ChanMap StereoMap[2] = {
418 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
419 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
421 bool DirectChannels = props->DirectChannels;
422 const ALsizei NumSends = Device->NumAuxSends;
423 const ALuint Frequency = Device->Frequency;
424 const struct ChanMap *chans = NULL;
425 ALsizei num_channels = 0;
426 bool isbformat = false;
427 ALfloat downmix_gain = 1.0f;
428 ALsizei c, i, j;
430 switch(Buffer->FmtChannels)
432 case FmtMono:
433 chans = MonoMap;
434 num_channels = 1;
435 /* Mono buffers are never played direct. */
436 DirectChannels = false;
437 break;
439 case FmtStereo:
440 /* Convert counter-clockwise to clockwise. */
441 StereoMap[0].angle = -props->StereoPan[0];
442 StereoMap[1].angle = -props->StereoPan[1];
444 chans = StereoMap;
445 num_channels = 2;
446 downmix_gain = 1.0f / 2.0f;
447 break;
449 case FmtRear:
450 chans = RearMap;
451 num_channels = 2;
452 downmix_gain = 1.0f / 2.0f;
453 break;
455 case FmtQuad:
456 chans = QuadMap;
457 num_channels = 4;
458 downmix_gain = 1.0f / 4.0f;
459 break;
461 case FmtX51:
462 chans = X51Map;
463 num_channels = 6;
464 /* NOTE: Excludes LFE. */
465 downmix_gain = 1.0f / 5.0f;
466 break;
468 case FmtX61:
469 chans = X61Map;
470 num_channels = 7;
471 /* NOTE: Excludes LFE. */
472 downmix_gain = 1.0f / 6.0f;
473 break;
475 case FmtX71:
476 chans = X71Map;
477 num_channels = 8;
478 /* NOTE: Excludes LFE. */
479 downmix_gain = 1.0f / 7.0f;
480 break;
482 case FmtBFormat2D:
483 num_channels = 3;
484 isbformat = true;
485 DirectChannels = false;
486 break;
488 case FmtBFormat3D:
489 num_channels = 4;
490 isbformat = true;
491 DirectChannels = false;
492 break;
495 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
496 if(isbformat)
498 /* Special handling for B-Format sources. */
500 if(Distance > FLT_EPSILON)
502 /* Panning a B-Format sound toward some direction is easy. Just pan
503 * the first (W) channel as a normal mono sound and silence the
504 * others.
506 ALfloat coeffs[MAX_AMBI_COEFFS];
508 if(Device->AvgSpeakerDist > 0.0f)
510 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
511 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
512 (mdist * (ALfloat)Device->Frequency);
513 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
514 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
515 /* Clamp w0 for really close distances, to prevent excessive
516 * bass.
518 w0 = minf(w0, w1*4.0f);
520 /* Only need to adjust the first channel of a B-Format source. */
521 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
522 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
523 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
525 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
526 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
527 voice->Flags |= VOICE_HAS_NFC;
530 if(Device->Render_Mode == StereoPair)
532 ALfloat ev = asinf(Dir[1]);
533 ALfloat az = atan2f(Dir[0], -Dir[2]);
534 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
536 else
537 CalcDirectionCoeffs(Dir, Spread, coeffs);
539 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
540 ComputePanningGains(Device->Dry, coeffs, DryGain*1.414213562f,
541 voice->Direct.Params[0].Gains.Target);
542 for(c = 1;c < num_channels;c++)
544 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
545 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
548 for(i = 0;i < NumSends;i++)
550 const ALeffectslot *Slot = SendSlots[i];
551 if(Slot)
552 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
553 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
555 else
556 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
557 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
558 for(c = 1;c < num_channels;c++)
560 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
561 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
565 else
567 /* Local B-Format sources have their XYZ channels rotated according
568 * to the orientation.
570 ALfloat N[3], V[3], U[3];
571 aluMatrixf matrix;
572 ALfloat scale;
574 if(Device->AvgSpeakerDist > 0.0f)
576 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
577 * is what we want for FOA input. The first channel may have
578 * been previously re-adjusted if panned, so reset it.
580 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f);
581 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f);
582 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 0.0f);
584 voice->Direct.ChannelsPerOrder[0] = 1;
585 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
586 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
587 voice->Direct.ChannelsPerOrder[i] = 0;
588 voice->Flags |= VOICE_HAS_NFC;
591 /* AT then UP */
592 N[0] = props->Orientation[0][0];
593 N[1] = props->Orientation[0][1];
594 N[2] = props->Orientation[0][2];
595 aluNormalize(N);
596 V[0] = props->Orientation[1][0];
597 V[1] = props->Orientation[1][1];
598 V[2] = props->Orientation[1][2];
599 aluNormalize(V);
600 if(!props->HeadRelative)
602 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
603 aluMatrixfFloat3(N, 0.0f, lmatrix);
604 aluMatrixfFloat3(V, 0.0f, lmatrix);
606 /* Build and normalize right-vector */
607 aluCrossproduct(N, V, U);
608 aluNormalize(U);
610 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
611 scale = 1.732050808f;
612 aluMatrixfSet(&matrix,
613 1.414213562f, 0.0f, 0.0f, 0.0f,
614 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
615 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
616 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
619 voice->Direct.Buffer = Device->FOAOut.Buffer;
620 voice->Direct.Channels = Device->FOAOut.NumChannels;
621 for(c = 0;c < num_channels;c++)
622 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
623 voice->Direct.Params[c].Gains.Target);
624 for(i = 0;i < NumSends;i++)
626 const ALeffectslot *Slot = SendSlots[i];
627 if(Slot)
629 for(c = 0;c < num_channels;c++)
630 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
631 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
634 else
636 for(c = 0;c < num_channels;c++)
637 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
638 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
643 else if(DirectChannels)
645 /* Direct source channels always play local. Skip the virtual channels
646 * and write inputs to the matching real outputs.
648 voice->Direct.Buffer = Device->RealOut.Buffer;
649 voice->Direct.Channels = Device->RealOut.NumChannels;
651 for(c = 0;c < num_channels;c++)
653 int idx;
654 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
655 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
656 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
657 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
660 /* Auxiliary sends still use normal channel panning since they mix to
661 * B-Format, which can't channel-match.
663 for(c = 0;c < num_channels;c++)
665 ALfloat coeffs[MAX_AMBI_COEFFS];
666 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
668 for(i = 0;i < NumSends;i++)
670 const ALeffectslot *Slot = SendSlots[i];
671 if(Slot)
672 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
673 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
675 else
676 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
677 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
681 else if(Device->Render_Mode == HrtfRender)
683 /* Full HRTF rendering. Skip the virtual channels and render to the
684 * real outputs.
686 voice->Direct.Buffer = Device->RealOut.Buffer;
687 voice->Direct.Channels = Device->RealOut.NumChannels;
689 if(Distance > FLT_EPSILON)
691 ALfloat coeffs[MAX_AMBI_COEFFS];
692 ALfloat ev, az;
694 ev = asinf(Dir[1]);
695 az = atan2f(Dir[0], -Dir[2]);
697 /* Get the HRIR coefficients and delays just once, for the given
698 * source direction.
700 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
701 voice->Direct.Params[0].Hrtf.Target.Coeffs,
702 voice->Direct.Params[0].Hrtf.Target.Delay);
703 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
705 /* Remaining channels use the same results as the first. */
706 for(c = 1;c < num_channels;c++)
708 /* Skip LFE */
709 if(chans[c].channel == LFE)
710 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
711 sizeof(voice->Direct.Params[c].Hrtf.Target));
712 else
713 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
716 /* Calculate the directional coefficients once, which apply to all
717 * input channels of the source sends.
719 CalcDirectionCoeffs(Dir, Spread, coeffs);
721 for(i = 0;i < NumSends;i++)
723 const ALeffectslot *Slot = SendSlots[i];
724 if(Slot)
725 for(c = 0;c < num_channels;c++)
727 /* Skip LFE */
728 if(chans[c].channel == LFE)
729 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
730 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
731 else
732 ComputePanningGainsBF(Slot->ChanMap,
733 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
734 voice->Send[i].Params[c].Gains.Target
737 else
738 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
739 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
742 else
744 /* Local sources on HRTF play with each channel panned to its
745 * relative location around the listener, providing "virtual
746 * speaker" responses.
748 for(c = 0;c < num_channels;c++)
750 ALfloat coeffs[MAX_AMBI_COEFFS];
752 if(chans[c].channel == LFE)
754 /* Skip LFE */
755 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
756 sizeof(voice->Direct.Params[c].Hrtf.Target));
757 for(i = 0;i < NumSends;i++)
759 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
760 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
762 continue;
765 /* Get the HRIR coefficients and delays for this channel
766 * position.
768 GetHrtfCoeffs(Device->HrtfHandle,
769 chans[c].elevation, chans[c].angle, Spread,
770 voice->Direct.Params[c].Hrtf.Target.Coeffs,
771 voice->Direct.Params[c].Hrtf.Target.Delay
773 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
775 /* Normal panning for auxiliary sends. */
776 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
778 for(i = 0;i < NumSends;i++)
780 const ALeffectslot *Slot = SendSlots[i];
781 if(Slot)
782 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
783 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
785 else
786 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
787 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
792 voice->Flags |= VOICE_HAS_HRTF;
794 else
796 /* Non-HRTF rendering. Use normal panning to the output. */
798 if(Distance > FLT_EPSILON)
800 ALfloat coeffs[MAX_AMBI_COEFFS];
801 ALfloat w0 = 0.0f;
803 /* Calculate NFC filter coefficient if needed. */
804 if(Device->AvgSpeakerDist > 0.0f)
806 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
807 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
808 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
809 w0 = SPEEDOFSOUNDMETRESPERSEC /
810 (mdist * (ALfloat)Device->Frequency);
811 /* Clamp w0 for really close distances, to prevent excessive
812 * bass.
814 w0 = minf(w0, w1*4.0f);
816 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
817 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
818 voice->Flags |= VOICE_HAS_NFC;
821 /* Calculate the directional coefficients once, which apply to all
822 * input channels.
824 if(Device->Render_Mode == StereoPair)
826 ALfloat ev = asinf(Dir[1]);
827 ALfloat az = atan2f(Dir[0], -Dir[2]);
828 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
830 else
831 CalcDirectionCoeffs(Dir, Spread, coeffs);
833 for(c = 0;c < num_channels;c++)
835 /* Adjust NFC filters if needed. */
836 if((voice->Flags&VOICE_HAS_NFC))
838 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
839 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
840 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
843 /* Special-case LFE */
844 if(chans[c].channel == LFE)
846 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
847 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
848 if(Device->Dry.Buffer == Device->RealOut.Buffer)
850 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
851 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
853 continue;
856 ComputePanningGains(Device->Dry,
857 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
861 for(i = 0;i < NumSends;i++)
863 const ALeffectslot *Slot = SendSlots[i];
864 if(Slot)
865 for(c = 0;c < num_channels;c++)
867 /* Skip LFE */
868 if(chans[c].channel == LFE)
869 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
870 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
871 else
872 ComputePanningGainsBF(Slot->ChanMap,
873 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
874 voice->Send[i].Params[c].Gains.Target
877 else
878 for(c = 0;c < num_channels;c++)
880 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
881 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
885 else
887 ALfloat w0 = 0.0f;
889 if(Device->AvgSpeakerDist > 0.0f)
891 /* If the source distance is 0, set w0 to w1 to act as a pass-
892 * through. We still want to pass the signal through the
893 * filters so they keep an appropriate history, in case the
894 * source moves away from the listener.
896 w0 = SPEEDOFSOUNDMETRESPERSEC /
897 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
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 for(c = 0;c < num_channels;c++)
906 ALfloat coeffs[MAX_AMBI_COEFFS];
908 if((voice->Flags&VOICE_HAS_NFC))
910 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
911 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
912 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
915 /* Special-case LFE */
916 if(chans[c].channel == LFE)
918 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
919 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
920 if(Device->Dry.Buffer == Device->RealOut.Buffer)
922 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
923 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
926 for(i = 0;i < NumSends;i++)
928 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
929 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
931 continue;
934 if(Device->Render_Mode == StereoPair)
935 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
936 else
937 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
938 ComputePanningGains(Device->Dry,
939 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
942 for(i = 0;i < NumSends;i++)
944 const ALeffectslot *Slot = SendSlots[i];
945 if(Slot)
946 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
947 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
949 else
950 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
951 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
958 ALfloat hfScale = props->Direct.HFReference / Frequency;
959 ALfloat lfScale = props->Direct.LFReference / Frequency;
960 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
961 ALfloat gainLF = maxf(DryGainLF, 0.001f);
963 voice->Direct.FilterType = AF_None;
964 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
965 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
966 ALfilterState_setParams(
967 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
968 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
970 ALfilterState_setParams(
971 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
972 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
974 for(c = 1;c < num_channels;c++)
976 ALfilterState_copyParams(&voice->Direct.Params[c].LowPass,
977 &voice->Direct.Params[0].LowPass);
978 ALfilterState_copyParams(&voice->Direct.Params[c].HighPass,
979 &voice->Direct.Params[0].HighPass);
982 for(i = 0;i < NumSends;i++)
984 ALfloat hfScale = props->Send[i].HFReference / Frequency;
985 ALfloat lfScale = props->Send[i].LFReference / Frequency;
986 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
987 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
989 voice->Send[i].FilterType = AF_None;
990 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
991 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
992 ALfilterState_setParams(
993 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
994 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
996 ALfilterState_setParams(
997 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
998 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1000 for(c = 1;c < num_channels;c++)
1002 ALfilterState_copyParams(&voice->Send[i].Params[c].LowPass,
1003 &voice->Send[i].Params[0].LowPass);
1004 ALfilterState_copyParams(&voice->Send[i].Params[c].HighPass,
1005 &voice->Send[i].Params[0].HighPass);
1010 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1012 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1013 const ALCdevice *Device = ALContext->Device;
1014 const ALlistener *Listener = ALContext->Listener;
1015 ALfloat DryGain, DryGainHF, DryGainLF;
1016 ALfloat WetGain[MAX_SENDS];
1017 ALfloat WetGainHF[MAX_SENDS];
1018 ALfloat WetGainLF[MAX_SENDS];
1019 ALeffectslot *SendSlots[MAX_SENDS];
1020 ALfloat Pitch;
1021 ALsizei i;
1023 voice->Direct.Buffer = Device->Dry.Buffer;
1024 voice->Direct.Channels = Device->Dry.NumChannels;
1025 for(i = 0;i < Device->NumAuxSends;i++)
1027 SendSlots[i] = props->Send[i].Slot;
1028 if(!SendSlots[i] && i == 0)
1029 SendSlots[i] = ALContext->DefaultSlot;
1030 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1032 SendSlots[i] = NULL;
1033 voice->Send[i].Buffer = NULL;
1034 voice->Send[i].Channels = 0;
1036 else
1038 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1039 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1043 /* Calculate the stepping value */
1044 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1045 if(Pitch > (ALfloat)MAX_PITCH)
1046 voice->Step = MAX_PITCH<<FRACTIONBITS;
1047 else
1048 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1049 if(props->Resampler == BSinc24Resampler)
1050 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1051 else if(props->Resampler == BSinc12Resampler)
1052 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1053 else
1055 voice->ResampleState.sinc4.filter = sinc4Tab;
1056 voice->Resampler = SelectResampler(props->Resampler);
1058 /* Calculate gains */
1059 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1060 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1061 DryGain = minf(DryGain, GAIN_MIX_MAX);
1062 DryGainHF = props->Direct.GainHF;
1063 DryGainLF = props->Direct.GainLF;
1064 for(i = 0;i < Device->NumAuxSends;i++)
1066 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1067 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1068 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1069 WetGainHF[i] = props->Send[i].GainHF;
1070 WetGainLF[i] = props->Send[i].GainLF;
1073 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1074 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1077 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1079 const ALCdevice *Device = ALContext->Device;
1080 const ALlistener *Listener = ALContext->Listener;
1081 const ALsizei NumSends = Device->NumAuxSends;
1082 aluVector Position, Velocity, Direction, SourceToListener;
1083 ALfloat Distance, ClampedDist, DopplerFactor;
1084 ALeffectslot *SendSlots[MAX_SENDS];
1085 ALfloat RoomRolloff[MAX_SENDS];
1086 ALfloat DecayDistance[MAX_SENDS];
1087 ALfloat DecayHFDistance[MAX_SENDS];
1088 ALfloat DryGain, DryGainHF, DryGainLF;
1089 ALfloat WetGain[MAX_SENDS];
1090 ALfloat WetGainHF[MAX_SENDS];
1091 ALfloat WetGainLF[MAX_SENDS];
1092 bool directional;
1093 ALfloat dir[3];
1094 ALfloat spread;
1095 ALfloat Pitch;
1096 ALint i;
1098 /* Set mixing buffers and get send parameters. */
1099 voice->Direct.Buffer = Device->Dry.Buffer;
1100 voice->Direct.Channels = Device->Dry.NumChannels;
1101 for(i = 0;i < NumSends;i++)
1103 SendSlots[i] = props->Send[i].Slot;
1104 if(!SendSlots[i] && i == 0)
1105 SendSlots[i] = ALContext->DefaultSlot;
1106 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1108 SendSlots[i] = NULL;
1109 RoomRolloff[i] = 0.0f;
1110 DecayDistance[i] = 0.0f;
1111 DecayHFDistance[i] = 0.0f;
1113 else if(SendSlots[i]->Params.AuxSendAuto)
1115 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1116 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1117 Listener->Params.ReverbSpeedOfSound;
1118 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1119 if(SendSlots[i]->Params.DecayHFLimit)
1121 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1122 if(airAbsorption < 1.0f)
1124 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1125 DecayHFDistance[i] = minf(limitRatio, DecayHFDistance[i]);
1129 else
1131 /* If the slot's auxiliary send auto is off, the data sent to the
1132 * effect slot is the same as the dry path, sans filter effects */
1133 RoomRolloff[i] = props->RolloffFactor;
1134 DecayDistance[i] = 0.0f;
1135 DecayHFDistance[i] = 0.0f;
1138 if(!SendSlots[i])
1140 voice->Send[i].Buffer = NULL;
1141 voice->Send[i].Channels = 0;
1143 else
1145 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1146 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1150 /* Transform source to listener space (convert to head relative) */
1151 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1152 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1153 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1154 if(props->HeadRelative == AL_FALSE)
1156 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1157 /* Transform source vectors */
1158 Position = aluMatrixfVector(Matrix, &Position);
1159 Velocity = aluMatrixfVector(Matrix, &Velocity);
1160 Direction = aluMatrixfVector(Matrix, &Direction);
1162 else
1164 const aluVector *lvelocity = &Listener->Params.Velocity;
1165 /* Offset the source velocity to be relative of the listener velocity */
1166 Velocity.v[0] += lvelocity->v[0];
1167 Velocity.v[1] += lvelocity->v[1];
1168 Velocity.v[2] += lvelocity->v[2];
1171 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1172 SourceToListener.v[0] = -Position.v[0];
1173 SourceToListener.v[1] = -Position.v[1];
1174 SourceToListener.v[2] = -Position.v[2];
1175 SourceToListener.v[3] = 0.0f;
1176 Distance = aluNormalize(SourceToListener.v);
1178 /* Initial source gain */
1179 DryGain = props->Gain;
1180 DryGainHF = 1.0f;
1181 DryGainLF = 1.0f;
1182 for(i = 0;i < NumSends;i++)
1184 WetGain[i] = props->Gain;
1185 WetGainHF[i] = 1.0f;
1186 WetGainLF[i] = 1.0f;
1189 /* Calculate distance attenuation */
1190 ClampedDist = Distance;
1192 switch(Listener->Params.SourceDistanceModel ?
1193 props->DistanceModel : Listener->Params.DistanceModel)
1195 case InverseDistanceClamped:
1196 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1197 if(props->MaxDistance < props->RefDistance)
1198 break;
1199 /*fall-through*/
1200 case InverseDistance:
1201 if(!(props->RefDistance > 0.0f))
1202 ClampedDist = props->RefDistance;
1203 else
1205 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1206 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1207 for(i = 0;i < NumSends;i++)
1209 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1210 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1213 break;
1215 case LinearDistanceClamped:
1216 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1217 if(props->MaxDistance < props->RefDistance)
1218 break;
1219 /*fall-through*/
1220 case LinearDistance:
1221 if(!(props->MaxDistance != props->RefDistance))
1222 ClampedDist = props->RefDistance;
1223 else
1225 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1226 (props->MaxDistance-props->RefDistance);
1227 DryGain *= maxf(1.0f - attn, 0.0f);
1228 for(i = 0;i < NumSends;i++)
1230 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1231 (props->MaxDistance-props->RefDistance);
1232 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1235 break;
1237 case ExponentDistanceClamped:
1238 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1239 if(props->MaxDistance < props->RefDistance)
1240 break;
1241 /*fall-through*/
1242 case ExponentDistance:
1243 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1244 ClampedDist = props->RefDistance;
1245 else
1247 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1248 for(i = 0;i < NumSends;i++)
1249 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1251 break;
1253 case DisableDistance:
1254 ClampedDist = props->RefDistance;
1255 break;
1258 /* Distance-based air absorption */
1259 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1261 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1262 Listener->Params.MetersPerUnit;
1263 if(props->AirAbsorptionFactor > 0.0f)
1265 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1266 DryGainHF *= hfattn;
1267 for(i = 0;i < NumSends;i++)
1268 WetGainHF[i] *= hfattn;
1271 if(props->WetGainAuto)
1273 /* Apply a decay-time transformation to the wet path, based on the
1274 * source distance in meters. The initial decay of the reverb
1275 * effect is calculated and applied to the wet path.
1277 for(i = 0;i < NumSends;i++)
1279 ALfloat gain;
1281 if(!(DecayDistance[i] > 0.0f))
1282 continue;
1284 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1285 WetGain[i] *= gain;
1286 /* Yes, the wet path's air absorption is applied with
1287 * WetGainAuto on, rather than WetGainHFAuto.
1289 if(gain > 0.0f)
1291 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1292 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1298 /* Calculate directional soundcones */
1299 if(directional && props->InnerAngle < 360.0f)
1301 ALfloat ConeVolume;
1302 ALfloat ConeHF;
1303 ALfloat Angle;
1305 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1306 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1307 if(!(Angle > props->InnerAngle))
1309 ConeVolume = 1.0f;
1310 ConeHF = 1.0f;
1312 else if(Angle < props->OuterAngle)
1314 ALfloat scale = ( Angle-props->InnerAngle) /
1315 (props->OuterAngle-props->InnerAngle);
1316 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1317 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1319 else
1321 ConeVolume = props->OuterGain;
1322 ConeHF = props->OuterGainHF;
1325 DryGain *= ConeVolume;
1326 if(props->DryGainHFAuto)
1327 DryGainHF *= ConeHF;
1328 if(props->WetGainAuto)
1330 for(i = 0;i < NumSends;i++)
1331 WetGain[i] *= ConeVolume;
1333 if(props->WetGainHFAuto)
1335 for(i = 0;i < NumSends;i++)
1336 WetGainHF[i] *= ConeHF;
1340 /* Apply gain and frequency filters */
1341 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1342 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1343 DryGainHF *= props->Direct.GainHF;
1344 DryGainLF *= props->Direct.GainLF;
1345 for(i = 0;i < NumSends;i++)
1347 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1348 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1349 WetGainHF[i] *= props->Send[i].GainHF;
1350 WetGainLF[i] *= props->Send[i].GainLF;
1354 /* Initial source pitch */
1355 Pitch = props->Pitch;
1357 /* Calculate velocity-based doppler effect */
1358 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1359 if(DopplerFactor > 0.0f)
1361 const aluVector *lvelocity = &Listener->Params.Velocity;
1362 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1363 ALfloat vss, vls;
1365 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1366 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1368 if(!(vls < SpeedOfSound))
1370 /* Listener moving away from the source at the speed of sound.
1371 * Sound waves can't catch it.
1373 Pitch = 0.0f;
1375 else if(!(vss < SpeedOfSound))
1377 /* Source moving toward the listener at the speed of sound. Sound
1378 * waves bunch up to extreme frequencies.
1380 Pitch = HUGE_VALF;
1382 else
1384 /* Source and listener movement is nominal. Calculate the proper
1385 * doppler shift.
1387 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1391 /* Adjust pitch based on the buffer and output frequencies, and calculate
1392 * fixed-point stepping value.
1394 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1395 if(Pitch > (ALfloat)MAX_PITCH)
1396 voice->Step = MAX_PITCH<<FRACTIONBITS;
1397 else
1398 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1399 if(props->Resampler == BSinc24Resampler)
1400 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1401 else if(props->Resampler == BSinc12Resampler)
1402 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1403 else
1404 voice->ResampleState.sinc4.filter = sinc4Tab;
1405 voice->Resampler = SelectResampler(props->Resampler);
1407 if(Distance > FLT_EPSILON)
1409 dir[0] = -SourceToListener.v[0];
1410 /* Clamp Y, in case rounding errors caused it to end up outside of
1411 * -1...+1.
1413 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1414 dir[2] = -SourceToListener.v[2] * ZScale;
1416 else
1418 dir[0] = 0.0f;
1419 dir[1] = 0.0f;
1420 dir[2] = -1.0f;
1422 if(props->Radius > Distance)
1423 spread = F_TAU - Distance/props->Radius*F_PI;
1424 else if(Distance > FLT_EPSILON)
1425 spread = asinf(props->Radius / Distance) * 2.0f;
1426 else
1427 spread = 0.0f;
1429 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1430 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1433 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1435 ALbufferlistitem *BufferListItem;
1436 struct ALvoiceProps *props;
1438 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1439 if(!props && !force) return;
1441 if(props)
1443 memcpy(voice->Props, props,
1444 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1447 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &voice->FreeList, props);
1449 props = voice->Props;
1451 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1452 while(BufferListItem != NULL)
1454 const ALbuffer *buffer;
1455 if((buffer=BufferListItem->buffer) != NULL)
1457 if(props->SpatializeMode == SpatializeOn ||
1458 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1459 CalcAttnSourceParams(voice, props, buffer, context);
1460 else
1461 CalcNonAttnSourceParams(voice, props, buffer, context);
1462 break;
1464 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1469 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1471 ALvoice **voice, **voice_end;
1472 ALsource *source;
1473 ALsizei i;
1475 IncrementRef(&ctx->UpdateCount);
1476 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1478 ALboolean force = CalcListenerParams(ctx);
1479 for(i = 0;i < slots->count;i++)
1480 force |= CalcEffectSlotParams(slots->slot[i], ctx);
1482 voice = ctx->Voices;
1483 voice_end = voice + ctx->VoiceCount;
1484 for(;voice != voice_end;++voice)
1486 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1487 if(source) CalcSourceParams(*voice, ctx, force);
1490 IncrementRef(&ctx->UpdateCount);
1494 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1495 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1496 ALsizei NumChannels)
1498 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1499 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1500 ALsizei i;
1502 /* Apply an all-pass to all channels, except the front-left and front-
1503 * right, so they maintain the same relative phase.
1505 for(i = 0;i < NumChannels;i++)
1507 if(i == lidx || i == ridx)
1508 continue;
1509 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1512 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1513 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1515 for(i = 0;i < SamplesToDo;i++)
1517 ALfloat lfsum, hfsum;
1518 ALfloat m, s, c;
1520 lfsum = lsplit[0][i] + rsplit[0][i];
1521 hfsum = lsplit[1][i] + rsplit[1][i];
1522 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1524 /* This pans the separate low- and high-frequency sums between being on
1525 * the center channel and the left/right channels. The low-frequency
1526 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1527 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1528 * values can be tweaked.
1530 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1531 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1533 /* The generated center channel signal adds to the existing signal,
1534 * while the modified left and right channels replace.
1536 Buffer[lidx][i] = (m + s) * 0.5f;
1537 Buffer[ridx][i] = (m - s) * 0.5f;
1538 Buffer[cidx][i] += c * 0.5f;
1542 static void ApplyDistanceComp(ALfloatBUFFERSIZE *restrict Samples, DistanceComp *distcomp,
1543 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1545 ALsizei i, c;
1547 Values = ASSUME_ALIGNED(Values, 16);
1548 for(c = 0;c < numchans;c++)
1550 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1551 const ALfloat gain = distcomp[c].Gain;
1552 const ALsizei base = distcomp[c].Length;
1553 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1555 if(base == 0)
1557 if(gain < 1.0f)
1559 for(i = 0;i < SamplesToDo;i++)
1560 inout[i] *= gain;
1562 continue;
1565 if(SamplesToDo >= base)
1567 for(i = 0;i < base;i++)
1568 Values[i] = distbuf[i];
1569 for(;i < SamplesToDo;i++)
1570 Values[i] = inout[i-base];
1571 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1573 else
1575 for(i = 0;i < SamplesToDo;i++)
1576 Values[i] = distbuf[i];
1577 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1578 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1580 for(i = 0;i < SamplesToDo;i++)
1581 inout[i] = Values[i]*gain;
1585 static void ApplyDither(ALfloatBUFFERSIZE *restrict Samples, ALuint *dither_seed,
1586 const ALfloat quant_scale, const ALsizei SamplesToDo,
1587 const ALsizei numchans)
1589 const ALfloat invscale = 1.0f / quant_scale;
1590 ALuint seed = *dither_seed;
1591 ALsizei c, i;
1593 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1594 * values between -1 and +1). Step 2 is to add the noise to the samples,
1595 * before rounding and after scaling up to the desired quantization depth.
1597 for(c = 0;c < numchans;c++)
1599 ALfloat *restrict samples = Samples[c];
1600 for(i = 0;i < SamplesToDo;i++)
1602 ALfloat val = samples[i] * quant_scale;
1603 ALuint rng0 = dither_rng(&seed);
1604 ALuint rng1 = dither_rng(&seed);
1605 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1606 samples[i] = roundf(val) * invscale;
1609 *dither_seed = seed;
1613 static inline ALfloat Conv_ALfloat(ALfloat val)
1614 { return val; }
1615 static inline ALint Conv_ALint(ALfloat val)
1617 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1618 * integer range normalized floats can be safely converted to (a bit of the
1619 * exponent helps out, effectively giving 25 bits).
1621 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1623 static inline ALshort Conv_ALshort(ALfloat val)
1624 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1625 static inline ALbyte Conv_ALbyte(ALfloat val)
1626 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1628 /* Define unsigned output variations. */
1629 #define DECL_TEMPLATE(T, func, O) \
1630 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1632 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1633 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1634 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1636 #undef DECL_TEMPLATE
1638 #define DECL_TEMPLATE(T, A) \
1639 static void Write##A(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1640 ALsizei Offset, ALsizei SamplesToDo, ALsizei numchans) \
1642 ALsizei i, j; \
1643 for(j = 0;j < numchans;j++) \
1645 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1646 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1648 for(i = 0;i < SamplesToDo;i++) \
1649 out[i*numchans] = Conv_##T(in[i]); \
1653 DECL_TEMPLATE(ALfloat, F32)
1654 DECL_TEMPLATE(ALuint, UI32)
1655 DECL_TEMPLATE(ALint, I32)
1656 DECL_TEMPLATE(ALushort, UI16)
1657 DECL_TEMPLATE(ALshort, I16)
1658 DECL_TEMPLATE(ALubyte, UI8)
1659 DECL_TEMPLATE(ALbyte, I8)
1661 #undef DECL_TEMPLATE
1664 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1666 ALsizei SamplesToDo;
1667 ALsizei SamplesDone;
1668 ALCcontext *ctx;
1669 ALsizei i, c;
1671 START_MIXER_MODE();
1672 for(SamplesDone = 0;SamplesDone < NumSamples;)
1674 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1675 for(c = 0;c < device->Dry.NumChannels;c++)
1676 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1677 if(device->Dry.Buffer != device->FOAOut.Buffer)
1678 for(c = 0;c < device->FOAOut.NumChannels;c++)
1679 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1680 if(device->Dry.Buffer != device->RealOut.Buffer)
1681 for(c = 0;c < device->RealOut.NumChannels;c++)
1682 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1684 IncrementRef(&device->MixCount);
1686 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1687 while(ctx)
1689 const struct ALeffectslotArray *auxslots;
1691 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1692 UpdateContextSources(ctx, auxslots);
1694 for(i = 0;i < auxslots->count;i++)
1696 ALeffectslot *slot = auxslots->slot[i];
1697 for(c = 0;c < slot->NumChannels;c++)
1698 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1701 /* source processing */
1702 for(i = 0;i < ctx->VoiceCount;i++)
1704 ALvoice *voice = ctx->Voices[i];
1705 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1706 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1707 voice->Step > 0)
1709 if(!MixSource(voice, source, device, SamplesToDo))
1711 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1712 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1717 /* effect slot processing */
1718 for(i = 0;i < auxslots->count;i++)
1720 const ALeffectslot *slot = auxslots->slot[i];
1721 ALeffectState *state = slot->Params.EffectState;
1722 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1723 state->OutChannels);
1726 ctx = ctx->next;
1729 /* Increment the clock time. Every second's worth of samples is
1730 * converted and added to clock base so that large sample counts don't
1731 * overflow during conversion. This also guarantees an exact, stable
1732 * conversion. */
1733 device->SamplesDone += SamplesToDo;
1734 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1735 device->SamplesDone %= device->Frequency;
1736 IncrementRef(&device->MixCount);
1738 if(device->HrtfHandle)
1740 DirectHrtfState *state;
1741 int lidx, ridx;
1743 if(device->AmbiUp)
1744 ambiup_process(device->AmbiUp,
1745 device->Dry.Buffer, device->Dry.NumChannels,
1746 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1749 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1750 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1751 assert(lidx != -1 && ridx != -1);
1753 state = device->Hrtf;
1754 for(c = 0;c < device->Dry.NumChannels;c++)
1756 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1757 device->Dry.Buffer[c], state->Offset, state->IrSize,
1758 SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
1759 state->Chan[c].Values, SamplesToDo
1762 state->Offset += SamplesToDo;
1764 else if(device->AmbiDecoder)
1766 if(device->Dry.Buffer != device->FOAOut.Buffer)
1767 bformatdec_upSample(device->AmbiDecoder,
1768 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1769 device->FOAOut.NumChannels, SamplesToDo
1771 bformatdec_process(device->AmbiDecoder,
1772 device->RealOut.Buffer, device->RealOut.NumChannels,
1773 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1776 else if(device->AmbiUp)
1778 ambiup_process(device->AmbiUp,
1779 device->RealOut.Buffer, device->RealOut.NumChannels,
1780 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1783 else if(device->Uhj_Encoder)
1785 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1786 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1787 if(lidx != -1 && ridx != -1)
1789 /* Encode to stereo-compatible 2-channel UHJ output. */
1790 EncodeUhj2(device->Uhj_Encoder,
1791 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1792 device->Dry.Buffer, SamplesToDo
1796 else if(device->Bs2b)
1798 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1799 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1800 if(lidx != -1 && ridx != -1)
1802 /* Apply binaural/crossfeed filter */
1803 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1804 device->RealOut.Buffer[ridx], SamplesToDo);
1808 if(OutBuffer)
1810 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1811 ALsizei Channels = device->RealOut.NumChannels;
1813 if(device->Stablizer)
1815 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1816 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1817 int cidx = GetChannelIdxByName(device->RealOut, FrontCenter);
1818 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1820 ApplyStablizer(device->Stablizer, Buffer, lidx, ridx, cidx,
1821 SamplesToDo, Channels);
1824 /* Use NFCtrlData for temp value storage. */
1825 ApplyDistanceComp(Buffer, device->ChannelDelay, device->NFCtrlData,
1826 SamplesToDo, Channels);
1828 if(device->Limiter)
1829 ApplyCompression(device->Limiter, Channels, SamplesToDo, Buffer);
1831 if(device->DitherDepth > 0.0f)
1832 ApplyDither(Buffer, &device->DitherSeed, device->DitherDepth, SamplesToDo,
1833 Channels);
1835 switch(device->FmtType)
1837 case DevFmtByte:
1838 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1839 break;
1840 case DevFmtUByte:
1841 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1842 break;
1843 case DevFmtShort:
1844 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1845 break;
1846 case DevFmtUShort:
1847 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1848 break;
1849 case DevFmtInt:
1850 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1851 break;
1852 case DevFmtUInt:
1853 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1854 break;
1855 case DevFmtFloat:
1856 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1857 break;
1861 SamplesDone += SamplesToDo;
1863 END_MIXER_MODE();
1867 void aluHandleDisconnect(ALCdevice *device)
1869 ALCcontext *ctx;
1871 device->Connected = ALC_FALSE;
1873 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1874 while(ctx)
1876 ALsizei i;
1877 for(i = 0;i < ctx->VoiceCount;i++)
1879 ALvoice *voice = ctx->Voices[i];
1880 ALsource *source;
1882 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_acq_rel);
1883 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1885 if(source)
1887 ALenum playing = AL_PLAYING;
1888 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1891 ctx->VoiceCount = 0;
1893 ctx = ctx->next;