Don't hide -msse and -mfpu=neon checks behind a not-msvc check
[openal-soft.git] / Alc / ALu.c
blob29accf57ca32ded9d1a3c4ae5342176fbaa77f9c
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 const aluMatrixf IdentityMatrixf = {{
93 { 1.0f, 0.0f, 0.0f, 0.0f },
94 { 0.0f, 1.0f, 0.0f, 0.0f },
95 { 0.0f, 0.0f, 1.0f, 0.0f },
96 { 0.0f, 0.0f, 0.0f, 1.0f },
97 }};
100 struct ChanMap {
101 enum Channel channel;
102 ALfloat angle;
103 ALfloat elevation;
106 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
109 void DeinitVoice(ALvoice *voice)
111 struct ALvoiceProps *props;
112 size_t count = 0;
114 props = ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL);
115 if(props) al_free(props);
117 props = ATOMIC_EXCHANGE_PTR(&voice->FreeList, NULL, almemory_order_relaxed);
118 while(props)
120 struct ALvoiceProps *next;
121 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
122 al_free(props);
123 props = next;
124 ++count;
126 /* This is excessively spammy if it traces every voice destruction, so just
127 * warn if it was unexpectedly large.
129 if(count > 3)
130 WARN("Freed "SZFMT" voice property objects\n", count);
134 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
136 #ifdef HAVE_NEON
137 if((CPUCapFlags&CPU_CAP_NEON))
138 return MixDirectHrtf_Neon;
139 #endif
140 #ifdef HAVE_SSE
141 if((CPUCapFlags&CPU_CAP_SSE))
142 return MixDirectHrtf_SSE;
143 #endif
145 return MixDirectHrtf_C;
149 /* Prior to VS2013, MSVC lacks the round() family of functions. */
150 #if defined(_MSC_VER) && _MSC_VER < 1800
151 static float roundf(float val)
153 if(val < 0.0f)
154 return ceilf(val-0.5f);
155 return floorf(val+0.5f);
157 #endif
159 /* This RNG method was created based on the math found in opusdec. It's quick,
160 * and starting with a seed value of 22222, is suitable for generating
161 * whitenoise.
163 static inline ALuint dither_rng(ALuint *seed)
165 *seed = (*seed * 96314165) + 907633515;
166 return *seed;
170 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
172 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
173 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
174 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
177 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
179 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
182 static ALfloat aluNormalize(ALfloat *vec)
184 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
185 if(length > 0.0f)
187 ALfloat inv_length = 1.0f/length;
188 vec[0] *= inv_length;
189 vec[1] *= inv_length;
190 vec[2] *= inv_length;
192 return length;
195 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
197 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
199 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];
200 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];
201 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];
204 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
206 aluVector v;
207 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];
208 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];
209 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];
210 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];
211 return v;
215 void aluInit(void)
217 MixDirectHrtf = SelectHrtfMixer();
220 /* Prepares the interpolator for a given rate (determined by increment). A
221 * result of AL_FALSE indicates that the filter output will completely cut
222 * the input signal.
224 * With a bit of work, and a trade of memory for CPU cost, this could be
225 * modified for use with an interpolated increment for buttery-smooth pitch
226 * changes.
228 ALboolean BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
230 ALboolean uncut = AL_TRUE;
231 ALfloat sf;
232 ALsizei si;
234 if(increment > FRACTIONONE)
236 sf = (ALfloat)FRACTIONONE / increment;
237 if(sf < table->scaleBase)
239 /* Signal has been completely cut. The return result can be used
240 * to skip the filter (and output zeros) as an optimization.
242 sf = 0.0f;
243 si = 0;
244 uncut = AL_FALSE;
246 else
248 sf = (BSINC_SCALE_COUNT - 1) * (sf - table->scaleBase) * table->scaleRange;
249 si = fastf2i(sf);
250 /* The interpolation factor is fit to this diagonally-symmetric
251 * curve to reduce the transition ripple caused by interpolating
252 * different scales of the sinc function.
254 sf = 1.0f - cosf(asinf(sf - si));
257 else
259 sf = 0.0f;
260 si = BSINC_SCALE_COUNT - 1;
263 state->sf = sf;
264 state->m = table->m[si];
265 state->l = -((state->m/2) - 1);
266 state->filter = table->Tab + table->filterOffset[si];
267 return uncut;
271 static ALboolean CalcListenerParams(ALCcontext *Context)
273 ALlistener *Listener = Context->Listener;
274 ALfloat N[3], V[3], U[3], P[3];
275 struct ALlistenerProps *props;
276 aluVector vel;
278 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
279 if(!props) return AL_FALSE;
281 /* AT then UP */
282 N[0] = props->Forward[0];
283 N[1] = props->Forward[1];
284 N[2] = props->Forward[2];
285 aluNormalize(N);
286 V[0] = props->Up[0];
287 V[1] = props->Up[1];
288 V[2] = props->Up[2];
289 aluNormalize(V);
290 /* Build and normalize right-vector */
291 aluCrossproduct(N, V, U);
292 aluNormalize(U);
294 aluMatrixfSet(&Listener->Params.Matrix,
295 U[0], V[0], -N[0], 0.0,
296 U[1], V[1], -N[1], 0.0,
297 U[2], V[2], -N[2], 0.0,
298 0.0, 0.0, 0.0, 1.0
301 P[0] = props->Position[0];
302 P[1] = props->Position[1];
303 P[2] = props->Position[2];
304 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
305 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
307 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
308 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
310 Listener->Params.Gain = props->Gain * Context->GainBoost;
311 Listener->Params.MetersPerUnit = props->MetersPerUnit;
313 Listener->Params.DopplerFactor = props->DopplerFactor;
314 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
316 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
317 Listener->Params.DistanceModel = props->DistanceModel;
319 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
320 return AL_TRUE;
323 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
325 struct ALeffectslotProps *props;
326 ALeffectState *state;
328 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
329 if(!props) return AL_FALSE;
331 slot->Params.Gain = props->Gain;
332 slot->Params.AuxSendAuto = props->AuxSendAuto;
333 slot->Params.EffectType = props->Type;
334 if(IsReverbEffect(slot->Params.EffectType))
336 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
337 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
338 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
339 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
340 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
342 else
344 slot->Params.RoomRolloff = 0.0f;
345 slot->Params.DecayTime = 0.0f;
346 slot->Params.DecayHFRatio = 0.0f;
347 slot->Params.DecayHFLimit = AL_FALSE;
348 slot->Params.AirAbsorptionGainHF = 1.0f;
351 /* Swap effect states. No need to play with the ref counts since they keep
352 * the same number of refs.
354 state = props->State;
355 props->State = slot->Params.EffectState;
356 slot->Params.EffectState = state;
358 V(state,update)(device, slot, &props->Props);
360 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
361 return AL_TRUE;
365 static const struct ChanMap MonoMap[1] = {
366 { FrontCenter, 0.0f, 0.0f }
367 }, RearMap[2] = {
368 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
369 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
370 }, QuadMap[4] = {
371 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
372 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
373 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
374 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
375 }, X51Map[6] = {
376 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
377 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
378 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
379 { LFE, 0.0f, 0.0f },
380 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
381 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
382 }, X61Map[7] = {
383 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
384 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
385 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
386 { LFE, 0.0f, 0.0f },
387 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
388 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
389 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
390 }, X71Map[8] = {
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 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
396 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
397 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
398 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
401 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
402 const ALfloat Spread, const ALfloat DryGain,
403 const ALfloat DryGainHF, const ALfloat DryGainLF,
404 const ALfloat *WetGain, const ALfloat *WetGainLF,
405 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
406 const ALbuffer *Buffer, const struct ALvoiceProps *props,
407 const ALlistener *Listener, const ALCdevice *Device)
409 struct ChanMap StereoMap[2] = {
410 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
411 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
413 bool DirectChannels = props->DirectChannels;
414 const ALsizei NumSends = Device->NumAuxSends;
415 const ALuint Frequency = Device->Frequency;
416 const struct ChanMap *chans = NULL;
417 ALsizei num_channels = 0;
418 bool isbformat = false;
419 ALfloat downmix_gain = 1.0f;
420 ALsizei c, i, j;
422 switch(Buffer->FmtChannels)
424 case FmtMono:
425 chans = MonoMap;
426 num_channels = 1;
427 /* Mono buffers are never played direct. */
428 DirectChannels = false;
429 break;
431 case FmtStereo:
432 /* Convert counter-clockwise to clockwise. */
433 StereoMap[0].angle = -props->StereoPan[0];
434 StereoMap[1].angle = -props->StereoPan[1];
436 chans = StereoMap;
437 num_channels = 2;
438 downmix_gain = 1.0f / 2.0f;
439 break;
441 case FmtRear:
442 chans = RearMap;
443 num_channels = 2;
444 downmix_gain = 1.0f / 2.0f;
445 break;
447 case FmtQuad:
448 chans = QuadMap;
449 num_channels = 4;
450 downmix_gain = 1.0f / 4.0f;
451 break;
453 case FmtX51:
454 chans = X51Map;
455 num_channels = 6;
456 /* NOTE: Excludes LFE. */
457 downmix_gain = 1.0f / 5.0f;
458 break;
460 case FmtX61:
461 chans = X61Map;
462 num_channels = 7;
463 /* NOTE: Excludes LFE. */
464 downmix_gain = 1.0f / 6.0f;
465 break;
467 case FmtX71:
468 chans = X71Map;
469 num_channels = 8;
470 /* NOTE: Excludes LFE. */
471 downmix_gain = 1.0f / 7.0f;
472 break;
474 case FmtBFormat2D:
475 num_channels = 3;
476 isbformat = true;
477 DirectChannels = false;
478 break;
480 case FmtBFormat3D:
481 num_channels = 4;
482 isbformat = true;
483 DirectChannels = false;
484 break;
487 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
488 if(isbformat)
490 /* Special handling for B-Format sources. */
492 if(Distance > FLT_EPSILON)
494 /* Panning a B-Format sound toward some direction is easy. Just pan
495 * the first (W) channel as a normal mono sound and silence the
496 * others.
498 ALfloat coeffs[MAX_AMBI_COEFFS];
500 if(Device->AvgSpeakerDist > 0.0f)
502 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
503 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
504 (mdist * (ALfloat)Device->Frequency);
505 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
506 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
507 /* Clamp w0 for really close distances, to prevent excessive
508 * bass.
510 w0 = minf(w0, w1*4.0f);
512 /* Only need to adjust the first channel of a B-Format source. */
513 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
514 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
515 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
517 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
518 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
519 voice->Flags |= VOICE_HAS_NFC;
522 if(Device->Render_Mode == StereoPair)
524 ALfloat ev = asinf(Dir[1]);
525 ALfloat az = atan2f(Dir[0], -Dir[2]);
526 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
528 else
529 CalcDirectionCoeffs(Dir, Spread, coeffs);
531 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
532 ComputePanningGains(Device->Dry, coeffs, DryGain*1.414213562f,
533 voice->Direct.Params[0].Gains.Target);
534 for(c = 1;c < num_channels;c++)
536 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
537 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
540 for(i = 0;i < NumSends;i++)
542 const ALeffectslot *Slot = SendSlots[i];
543 if(Slot)
544 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
545 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
547 else
548 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
549 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
550 for(c = 1;c < num_channels;c++)
552 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
553 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
557 else
559 /* Local B-Format sources have their XYZ channels rotated according
560 * to the orientation.
562 ALfloat N[3], V[3], U[3];
563 aluMatrixf matrix;
564 ALfloat scale;
566 if(Device->AvgSpeakerDist > 0.0f)
568 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
569 * is what we want for FOA input. The first channel may have
570 * been previously re-adjusted if panned, so reset it.
572 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f);
573 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f);
574 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 0.0f);
576 voice->Direct.ChannelsPerOrder[0] = 1;
577 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
578 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
579 voice->Direct.ChannelsPerOrder[i] = 0;
580 voice->Flags |= VOICE_HAS_NFC;
583 /* AT then UP */
584 N[0] = props->Orientation[0][0];
585 N[1] = props->Orientation[0][1];
586 N[2] = props->Orientation[0][2];
587 aluNormalize(N);
588 V[0] = props->Orientation[1][0];
589 V[1] = props->Orientation[1][1];
590 V[2] = props->Orientation[1][2];
591 aluNormalize(V);
592 if(!props->HeadRelative)
594 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
595 aluMatrixfFloat3(N, 0.0f, lmatrix);
596 aluMatrixfFloat3(V, 0.0f, lmatrix);
598 /* Build and normalize right-vector */
599 aluCrossproduct(N, V, U);
600 aluNormalize(U);
602 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
603 scale = 1.732050808f;
604 aluMatrixfSet(&matrix,
605 1.414213562f, 0.0f, 0.0f, 0.0f,
606 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
607 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
608 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
611 voice->Direct.Buffer = Device->FOAOut.Buffer;
612 voice->Direct.Channels = Device->FOAOut.NumChannels;
613 for(c = 0;c < num_channels;c++)
614 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
615 voice->Direct.Params[c].Gains.Target);
616 for(i = 0;i < NumSends;i++)
618 const ALeffectslot *Slot = SendSlots[i];
619 if(Slot)
621 for(c = 0;c < num_channels;c++)
622 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
623 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
626 else
628 for(c = 0;c < num_channels;c++)
629 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
630 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
635 else if(DirectChannels)
637 /* Direct source channels always play local. Skip the virtual channels
638 * and write inputs to the matching real outputs.
640 voice->Direct.Buffer = Device->RealOut.Buffer;
641 voice->Direct.Channels = Device->RealOut.NumChannels;
643 for(c = 0;c < num_channels;c++)
645 int idx;
646 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
647 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
648 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
649 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
652 /* Auxiliary sends still use normal channel panning since they mix to
653 * B-Format, which can't channel-match.
655 for(c = 0;c < num_channels;c++)
657 ALfloat coeffs[MAX_AMBI_COEFFS];
658 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
660 for(i = 0;i < NumSends;i++)
662 const ALeffectslot *Slot = SendSlots[i];
663 if(Slot)
664 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
665 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
667 else
668 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
669 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
673 else if(Device->Render_Mode == HrtfRender)
675 /* Full HRTF rendering. Skip the virtual channels and render to the
676 * real outputs.
678 voice->Direct.Buffer = Device->RealOut.Buffer;
679 voice->Direct.Channels = Device->RealOut.NumChannels;
681 if(Distance > FLT_EPSILON)
683 ALfloat coeffs[MAX_AMBI_COEFFS];
684 ALfloat ev, az;
686 ev = asinf(Dir[1]);
687 az = atan2f(Dir[0], -Dir[2]);
689 /* Get the HRIR coefficients and delays just once, for the given
690 * source direction.
692 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
693 voice->Direct.Params[0].Hrtf.Target.Coeffs,
694 voice->Direct.Params[0].Hrtf.Target.Delay);
695 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
697 /* Remaining channels use the same results as the first. */
698 for(c = 1;c < num_channels;c++)
700 /* Skip LFE */
701 if(chans[c].channel == LFE)
702 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
703 sizeof(voice->Direct.Params[c].Hrtf.Target));
704 else
705 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
708 /* Calculate the directional coefficients once, which apply to all
709 * input channels of the source sends.
711 CalcDirectionCoeffs(Dir, Spread, coeffs);
713 for(i = 0;i < NumSends;i++)
715 const ALeffectslot *Slot = SendSlots[i];
716 if(Slot)
717 for(c = 0;c < num_channels;c++)
719 /* Skip LFE */
720 if(chans[c].channel == LFE)
721 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
722 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
723 else
724 ComputePanningGainsBF(Slot->ChanMap,
725 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
726 voice->Send[i].Params[c].Gains.Target
729 else
730 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
731 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
734 else
736 /* Local sources on HRTF play with each channel panned to its
737 * relative location around the listener, providing "virtual
738 * speaker" responses.
740 for(c = 0;c < num_channels;c++)
742 ALfloat coeffs[MAX_AMBI_COEFFS];
744 if(chans[c].channel == LFE)
746 /* Skip LFE */
747 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
748 sizeof(voice->Direct.Params[c].Hrtf.Target));
749 for(i = 0;i < NumSends;i++)
751 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
752 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
754 continue;
757 /* Get the HRIR coefficients and delays for this channel
758 * position.
760 GetHrtfCoeffs(Device->HrtfHandle,
761 chans[c].elevation, chans[c].angle, Spread,
762 voice->Direct.Params[c].Hrtf.Target.Coeffs,
763 voice->Direct.Params[c].Hrtf.Target.Delay
765 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
767 /* Normal panning for auxiliary sends. */
768 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
770 for(i = 0;i < NumSends;i++)
772 const ALeffectslot *Slot = SendSlots[i];
773 if(Slot)
774 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
775 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
777 else
778 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
779 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
784 voice->Flags |= VOICE_HAS_HRTF;
786 else
788 /* Non-HRTF rendering. Use normal panning to the output. */
790 if(Distance > FLT_EPSILON)
792 ALfloat coeffs[MAX_AMBI_COEFFS];
793 ALfloat w0 = 0.0f;
795 /* Calculate NFC filter coefficient if needed. */
796 if(Device->AvgSpeakerDist > 0.0f)
798 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
799 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
800 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
801 w0 = SPEEDOFSOUNDMETRESPERSEC /
802 (mdist * (ALfloat)Device->Frequency);
803 /* Clamp w0 for really close distances, to prevent excessive
804 * bass.
806 w0 = minf(w0, w1*4.0f);
808 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
809 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
810 voice->Flags |= VOICE_HAS_NFC;
813 /* Calculate the directional coefficients once, which apply to all
814 * input channels.
816 if(Device->Render_Mode == StereoPair)
818 ALfloat ev = asinf(Dir[1]);
819 ALfloat az = atan2f(Dir[0], -Dir[2]);
820 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
822 else
823 CalcDirectionCoeffs(Dir, Spread, coeffs);
825 for(c = 0;c < num_channels;c++)
827 /* Adjust NFC filters if needed. */
828 if((voice->Flags&VOICE_HAS_NFC))
830 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
831 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
832 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
835 /* Special-case LFE */
836 if(chans[c].channel == LFE)
838 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
839 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
840 if(Device->Dry.Buffer == Device->RealOut.Buffer)
842 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
843 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
845 continue;
848 ComputePanningGains(Device->Dry,
849 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
853 for(i = 0;i < NumSends;i++)
855 const ALeffectslot *Slot = SendSlots[i];
856 if(Slot)
857 for(c = 0;c < num_channels;c++)
859 /* Skip LFE */
860 if(chans[c].channel == LFE)
861 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
862 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
863 else
864 ComputePanningGainsBF(Slot->ChanMap,
865 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
866 voice->Send[i].Params[c].Gains.Target
869 else
870 for(c = 0;c < num_channels;c++)
872 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
873 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
877 else
879 ALfloat w0 = 0.0f;
881 if(Device->AvgSpeakerDist > 0.0f)
883 /* If the source distance is 0, set w0 to w1 to act as a pass-
884 * through. We still want to pass the signal through the
885 * filters so they keep an appropriate history, in case the
886 * source moves away from the listener.
888 w0 = SPEEDOFSOUNDMETRESPERSEC /
889 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
891 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
892 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
893 voice->Flags |= VOICE_HAS_NFC;
896 for(c = 0;c < num_channels;c++)
898 ALfloat coeffs[MAX_AMBI_COEFFS];
900 if((voice->Flags&VOICE_HAS_NFC))
902 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
903 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
904 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
907 /* Special-case LFE */
908 if(chans[c].channel == LFE)
910 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
911 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
912 if(Device->Dry.Buffer == Device->RealOut.Buffer)
914 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
915 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
918 for(i = 0;i < NumSends;i++)
920 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
921 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
923 continue;
926 if(Device->Render_Mode == StereoPair)
927 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
928 else
929 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
930 ComputePanningGains(Device->Dry,
931 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
934 for(i = 0;i < NumSends;i++)
936 const ALeffectslot *Slot = SendSlots[i];
937 if(Slot)
938 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
939 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
941 else
942 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
943 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
950 ALfloat hfScale = props->Direct.HFReference / Frequency;
951 ALfloat lfScale = props->Direct.LFReference / Frequency;
952 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
953 ALfloat gainLF = maxf(DryGainLF, 0.001f);
955 voice->Direct.FilterType = AF_None;
956 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
957 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
958 ALfilterState_setParams(
959 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
960 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
962 ALfilterState_setParams(
963 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
964 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
966 for(c = 1;c < num_channels;c++)
968 ALfilterState_copyParams(&voice->Direct.Params[c].LowPass,
969 &voice->Direct.Params[0].LowPass);
970 ALfilterState_copyParams(&voice->Direct.Params[c].HighPass,
971 &voice->Direct.Params[0].HighPass);
974 for(i = 0;i < NumSends;i++)
976 ALfloat hfScale = props->Send[i].HFReference / Frequency;
977 ALfloat lfScale = props->Send[i].LFReference / Frequency;
978 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
979 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
981 voice->Send[i].FilterType = AF_None;
982 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
983 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
984 ALfilterState_setParams(
985 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
986 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
988 ALfilterState_setParams(
989 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
990 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
992 for(c = 1;c < num_channels;c++)
994 ALfilterState_copyParams(&voice->Send[i].Params[c].LowPass,
995 &voice->Send[i].Params[0].LowPass);
996 ALfilterState_copyParams(&voice->Send[i].Params[c].HighPass,
997 &voice->Send[i].Params[0].HighPass);
1002 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1004 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1005 const ALCdevice *Device = ALContext->Device;
1006 const ALlistener *Listener = ALContext->Listener;
1007 ALfloat DryGain, DryGainHF, DryGainLF;
1008 ALfloat WetGain[MAX_SENDS];
1009 ALfloat WetGainHF[MAX_SENDS];
1010 ALfloat WetGainLF[MAX_SENDS];
1011 ALeffectslot *SendSlots[MAX_SENDS];
1012 ALfloat Pitch;
1013 ALsizei i;
1015 voice->Direct.Buffer = Device->Dry.Buffer;
1016 voice->Direct.Channels = Device->Dry.NumChannels;
1017 for(i = 0;i < Device->NumAuxSends;i++)
1019 SendSlots[i] = props->Send[i].Slot;
1020 if(!SendSlots[i] && i == 0)
1021 SendSlots[i] = ALContext->DefaultSlot;
1022 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1024 SendSlots[i] = NULL;
1025 voice->Send[i].Buffer = NULL;
1026 voice->Send[i].Channels = 0;
1028 else
1030 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1031 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1035 /* Calculate the stepping value */
1036 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1037 if(Pitch > (ALfloat)MAX_PITCH)
1038 voice->Step = MAX_PITCH<<FRACTIONBITS;
1039 else
1040 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1041 if(props->Resampler == BSinc24Resampler)
1042 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1043 else if(props->Resampler == BSinc12Resampler)
1044 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1045 else
1047 voice->ResampleState.sinc4.filter = sinc4Tab;
1048 voice->Resampler = SelectResampler(props->Resampler);
1050 /* Calculate gains */
1051 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1052 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1053 DryGain = minf(DryGain, GAIN_MIX_MAX);
1054 DryGainHF = props->Direct.GainHF;
1055 DryGainLF = props->Direct.GainLF;
1056 for(i = 0;i < Device->NumAuxSends;i++)
1058 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1059 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1060 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1061 WetGainHF[i] = props->Send[i].GainHF;
1062 WetGainLF[i] = props->Send[i].GainLF;
1065 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1066 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1069 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1071 const ALCdevice *Device = ALContext->Device;
1072 const ALlistener *Listener = ALContext->Listener;
1073 const ALsizei NumSends = Device->NumAuxSends;
1074 aluVector Position, Velocity, Direction, SourceToListener;
1075 ALfloat Distance, ClampedDist, DopplerFactor;
1076 ALeffectslot *SendSlots[MAX_SENDS];
1077 ALfloat RoomRolloff[MAX_SENDS];
1078 ALfloat DecayDistance[MAX_SENDS];
1079 ALfloat DecayHFDistance[MAX_SENDS];
1080 ALfloat DryGain, DryGainHF, DryGainLF;
1081 ALfloat WetGain[MAX_SENDS];
1082 ALfloat WetGainHF[MAX_SENDS];
1083 ALfloat WetGainLF[MAX_SENDS];
1084 bool directional;
1085 ALfloat dir[3];
1086 ALfloat spread;
1087 ALfloat Pitch;
1088 ALint i;
1090 /* Set mixing buffers and get send parameters. */
1091 voice->Direct.Buffer = Device->Dry.Buffer;
1092 voice->Direct.Channels = Device->Dry.NumChannels;
1093 for(i = 0;i < NumSends;i++)
1095 SendSlots[i] = props->Send[i].Slot;
1096 if(!SendSlots[i] && i == 0)
1097 SendSlots[i] = ALContext->DefaultSlot;
1098 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1100 SendSlots[i] = NULL;
1101 RoomRolloff[i] = 0.0f;
1102 DecayDistance[i] = 0.0f;
1103 DecayHFDistance[i] = 0.0f;
1105 else if(SendSlots[i]->Params.AuxSendAuto)
1107 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1108 DecayDistance[i] = SendSlots[i]->Params.DecayTime * SPEEDOFSOUNDMETRESPERSEC;
1109 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1110 if(SendSlots[i]->Params.DecayHFLimit)
1112 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1113 if(airAbsorption < 1.0f)
1115 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1116 DecayHFDistance[i] = minf(limitRatio, DecayHFDistance[i]);
1120 else
1122 /* If the slot's auxiliary send auto is off, the data sent to the
1123 * effect slot is the same as the dry path, sans filter effects */
1124 RoomRolloff[i] = props->RolloffFactor;
1125 DecayDistance[i] = 0.0f;
1126 DecayHFDistance[i] = 0.0f;
1129 if(!SendSlots[i])
1131 voice->Send[i].Buffer = NULL;
1132 voice->Send[i].Channels = 0;
1134 else
1136 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1137 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1141 /* Transform source to listener space (convert to head relative) */
1142 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1143 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1144 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1145 if(props->HeadRelative == AL_FALSE)
1147 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1148 /* Transform source vectors */
1149 Position = aluMatrixfVector(Matrix, &Position);
1150 Velocity = aluMatrixfVector(Matrix, &Velocity);
1151 Direction = aluMatrixfVector(Matrix, &Direction);
1153 else
1155 const aluVector *lvelocity = &Listener->Params.Velocity;
1156 /* Offset the source velocity to be relative of the listener velocity */
1157 Velocity.v[0] += lvelocity->v[0];
1158 Velocity.v[1] += lvelocity->v[1];
1159 Velocity.v[2] += lvelocity->v[2];
1162 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1163 SourceToListener.v[0] = -Position.v[0];
1164 SourceToListener.v[1] = -Position.v[1];
1165 SourceToListener.v[2] = -Position.v[2];
1166 SourceToListener.v[3] = 0.0f;
1167 Distance = aluNormalize(SourceToListener.v);
1169 /* Initial source gain */
1170 DryGain = props->Gain;
1171 DryGainHF = 1.0f;
1172 DryGainLF = 1.0f;
1173 for(i = 0;i < NumSends;i++)
1175 WetGain[i] = props->Gain;
1176 WetGainHF[i] = 1.0f;
1177 WetGainLF[i] = 1.0f;
1180 /* Calculate distance attenuation */
1181 ClampedDist = Distance;
1183 switch(Listener->Params.SourceDistanceModel ?
1184 props->DistanceModel : Listener->Params.DistanceModel)
1186 case InverseDistanceClamped:
1187 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1188 if(props->MaxDistance < props->RefDistance)
1189 break;
1190 /*fall-through*/
1191 case InverseDistance:
1192 if(!(props->RefDistance > 0.0f))
1193 ClampedDist = props->RefDistance;
1194 else
1196 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1197 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1198 for(i = 0;i < NumSends;i++)
1200 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1201 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1204 break;
1206 case LinearDistanceClamped:
1207 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1208 if(props->MaxDistance < props->RefDistance)
1209 break;
1210 /*fall-through*/
1211 case LinearDistance:
1212 if(!(props->MaxDistance != props->RefDistance))
1213 ClampedDist = props->RefDistance;
1214 else
1216 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1217 (props->MaxDistance-props->RefDistance);
1218 DryGain *= maxf(1.0f - attn, 0.0f);
1219 for(i = 0;i < NumSends;i++)
1221 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1222 (props->MaxDistance-props->RefDistance);
1223 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1226 break;
1228 case ExponentDistanceClamped:
1229 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1230 if(props->MaxDistance < props->RefDistance)
1231 break;
1232 /*fall-through*/
1233 case ExponentDistance:
1234 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1235 ClampedDist = props->RefDistance;
1236 else
1238 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1239 for(i = 0;i < NumSends;i++)
1240 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1242 break;
1244 case DisableDistance:
1245 ClampedDist = props->RefDistance;
1246 break;
1249 /* Distance-based air absorption */
1250 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1252 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1253 Listener->Params.MetersPerUnit;
1254 if(props->AirAbsorptionFactor > 0.0f)
1256 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1257 DryGainHF *= hfattn;
1258 for(i = 0;i < NumSends;i++)
1259 WetGainHF[i] *= hfattn;
1262 if(props->WetGainAuto)
1264 /* Apply a decay-time transformation to the wet path, based on the
1265 * source distance in meters. The initial decay of the reverb
1266 * effect is calculated and applied to the wet path.
1268 for(i = 0;i < NumSends;i++)
1270 ALfloat gain;
1272 if(!(DecayDistance[i] > 0.0f))
1273 continue;
1275 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1276 WetGain[i] *= gain;
1277 /* Yes, the wet path's air absorption is applied with
1278 * WetGainAuto on, rather than WetGainHFAuto.
1280 if(gain > 0.0f)
1282 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1283 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1289 /* Calculate directional soundcones */
1290 if(directional && props->InnerAngle < 360.0f)
1292 ALfloat ConeVolume;
1293 ALfloat ConeHF;
1294 ALfloat Angle;
1296 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1297 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1298 if(!(Angle > props->InnerAngle))
1300 ConeVolume = 1.0f;
1301 ConeHF = 1.0f;
1303 else if(Angle < props->OuterAngle)
1305 ALfloat scale = ( Angle-props->InnerAngle) /
1306 (props->OuterAngle-props->InnerAngle);
1307 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1308 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1310 else
1312 ConeVolume = props->OuterGain;
1313 ConeHF = props->OuterGainHF;
1316 DryGain *= ConeVolume;
1317 if(props->DryGainHFAuto)
1318 DryGainHF *= ConeHF;
1319 if(props->WetGainAuto)
1321 for(i = 0;i < NumSends;i++)
1322 WetGain[i] *= ConeVolume;
1324 if(props->WetGainHFAuto)
1326 for(i = 0;i < NumSends;i++)
1327 WetGainHF[i] *= ConeHF;
1331 /* Apply gain and frequency filters */
1332 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1333 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1334 DryGainHF *= props->Direct.GainHF;
1335 DryGainLF *= props->Direct.GainLF;
1336 for(i = 0;i < NumSends;i++)
1338 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1339 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1340 WetGainHF[i] *= props->Send[i].GainHF;
1341 WetGainLF[i] *= props->Send[i].GainLF;
1345 /* Initial source pitch */
1346 Pitch = props->Pitch;
1348 /* Calculate velocity-based doppler effect */
1349 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1350 if(DopplerFactor > 0.0f)
1352 const aluVector *lvelocity = &Listener->Params.Velocity;
1353 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1354 ALfloat vss, vls;
1356 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1357 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1359 if(!(vls < SpeedOfSound))
1361 /* Listener moving away from the source at the speed of sound.
1362 * Sound waves can't catch it.
1364 Pitch = 0.0f;
1366 else if(!(vss < SpeedOfSound))
1368 /* Source moving toward the listener at the speed of sound. Sound
1369 * waves bunch up to extreme frequencies.
1371 Pitch = HUGE_VALF;
1373 else
1375 /* Source and listener movement is nominal. Calculate the proper
1376 * doppler shift.
1378 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1382 /* Adjust pitch based on the buffer and output frequencies, and calculate
1383 * fixed-point stepping value.
1385 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1386 if(Pitch > (ALfloat)MAX_PITCH)
1387 voice->Step = MAX_PITCH<<FRACTIONBITS;
1388 else
1389 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1390 if(props->Resampler == BSinc24Resampler)
1391 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1392 else if(props->Resampler == BSinc12Resampler)
1393 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1394 else
1395 voice->ResampleState.sinc4.filter = sinc4Tab;
1396 voice->Resampler = SelectResampler(props->Resampler);
1398 if(Distance > FLT_EPSILON)
1400 dir[0] = -SourceToListener.v[0];
1401 /* Clamp Y, in case rounding errors caused it to end up outside of
1402 * -1...+1.
1404 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1405 dir[2] = -SourceToListener.v[2] * ZScale;
1407 else
1409 dir[0] = 0.0f;
1410 dir[1] = 0.0f;
1411 dir[2] = -1.0f;
1413 if(props->Radius > Distance)
1414 spread = F_TAU - Distance/props->Radius*F_PI;
1415 else if(Distance > FLT_EPSILON)
1416 spread = asinf(props->Radius / Distance) * 2.0f;
1417 else
1418 spread = 0.0f;
1420 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1421 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1424 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1426 ALbufferlistitem *BufferListItem;
1427 struct ALvoiceProps *props;
1429 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1430 if(!props && !force) return;
1432 if(props)
1434 memcpy(voice->Props, props,
1435 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1438 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &voice->FreeList, props);
1440 props = voice->Props;
1442 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1443 while(BufferListItem != NULL)
1445 const ALbuffer *buffer;
1446 if((buffer=BufferListItem->buffer) != NULL)
1448 if(props->SpatializeMode == SpatializeOn ||
1449 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1450 CalcAttnSourceParams(voice, props, buffer, context);
1451 else
1452 CalcNonAttnSourceParams(voice, props, buffer, context);
1453 break;
1455 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1460 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1462 ALvoice **voice, **voice_end;
1463 ALsource *source;
1464 ALsizei i;
1466 IncrementRef(&ctx->UpdateCount);
1467 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1469 ALboolean force = CalcListenerParams(ctx);
1470 for(i = 0;i < slots->count;i++)
1471 force |= CalcEffectSlotParams(slots->slot[i], ctx->Device);
1473 voice = ctx->Voices;
1474 voice_end = voice + ctx->VoiceCount;
1475 for(;voice != voice_end;++voice)
1477 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1478 if(source) CalcSourceParams(*voice, ctx, force);
1481 IncrementRef(&ctx->UpdateCount);
1485 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1486 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1487 ALsizei NumChannels)
1489 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1490 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1491 ALsizei i;
1493 /* Apply an all-pass to all channels, except the front-left and front-
1494 * right, so they maintain the same relative phase.
1496 for(i = 0;i < NumChannels;i++)
1498 if(i == lidx || i == ridx)
1499 continue;
1500 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1503 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1504 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1506 for(i = 0;i < SamplesToDo;i++)
1508 ALfloat lfsum, hfsum;
1509 ALfloat m, s, c;
1511 lfsum = lsplit[0][i] + rsplit[0][i];
1512 hfsum = lsplit[1][i] + rsplit[1][i];
1513 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1515 /* This pans the separate low- and high-frequency sums between being on
1516 * the center channel and the left/right channels. The low-frequency
1517 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1518 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1519 * values can be tweaked.
1521 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1522 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1524 /* The generated center channel signal adds to the existing signal,
1525 * while the modified left and right channels replace.
1527 Buffer[lidx][i] = (m + s) * 0.5f;
1528 Buffer[ridx][i] = (m - s) * 0.5f;
1529 Buffer[cidx][i] += c * 0.5f;
1533 static void ApplyDistanceComp(ALfloatBUFFERSIZE *restrict Samples, DistanceComp *distcomp,
1534 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1536 ALsizei i, c;
1538 Values = ASSUME_ALIGNED(Values, 16);
1539 for(c = 0;c < numchans;c++)
1541 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1542 const ALfloat gain = distcomp[c].Gain;
1543 const ALsizei base = distcomp[c].Length;
1544 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1546 if(base == 0)
1548 if(gain < 1.0f)
1550 for(i = 0;i < SamplesToDo;i++)
1551 inout[i] *= gain;
1553 continue;
1556 if(SamplesToDo >= base)
1558 for(i = 0;i < base;i++)
1559 Values[i] = distbuf[i];
1560 for(;i < SamplesToDo;i++)
1561 Values[i] = inout[i-base];
1562 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1564 else
1566 for(i = 0;i < SamplesToDo;i++)
1567 Values[i] = distbuf[i];
1568 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1569 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1571 for(i = 0;i < SamplesToDo;i++)
1572 inout[i] = Values[i]*gain;
1576 static void ApplyDither(ALfloatBUFFERSIZE *restrict Samples, ALuint *dither_seed,
1577 const ALfloat quant_scale, const ALsizei SamplesToDo,
1578 const ALsizei numchans)
1580 const ALfloat invscale = 1.0f / quant_scale;
1581 ALuint seed = *dither_seed;
1582 ALsizei c, i;
1584 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1585 * values between -1 and +1). Step 2 is to add the noise to the samples,
1586 * before rounding and after scaling up to the desired quantization depth.
1588 for(c = 0;c < numchans;c++)
1590 ALfloat *restrict samples = Samples[c];
1591 for(i = 0;i < SamplesToDo;i++)
1593 ALfloat val = samples[i] * quant_scale;
1594 ALuint rng0 = dither_rng(&seed);
1595 ALuint rng1 = dither_rng(&seed);
1596 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1597 samples[i] = roundf(val) * invscale;
1600 *dither_seed = seed;
1604 static inline ALfloat Conv_ALfloat(ALfloat val)
1605 { return val; }
1606 static inline ALint Conv_ALint(ALfloat val)
1608 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1609 * integer range normalized floats can be safely converted to (a bit of the
1610 * exponent helps out, effectively giving 25 bits).
1612 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1614 static inline ALshort Conv_ALshort(ALfloat val)
1615 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1616 static inline ALbyte Conv_ALbyte(ALfloat val)
1617 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1619 /* Define unsigned output variations. */
1620 #define DECL_TEMPLATE(T, func, O) \
1621 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1623 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1624 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1625 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1627 #undef DECL_TEMPLATE
1629 #define DECL_TEMPLATE(T, A) \
1630 static void Write##A(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1631 ALsizei Offset, ALsizei SamplesToDo, ALsizei numchans) \
1633 ALsizei i, j; \
1634 for(j = 0;j < numchans;j++) \
1636 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1637 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1639 for(i = 0;i < SamplesToDo;i++) \
1640 out[i*numchans] = Conv_##T(in[i]); \
1644 DECL_TEMPLATE(ALfloat, F32)
1645 DECL_TEMPLATE(ALuint, UI32)
1646 DECL_TEMPLATE(ALint, I32)
1647 DECL_TEMPLATE(ALushort, UI16)
1648 DECL_TEMPLATE(ALshort, I16)
1649 DECL_TEMPLATE(ALubyte, UI8)
1650 DECL_TEMPLATE(ALbyte, I8)
1652 #undef DECL_TEMPLATE
1655 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1657 ALsizei SamplesToDo;
1658 ALsizei SamplesDone;
1659 ALCcontext *ctx;
1660 ALsizei i, c;
1662 START_MIXER_MODE();
1663 for(SamplesDone = 0;SamplesDone < NumSamples;)
1665 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1666 for(c = 0;c < device->Dry.NumChannels;c++)
1667 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1668 if(device->Dry.Buffer != device->FOAOut.Buffer)
1669 for(c = 0;c < device->FOAOut.NumChannels;c++)
1670 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1671 if(device->Dry.Buffer != device->RealOut.Buffer)
1672 for(c = 0;c < device->RealOut.NumChannels;c++)
1673 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1675 IncrementRef(&device->MixCount);
1677 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1678 while(ctx)
1680 const struct ALeffectslotArray *auxslots;
1682 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1683 UpdateContextSources(ctx, auxslots);
1685 for(i = 0;i < auxslots->count;i++)
1687 ALeffectslot *slot = auxslots->slot[i];
1688 for(c = 0;c < slot->NumChannels;c++)
1689 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1692 /* source processing */
1693 for(i = 0;i < ctx->VoiceCount;i++)
1695 ALvoice *voice = ctx->Voices[i];
1696 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1697 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1698 voice->Step > 0)
1700 if(!MixSource(voice, source, device, SamplesToDo))
1702 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1703 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1708 /* effect slot processing */
1709 for(i = 0;i < auxslots->count;i++)
1711 const ALeffectslot *slot = auxslots->slot[i];
1712 ALeffectState *state = slot->Params.EffectState;
1713 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1714 state->OutChannels);
1717 ctx = ctx->next;
1720 /* Increment the clock time. Every second's worth of samples is
1721 * converted and added to clock base so that large sample counts don't
1722 * overflow during conversion. This also guarantees an exact, stable
1723 * conversion. */
1724 device->SamplesDone += SamplesToDo;
1725 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1726 device->SamplesDone %= device->Frequency;
1727 IncrementRef(&device->MixCount);
1729 if(device->HrtfHandle)
1731 DirectHrtfState *state;
1732 int lidx, ridx;
1734 if(device->AmbiUp)
1735 ambiup_process(device->AmbiUp,
1736 device->Dry.Buffer, device->Dry.NumChannels,
1737 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1740 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1741 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1742 assert(lidx != -1 && ridx != -1);
1744 state = device->Hrtf;
1745 for(c = 0;c < device->Dry.NumChannels;c++)
1747 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1748 device->Dry.Buffer[c], state->Offset, state->IrSize,
1749 SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
1750 state->Chan[c].Values, SamplesToDo
1753 state->Offset += SamplesToDo;
1755 else if(device->AmbiDecoder)
1757 if(device->Dry.Buffer != device->FOAOut.Buffer)
1758 bformatdec_upSample(device->AmbiDecoder,
1759 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1760 device->FOAOut.NumChannels, SamplesToDo
1762 bformatdec_process(device->AmbiDecoder,
1763 device->RealOut.Buffer, device->RealOut.NumChannels,
1764 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1767 else if(device->AmbiUp)
1769 ambiup_process(device->AmbiUp,
1770 device->RealOut.Buffer, device->RealOut.NumChannels,
1771 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1774 else if(device->Uhj_Encoder)
1776 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1777 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1778 if(lidx != -1 && ridx != -1)
1780 /* Encode to stereo-compatible 2-channel UHJ output. */
1781 EncodeUhj2(device->Uhj_Encoder,
1782 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1783 device->Dry.Buffer, SamplesToDo
1787 else if(device->Bs2b)
1789 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1790 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1791 if(lidx != -1 && ridx != -1)
1793 /* Apply binaural/crossfeed filter */
1794 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1795 device->RealOut.Buffer[ridx], SamplesToDo);
1799 if(OutBuffer)
1801 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1802 ALsizei Channels = device->RealOut.NumChannels;
1804 if(device->Stablizer)
1806 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1807 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1808 int cidx = GetChannelIdxByName(device->RealOut, FrontCenter);
1809 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1811 ApplyStablizer(device->Stablizer, Buffer, lidx, ridx, cidx,
1812 SamplesToDo, Channels);
1815 /* Use NFCtrlData for temp value storage. */
1816 ApplyDistanceComp(Buffer, device->ChannelDelay, device->NFCtrlData,
1817 SamplesToDo, Channels);
1819 if(device->Limiter)
1820 ApplyCompression(device->Limiter, Channels, SamplesToDo, Buffer);
1822 if(device->DitherDepth > 0.0f)
1823 ApplyDither(Buffer, &device->DitherSeed, device->DitherDepth, SamplesToDo,
1824 Channels);
1826 switch(device->FmtType)
1828 case DevFmtByte:
1829 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1830 break;
1831 case DevFmtUByte:
1832 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1833 break;
1834 case DevFmtShort:
1835 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1836 break;
1837 case DevFmtUShort:
1838 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1839 break;
1840 case DevFmtInt:
1841 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1842 break;
1843 case DevFmtUInt:
1844 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1845 break;
1846 case DevFmtFloat:
1847 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1848 break;
1852 SamplesDone += SamplesToDo;
1854 END_MIXER_MODE();
1858 void aluHandleDisconnect(ALCdevice *device)
1860 ALCcontext *ctx;
1862 device->Connected = ALC_FALSE;
1864 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1865 while(ctx)
1867 ALsizei i;
1868 for(i = 0;i < ctx->VoiceCount;i++)
1870 ALvoice *voice = ctx->Voices[i];
1871 ALsource *source;
1873 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_acq_rel);
1874 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1876 if(source)
1878 ALenum playing = AL_PLAYING;
1879 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1882 ctx->VoiceCount = 0;
1884 ctx = ctx->next;