Simplify bsinc filter storage in the filter state
[openal-soft.git] / Alc / ALu.c
blob2b83ea009703c1d372045c692c46c8239251a9fe
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, ALsizei frac);
74 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
76 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
77 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
78 extern inline void aluMatrixfSet(aluMatrixf *matrix,
79 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
80 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
81 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
82 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
85 /* Cone scalar */
86 ALfloat ConeScale = 1.0f;
88 /* Localized Z scalar for mono sources */
89 ALfloat ZScale = 1.0f;
91 const aluMatrixf IdentityMatrixf = {{
92 { 1.0f, 0.0f, 0.0f, 0.0f },
93 { 0.0f, 1.0f, 0.0f, 0.0f },
94 { 0.0f, 0.0f, 1.0f, 0.0f },
95 { 0.0f, 0.0f, 0.0f, 1.0f },
96 }};
99 struct ChanMap {
100 enum Channel channel;
101 ALfloat angle;
102 ALfloat elevation;
105 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
108 void DeinitVoice(ALvoice *voice)
110 struct ALvoiceProps *props;
111 size_t count = 0;
113 props = ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL);
114 if(props) al_free(props);
116 props = ATOMIC_EXCHANGE_PTR(&voice->FreeList, NULL, almemory_order_relaxed);
117 while(props)
119 struct ALvoiceProps *next;
120 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
121 al_free(props);
122 props = next;
123 ++count;
125 /* This is excessively spammy if it traces every voice destruction, so just
126 * warn if it was unexpectedly large.
128 if(count > 3)
129 WARN("Freed "SZFMT" voice property objects\n", count);
133 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
135 #ifdef HAVE_NEON
136 if((CPUCapFlags&CPU_CAP_NEON))
137 return MixDirectHrtf_Neon;
138 #endif
139 #ifdef HAVE_SSE
140 if((CPUCapFlags&CPU_CAP_SSE))
141 return MixDirectHrtf_SSE;
142 #endif
144 return MixDirectHrtf_C;
148 /* Prior to VS2013, MSVC lacks the round() family of functions. */
149 #if defined(_MSC_VER) && _MSC_VER < 1800
150 static float roundf(float val)
152 if(val < 0.0f)
153 return ceilf(val-0.5f);
154 return floorf(val+0.5f);
156 #endif
158 /* This RNG method was created based on the math found in opusdec. It's quick,
159 * and starting with a seed value of 22222, is suitable for generating
160 * whitenoise.
162 static inline ALuint dither_rng(ALuint *seed)
164 *seed = (*seed * 96314165) + 907633515;
165 return *seed;
169 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
171 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
172 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
173 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
176 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
178 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
181 static ALfloat aluNormalize(ALfloat *vec)
183 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
184 if(length > 0.0f)
186 ALfloat inv_length = 1.0f/length;
187 vec[0] *= inv_length;
188 vec[1] *= inv_length;
189 vec[2] *= inv_length;
191 return length;
194 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
196 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
198 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];
199 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];
200 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];
203 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
205 aluVector v;
206 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];
207 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];
208 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];
209 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];
210 return v;
214 void aluInit(void)
216 MixDirectHrtf = SelectHrtfMixer();
219 /* Prepares the interpolator for a given rate (determined by increment). A
220 * result of AL_FALSE indicates that the filter output will completely cut
221 * the input signal.
223 * With a bit of work, and a trade of memory for CPU cost, this could be
224 * modified for use with an interpolated increment for buttery-smooth pitch
225 * changes.
227 ALboolean BsincPrepare(const ALuint increment, BsincState *state)
229 ALboolean uncut = AL_TRUE;
230 ALfloat sf;
231 ALsizei si;
233 if(increment > FRACTIONONE)
235 sf = (ALfloat)FRACTIONONE / increment;
236 if(sf < bsinc.scaleBase)
238 /* Signal has been completely cut. The return result can be used
239 * to skip the filter (and output zeros) as an optimization.
241 sf = 0.0f;
242 si = 0;
243 uncut = AL_FALSE;
245 else
247 sf = (BSINC_SCALE_COUNT - 1) * (sf - bsinc.scaleBase) * bsinc.scaleRange;
248 si = fastf2i(sf);
249 /* The interpolation factor is fit to this diagonally-symmetric
250 * curve to reduce the transition ripple caused by interpolating
251 * different scales of the sinc function.
253 sf = 1.0f - cosf(asinf(sf - si));
256 else
258 sf = 0.0f;
259 si = BSINC_SCALE_COUNT - 1;
262 state->sf = sf;
263 state->m = bsinc.m[si];
264 state->l = -((state->m/2) - 1);
265 state->filter = bsinc.Tab + bsinc.filterOffset[si];
266 return uncut;
270 static ALboolean CalcListenerParams(ALCcontext *Context)
272 ALlistener *Listener = Context->Listener;
273 ALfloat N[3], V[3], U[3], P[3];
274 struct ALlistenerProps *props;
275 aluVector vel;
277 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
278 if(!props) return AL_FALSE;
280 /* AT then UP */
281 N[0] = props->Forward[0];
282 N[1] = props->Forward[1];
283 N[2] = props->Forward[2];
284 aluNormalize(N);
285 V[0] = props->Up[0];
286 V[1] = props->Up[1];
287 V[2] = props->Up[2];
288 aluNormalize(V);
289 /* Build and normalize right-vector */
290 aluCrossproduct(N, V, U);
291 aluNormalize(U);
293 aluMatrixfSet(&Listener->Params.Matrix,
294 U[0], V[0], -N[0], 0.0,
295 U[1], V[1], -N[1], 0.0,
296 U[2], V[2], -N[2], 0.0,
297 0.0, 0.0, 0.0, 1.0
300 P[0] = props->Position[0];
301 P[1] = props->Position[1];
302 P[2] = props->Position[2];
303 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
304 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
306 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
307 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
309 Listener->Params.Gain = props->Gain * Context->GainBoost;
310 Listener->Params.MetersPerUnit = props->MetersPerUnit;
312 Listener->Params.DopplerFactor = props->DopplerFactor;
313 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
315 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
316 Listener->Params.DistanceModel = props->DistanceModel;
318 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
319 return AL_TRUE;
322 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
324 struct ALeffectslotProps *props;
325 ALeffectState *state;
327 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
328 if(!props) return AL_FALSE;
330 slot->Params.Gain = props->Gain;
331 slot->Params.AuxSendAuto = props->AuxSendAuto;
332 slot->Params.EffectType = props->Type;
333 if(IsReverbEffect(slot->Params.EffectType))
335 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
336 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
337 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
338 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
339 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
341 else
343 slot->Params.RoomRolloff = 0.0f;
344 slot->Params.DecayTime = 0.0f;
345 slot->Params.DecayHFRatio = 0.0f;
346 slot->Params.DecayHFLimit = AL_FALSE;
347 slot->Params.AirAbsorptionGainHF = 1.0f;
350 /* Swap effect states. No need to play with the ref counts since they keep
351 * the same number of refs.
353 state = props->State;
354 props->State = slot->Params.EffectState;
355 slot->Params.EffectState = state;
357 V(state,update)(device, slot, &props->Props);
359 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
360 return AL_TRUE;
364 static const struct ChanMap MonoMap[1] = {
365 { FrontCenter, 0.0f, 0.0f }
366 }, RearMap[2] = {
367 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
368 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
369 }, QuadMap[4] = {
370 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
371 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
372 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
373 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
374 }, X51Map[6] = {
375 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
376 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
377 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
378 { LFE, 0.0f, 0.0f },
379 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
380 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
381 }, X61Map[7] = {
382 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
383 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
384 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
385 { LFE, 0.0f, 0.0f },
386 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
387 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
388 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
389 }, X71Map[8] = {
390 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
391 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
392 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
393 { LFE, 0.0f, 0.0f },
394 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
395 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
396 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
397 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
400 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
401 const ALfloat Spread, const ALfloat DryGain,
402 const ALfloat DryGainHF, const ALfloat DryGainLF,
403 const ALfloat *WetGain, const ALfloat *WetGainLF,
404 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
405 const ALbuffer *Buffer, const struct ALvoiceProps *props,
406 const ALlistener *Listener, const ALCdevice *Device)
408 struct ChanMap StereoMap[2] = {
409 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
410 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
412 bool DirectChannels = props->DirectChannels;
413 const ALsizei NumSends = Device->NumAuxSends;
414 const ALuint Frequency = Device->Frequency;
415 const struct ChanMap *chans = NULL;
416 ALsizei num_channels = 0;
417 bool isbformat = false;
418 ALfloat downmix_gain = 1.0f;
419 ALsizei c, i, j;
421 switch(Buffer->FmtChannels)
423 case FmtMono:
424 chans = MonoMap;
425 num_channels = 1;
426 /* Mono buffers are never played direct. */
427 DirectChannels = false;
428 break;
430 case FmtStereo:
431 /* Convert counter-clockwise to clockwise. */
432 StereoMap[0].angle = -props->StereoPan[0];
433 StereoMap[1].angle = -props->StereoPan[1];
435 chans = StereoMap;
436 num_channels = 2;
437 downmix_gain = 1.0f / 2.0f;
438 break;
440 case FmtRear:
441 chans = RearMap;
442 num_channels = 2;
443 downmix_gain = 1.0f / 2.0f;
444 break;
446 case FmtQuad:
447 chans = QuadMap;
448 num_channels = 4;
449 downmix_gain = 1.0f / 4.0f;
450 break;
452 case FmtX51:
453 chans = X51Map;
454 num_channels = 6;
455 /* NOTE: Excludes LFE. */
456 downmix_gain = 1.0f / 5.0f;
457 break;
459 case FmtX61:
460 chans = X61Map;
461 num_channels = 7;
462 /* NOTE: Excludes LFE. */
463 downmix_gain = 1.0f / 6.0f;
464 break;
466 case FmtX71:
467 chans = X71Map;
468 num_channels = 8;
469 /* NOTE: Excludes LFE. */
470 downmix_gain = 1.0f / 7.0f;
471 break;
473 case FmtBFormat2D:
474 num_channels = 3;
475 isbformat = true;
476 DirectChannels = false;
477 break;
479 case FmtBFormat3D:
480 num_channels = 4;
481 isbformat = true;
482 DirectChannels = false;
483 break;
486 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
487 if(isbformat)
489 /* Special handling for B-Format sources. */
491 if(Distance > FLT_EPSILON)
493 /* Panning a B-Format sound toward some direction is easy. Just pan
494 * the first (W) channel as a normal mono sound and silence the
495 * others.
497 ALfloat coeffs[MAX_AMBI_COEFFS];
499 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
501 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
502 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
503 (mdist * (ALfloat)Device->Frequency);
504 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
505 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
506 /* Clamp w0 for really close distances, to prevent excessive
507 * bass.
509 w0 = minf(w0, w1*4.0f);
511 /* Only need to adjust the first channel of a B-Format source. */
512 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
513 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
514 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
516 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
517 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
518 voice->Flags |= VOICE_HAS_NFC;
521 if(Device->Render_Mode == StereoPair)
523 ALfloat ev = asinf(Dir[1]);
524 ALfloat az = atan2f(Dir[0], -Dir[2]);
525 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
527 else
528 CalcDirectionCoeffs(Dir, Spread, coeffs);
530 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
531 ComputePanningGains(Device->Dry, coeffs, DryGain*1.414213562f,
532 voice->Direct.Params[0].Gains.Target);
533 for(c = 1;c < num_channels;c++)
535 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
536 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
539 for(i = 0;i < NumSends;i++)
541 const ALeffectslot *Slot = SendSlots[i];
542 if(Slot)
543 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
544 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
546 else
547 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
548 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
549 for(c = 1;c < num_channels;c++)
551 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
552 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
556 else
558 /* Local B-Format sources have their XYZ channels rotated according
559 * to the orientation.
561 ALfloat N[3], V[3], U[3];
562 aluMatrixf matrix;
563 ALfloat scale;
565 if(Device->AvgSpeakerDist > 0.0f)
567 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
568 * is what we want for FOA input. The first channel may have
569 * been previously re-adjusted if panned, so reset it.
571 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f);
572 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f);
573 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 0.0f);
575 voice->Direct.ChannelsPerOrder[0] = 1;
576 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
577 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
578 voice->Direct.ChannelsPerOrder[2] = 0;
579 voice->Flags |= VOICE_HAS_NFC;
582 /* AT then UP */
583 N[0] = props->Orientation[0][0];
584 N[1] = props->Orientation[0][1];
585 N[2] = props->Orientation[0][2];
586 aluNormalize(N);
587 V[0] = props->Orientation[1][0];
588 V[1] = props->Orientation[1][1];
589 V[2] = props->Orientation[1][2];
590 aluNormalize(V);
591 if(!props->HeadRelative)
593 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
594 aluMatrixfFloat3(N, 0.0f, lmatrix);
595 aluMatrixfFloat3(V, 0.0f, lmatrix);
597 /* Build and normalize right-vector */
598 aluCrossproduct(N, V, U);
599 aluNormalize(U);
601 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
602 scale = 1.732050808f;
603 aluMatrixfSet(&matrix,
604 1.414213562f, 0.0f, 0.0f, 0.0f,
605 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
606 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
607 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
610 voice->Direct.Buffer = Device->FOAOut.Buffer;
611 voice->Direct.Channels = Device->FOAOut.NumChannels;
612 for(c = 0;c < num_channels;c++)
613 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
614 voice->Direct.Params[c].Gains.Target);
615 for(i = 0;i < NumSends;i++)
617 const ALeffectslot *Slot = SendSlots[i];
618 if(Slot)
620 for(c = 0;c < num_channels;c++)
621 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
622 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
625 else
627 for(c = 0;c < num_channels;c++)
628 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
629 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
634 else if(DirectChannels)
636 /* Direct source channels always play local. Skip the virtual channels
637 * and write inputs to the matching real outputs.
639 voice->Direct.Buffer = Device->RealOut.Buffer;
640 voice->Direct.Channels = Device->RealOut.NumChannels;
642 for(c = 0;c < num_channels;c++)
644 int idx;
645 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
646 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
647 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
648 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
651 /* Auxiliary sends still use normal channel panning since they mix to
652 * B-Format, which can't channel-match.
654 for(c = 0;c < num_channels;c++)
656 ALfloat coeffs[MAX_AMBI_COEFFS];
657 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
659 for(i = 0;i < NumSends;i++)
661 const ALeffectslot *Slot = SendSlots[i];
662 if(Slot)
663 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
664 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
666 else
667 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
668 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
672 else if(Device->Render_Mode == HrtfRender)
674 /* Full HRTF rendering. Skip the virtual channels and render to the
675 * real outputs.
677 voice->Direct.Buffer = Device->RealOut.Buffer;
678 voice->Direct.Channels = Device->RealOut.NumChannels;
680 if(Distance > FLT_EPSILON)
682 ALfloat coeffs[MAX_AMBI_COEFFS];
683 ALfloat ev, az;
685 ev = asinf(Dir[1]);
686 az = atan2f(Dir[0], -Dir[2]);
688 /* Get the HRIR coefficients and delays just once, for the given
689 * source direction.
691 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
692 voice->Direct.Params[0].Hrtf.Target.Coeffs,
693 voice->Direct.Params[0].Hrtf.Target.Delay);
694 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
696 /* Remaining channels use the same results as the first. */
697 for(c = 1;c < num_channels;c++)
699 /* Skip LFE */
700 if(chans[c].channel == LFE)
701 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
702 sizeof(voice->Direct.Params[c].Hrtf.Target));
703 else
704 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
707 /* Calculate the directional coefficients once, which apply to all
708 * input channels of the source sends.
710 CalcDirectionCoeffs(Dir, Spread, coeffs);
712 for(i = 0;i < NumSends;i++)
714 const ALeffectslot *Slot = SendSlots[i];
715 if(Slot)
716 for(c = 0;c < num_channels;c++)
718 /* Skip LFE */
719 if(chans[c].channel == LFE)
720 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
721 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
722 else
723 ComputePanningGainsBF(Slot->ChanMap,
724 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
725 voice->Send[i].Params[c].Gains.Target
728 else
729 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
730 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
733 else
735 /* Local sources on HRTF play with each channel panned to its
736 * relative location around the listener, providing "virtual
737 * speaker" responses.
739 for(c = 0;c < num_channels;c++)
741 ALfloat coeffs[MAX_AMBI_COEFFS];
743 if(chans[c].channel == LFE)
745 /* Skip LFE */
746 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
747 sizeof(voice->Direct.Params[c].Hrtf.Target));
748 for(i = 0;i < NumSends;i++)
750 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
751 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
753 continue;
756 /* Get the HRIR coefficients and delays for this channel
757 * position.
759 GetHrtfCoeffs(Device->HrtfHandle,
760 chans[c].elevation, chans[c].angle, Spread,
761 voice->Direct.Params[c].Hrtf.Target.Coeffs,
762 voice->Direct.Params[c].Hrtf.Target.Delay
764 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
766 /* Normal panning for auxiliary sends. */
767 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
769 for(i = 0;i < NumSends;i++)
771 const ALeffectslot *Slot = SendSlots[i];
772 if(Slot)
773 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
774 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
776 else
777 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
778 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
783 voice->Flags |= VOICE_HAS_HRTF;
785 else
787 /* Non-HRTF rendering. Use normal panning to the output. */
789 if(Distance > FLT_EPSILON)
791 ALfloat coeffs[MAX_AMBI_COEFFS];
792 ALfloat w0 = 0.0f;
794 /* Calculate NFC filter coefficient if needed. */
795 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
797 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
798 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
799 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
800 w0 = SPEEDOFSOUNDMETRESPERSEC /
801 (mdist * (ALfloat)Device->Frequency);
802 /* Clamp w0 for really close distances, to prevent excessive
803 * bass.
805 w0 = minf(w0, w1*4.0f);
807 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
808 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
809 voice->Flags |= VOICE_HAS_NFC;
812 /* Calculate the directional coefficients once, which apply to all
813 * input channels.
815 if(Device->Render_Mode == StereoPair)
817 ALfloat ev = asinf(Dir[1]);
818 ALfloat az = atan2f(Dir[0], -Dir[2]);
819 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
821 else
822 CalcDirectionCoeffs(Dir, Spread, coeffs);
824 for(c = 0;c < num_channels;c++)
826 /* Adjust NFC filters if needed. */
827 if((voice->Flags&VOICE_HAS_NFC))
829 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
830 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
831 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
834 /* Special-case LFE */
835 if(chans[c].channel == LFE)
837 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
838 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
839 if(Device->Dry.Buffer == Device->RealOut.Buffer)
841 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
842 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
844 continue;
847 ComputePanningGains(Device->Dry,
848 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
852 for(i = 0;i < NumSends;i++)
854 const ALeffectslot *Slot = SendSlots[i];
855 if(Slot)
856 for(c = 0;c < num_channels;c++)
858 /* Skip LFE */
859 if(chans[c].channel == LFE)
860 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
861 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
862 else
863 ComputePanningGainsBF(Slot->ChanMap,
864 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
865 voice->Send[i].Params[c].Gains.Target
868 else
869 for(c = 0;c < num_channels;c++)
871 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
872 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
876 else
878 ALfloat w0 = 0.0f;
880 if(Device->AvgSpeakerDist > 0.0f)
882 /* If the source distance is 0, set w0 to w1 to act as a pass-
883 * through. We still want to pass the signal through the
884 * filters so they keep an appropriate history, in case the
885 * source moves away from the listener.
887 w0 = SPEEDOFSOUNDMETRESPERSEC /
888 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
890 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
891 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
892 voice->Flags |= VOICE_HAS_NFC;
895 for(c = 0;c < num_channels;c++)
897 ALfloat coeffs[MAX_AMBI_COEFFS];
899 if((voice->Flags&VOICE_HAS_NFC))
901 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
902 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
903 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
906 /* Special-case LFE */
907 if(chans[c].channel == LFE)
909 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
910 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
911 if(Device->Dry.Buffer == Device->RealOut.Buffer)
913 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
914 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
917 for(i = 0;i < NumSends;i++)
919 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
920 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
922 continue;
925 if(Device->Render_Mode == StereoPair)
926 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
927 else
928 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
929 ComputePanningGains(Device->Dry,
930 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
933 for(i = 0;i < NumSends;i++)
935 const ALeffectslot *Slot = SendSlots[i];
936 if(Slot)
937 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
938 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
940 else
941 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
942 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
949 ALfloat hfScale = props->Direct.HFReference / Frequency;
950 ALfloat lfScale = props->Direct.LFReference / Frequency;
951 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
952 ALfloat gainLF = maxf(DryGainLF, 0.001f);
954 voice->Direct.FilterType = AF_None;
955 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
956 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
957 ALfilterState_setParams(
958 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
959 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
961 ALfilterState_setParams(
962 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
963 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
965 for(c = 1;c < num_channels;c++)
967 ALfilterState_copyParams(&voice->Direct.Params[c].LowPass,
968 &voice->Direct.Params[0].LowPass);
969 ALfilterState_copyParams(&voice->Direct.Params[c].HighPass,
970 &voice->Direct.Params[0].HighPass);
973 for(i = 0;i < NumSends;i++)
975 ALfloat hfScale = props->Send[i].HFReference / Frequency;
976 ALfloat lfScale = props->Send[i].LFReference / Frequency;
977 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
978 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
980 voice->Send[i].FilterType = AF_None;
981 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
982 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
983 ALfilterState_setParams(
984 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
985 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
987 ALfilterState_setParams(
988 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
989 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
991 for(c = 1;c < num_channels;c++)
993 ALfilterState_copyParams(&voice->Send[i].Params[c].LowPass,
994 &voice->Send[i].Params[0].LowPass);
995 ALfilterState_copyParams(&voice->Send[i].Params[c].HighPass,
996 &voice->Send[i].Params[0].HighPass);
1001 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1003 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1004 const ALCdevice *Device = ALContext->Device;
1005 const ALlistener *Listener = ALContext->Listener;
1006 ALfloat DryGain, DryGainHF, DryGainLF;
1007 ALfloat WetGain[MAX_SENDS];
1008 ALfloat WetGainHF[MAX_SENDS];
1009 ALfloat WetGainLF[MAX_SENDS];
1010 ALeffectslot *SendSlots[MAX_SENDS];
1011 ALfloat Pitch;
1012 ALsizei i;
1014 voice->Direct.Buffer = Device->Dry.Buffer;
1015 voice->Direct.Channels = Device->Dry.NumChannels;
1016 for(i = 0;i < Device->NumAuxSends;i++)
1018 SendSlots[i] = props->Send[i].Slot;
1019 if(!SendSlots[i] && i == 0)
1020 SendSlots[i] = ALContext->DefaultSlot;
1021 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1023 SendSlots[i] = NULL;
1024 voice->Send[i].Buffer = NULL;
1025 voice->Send[i].Channels = 0;
1027 else
1029 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1030 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1034 /* Calculate the stepping value */
1035 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1036 if(Pitch > (ALfloat)MAX_PITCH)
1037 voice->Step = MAX_PITCH<<FRACTIONBITS;
1038 else
1039 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1040 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1041 voice->Resampler = SelectResampler(props->Resampler);
1043 /* Calculate gains */
1044 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1045 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1046 DryGain = minf(DryGain, GAIN_MIX_MAX);
1047 DryGainHF = props->Direct.GainHF;
1048 DryGainLF = props->Direct.GainLF;
1049 for(i = 0;i < Device->NumAuxSends;i++)
1051 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1052 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1053 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1054 WetGainHF[i] = props->Send[i].GainHF;
1055 WetGainLF[i] = props->Send[i].GainLF;
1058 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1059 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1062 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1064 const ALCdevice *Device = ALContext->Device;
1065 const ALlistener *Listener = ALContext->Listener;
1066 const ALsizei NumSends = Device->NumAuxSends;
1067 aluVector Position, Velocity, Direction, SourceToListener;
1068 ALfloat Distance, ClampedDist, DopplerFactor;
1069 ALeffectslot *SendSlots[MAX_SENDS];
1070 ALfloat RoomRolloff[MAX_SENDS];
1071 ALfloat DecayDistance[MAX_SENDS];
1072 ALfloat DecayHFDistance[MAX_SENDS];
1073 ALfloat DryGain, DryGainHF, DryGainLF;
1074 ALfloat WetGain[MAX_SENDS];
1075 ALfloat WetGainHF[MAX_SENDS];
1076 ALfloat WetGainLF[MAX_SENDS];
1077 bool directional;
1078 ALfloat dir[3];
1079 ALfloat spread;
1080 ALfloat Pitch;
1081 ALint i;
1083 /* Set mixing buffers and get send parameters. */
1084 voice->Direct.Buffer = Device->Dry.Buffer;
1085 voice->Direct.Channels = Device->Dry.NumChannels;
1086 for(i = 0;i < NumSends;i++)
1088 SendSlots[i] = props->Send[i].Slot;
1089 if(!SendSlots[i] && i == 0)
1090 SendSlots[i] = ALContext->DefaultSlot;
1091 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1093 SendSlots[i] = NULL;
1094 RoomRolloff[i] = 0.0f;
1095 DecayDistance[i] = 0.0f;
1096 DecayHFDistance[i] = 0.0f;
1098 else if(SendSlots[i]->Params.AuxSendAuto)
1100 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1101 DecayDistance[i] = SendSlots[i]->Params.DecayTime * SPEEDOFSOUNDMETRESPERSEC;
1102 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1103 if(SendSlots[i]->Params.DecayHFLimit)
1105 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1106 if(airAbsorption < 1.0f)
1108 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1109 DecayHFDistance[i] = minf(limitRatio, DecayHFDistance[i]);
1113 else
1115 /* If the slot's auxiliary send auto is off, the data sent to the
1116 * effect slot is the same as the dry path, sans filter effects */
1117 RoomRolloff[i] = props->RolloffFactor;
1118 DecayDistance[i] = 0.0f;
1119 DecayHFDistance[i] = 0.0f;
1122 if(!SendSlots[i])
1124 voice->Send[i].Buffer = NULL;
1125 voice->Send[i].Channels = 0;
1127 else
1129 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1130 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1134 /* Transform source to listener space (convert to head relative) */
1135 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1136 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1137 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1138 if(props->HeadRelative == AL_FALSE)
1140 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1141 /* Transform source vectors */
1142 Position = aluMatrixfVector(Matrix, &Position);
1143 Velocity = aluMatrixfVector(Matrix, &Velocity);
1144 Direction = aluMatrixfVector(Matrix, &Direction);
1146 else
1148 const aluVector *lvelocity = &Listener->Params.Velocity;
1149 /* Offset the source velocity to be relative of the listener velocity */
1150 Velocity.v[0] += lvelocity->v[0];
1151 Velocity.v[1] += lvelocity->v[1];
1152 Velocity.v[2] += lvelocity->v[2];
1155 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1156 SourceToListener.v[0] = -Position.v[0];
1157 SourceToListener.v[1] = -Position.v[1];
1158 SourceToListener.v[2] = -Position.v[2];
1159 SourceToListener.v[3] = 0.0f;
1160 Distance = aluNormalize(SourceToListener.v);
1162 /* Initial source gain */
1163 DryGain = props->Gain;
1164 DryGainHF = 1.0f;
1165 DryGainLF = 1.0f;
1166 for(i = 0;i < NumSends;i++)
1168 WetGain[i] = props->Gain;
1169 WetGainHF[i] = 1.0f;
1170 WetGainLF[i] = 1.0f;
1173 /* Calculate distance attenuation */
1174 ClampedDist = Distance;
1176 switch(Listener->Params.SourceDistanceModel ?
1177 props->DistanceModel : Listener->Params.DistanceModel)
1179 case InverseDistanceClamped:
1180 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1181 if(props->MaxDistance < props->RefDistance)
1182 break;
1183 /*fall-through*/
1184 case InverseDistance:
1185 if(!(props->RefDistance > 0.0f))
1186 ClampedDist = props->RefDistance;
1187 else
1189 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1190 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1191 for(i = 0;i < NumSends;i++)
1193 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1194 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1197 break;
1199 case LinearDistanceClamped:
1200 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1201 if(props->MaxDistance < props->RefDistance)
1202 break;
1203 /*fall-through*/
1204 case LinearDistance:
1205 if(!(props->MaxDistance != props->RefDistance))
1206 ClampedDist = props->RefDistance;
1207 else
1209 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1210 (props->MaxDistance-props->RefDistance);
1211 DryGain *= maxf(1.0f - attn, 0.0f);
1212 for(i = 0;i < NumSends;i++)
1214 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1215 (props->MaxDistance-props->RefDistance);
1216 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1219 break;
1221 case ExponentDistanceClamped:
1222 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1223 if(props->MaxDistance < props->RefDistance)
1224 break;
1225 /*fall-through*/
1226 case ExponentDistance:
1227 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1228 ClampedDist = props->RefDistance;
1229 else
1231 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1232 for(i = 0;i < NumSends;i++)
1233 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1235 break;
1237 case DisableDistance:
1238 ClampedDist = props->RefDistance;
1239 break;
1242 /* Distance-based air absorption */
1243 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1245 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1246 Listener->Params.MetersPerUnit;
1247 if(props->AirAbsorptionFactor > 0.0f)
1249 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1250 DryGainHF *= hfattn;
1251 for(i = 0;i < NumSends;i++)
1252 WetGainHF[i] *= hfattn;
1255 if(props->WetGainAuto)
1257 /* Apply a decay-time transformation to the wet path, based on the
1258 * source distance in meters. The initial decay of the reverb
1259 * effect is calculated and applied to the wet path.
1261 for(i = 0;i < NumSends;i++)
1263 ALfloat gain;
1265 if(!(DecayDistance[i] > 0.0f))
1266 continue;
1268 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1269 WetGain[i] *= gain;
1270 /* Yes, the wet path's air absorption is applied with
1271 * WetGainAuto on, rather than WetGainHFAuto.
1273 if(gain > 0.0f)
1275 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1276 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1282 /* Calculate directional soundcones */
1283 if(directional && props->InnerAngle < 360.0f)
1285 ALfloat ConeVolume;
1286 ALfloat ConeHF;
1287 ALfloat Angle;
1289 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1290 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1291 if(!(Angle > props->InnerAngle))
1293 ConeVolume = 1.0f;
1294 ConeHF = 1.0f;
1296 else if(Angle < props->OuterAngle)
1298 ALfloat scale = ( Angle-props->InnerAngle) /
1299 (props->OuterAngle-props->InnerAngle);
1300 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1301 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1303 else
1305 ConeVolume = props->OuterGain;
1306 ConeHF = props->OuterGainHF;
1309 DryGain *= ConeVolume;
1310 if(props->DryGainHFAuto)
1311 DryGainHF *= ConeHF;
1312 if(props->WetGainAuto)
1314 for(i = 0;i < NumSends;i++)
1315 WetGain[i] *= ConeVolume;
1317 if(props->WetGainHFAuto)
1319 for(i = 0;i < NumSends;i++)
1320 WetGainHF[i] *= ConeHF;
1324 /* Apply gain and frequency filters */
1325 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1326 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1327 DryGainHF *= props->Direct.GainHF;
1328 DryGainLF *= props->Direct.GainLF;
1329 for(i = 0;i < NumSends;i++)
1331 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1332 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1333 WetGainHF[i] *= props->Send[i].GainHF;
1334 WetGainLF[i] *= props->Send[i].GainLF;
1338 /* Initial source pitch */
1339 Pitch = props->Pitch;
1341 /* Calculate velocity-based doppler effect */
1342 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1343 if(DopplerFactor > 0.0f)
1345 const aluVector *lvelocity = &Listener->Params.Velocity;
1346 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1347 ALfloat vss, vls;
1349 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1350 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1352 if(!(vls < SpeedOfSound))
1354 /* Listener moving away from the source at the speed of sound.
1355 * Sound waves can't catch it.
1357 Pitch = 0.0f;
1359 else if(!(vss < SpeedOfSound))
1361 /* Source moving toward the listener at the speed of sound. Sound
1362 * waves bunch up to extreme frequencies.
1364 Pitch = HUGE_VALF;
1366 else
1368 /* Source and listener movement is nominal. Calculate the proper
1369 * doppler shift.
1371 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1375 /* Adjust pitch based on the buffer and output frequencies, and calculate
1376 * fixed-point stepping value.
1378 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1379 if(Pitch > (ALfloat)MAX_PITCH)
1380 voice->Step = MAX_PITCH<<FRACTIONBITS;
1381 else
1382 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1383 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1384 voice->Resampler = SelectResampler(props->Resampler);
1386 if(Distance > FLT_EPSILON)
1388 dir[0] = -SourceToListener.v[0];
1389 /* Clamp Y, in case rounding errors caused it to end up outside of
1390 * -1...+1.
1392 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1393 dir[2] = -SourceToListener.v[2] * ZScale;
1395 else
1397 dir[0] = 0.0f;
1398 dir[1] = 0.0f;
1399 dir[2] = -1.0f;
1401 if(props->Radius > Distance)
1402 spread = F_TAU - Distance/props->Radius*F_PI;
1403 else if(Distance > FLT_EPSILON)
1404 spread = asinf(props->Radius / Distance) * 2.0f;
1405 else
1406 spread = 0.0f;
1408 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1409 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1412 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1414 ALbufferlistitem *BufferListItem;
1415 struct ALvoiceProps *props;
1417 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1418 if(!props && !force) return;
1420 if(props)
1422 memcpy(voice->Props, props,
1423 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1426 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &voice->FreeList, props);
1428 props = voice->Props;
1430 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1431 while(BufferListItem != NULL)
1433 const ALbuffer *buffer;
1434 if((buffer=BufferListItem->buffer) != NULL)
1436 if(props->SpatializeMode == SpatializeOn ||
1437 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1438 CalcAttnSourceParams(voice, props, buffer, context);
1439 else
1440 CalcNonAttnSourceParams(voice, props, buffer, context);
1441 break;
1443 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1448 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1450 ALvoice **voice, **voice_end;
1451 ALsource *source;
1452 ALsizei i;
1454 IncrementRef(&ctx->UpdateCount);
1455 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1457 ALboolean force = CalcListenerParams(ctx);
1458 for(i = 0;i < slots->count;i++)
1459 force |= CalcEffectSlotParams(slots->slot[i], ctx->Device);
1461 voice = ctx->Voices;
1462 voice_end = voice + ctx->VoiceCount;
1463 for(;voice != voice_end;++voice)
1465 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1466 if(source) CalcSourceParams(*voice, ctx, force);
1469 IncrementRef(&ctx->UpdateCount);
1473 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1474 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1475 ALsizei NumChannels)
1477 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1478 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1479 ALsizei i;
1481 /* Apply an all-pass to all channels, except the front-left and front-
1482 * right, so they maintain the same relative phase.
1484 for(i = 0;i < NumChannels;i++)
1486 if(i == lidx || i == ridx)
1487 continue;
1488 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1491 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1492 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1494 for(i = 0;i < SamplesToDo;i++)
1496 ALfloat lfsum, hfsum;
1497 ALfloat m, s, c;
1499 lfsum = lsplit[0][i] + rsplit[0][i];
1500 hfsum = lsplit[1][i] + rsplit[1][i];
1501 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1503 /* This pans the separate low- and high-frequency sums between being on
1504 * the center channel and the left/right channels. The low-frequency
1505 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1506 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1507 * values can be tweaked.
1509 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1510 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1512 /* The generated center channel signal adds to the existing signal,
1513 * while the modified left and right channels replace.
1515 Buffer[lidx][i] = (m + s) * 0.5f;
1516 Buffer[ridx][i] = (m - s) * 0.5f;
1517 Buffer[cidx][i] += c * 0.5f;
1521 static void ApplyDistanceComp(ALfloatBUFFERSIZE *restrict Samples, DistanceComp *distcomp,
1522 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1524 ALsizei i, c;
1526 Values = ASSUME_ALIGNED(Values, 16);
1527 for(c = 0;c < numchans;c++)
1529 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1530 const ALfloat gain = distcomp[c].Gain;
1531 const ALsizei base = distcomp[c].Length;
1532 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1534 if(base == 0)
1536 if(gain < 1.0f)
1538 for(i = 0;i < SamplesToDo;i++)
1539 inout[i] *= gain;
1541 continue;
1544 if(SamplesToDo >= base)
1546 for(i = 0;i < base;i++)
1547 Values[i] = distbuf[i];
1548 for(;i < SamplesToDo;i++)
1549 Values[i] = inout[i-base];
1550 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1552 else
1554 for(i = 0;i < SamplesToDo;i++)
1555 Values[i] = distbuf[i];
1556 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1557 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1559 for(i = 0;i < SamplesToDo;i++)
1560 inout[i] = Values[i]*gain;
1564 static void ApplyDither(ALfloatBUFFERSIZE *restrict Samples, ALuint *dither_seed,
1565 const ALfloat quant_scale, const ALsizei SamplesToDo,
1566 const ALsizei numchans)
1568 const ALfloat invscale = 1.0f / quant_scale;
1569 ALuint seed = *dither_seed;
1570 ALsizei c, i;
1572 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1573 * values between -1 and +1). Step 2 is to add the noise to the samples,
1574 * before rounding and after scaling up to the desired quantization depth.
1576 for(c = 0;c < numchans;c++)
1578 ALfloat *restrict samples = Samples[c];
1579 for(i = 0;i < SamplesToDo;i++)
1581 ALfloat val = samples[i] * quant_scale;
1582 ALuint rng0 = dither_rng(&seed);
1583 ALuint rng1 = dither_rng(&seed);
1584 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1585 samples[i] = roundf(val) * invscale;
1588 *dither_seed = seed;
1592 static inline ALfloat Conv_ALfloat(ALfloat val)
1593 { return val; }
1594 static inline ALint Conv_ALint(ALfloat val)
1596 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1597 * integer range normalized floats can be safely converted to (a bit of the
1598 * exponent helps out, effectively giving 25 bits).
1600 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1602 static inline ALshort Conv_ALshort(ALfloat val)
1603 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1604 static inline ALbyte Conv_ALbyte(ALfloat val)
1605 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1607 /* Define unsigned output variations. */
1608 #define DECL_TEMPLATE(T, func, O) \
1609 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1611 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1612 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1613 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1615 #undef DECL_TEMPLATE
1617 #define DECL_TEMPLATE(T, A) \
1618 static void Write##A(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1619 ALsizei Offset, ALsizei SamplesToDo, ALsizei numchans) \
1621 ALsizei i, j; \
1622 for(j = 0;j < numchans;j++) \
1624 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1625 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1627 for(i = 0;i < SamplesToDo;i++) \
1628 out[i*numchans] = Conv_##T(in[i]); \
1632 DECL_TEMPLATE(ALfloat, F32)
1633 DECL_TEMPLATE(ALuint, UI32)
1634 DECL_TEMPLATE(ALint, I32)
1635 DECL_TEMPLATE(ALushort, UI16)
1636 DECL_TEMPLATE(ALshort, I16)
1637 DECL_TEMPLATE(ALubyte, UI8)
1638 DECL_TEMPLATE(ALbyte, I8)
1640 #undef DECL_TEMPLATE
1643 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1645 ALsizei SamplesToDo;
1646 ALsizei SamplesDone;
1647 ALCcontext *ctx;
1648 ALsizei i, c;
1650 START_MIXER_MODE();
1651 for(SamplesDone = 0;SamplesDone < NumSamples;)
1653 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1654 for(c = 0;c < device->Dry.NumChannels;c++)
1655 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1656 if(device->Dry.Buffer != device->FOAOut.Buffer)
1657 for(c = 0;c < device->FOAOut.NumChannels;c++)
1658 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1659 if(device->Dry.Buffer != device->RealOut.Buffer)
1660 for(c = 0;c < device->RealOut.NumChannels;c++)
1661 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1663 IncrementRef(&device->MixCount);
1665 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1666 while(ctx)
1668 const struct ALeffectslotArray *auxslots;
1670 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1671 UpdateContextSources(ctx, auxslots);
1673 for(i = 0;i < auxslots->count;i++)
1675 ALeffectslot *slot = auxslots->slot[i];
1676 for(c = 0;c < slot->NumChannels;c++)
1677 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1680 /* source processing */
1681 for(i = 0;i < ctx->VoiceCount;i++)
1683 ALvoice *voice = ctx->Voices[i];
1684 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1685 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1686 voice->Step > 0)
1688 if(!MixSource(voice, source, device, SamplesToDo))
1690 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1691 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1696 /* effect slot processing */
1697 for(i = 0;i < auxslots->count;i++)
1699 const ALeffectslot *slot = auxslots->slot[i];
1700 ALeffectState *state = slot->Params.EffectState;
1701 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1702 state->OutChannels);
1705 ctx = ctx->next;
1708 /* Increment the clock time. Every second's worth of samples is
1709 * converted and added to clock base so that large sample counts don't
1710 * overflow during conversion. This also guarantees an exact, stable
1711 * conversion. */
1712 device->SamplesDone += SamplesToDo;
1713 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1714 device->SamplesDone %= device->Frequency;
1715 IncrementRef(&device->MixCount);
1717 if(device->HrtfHandle)
1719 DirectHrtfState *state;
1720 int lidx, ridx;
1722 if(device->AmbiUp)
1723 ambiup_process(device->AmbiUp,
1724 device->Dry.Buffer, device->Dry.NumChannels,
1725 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1728 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1729 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1730 assert(lidx != -1 && ridx != -1);
1732 state = device->Hrtf;
1733 for(c = 0;c < device->Dry.NumChannels;c++)
1735 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1736 device->Dry.Buffer[c], state->Offset, state->IrSize,
1737 SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
1738 state->Chan[c].Values, SamplesToDo
1741 state->Offset += SamplesToDo;
1743 else if(device->AmbiDecoder)
1745 if(device->Dry.Buffer != device->FOAOut.Buffer)
1746 bformatdec_upSample(device->AmbiDecoder,
1747 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1748 device->FOAOut.NumChannels, SamplesToDo
1750 bformatdec_process(device->AmbiDecoder,
1751 device->RealOut.Buffer, device->RealOut.NumChannels,
1752 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1755 else if(device->AmbiUp)
1757 ambiup_process(device->AmbiUp,
1758 device->RealOut.Buffer, device->RealOut.NumChannels,
1759 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1762 else if(device->Uhj_Encoder)
1764 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1765 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1766 if(lidx != -1 && ridx != -1)
1768 /* Encode to stereo-compatible 2-channel UHJ output. */
1769 EncodeUhj2(device->Uhj_Encoder,
1770 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1771 device->Dry.Buffer, SamplesToDo
1775 else if(device->Bs2b)
1777 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1778 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1779 if(lidx != -1 && ridx != -1)
1781 /* Apply binaural/crossfeed filter */
1782 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1783 device->RealOut.Buffer[ridx], SamplesToDo);
1787 if(OutBuffer)
1789 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1790 ALsizei Channels = device->RealOut.NumChannels;
1792 if(device->Stablizer)
1794 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1795 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1796 int cidx = GetChannelIdxByName(device->RealOut, FrontCenter);
1797 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1799 ApplyStablizer(device->Stablizer, Buffer, lidx, ridx, cidx,
1800 SamplesToDo, Channels);
1803 /* Use NFCtrlData for temp value storage. */
1804 ApplyDistanceComp(Buffer, device->ChannelDelay, device->NFCtrlData,
1805 SamplesToDo, Channels);
1807 if(device->Limiter)
1808 ApplyCompression(device->Limiter, Channels, SamplesToDo, Buffer);
1810 if(device->DitherDepth > 0.0f)
1811 ApplyDither(Buffer, &device->DitherSeed, device->DitherDepth, SamplesToDo,
1812 Channels);
1814 switch(device->FmtType)
1816 case DevFmtByte:
1817 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1818 break;
1819 case DevFmtUByte:
1820 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1821 break;
1822 case DevFmtShort:
1823 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1824 break;
1825 case DevFmtUShort:
1826 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1827 break;
1828 case DevFmtInt:
1829 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1830 break;
1831 case DevFmtUInt:
1832 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1833 break;
1834 case DevFmtFloat:
1835 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1836 break;
1840 SamplesDone += SamplesToDo;
1842 END_MIXER_MODE();
1846 void aluHandleDisconnect(ALCdevice *device)
1848 ALCcontext *ctx;
1850 device->Connected = ALC_FALSE;
1852 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1853 while(ctx)
1855 ALsizei i;
1856 for(i = 0;i < ctx->VoiceCount;i++)
1858 ALvoice *voice = ctx->Voices[i];
1859 ALsource *source;
1861 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_acq_rel);
1862 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1864 if(source)
1866 ALenum playing = AL_PLAYING;
1867 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1870 ctx->VoiceCount = 0;
1872 ctx = ctx->next;