Pass the filter entry to apply to resample_fir4
[openal-soft.git] / Alc / ALu.c
blob6c37df78ab59296fdb8cef09d8f7dd0a201ea79c
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.c"
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)
230 ALboolean uncut = AL_TRUE;
231 ALfloat sf;
232 ALsizei si;
234 if(increment > FRACTIONONE)
236 sf = (ALfloat)FRACTIONONE / increment;
237 if(sf < bsinc.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 - bsinc.scaleBase) * bsinc.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 = bsinc.m[si];
265 state->l = -((state->m/2) - 1);
266 state->filter = bsinc.Tab + bsinc.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 && Listener->Params.MetersPerUnit > 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[2] = 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 && Listener->Params.MetersPerUnit > 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 == BSincResampler)
1042 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1043 else
1044 voice->ResampleState.sinc4.filter = sinc4Tab;
1045 voice->Resampler = SelectResampler(props->Resampler);
1047 /* Calculate gains */
1048 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1049 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1050 DryGain = minf(DryGain, GAIN_MIX_MAX);
1051 DryGainHF = props->Direct.GainHF;
1052 DryGainLF = props->Direct.GainLF;
1053 for(i = 0;i < Device->NumAuxSends;i++)
1055 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1056 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1057 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1058 WetGainHF[i] = props->Send[i].GainHF;
1059 WetGainLF[i] = props->Send[i].GainLF;
1062 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1063 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1066 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1068 const ALCdevice *Device = ALContext->Device;
1069 const ALlistener *Listener = ALContext->Listener;
1070 const ALsizei NumSends = Device->NumAuxSends;
1071 aluVector Position, Velocity, Direction, SourceToListener;
1072 ALfloat Distance, ClampedDist, DopplerFactor;
1073 ALeffectslot *SendSlots[MAX_SENDS];
1074 ALfloat RoomRolloff[MAX_SENDS];
1075 ALfloat DecayDistance[MAX_SENDS];
1076 ALfloat DecayHFDistance[MAX_SENDS];
1077 ALfloat DryGain, DryGainHF, DryGainLF;
1078 ALfloat WetGain[MAX_SENDS];
1079 ALfloat WetGainHF[MAX_SENDS];
1080 ALfloat WetGainLF[MAX_SENDS];
1081 bool directional;
1082 ALfloat dir[3];
1083 ALfloat spread;
1084 ALfloat Pitch;
1085 ALint i;
1087 /* Set mixing buffers and get send parameters. */
1088 voice->Direct.Buffer = Device->Dry.Buffer;
1089 voice->Direct.Channels = Device->Dry.NumChannels;
1090 for(i = 0;i < NumSends;i++)
1092 SendSlots[i] = props->Send[i].Slot;
1093 if(!SendSlots[i] && i == 0)
1094 SendSlots[i] = ALContext->DefaultSlot;
1095 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1097 SendSlots[i] = NULL;
1098 RoomRolloff[i] = 0.0f;
1099 DecayDistance[i] = 0.0f;
1100 DecayHFDistance[i] = 0.0f;
1102 else if(SendSlots[i]->Params.AuxSendAuto)
1104 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1105 DecayDistance[i] = SendSlots[i]->Params.DecayTime * SPEEDOFSOUNDMETRESPERSEC;
1106 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1107 if(SendSlots[i]->Params.DecayHFLimit)
1109 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1110 if(airAbsorption < 1.0f)
1112 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1113 DecayHFDistance[i] = minf(limitRatio, DecayHFDistance[i]);
1117 else
1119 /* If the slot's auxiliary send auto is off, the data sent to the
1120 * effect slot is the same as the dry path, sans filter effects */
1121 RoomRolloff[i] = props->RolloffFactor;
1122 DecayDistance[i] = 0.0f;
1123 DecayHFDistance[i] = 0.0f;
1126 if(!SendSlots[i])
1128 voice->Send[i].Buffer = NULL;
1129 voice->Send[i].Channels = 0;
1131 else
1133 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1134 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1138 /* Transform source to listener space (convert to head relative) */
1139 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1140 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1141 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1142 if(props->HeadRelative == AL_FALSE)
1144 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1145 /* Transform source vectors */
1146 Position = aluMatrixfVector(Matrix, &Position);
1147 Velocity = aluMatrixfVector(Matrix, &Velocity);
1148 Direction = aluMatrixfVector(Matrix, &Direction);
1150 else
1152 const aluVector *lvelocity = &Listener->Params.Velocity;
1153 /* Offset the source velocity to be relative of the listener velocity */
1154 Velocity.v[0] += lvelocity->v[0];
1155 Velocity.v[1] += lvelocity->v[1];
1156 Velocity.v[2] += lvelocity->v[2];
1159 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1160 SourceToListener.v[0] = -Position.v[0];
1161 SourceToListener.v[1] = -Position.v[1];
1162 SourceToListener.v[2] = -Position.v[2];
1163 SourceToListener.v[3] = 0.0f;
1164 Distance = aluNormalize(SourceToListener.v);
1166 /* Initial source gain */
1167 DryGain = props->Gain;
1168 DryGainHF = 1.0f;
1169 DryGainLF = 1.0f;
1170 for(i = 0;i < NumSends;i++)
1172 WetGain[i] = props->Gain;
1173 WetGainHF[i] = 1.0f;
1174 WetGainLF[i] = 1.0f;
1177 /* Calculate distance attenuation */
1178 ClampedDist = Distance;
1180 switch(Listener->Params.SourceDistanceModel ?
1181 props->DistanceModel : Listener->Params.DistanceModel)
1183 case InverseDistanceClamped:
1184 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1185 if(props->MaxDistance < props->RefDistance)
1186 break;
1187 /*fall-through*/
1188 case InverseDistance:
1189 if(!(props->RefDistance > 0.0f))
1190 ClampedDist = props->RefDistance;
1191 else
1193 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1194 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1195 for(i = 0;i < NumSends;i++)
1197 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1198 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1201 break;
1203 case LinearDistanceClamped:
1204 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1205 if(props->MaxDistance < props->RefDistance)
1206 break;
1207 /*fall-through*/
1208 case LinearDistance:
1209 if(!(props->MaxDistance != props->RefDistance))
1210 ClampedDist = props->RefDistance;
1211 else
1213 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1214 (props->MaxDistance-props->RefDistance);
1215 DryGain *= maxf(1.0f - attn, 0.0f);
1216 for(i = 0;i < NumSends;i++)
1218 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1219 (props->MaxDistance-props->RefDistance);
1220 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1223 break;
1225 case ExponentDistanceClamped:
1226 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1227 if(props->MaxDistance < props->RefDistance)
1228 break;
1229 /*fall-through*/
1230 case ExponentDistance:
1231 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1232 ClampedDist = props->RefDistance;
1233 else
1235 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1236 for(i = 0;i < NumSends;i++)
1237 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1239 break;
1241 case DisableDistance:
1242 ClampedDist = props->RefDistance;
1243 break;
1246 /* Distance-based air absorption */
1247 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1249 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1250 Listener->Params.MetersPerUnit;
1251 if(props->AirAbsorptionFactor > 0.0f)
1253 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1254 DryGainHF *= hfattn;
1255 for(i = 0;i < NumSends;i++)
1256 WetGainHF[i] *= hfattn;
1259 if(props->WetGainAuto)
1261 /* Apply a decay-time transformation to the wet path, based on the
1262 * source distance in meters. The initial decay of the reverb
1263 * effect is calculated and applied to the wet path.
1265 for(i = 0;i < NumSends;i++)
1267 ALfloat gain;
1269 if(!(DecayDistance[i] > 0.0f))
1270 continue;
1272 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1273 WetGain[i] *= gain;
1274 /* Yes, the wet path's air absorption is applied with
1275 * WetGainAuto on, rather than WetGainHFAuto.
1277 if(gain > 0.0f)
1279 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1280 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1286 /* Calculate directional soundcones */
1287 if(directional && props->InnerAngle < 360.0f)
1289 ALfloat ConeVolume;
1290 ALfloat ConeHF;
1291 ALfloat Angle;
1293 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1294 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1295 if(!(Angle > props->InnerAngle))
1297 ConeVolume = 1.0f;
1298 ConeHF = 1.0f;
1300 else if(Angle < props->OuterAngle)
1302 ALfloat scale = ( Angle-props->InnerAngle) /
1303 (props->OuterAngle-props->InnerAngle);
1304 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1305 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1307 else
1309 ConeVolume = props->OuterGain;
1310 ConeHF = props->OuterGainHF;
1313 DryGain *= ConeVolume;
1314 if(props->DryGainHFAuto)
1315 DryGainHF *= ConeHF;
1316 if(props->WetGainAuto)
1318 for(i = 0;i < NumSends;i++)
1319 WetGain[i] *= ConeVolume;
1321 if(props->WetGainHFAuto)
1323 for(i = 0;i < NumSends;i++)
1324 WetGainHF[i] *= ConeHF;
1328 /* Apply gain and frequency filters */
1329 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1330 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1331 DryGainHF *= props->Direct.GainHF;
1332 DryGainLF *= props->Direct.GainLF;
1333 for(i = 0;i < NumSends;i++)
1335 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1336 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1337 WetGainHF[i] *= props->Send[i].GainHF;
1338 WetGainLF[i] *= props->Send[i].GainLF;
1342 /* Initial source pitch */
1343 Pitch = props->Pitch;
1345 /* Calculate velocity-based doppler effect */
1346 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1347 if(DopplerFactor > 0.0f)
1349 const aluVector *lvelocity = &Listener->Params.Velocity;
1350 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1351 ALfloat vss, vls;
1353 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1354 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1356 if(!(vls < SpeedOfSound))
1358 /* Listener moving away from the source at the speed of sound.
1359 * Sound waves can't catch it.
1361 Pitch = 0.0f;
1363 else if(!(vss < SpeedOfSound))
1365 /* Source moving toward the listener at the speed of sound. Sound
1366 * waves bunch up to extreme frequencies.
1368 Pitch = HUGE_VALF;
1370 else
1372 /* Source and listener movement is nominal. Calculate the proper
1373 * doppler shift.
1375 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1379 /* Adjust pitch based on the buffer and output frequencies, and calculate
1380 * fixed-point stepping value.
1382 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1383 if(Pitch > (ALfloat)MAX_PITCH)
1384 voice->Step = MAX_PITCH<<FRACTIONBITS;
1385 else
1386 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1387 if(props->Resampler == BSincResampler)
1388 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1389 else
1390 voice->ResampleState.sinc4.filter = sinc4Tab;
1391 voice->Resampler = SelectResampler(props->Resampler);
1393 if(Distance > FLT_EPSILON)
1395 dir[0] = -SourceToListener.v[0];
1396 /* Clamp Y, in case rounding errors caused it to end up outside of
1397 * -1...+1.
1399 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1400 dir[2] = -SourceToListener.v[2] * ZScale;
1402 else
1404 dir[0] = 0.0f;
1405 dir[1] = 0.0f;
1406 dir[2] = -1.0f;
1408 if(props->Radius > Distance)
1409 spread = F_TAU - Distance/props->Radius*F_PI;
1410 else if(Distance > FLT_EPSILON)
1411 spread = asinf(props->Radius / Distance) * 2.0f;
1412 else
1413 spread = 0.0f;
1415 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1416 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1419 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1421 ALbufferlistitem *BufferListItem;
1422 struct ALvoiceProps *props;
1424 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1425 if(!props && !force) return;
1427 if(props)
1429 memcpy(voice->Props, props,
1430 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1433 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &voice->FreeList, props);
1435 props = voice->Props;
1437 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1438 while(BufferListItem != NULL)
1440 const ALbuffer *buffer;
1441 if((buffer=BufferListItem->buffer) != NULL)
1443 if(props->SpatializeMode == SpatializeOn ||
1444 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1445 CalcAttnSourceParams(voice, props, buffer, context);
1446 else
1447 CalcNonAttnSourceParams(voice, props, buffer, context);
1448 break;
1450 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1455 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1457 ALvoice **voice, **voice_end;
1458 ALsource *source;
1459 ALsizei i;
1461 IncrementRef(&ctx->UpdateCount);
1462 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1464 ALboolean force = CalcListenerParams(ctx);
1465 for(i = 0;i < slots->count;i++)
1466 force |= CalcEffectSlotParams(slots->slot[i], ctx->Device);
1468 voice = ctx->Voices;
1469 voice_end = voice + ctx->VoiceCount;
1470 for(;voice != voice_end;++voice)
1472 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1473 if(source) CalcSourceParams(*voice, ctx, force);
1476 IncrementRef(&ctx->UpdateCount);
1480 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1481 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1482 ALsizei NumChannels)
1484 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1485 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1486 ALsizei i;
1488 /* Apply an all-pass to all channels, except the front-left and front-
1489 * right, so they maintain the same relative phase.
1491 for(i = 0;i < NumChannels;i++)
1493 if(i == lidx || i == ridx)
1494 continue;
1495 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1498 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1499 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1501 for(i = 0;i < SamplesToDo;i++)
1503 ALfloat lfsum, hfsum;
1504 ALfloat m, s, c;
1506 lfsum = lsplit[0][i] + rsplit[0][i];
1507 hfsum = lsplit[1][i] + rsplit[1][i];
1508 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1510 /* This pans the separate low- and high-frequency sums between being on
1511 * the center channel and the left/right channels. The low-frequency
1512 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1513 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1514 * values can be tweaked.
1516 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1517 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1519 /* The generated center channel signal adds to the existing signal,
1520 * while the modified left and right channels replace.
1522 Buffer[lidx][i] = (m + s) * 0.5f;
1523 Buffer[ridx][i] = (m - s) * 0.5f;
1524 Buffer[cidx][i] += c * 0.5f;
1528 static void ApplyDistanceComp(ALfloatBUFFERSIZE *restrict Samples, DistanceComp *distcomp,
1529 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1531 ALsizei i, c;
1533 Values = ASSUME_ALIGNED(Values, 16);
1534 for(c = 0;c < numchans;c++)
1536 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1537 const ALfloat gain = distcomp[c].Gain;
1538 const ALsizei base = distcomp[c].Length;
1539 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1541 if(base == 0)
1543 if(gain < 1.0f)
1545 for(i = 0;i < SamplesToDo;i++)
1546 inout[i] *= gain;
1548 continue;
1551 if(SamplesToDo >= base)
1553 for(i = 0;i < base;i++)
1554 Values[i] = distbuf[i];
1555 for(;i < SamplesToDo;i++)
1556 Values[i] = inout[i-base];
1557 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1559 else
1561 for(i = 0;i < SamplesToDo;i++)
1562 Values[i] = distbuf[i];
1563 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1564 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1566 for(i = 0;i < SamplesToDo;i++)
1567 inout[i] = Values[i]*gain;
1571 static void ApplyDither(ALfloatBUFFERSIZE *restrict Samples, ALuint *dither_seed,
1572 const ALfloat quant_scale, const ALsizei SamplesToDo,
1573 const ALsizei numchans)
1575 const ALfloat invscale = 1.0f / quant_scale;
1576 ALuint seed = *dither_seed;
1577 ALsizei c, i;
1579 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1580 * values between -1 and +1). Step 2 is to add the noise to the samples,
1581 * before rounding and after scaling up to the desired quantization depth.
1583 for(c = 0;c < numchans;c++)
1585 ALfloat *restrict samples = Samples[c];
1586 for(i = 0;i < SamplesToDo;i++)
1588 ALfloat val = samples[i] * quant_scale;
1589 ALuint rng0 = dither_rng(&seed);
1590 ALuint rng1 = dither_rng(&seed);
1591 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1592 samples[i] = roundf(val) * invscale;
1595 *dither_seed = seed;
1599 static inline ALfloat Conv_ALfloat(ALfloat val)
1600 { return val; }
1601 static inline ALint Conv_ALint(ALfloat val)
1603 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1604 * integer range normalized floats can be safely converted to (a bit of the
1605 * exponent helps out, effectively giving 25 bits).
1607 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1609 static inline ALshort Conv_ALshort(ALfloat val)
1610 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1611 static inline ALbyte Conv_ALbyte(ALfloat val)
1612 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1614 /* Define unsigned output variations. */
1615 #define DECL_TEMPLATE(T, func, O) \
1616 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1618 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1619 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1620 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1622 #undef DECL_TEMPLATE
1624 #define DECL_TEMPLATE(T, A) \
1625 static void Write##A(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1626 ALsizei Offset, ALsizei SamplesToDo, ALsizei numchans) \
1628 ALsizei i, j; \
1629 for(j = 0;j < numchans;j++) \
1631 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1632 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1634 for(i = 0;i < SamplesToDo;i++) \
1635 out[i*numchans] = Conv_##T(in[i]); \
1639 DECL_TEMPLATE(ALfloat, F32)
1640 DECL_TEMPLATE(ALuint, UI32)
1641 DECL_TEMPLATE(ALint, I32)
1642 DECL_TEMPLATE(ALushort, UI16)
1643 DECL_TEMPLATE(ALshort, I16)
1644 DECL_TEMPLATE(ALubyte, UI8)
1645 DECL_TEMPLATE(ALbyte, I8)
1647 #undef DECL_TEMPLATE
1650 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1652 ALsizei SamplesToDo;
1653 ALsizei SamplesDone;
1654 ALCcontext *ctx;
1655 ALsizei i, c;
1657 START_MIXER_MODE();
1658 for(SamplesDone = 0;SamplesDone < NumSamples;)
1660 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1661 for(c = 0;c < device->Dry.NumChannels;c++)
1662 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1663 if(device->Dry.Buffer != device->FOAOut.Buffer)
1664 for(c = 0;c < device->FOAOut.NumChannels;c++)
1665 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1666 if(device->Dry.Buffer != device->RealOut.Buffer)
1667 for(c = 0;c < device->RealOut.NumChannels;c++)
1668 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1670 IncrementRef(&device->MixCount);
1672 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1673 while(ctx)
1675 const struct ALeffectslotArray *auxslots;
1677 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1678 UpdateContextSources(ctx, auxslots);
1680 for(i = 0;i < auxslots->count;i++)
1682 ALeffectslot *slot = auxslots->slot[i];
1683 for(c = 0;c < slot->NumChannels;c++)
1684 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1687 /* source processing */
1688 for(i = 0;i < ctx->VoiceCount;i++)
1690 ALvoice *voice = ctx->Voices[i];
1691 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1692 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1693 voice->Step > 0)
1695 if(!MixSource(voice, source, device, SamplesToDo))
1697 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1698 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1703 /* effect slot processing */
1704 for(i = 0;i < auxslots->count;i++)
1706 const ALeffectslot *slot = auxslots->slot[i];
1707 ALeffectState *state = slot->Params.EffectState;
1708 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1709 state->OutChannels);
1712 ctx = ctx->next;
1715 /* Increment the clock time. Every second's worth of samples is
1716 * converted and added to clock base so that large sample counts don't
1717 * overflow during conversion. This also guarantees an exact, stable
1718 * conversion. */
1719 device->SamplesDone += SamplesToDo;
1720 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1721 device->SamplesDone %= device->Frequency;
1722 IncrementRef(&device->MixCount);
1724 if(device->HrtfHandle)
1726 DirectHrtfState *state;
1727 int lidx, ridx;
1729 if(device->AmbiUp)
1730 ambiup_process(device->AmbiUp,
1731 device->Dry.Buffer, device->Dry.NumChannels,
1732 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1735 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1736 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1737 assert(lidx != -1 && ridx != -1);
1739 state = device->Hrtf;
1740 for(c = 0;c < device->Dry.NumChannels;c++)
1742 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1743 device->Dry.Buffer[c], state->Offset, state->IrSize,
1744 SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
1745 state->Chan[c].Values, SamplesToDo
1748 state->Offset += SamplesToDo;
1750 else if(device->AmbiDecoder)
1752 if(device->Dry.Buffer != device->FOAOut.Buffer)
1753 bformatdec_upSample(device->AmbiDecoder,
1754 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1755 device->FOAOut.NumChannels, SamplesToDo
1757 bformatdec_process(device->AmbiDecoder,
1758 device->RealOut.Buffer, device->RealOut.NumChannels,
1759 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1762 else if(device->AmbiUp)
1764 ambiup_process(device->AmbiUp,
1765 device->RealOut.Buffer, device->RealOut.NumChannels,
1766 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1769 else if(device->Uhj_Encoder)
1771 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1772 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1773 if(lidx != -1 && ridx != -1)
1775 /* Encode to stereo-compatible 2-channel UHJ output. */
1776 EncodeUhj2(device->Uhj_Encoder,
1777 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1778 device->Dry.Buffer, SamplesToDo
1782 else if(device->Bs2b)
1784 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1785 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1786 if(lidx != -1 && ridx != -1)
1788 /* Apply binaural/crossfeed filter */
1789 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1790 device->RealOut.Buffer[ridx], SamplesToDo);
1794 if(OutBuffer)
1796 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1797 ALsizei Channels = device->RealOut.NumChannels;
1799 if(device->Stablizer)
1801 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1802 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1803 int cidx = GetChannelIdxByName(device->RealOut, FrontCenter);
1804 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1806 ApplyStablizer(device->Stablizer, Buffer, lidx, ridx, cidx,
1807 SamplesToDo, Channels);
1810 /* Use NFCtrlData for temp value storage. */
1811 ApplyDistanceComp(Buffer, device->ChannelDelay, device->NFCtrlData,
1812 SamplesToDo, Channels);
1814 if(device->Limiter)
1815 ApplyCompression(device->Limiter, Channels, SamplesToDo, Buffer);
1817 if(device->DitherDepth > 0.0f)
1818 ApplyDither(Buffer, &device->DitherSeed, device->DitherDepth, SamplesToDo,
1819 Channels);
1821 switch(device->FmtType)
1823 case DevFmtByte:
1824 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1825 break;
1826 case DevFmtUByte:
1827 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1828 break;
1829 case DevFmtShort:
1830 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1831 break;
1832 case DevFmtUShort:
1833 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1834 break;
1835 case DevFmtInt:
1836 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1837 break;
1838 case DevFmtUInt:
1839 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1840 break;
1841 case DevFmtFloat:
1842 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1843 break;
1847 SamplesDone += SamplesToDo;
1849 END_MIXER_MODE();
1853 void aluHandleDisconnect(ALCdevice *device)
1855 ALCcontext *ctx;
1857 device->Connected = ALC_FALSE;
1859 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1860 while(ctx)
1862 ALsizei i;
1863 for(i = 0;i < ctx->VoiceCount;i++)
1865 ALvoice *voice = ctx->Voices[i];
1866 ALsource *source;
1868 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_acq_rel);
1869 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1871 if(source)
1873 ALenum playing = AL_PLAYING;
1874 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1877 ctx->VoiceCount = 0;
1879 ctx = ctx->next;