Avoid unnecessary doubles
[openal-soft.git] / Alc / ALu.c
blob22aca28ace8b8c3b7479d8128ee59bcc7889aa39
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"
43 #include "backends/base.h"
46 struct ChanMap {
47 enum Channel channel;
48 ALfloat angle;
49 ALfloat elevation;
52 /* Cone scalar */
53 ALfloat ConeScale = 1.0f;
55 /* Localized Z scalar for mono sources */
56 ALfloat ZScale = 1.0f;
58 extern inline ALfloat minf(ALfloat a, ALfloat b);
59 extern inline ALfloat maxf(ALfloat a, ALfloat b);
60 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
62 extern inline ALdouble mind(ALdouble a, ALdouble b);
63 extern inline ALdouble maxd(ALdouble a, ALdouble b);
64 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
66 extern inline ALuint minu(ALuint a, ALuint b);
67 extern inline ALuint maxu(ALuint a, ALuint b);
68 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
70 extern inline ALint mini(ALint a, ALint b);
71 extern inline ALint maxi(ALint a, ALint b);
72 extern inline ALint clampi(ALint val, ALint min, ALint max);
74 extern inline ALint64 mini64(ALint64 a, ALint64 b);
75 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
76 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
78 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
79 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
80 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
82 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
83 extern inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALsizei frac);
85 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
87 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
88 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
89 extern inline void aluMatrixfSet(aluMatrixf *matrix,
90 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
91 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
92 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
93 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
95 const aluMatrixf IdentityMatrixf = {{
96 { 1.0f, 0.0f, 0.0f, 0.0f },
97 { 0.0f, 1.0f, 0.0f, 0.0f },
98 { 0.0f, 0.0f, 1.0f, 0.0f },
99 { 0.0f, 0.0f, 0.0f, 1.0f },
103 struct OutputLimiter *alloc_limiter(void)
105 struct OutputLimiter *limiter = al_calloc(16, sizeof(*limiter));
106 /* Limiter attack drops -80dB in 50ms. */
107 limiter->AttackRate = 0.05f;
108 /* Limiter release raises +80dB in 1s. */
109 limiter->ReleaseRate = 1.0f;
110 limiter->Gain = 1.0f;
111 return limiter;
114 void DeinitVoice(ALvoice *voice)
116 struct ALvoiceProps *props;
117 size_t count = 0;
119 props = ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL);
120 if(props) al_free(props);
122 props = ATOMIC_EXCHANGE_PTR(&voice->FreeList, NULL, almemory_order_relaxed);
123 while(props)
125 struct ALvoiceProps *next;
126 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
127 al_free(props);
128 props = next;
129 ++count;
131 /* This is excessively spammy if it traces every voice destruction, so just
132 * warn if it was unexpectedly large.
134 if(count > 3)
135 WARN("Freed "SZFMT" voice property objects\n", count);
139 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
141 #ifdef HAVE_NEON
142 if((CPUCapFlags&CPU_CAP_NEON))
143 return MixDirectHrtf_Neon;
144 #endif
145 #ifdef HAVE_SSE
146 if((CPUCapFlags&CPU_CAP_SSE))
147 return MixDirectHrtf_SSE;
148 #endif
150 return MixDirectHrtf_C;
154 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
156 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
157 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
158 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
161 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
163 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
166 static ALfloat aluNormalize(ALfloat *vec)
168 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
169 if(length > 0.0f)
171 ALfloat inv_length = 1.0f/length;
172 vec[0] *= inv_length;
173 vec[1] *= inv_length;
174 vec[2] *= inv_length;
176 return length;
179 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
181 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
183 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];
184 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];
185 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];
188 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
190 aluVector v;
191 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];
192 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];
193 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];
194 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];
195 return v;
199 /* Prepares the interpolator for a given rate (determined by increment). A
200 * result of AL_FALSE indicates that the filter output will completely cut
201 * the input signal.
203 * With a bit of work, and a trade of memory for CPU cost, this could be
204 * modified for use with an interpolated increment for buttery-smooth pitch
205 * changes.
207 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
209 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
210 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
211 static const ALuint to[4][BSINC_SCALE_COUNT] =
213 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
214 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
215 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
216 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
218 static const ALuint tm[2][BSINC_SCALE_COUNT] =
220 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
221 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
223 ALfloat sf;
224 ALuint si, pi;
225 ALboolean uncut = AL_TRUE;
227 if(increment > FRACTIONONE)
229 sf = (ALfloat)FRACTIONONE / increment;
230 if(sf < scaleBase)
232 /* Signal has been completely cut. The return result can be used
233 * to skip the filter (and output zeros) as an optimization.
235 sf = 0.0f;
236 si = 0;
237 uncut = AL_FALSE;
239 else
241 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
242 si = fastf2u(sf);
243 /* The interpolation factor is fit to this diagonally-symmetric
244 * curve to reduce the transition ripple caused by interpolating
245 * different scales of the sinc function.
247 sf = 1.0f - cosf(asinf(sf - si));
250 else
252 sf = 0.0f;
253 si = BSINC_SCALE_COUNT - 1;
256 state->sf = sf;
257 state->m = m[si];
258 state->l = -(ALint)((m[si] / 2) - 1);
259 /* The CPU cost of this table re-mapping could be traded for the memory
260 * cost of a complete table map (1024 elements large).
262 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
264 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
265 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
266 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
267 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
269 return uncut;
273 static ALboolean CalcListenerParams(ALCcontext *Context)
275 ALlistener *Listener = Context->Listener;
276 ALfloat N[3], V[3], U[3], P[3];
277 struct ALlistenerProps *props;
278 aluVector vel;
280 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
281 if(!props) return AL_FALSE;
283 /* AT then UP */
284 N[0] = props->Forward[0];
285 N[1] = props->Forward[1];
286 N[2] = props->Forward[2];
287 aluNormalize(N);
288 V[0] = props->Up[0];
289 V[1] = props->Up[1];
290 V[2] = props->Up[2];
291 aluNormalize(V);
292 /* Build and normalize right-vector */
293 aluCrossproduct(N, V, U);
294 aluNormalize(U);
296 aluMatrixfSet(&Listener->Params.Matrix,
297 U[0], V[0], -N[0], 0.0,
298 U[1], V[1], -N[1], 0.0,
299 U[2], V[2], -N[2], 0.0,
300 0.0, 0.0, 0.0, 1.0
303 P[0] = props->Position[0];
304 P[1] = props->Position[1];
305 P[2] = props->Position[2];
306 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
307 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
309 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
310 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
312 Listener->Params.Gain = props->Gain * Context->GainBoost;
313 Listener->Params.MetersPerUnit = props->MetersPerUnit;
315 Listener->Params.DopplerFactor = props->DopplerFactor;
316 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
318 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
319 Listener->Params.DistanceModel = props->DistanceModel;
321 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
322 return AL_TRUE;
325 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
327 struct ALeffectslotProps *props;
328 ALeffectState *state;
330 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
331 if(!props) return AL_FALSE;
333 slot->Params.Gain = props->Gain;
334 slot->Params.AuxSendAuto = props->AuxSendAuto;
335 slot->Params.EffectType = props->Type;
336 if(IsReverbEffect(slot->Params.EffectType))
338 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
339 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
340 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
341 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
342 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
344 else
346 slot->Params.RoomRolloff = 0.0f;
347 slot->Params.DecayTime = 0.0f;
348 slot->Params.DecayHFRatio = 0.0f;
349 slot->Params.AirAbsorptionGainHF = 1.0f;
350 slot->Params.DecayHFLimit = AL_FALSE;
353 /* Swap effect states. No need to play with the ref counts since they keep
354 * the same number of refs.
356 state = props->State;
357 props->State = slot->Params.EffectState;
358 slot->Params.EffectState = state;
360 V(state,update)(device, slot, &props->Props);
362 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
363 return AL_TRUE;
367 static const struct ChanMap MonoMap[1] = {
368 { FrontCenter, 0.0f, 0.0f }
369 }, RearMap[2] = {
370 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
371 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
372 }, QuadMap[4] = {
373 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
374 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
375 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
376 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
377 }, X51Map[6] = {
378 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
379 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
380 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
381 { LFE, 0.0f, 0.0f },
382 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
383 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
384 }, X61Map[7] = {
385 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
386 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
387 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
388 { LFE, 0.0f, 0.0f },
389 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
390 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
391 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
392 }, X71Map[8] = {
393 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
394 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
395 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
396 { LFE, 0.0f, 0.0f },
397 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
398 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
399 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
400 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
403 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
404 const ALfloat Spread, const ALfloat DryGain,
405 const ALfloat DryGainHF, const ALfloat DryGainLF,
406 const ALfloat *WetGain, const ALfloat *WetGainLF,
407 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
408 const ALbuffer *Buffer, const struct ALvoiceProps *props,
409 const ALlistener *Listener, const ALCdevice *Device)
411 struct ChanMap StereoMap[2] = {
412 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
413 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
415 bool DirectChannels = props->DirectChannels;
416 const ALsizei NumSends = Device->NumAuxSends;
417 const ALuint Frequency = Device->Frequency;
418 const struct ChanMap *chans = NULL;
419 ALsizei num_channels = 0;
420 bool isbformat = false;
421 ALfloat downmix_gain = 1.0f;
422 ALsizei c, i, j;
424 switch(Buffer->FmtChannels)
426 case FmtMono:
427 chans = MonoMap;
428 num_channels = 1;
429 /* Mono buffers are never played direct. */
430 DirectChannels = false;
431 break;
433 case FmtStereo:
434 /* Convert counter-clockwise to clockwise. */
435 StereoMap[0].angle = -props->StereoPan[0];
436 StereoMap[1].angle = -props->StereoPan[1];
438 chans = StereoMap;
439 num_channels = 2;
440 downmix_gain = 1.0f / 2.0f;
441 break;
443 case FmtRear:
444 chans = RearMap;
445 num_channels = 2;
446 downmix_gain = 1.0f / 2.0f;
447 break;
449 case FmtQuad:
450 chans = QuadMap;
451 num_channels = 4;
452 downmix_gain = 1.0f / 4.0f;
453 break;
455 case FmtX51:
456 chans = X51Map;
457 num_channels = 6;
458 /* NOTE: Excludes LFE. */
459 downmix_gain = 1.0f / 5.0f;
460 break;
462 case FmtX61:
463 chans = X61Map;
464 num_channels = 7;
465 /* NOTE: Excludes LFE. */
466 downmix_gain = 1.0f / 6.0f;
467 break;
469 case FmtX71:
470 chans = X71Map;
471 num_channels = 8;
472 /* NOTE: Excludes LFE. */
473 downmix_gain = 1.0f / 7.0f;
474 break;
476 case FmtBFormat2D:
477 num_channels = 3;
478 isbformat = true;
479 DirectChannels = false;
480 break;
482 case FmtBFormat3D:
483 num_channels = 4;
484 isbformat = true;
485 DirectChannels = false;
486 break;
489 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
490 if(isbformat)
492 /* Special handling for B-Format sources. */
494 if(Distance > FLT_EPSILON)
496 /* Panning a B-Format sound toward some direction is easy. Just pan
497 * the first (W) channel as a normal mono sound and silence the
498 * others.
500 ALfloat coeffs[MAX_AMBI_COEFFS];
502 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
504 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
505 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
506 (mdist * (ALfloat)Device->Frequency);
507 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
508 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
509 /* Clamp w0 for really close distances, to prevent excessive
510 * bass.
512 w0 = minf(w0, w1*4.0f);
514 /* Only need to adjust the first channel of a B-Format source. */
515 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
516 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
517 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
519 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
520 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
521 voice->Flags |= VOICE_HAS_NFC;
524 if(Device->Render_Mode == StereoPair)
526 ALfloat ev = asinf(Dir[1]);
527 ALfloat az = atan2f(Dir[0], -Dir[2]);
528 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
530 else
531 CalcDirectionCoeffs(Dir, Spread, coeffs);
533 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
534 ComputePanningGains(Device->Dry, coeffs, DryGain*1.414213562f,
535 voice->Direct.Params[0].Gains.Target);
536 for(c = 1;c < num_channels;c++)
538 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
539 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
542 for(i = 0;i < NumSends;i++)
544 const ALeffectslot *Slot = SendSlots[i];
545 if(Slot)
546 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
547 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
549 else
550 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
551 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
552 for(c = 1;c < num_channels;c++)
554 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
555 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
559 else
561 /* Local B-Format sources have their XYZ channels rotated according
562 * to the orientation.
564 ALfloat N[3], V[3], U[3];
565 aluMatrixf matrix;
566 ALfloat scale;
568 if(Device->AvgSpeakerDist > 0.0f)
570 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
571 * is what we want for FOA input. The first channel may have
572 * been previously re-adjusted if panned, so reset it.
574 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f);
575 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f);
576 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 0.0f);
578 voice->Direct.ChannelsPerOrder[0] = 1;
579 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
580 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
581 voice->Direct.ChannelsPerOrder[2] = 0;
582 voice->Flags |= VOICE_HAS_NFC;
585 /* AT then UP */
586 N[0] = props->Orientation[0][0];
587 N[1] = props->Orientation[0][1];
588 N[2] = props->Orientation[0][2];
589 aluNormalize(N);
590 V[0] = props->Orientation[1][0];
591 V[1] = props->Orientation[1][1];
592 V[2] = props->Orientation[1][2];
593 aluNormalize(V);
594 if(!props->HeadRelative)
596 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
597 aluMatrixfFloat3(N, 0.0f, lmatrix);
598 aluMatrixfFloat3(V, 0.0f, lmatrix);
600 /* Build and normalize right-vector */
601 aluCrossproduct(N, V, U);
602 aluNormalize(U);
604 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
605 scale = 1.732050808f;
606 aluMatrixfSet(&matrix,
607 1.414213562f, 0.0f, 0.0f, 0.0f,
608 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
609 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
610 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
613 voice->Direct.Buffer = Device->FOAOut.Buffer;
614 voice->Direct.Channels = Device->FOAOut.NumChannels;
615 for(c = 0;c < num_channels;c++)
616 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
617 voice->Direct.Params[c].Gains.Target);
618 for(i = 0;i < NumSends;i++)
620 const ALeffectslot *Slot = SendSlots[i];
621 if(Slot)
623 for(c = 0;c < num_channels;c++)
624 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
625 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
628 else
630 for(c = 0;c < num_channels;c++)
631 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
632 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
637 else if(DirectChannels)
639 /* Direct source channels always play local. Skip the virtual channels
640 * and write inputs to the matching real outputs.
642 voice->Direct.Buffer = Device->RealOut.Buffer;
643 voice->Direct.Channels = Device->RealOut.NumChannels;
645 for(c = 0;c < num_channels;c++)
647 int idx;
648 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
649 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
650 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
651 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
654 /* Auxiliary sends still use normal channel panning since they mix to
655 * B-Format, which can't channel-match.
657 for(c = 0;c < num_channels;c++)
659 ALfloat coeffs[MAX_AMBI_COEFFS];
660 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
662 for(i = 0;i < NumSends;i++)
664 const ALeffectslot *Slot = SendSlots[i];
665 if(Slot)
666 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
667 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
669 else
670 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
671 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
675 else if(Device->Render_Mode == HrtfRender)
677 /* Full HRTF rendering. Skip the virtual channels and render to the
678 * real outputs.
680 voice->Direct.Buffer = Device->RealOut.Buffer;
681 voice->Direct.Channels = Device->RealOut.NumChannels;
683 if(Distance > FLT_EPSILON)
685 ALfloat coeffs[MAX_AMBI_COEFFS];
686 ALfloat ev, az;
688 ev = asinf(Dir[1]);
689 az = atan2f(Dir[0], -Dir[2]);
691 /* Get the HRIR coefficients and delays just once, for the given
692 * source direction.
694 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
695 voice->Direct.Params[0].Hrtf.Target.Coeffs,
696 voice->Direct.Params[0].Hrtf.Target.Delay);
697 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
699 /* Remaining channels use the same results as the first. */
700 for(c = 1;c < num_channels;c++)
702 /* Skip LFE */
703 if(chans[c].channel == LFE)
704 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
705 sizeof(voice->Direct.Params[c].Hrtf.Target));
706 else
707 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
710 /* Calculate the directional coefficients once, which apply to all
711 * input channels of the source sends.
713 CalcDirectionCoeffs(Dir, Spread, coeffs);
715 for(i = 0;i < NumSends;i++)
717 const ALeffectslot *Slot = SendSlots[i];
718 if(Slot)
719 for(c = 0;c < num_channels;c++)
721 /* Skip LFE */
722 if(chans[c].channel == LFE)
723 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
724 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
725 else
726 ComputePanningGainsBF(Slot->ChanMap,
727 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
728 voice->Send[i].Params[c].Gains.Target
731 else
732 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
733 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
736 else
738 /* Local sources on HRTF play with each channel panned to its
739 * relative location around the listener, providing "virtual
740 * speaker" responses.
742 for(c = 0;c < num_channels;c++)
744 ALfloat coeffs[MAX_AMBI_COEFFS];
746 if(chans[c].channel == LFE)
748 /* Skip LFE */
749 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
750 sizeof(voice->Direct.Params[c].Hrtf.Target));
751 for(i = 0;i < NumSends;i++)
753 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
754 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
756 continue;
759 /* Get the HRIR coefficients and delays for this channel
760 * position.
762 GetHrtfCoeffs(Device->HrtfHandle,
763 chans[c].elevation, chans[c].angle, Spread,
764 voice->Direct.Params[c].Hrtf.Target.Coeffs,
765 voice->Direct.Params[c].Hrtf.Target.Delay
767 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
769 /* Normal panning for auxiliary sends. */
770 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
772 for(i = 0;i < NumSends;i++)
774 const ALeffectslot *Slot = SendSlots[i];
775 if(Slot)
776 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
777 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
779 else
780 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
781 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
786 voice->Flags |= VOICE_HAS_HRTF;
788 else
790 /* Non-HRTF rendering. Use normal panning to the output. */
792 if(Distance > FLT_EPSILON)
794 ALfloat coeffs[MAX_AMBI_COEFFS];
795 ALfloat w0 = 0.0f;
797 /* Calculate NFC filter coefficient if needed. */
798 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
800 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
801 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
802 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
803 w0 = SPEEDOFSOUNDMETRESPERSEC /
804 (mdist * (ALfloat)Device->Frequency);
805 /* Clamp w0 for really close distances, to prevent excessive
806 * bass.
808 w0 = minf(w0, w1*4.0f);
810 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
811 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
812 voice->Flags |= VOICE_HAS_NFC;
815 /* Calculate the directional coefficients once, which apply to all
816 * input channels.
818 if(Device->Render_Mode == StereoPair)
820 ALfloat ev = asinf(Dir[1]);
821 ALfloat az = atan2f(Dir[0], -Dir[2]);
822 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
824 else
825 CalcDirectionCoeffs(Dir, Spread, coeffs);
827 for(c = 0;c < num_channels;c++)
829 /* Adjust NFC filters if needed. */
830 if((voice->Flags&VOICE_HAS_NFC))
832 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
833 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
834 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
837 /* Special-case LFE */
838 if(chans[c].channel == LFE)
840 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
841 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
842 if(Device->Dry.Buffer == Device->RealOut.Buffer)
844 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
845 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
847 continue;
850 ComputePanningGains(Device->Dry,
851 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
855 for(i = 0;i < NumSends;i++)
857 const ALeffectslot *Slot = SendSlots[i];
858 if(Slot)
859 for(c = 0;c < num_channels;c++)
861 /* Skip LFE */
862 if(chans[c].channel == LFE)
863 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
864 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
865 else
866 ComputePanningGainsBF(Slot->ChanMap,
867 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
868 voice->Send[i].Params[c].Gains.Target
871 else
872 for(c = 0;c < num_channels;c++)
874 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
875 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
879 else
881 ALfloat w0 = 0.0f;
883 if(Device->AvgSpeakerDist > 0.0f)
885 /* If the source distance is 0, set w0 to w1 to act as a pass-
886 * through. We still want to pass the signal through the
887 * filters so they keep an appropriate history, in case the
888 * source moves away from the listener.
890 w0 = SPEEDOFSOUNDMETRESPERSEC /
891 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
893 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
894 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
895 voice->Flags |= VOICE_HAS_NFC;
898 for(c = 0;c < num_channels;c++)
900 ALfloat coeffs[MAX_AMBI_COEFFS];
902 if((voice->Flags&VOICE_HAS_NFC))
904 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
905 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
906 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
909 /* Special-case LFE */
910 if(chans[c].channel == LFE)
912 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
913 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
914 if(Device->Dry.Buffer == Device->RealOut.Buffer)
916 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
917 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
920 for(i = 0;i < NumSends;i++)
922 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
923 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
925 continue;
928 if(Device->Render_Mode == StereoPair)
929 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
930 else
931 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
932 ComputePanningGains(Device->Dry,
933 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
936 for(i = 0;i < NumSends;i++)
938 const ALeffectslot *Slot = SendSlots[i];
939 if(Slot)
940 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
941 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
943 else
944 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
945 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
952 ALfloat hfScale = props->Direct.HFReference / Frequency;
953 ALfloat lfScale = props->Direct.LFReference / Frequency;
954 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
955 ALfloat gainLF = maxf(DryGainLF, 0.001f);
956 for(c = 0;c < num_channels;c++)
958 voice->Direct.Params[c].FilterType = AF_None;
959 if(gainHF != 1.0f) voice->Direct.Params[c].FilterType |= AF_LowPass;
960 if(gainLF != 1.0f) voice->Direct.Params[c].FilterType |= AF_HighPass;
961 ALfilterState_setParams(
962 &voice->Direct.Params[c].LowPass, ALfilterType_HighShelf,
963 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
965 ALfilterState_setParams(
966 &voice->Direct.Params[c].HighPass, ALfilterType_LowShelf,
967 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
971 for(i = 0;i < NumSends;i++)
973 ALfloat hfScale = props->Send[i].HFReference / Frequency;
974 ALfloat lfScale = props->Send[i].LFReference / Frequency;
975 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
976 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
977 for(c = 0;c < num_channels;c++)
979 voice->Send[i].Params[c].FilterType = AF_None;
980 if(gainHF != 1.0f) voice->Send[i].Params[c].FilterType |= AF_LowPass;
981 if(gainLF != 1.0f) voice->Send[i].Params[c].FilterType |= AF_HighPass;
982 ALfilterState_setParams(
983 &voice->Send[i].Params[c].LowPass, ALfilterType_HighShelf,
984 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
986 ALfilterState_setParams(
987 &voice->Send[i].Params[c].HighPass, ALfilterType_LowShelf,
988 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
994 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
996 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
997 const ALCdevice *Device = ALContext->Device;
998 const ALlistener *Listener = ALContext->Listener;
999 ALfloat DryGain, DryGainHF, DryGainLF;
1000 ALfloat WetGain[MAX_SENDS];
1001 ALfloat WetGainHF[MAX_SENDS];
1002 ALfloat WetGainLF[MAX_SENDS];
1003 ALeffectslot *SendSlots[MAX_SENDS];
1004 ALfloat Pitch;
1005 ALsizei i;
1007 voice->Direct.Buffer = Device->Dry.Buffer;
1008 voice->Direct.Channels = Device->Dry.NumChannels;
1009 for(i = 0;i < Device->NumAuxSends;i++)
1011 SendSlots[i] = props->Send[i].Slot;
1012 if(!SendSlots[i] && i == 0)
1013 SendSlots[i] = Device->DefaultSlot;
1014 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1016 SendSlots[i] = NULL;
1017 voice->Send[i].Buffer = NULL;
1018 voice->Send[i].Channels = 0;
1020 else
1022 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1023 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1027 /* Calculate the stepping value */
1028 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1029 if(Pitch > (ALfloat)MAX_PITCH)
1030 voice->Step = MAX_PITCH<<FRACTIONBITS;
1031 else
1032 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1033 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1034 voice->Resampler = SelectResampler(props->Resampler);
1036 /* Calculate gains */
1037 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1038 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1039 DryGain = minf(DryGain, GAIN_MIX_MAX);
1040 DryGainHF = props->Direct.GainHF;
1041 DryGainLF = props->Direct.GainLF;
1042 for(i = 0;i < Device->NumAuxSends;i++)
1044 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1045 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1046 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1047 WetGainHF[i] = props->Send[i].GainHF;
1048 WetGainLF[i] = props->Send[i].GainLF;
1051 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1052 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1055 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1057 const ALCdevice *Device = ALContext->Device;
1058 const ALlistener *Listener = ALContext->Listener;
1059 const ALsizei NumSends = Device->NumAuxSends;
1060 aluVector Position, Velocity, Direction, SourceToListener;
1061 ALfloat Distance, ClampedDist, DopplerFactor;
1062 ALfloat RoomAirAbsorption[MAX_SENDS];
1063 ALeffectslot *SendSlots[MAX_SENDS];
1064 ALfloat RoomRolloff[MAX_SENDS];
1065 ALfloat DecayDistance[MAX_SENDS];
1066 ALfloat DecayHFDistance[MAX_SENDS];
1067 ALfloat DryGain, DryGainHF, DryGainLF;
1068 ALfloat WetGain[MAX_SENDS];
1069 ALfloat WetGainHF[MAX_SENDS];
1070 ALfloat WetGainLF[MAX_SENDS];
1071 bool directional;
1072 ALfloat dir[3];
1073 ALfloat spread;
1074 ALfloat Pitch;
1075 ALint i;
1077 /* Set mixing buffers and get send parameters. */
1078 voice->Direct.Buffer = Device->Dry.Buffer;
1079 voice->Direct.Channels = Device->Dry.NumChannels;
1080 for(i = 0;i < NumSends;i++)
1082 SendSlots[i] = props->Send[i].Slot;
1083 if(!SendSlots[i] && i == 0)
1084 SendSlots[i] = Device->DefaultSlot;
1085 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1087 SendSlots[i] = NULL;
1088 RoomRolloff[i] = 0.0f;
1089 DecayDistance[i] = 0.0f;
1090 DecayHFDistance[i] = 0.0f;
1091 RoomAirAbsorption[i] = 1.0f;
1093 else if(SendSlots[i]->Params.AuxSendAuto)
1095 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1096 DecayDistance[i] = SendSlots[i]->Params.DecayTime * SPEEDOFSOUNDMETRESPERSEC;
1097 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1098 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
1099 if(SendSlots[i]->Params.DecayHFLimit && RoomAirAbsorption[i] < 1.0f)
1101 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) /
1102 (log10f(RoomAirAbsorption[i]) * DecayDistance[i]);
1103 limitRatio = minf(limitRatio, SendSlots[i]->Params.DecayHFRatio);
1104 DecayHFDistance[i] = minf(DecayHFDistance[i], limitRatio*DecayDistance[i]);
1107 else
1109 /* If the slot's auxiliary send auto is off, the data sent to the
1110 * effect slot is the same as the dry path, sans filter effects */
1111 RoomRolloff[i] = props->RolloffFactor;
1112 DecayDistance[i] = 0.0f;
1113 DecayHFDistance[i] = 0.0f;
1114 RoomAirAbsorption[i] = AIRABSORBGAINHF;
1117 if(!SendSlots[i])
1119 voice->Send[i].Buffer = NULL;
1120 voice->Send[i].Channels = 0;
1122 else
1124 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1125 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1129 /* Transform source to listener space (convert to head relative) */
1130 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1131 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1132 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1133 if(props->HeadRelative == AL_FALSE)
1135 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1136 /* Transform source vectors */
1137 Position = aluMatrixfVector(Matrix, &Position);
1138 Velocity = aluMatrixfVector(Matrix, &Velocity);
1139 Direction = aluMatrixfVector(Matrix, &Direction);
1141 else
1143 const aluVector *lvelocity = &Listener->Params.Velocity;
1144 /* Offset the source velocity to be relative of the listener velocity */
1145 Velocity.v[0] += lvelocity->v[0];
1146 Velocity.v[1] += lvelocity->v[1];
1147 Velocity.v[2] += lvelocity->v[2];
1150 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1151 SourceToListener.v[0] = -Position.v[0];
1152 SourceToListener.v[1] = -Position.v[1];
1153 SourceToListener.v[2] = -Position.v[2];
1154 SourceToListener.v[3] = 0.0f;
1155 Distance = aluNormalize(SourceToListener.v);
1157 /* Initial source gain */
1158 DryGain = props->Gain;
1159 DryGainHF = 1.0f;
1160 DryGainLF = 1.0f;
1161 for(i = 0;i < NumSends;i++)
1163 WetGain[i] = props->Gain;
1164 WetGainHF[i] = 1.0f;
1165 WetGainLF[i] = 1.0f;
1168 /* Calculate distance attenuation */
1169 ClampedDist = Distance;
1171 switch(Listener->Params.SourceDistanceModel ?
1172 props->DistanceModel : Listener->Params.DistanceModel)
1174 case InverseDistanceClamped:
1175 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1176 if(props->MaxDistance < props->RefDistance)
1177 break;
1178 /*fall-through*/
1179 case InverseDistance:
1180 if(props->RefDistance > 0.0f)
1182 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1183 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1184 for(i = 0;i < NumSends;i++)
1186 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1187 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1190 break;
1192 case LinearDistanceClamped:
1193 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1194 if(props->MaxDistance < props->RefDistance)
1195 break;
1196 /*fall-through*/
1197 case LinearDistance:
1198 if(props->MaxDistance != props->RefDistance)
1200 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1201 (props->MaxDistance-props->RefDistance);
1202 DryGain *= maxf(1.0f - attn, 0.0f);
1203 for(i = 0;i < NumSends;i++)
1205 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1206 (props->MaxDistance-props->RefDistance);
1207 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1210 break;
1212 case ExponentDistanceClamped:
1213 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1214 if(props->MaxDistance < props->RefDistance)
1215 break;
1216 /*fall-through*/
1217 case ExponentDistance:
1218 if(ClampedDist > 0.0f && props->RefDistance > 0.0f)
1220 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1221 for(i = 0;i < NumSends;i++)
1222 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1224 break;
1226 case DisableDistance:
1227 ClampedDist = props->RefDistance;
1228 break;
1231 /* Distance-based air absorption */
1232 if(ClampedDist > props->RefDistance)
1234 ALfloat meters_base = (ClampedDist-props->RefDistance) * Listener->Params.MetersPerUnit;
1235 if(props->AirAbsorptionFactor > 0.0f)
1237 ALfloat absorb = props->AirAbsorptionFactor * meters_base;
1238 DryGainHF *= powf(AIRABSORBGAINHF, absorb*props->RolloffFactor);
1239 for(i = 0;i < NumSends;i++)
1241 if(RoomRolloff[i] > 0.0f)
1242 WetGainHF[i] *= powf(RoomAirAbsorption[i], absorb*RoomRolloff[i]);
1246 if(props->WetGainAuto)
1248 meters_base *= props->RolloffFactor;
1250 /* Apply a decay-time transformation to the wet path, based on the
1251 * source distance in meters. The initial decay of the reverb
1252 * effect is calculated and applied to the wet path.
1254 for(i = 0;i < NumSends;i++)
1256 ALfloat gain;
1258 if(!(DecayDistance[i] > 0.0f))
1259 continue;
1261 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1262 WetGain[i] *= gain;
1263 /* Yes, the wet path's air absorption is applied with
1264 * WetGainAuto on, rather than WetGainHFAuto.
1266 if(gain > 0.0f)
1268 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1269 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1275 /* Calculate directional soundcones */
1276 if(directional && props->InnerAngle < 360.0f)
1278 ALfloat ConeVolume;
1279 ALfloat ConeHF;
1280 ALfloat Angle;
1282 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1283 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1284 if(!(Angle > props->InnerAngle))
1286 ConeVolume = 1.0f;
1287 ConeHF = 1.0f;
1289 else if(Angle < props->OuterAngle)
1291 ALfloat scale = ( Angle-props->InnerAngle) /
1292 (props->OuterAngle-props->InnerAngle);
1293 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1294 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1296 else
1298 ConeVolume = props->OuterGain;
1299 ConeHF = props->OuterGainHF;
1302 DryGain *= ConeVolume;
1303 if(props->DryGainHFAuto)
1304 DryGainHF *= ConeHF;
1305 if(props->WetGainAuto)
1307 for(i = 0;i < NumSends;i++)
1308 WetGain[i] *= ConeVolume;
1310 if(props->WetGainHFAuto)
1312 for(i = 0;i < NumSends;i++)
1313 WetGainHF[i] *= ConeHF;
1317 /* Apply gain and frequency filters */
1318 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1319 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1320 DryGainHF *= props->Direct.GainHF;
1321 DryGainLF *= props->Direct.GainLF;
1322 for(i = 0;i < NumSends;i++)
1324 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1325 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1326 WetGainHF[i] *= props->Send[i].GainHF;
1327 WetGainLF[i] *= props->Send[i].GainLF;
1331 /* Initial source pitch */
1332 Pitch = props->Pitch;
1334 /* Calculate velocity-based doppler effect */
1335 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1336 if(DopplerFactor > 0.0f)
1338 const aluVector *lvelocity = &Listener->Params.Velocity;
1339 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1340 ALfloat vss, vls;
1342 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1343 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1345 if(!(vls < SpeedOfSound))
1347 /* Listener moving away from the source at the speed of sound.
1348 * Sound waves can't catch it.
1350 Pitch = 0.0f;
1352 else if(!(vss < SpeedOfSound))
1354 /* Source moving toward the listener at the speed of sound. Sound
1355 * waves bunch up to extreme frequencies.
1357 Pitch = HUGE_VALF;
1359 else
1361 /* Source and listener movement is nominal. Calculate the proper
1362 * doppler shift.
1364 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1368 /* Adjust pitch based on the buffer and output frequencies, and calculate
1369 * fixed-point stepping value.
1371 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1372 if(Pitch > (ALfloat)MAX_PITCH)
1373 voice->Step = MAX_PITCH<<FRACTIONBITS;
1374 else
1375 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1376 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1377 voice->Resampler = SelectResampler(props->Resampler);
1379 if(Distance > FLT_EPSILON)
1381 dir[0] = -SourceToListener.v[0];
1382 /* Clamp Y, in case rounding errors caused it to end up outside of
1383 * -1...+1.
1385 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1386 dir[2] = -SourceToListener.v[2] * ZScale;
1388 else
1390 dir[0] = 0.0f;
1391 dir[1] = 0.0f;
1392 dir[2] = -1.0f;
1394 if(props->Radius > Distance)
1395 spread = F_TAU - Distance/props->Radius*F_PI;
1396 else if(Distance > FLT_EPSILON)
1397 spread = asinf(props->Radius / Distance) * 2.0f;
1398 else
1399 spread = 0.0f;
1401 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1402 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1405 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1407 ALbufferlistitem *BufferListItem;
1408 struct ALvoiceProps *props;
1410 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1411 if(!props && !force) return;
1413 if(props)
1415 memcpy(voice->Props, props,
1416 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1419 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &voice->FreeList, props);
1421 props = voice->Props;
1423 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1424 while(BufferListItem != NULL)
1426 const ALbuffer *buffer;
1427 if((buffer=BufferListItem->buffer) != NULL)
1429 if(props->SpatializeMode == SpatializeOn ||
1430 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1431 CalcAttnSourceParams(voice, props, buffer, context);
1432 else
1433 CalcNonAttnSourceParams(voice, props, buffer, context);
1434 break;
1436 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1441 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1443 ALvoice **voice, **voice_end;
1444 ALsource *source;
1445 ALsizei i;
1447 IncrementRef(&ctx->UpdateCount);
1448 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1450 ALboolean force = CalcListenerParams(ctx);
1451 for(i = 0;i < slots->count;i++)
1452 force |= CalcEffectSlotParams(slots->slot[i], ctx->Device);
1454 voice = ctx->Voices;
1455 voice_end = voice + ctx->VoiceCount;
1456 for(;voice != voice_end;++voice)
1458 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1459 if(source) CalcSourceParams(*voice, ctx, force);
1462 IncrementRef(&ctx->UpdateCount);
1466 static_assert(LIMITER_VALUE_MAX < (UINT_MAX/LIMITER_WINDOW_SIZE), "LIMITER_VALUE_MAX is too big");
1468 static void ApplyLimiter(struct OutputLimiter *Limiter,
1469 ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALsizei NumChans,
1470 const ALfloat AttackRate, const ALfloat ReleaseRate,
1471 ALfloat *restrict Values, const ALsizei SamplesToDo)
1473 bool do_limit = false;
1474 ALsizei c, i;
1476 OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
1477 Values = ASSUME_ALIGNED(Values, 16);
1479 for(i = 0;i < SamplesToDo;i++)
1480 Values[i] = 0.0f;
1482 /* First, find the maximum amplitude (squared) for each sample position in each channel. */
1483 for(c = 0;c < NumChans;c++)
1485 for(i = 0;i < SamplesToDo;i++)
1487 ALfloat amp = OutBuffer[c][i];
1488 Values[i] = maxf(Values[i], amp*amp);
1492 /* Next, calculate the gains needed to limit the output. */
1494 ALfloat lastgain = Limiter->Gain;
1495 ALsizei wpos = Limiter->Pos;
1496 ALuint sum = Limiter->SquaredSum;
1497 ALfloat gain, rms;
1499 for(i = 0;i < SamplesToDo;i++)
1501 sum -= Limiter->Window[wpos];
1502 Limiter->Window[wpos] = fastf2u(minf(Values[i]*65536.0f, LIMITER_VALUE_MAX));
1503 sum += Limiter->Window[wpos];
1505 rms = sqrtf((ALfloat)sum / ((ALfloat)LIMITER_WINDOW_SIZE*65536.0f));
1507 /* Clamp the minimum RMS to 0dB. The uint used for the squared sum
1508 * inherently limits the maximum RMS to about 21dB, thus the gain
1509 * ranges from 0dB to -21dB.
1511 gain = 1.0f / maxf(rms, 1.0f);
1512 if(lastgain >= gain)
1513 lastgain = maxf(lastgain*AttackRate, gain);
1514 else
1515 lastgain = minf(lastgain/ReleaseRate, gain);
1516 do_limit |= (lastgain < 1.0f);
1517 Values[i] = lastgain;
1519 wpos = (wpos+1)&LIMITER_WINDOW_MASK;
1522 Limiter->Gain = lastgain;
1523 Limiter->Pos = wpos;
1524 Limiter->SquaredSum = sum;
1526 if(do_limit)
1528 /* Finally, apply the gains to each channel. */
1529 for(c = 0;c < NumChans;c++)
1531 for(i = 0;i < SamplesToDo;i++)
1532 OutBuffer[c][i] *= Values[i];
1537 static inline ALfloat aluF2F(ALfloat val)
1538 { return val; }
1540 #define S25_MAX_NORM (16777215.0f/16777216.0f)
1541 static inline ALint aluF2I(ALfloat val)
1543 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1544 * integer range normalized floats can be safely converted to (a bit of the
1545 * exponent helps out, effectively giving 25 bits).
1547 return fastf2i(clampf(val, -1.0f, S25_MAX_NORM)*16777216.0f)<<7;
1549 static inline ALuint aluF2UI(ALfloat val)
1550 { return aluF2I(val)+2147483648u; }
1552 #define S16_MAX_NORM (32767.0f/32768.0f)
1553 static inline ALshort aluF2S(ALfloat val)
1554 { return fastf2i(clampf(val, -1.0f, S16_MAX_NORM)*32768.0f); }
1555 static inline ALushort aluF2US(ALfloat val)
1556 { return aluF2S(val)+32768; }
1558 #define S8_MAX_NORM (127.0f/128.0f)
1559 static inline ALbyte aluF2B(ALfloat val)
1560 { return fastf2i(clampf(val, -1.0f, S8_MAX_NORM)*128.0f); }
1561 static inline ALubyte aluF2UB(ALfloat val)
1562 { return aluF2B(val)+128; }
1564 #define DECL_TEMPLATE(T, func) \
1565 static void Write_##T(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1566 DistanceComp *distcomp, ALsizei SamplesToDo, \
1567 ALsizei numchans) \
1569 ALsizei i, j; \
1570 for(j = 0;j < numchans;j++) \
1572 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1573 T *restrict out = (T*)OutBuffer + j; \
1574 const ALfloat gain = distcomp[j].Gain; \
1575 const ALsizei base = distcomp[j].Length; \
1576 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[j].Buffer, 16); \
1577 if(base > 0 || gain != 1.0f) \
1579 if(SamplesToDo >= base) \
1581 for(i = 0;i < base;i++) \
1582 out[i*numchans] = func(distbuf[i]*gain); \
1583 for(;i < SamplesToDo;i++) \
1584 out[i*numchans] = func(in[i-base]*gain); \
1585 memcpy(distbuf, &in[SamplesToDo-base], base*sizeof(ALfloat)); \
1587 else \
1589 for(i = 0;i < SamplesToDo;i++) \
1590 out[i*numchans] = func(distbuf[i]*gain); \
1591 memmove(distbuf, distbuf+SamplesToDo, \
1592 (base-SamplesToDo)*sizeof(ALfloat)); \
1593 memcpy(distbuf+base-SamplesToDo, in, \
1594 SamplesToDo*sizeof(ALfloat)); \
1597 else for(i = 0;i < SamplesToDo;i++) \
1598 out[i*numchans] = func(in[i]); \
1602 DECL_TEMPLATE(ALfloat, aluF2F)
1603 DECL_TEMPLATE(ALuint, aluF2UI)
1604 DECL_TEMPLATE(ALint, aluF2I)
1605 DECL_TEMPLATE(ALushort, aluF2US)
1606 DECL_TEMPLATE(ALshort, aluF2S)
1607 DECL_TEMPLATE(ALubyte, aluF2UB)
1608 DECL_TEMPLATE(ALbyte, aluF2B)
1610 #undef DECL_TEMPLATE
1613 void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1615 ALsizei SamplesToDo;
1616 ALvoice **voice, **voice_end;
1617 ALeffectslot *slot;
1618 ALsource *source;
1619 ALCcontext *ctx;
1620 FPUCtl oldMode;
1621 ALsizei i, c;
1623 SetMixerFPUMode(&oldMode);
1625 while(size > 0)
1627 SamplesToDo = mini(size, BUFFERSIZE);
1628 for(c = 0;c < device->Dry.NumChannels;c++)
1629 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1630 if(device->Dry.Buffer != device->FOAOut.Buffer)
1631 for(c = 0;c < device->FOAOut.NumChannels;c++)
1632 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1633 if(device->Dry.Buffer != device->RealOut.Buffer)
1634 for(c = 0;c < device->RealOut.NumChannels;c++)
1635 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1637 IncrementRef(&device->MixCount);
1639 if((slot=device->DefaultSlot) != NULL)
1641 CalcEffectSlotParams(device->DefaultSlot, device);
1642 for(c = 0;c < slot->NumChannels;c++)
1643 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1646 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1647 while(ctx)
1649 const struct ALeffectslotArray *auxslots;
1651 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1652 UpdateContextSources(ctx, auxslots);
1654 for(i = 0;i < auxslots->count;i++)
1656 ALeffectslot *slot = auxslots->slot[i];
1657 for(c = 0;c < slot->NumChannels;c++)
1658 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1661 /* source processing */
1662 voice = ctx->Voices;
1663 voice_end = voice + ctx->VoiceCount;
1664 for(;voice != voice_end;++voice)
1666 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1667 if(source && ATOMIC_LOAD(&(*voice)->Playing, almemory_order_relaxed) &&
1668 (*voice)->Step > 0)
1670 if(!MixSource(*voice, source, device, SamplesToDo))
1672 ATOMIC_STORE(&(*voice)->Source, NULL, almemory_order_relaxed);
1673 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1678 /* effect slot processing */
1679 for(i = 0;i < auxslots->count;i++)
1681 const ALeffectslot *slot = auxslots->slot[i];
1682 ALeffectState *state = slot->Params.EffectState;
1683 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1684 state->OutChannels);
1687 ctx = ctx->next;
1690 if(device->DefaultSlot != NULL)
1692 const ALeffectslot *slot = device->DefaultSlot;
1693 ALeffectState *state = slot->Params.EffectState;
1694 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1695 state->OutChannels);
1698 /* Increment the clock time. Every second's worth of samples is
1699 * converted and added to clock base so that large sample counts don't
1700 * overflow during conversion. This also guarantees an exact, stable
1701 * conversion. */
1702 device->SamplesDone += SamplesToDo;
1703 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1704 device->SamplesDone %= device->Frequency;
1705 IncrementRef(&device->MixCount);
1707 if(device->HrtfHandle)
1709 HrtfDirectMixerFunc HrtfMix;
1710 DirectHrtfState *state;
1711 int lidx, ridx;
1713 if(device->AmbiUp)
1714 ambiup_process(device->AmbiUp,
1715 device->Dry.Buffer, device->Dry.NumChannels,
1716 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1719 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1720 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1721 assert(lidx != -1 && ridx != -1);
1723 HrtfMix = SelectHrtfMixer();
1724 state = device->Hrtf;
1725 for(c = 0;c < device->Dry.NumChannels;c++)
1727 HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1728 device->Dry.Buffer[c], state->Offset, state->IrSize,
1729 SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
1730 state->Chan[c].Values, SamplesToDo
1733 state->Offset += SamplesToDo;
1735 else if(device->AmbiDecoder)
1737 if(device->Dry.Buffer != device->FOAOut.Buffer)
1738 bformatdec_upSample(device->AmbiDecoder,
1739 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1740 device->FOAOut.NumChannels, SamplesToDo
1742 bformatdec_process(device->AmbiDecoder,
1743 device->RealOut.Buffer, device->RealOut.NumChannels,
1744 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1747 else if(device->AmbiUp)
1749 ambiup_process(device->AmbiUp,
1750 device->RealOut.Buffer, device->RealOut.NumChannels,
1751 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1754 else if(device->Uhj_Encoder)
1756 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1757 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1758 if(lidx != -1 && ridx != -1)
1760 /* Encode to stereo-compatible 2-channel UHJ output. */
1761 EncodeUhj2(device->Uhj_Encoder,
1762 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1763 device->Dry.Buffer, SamplesToDo
1767 else if(device->Bs2b)
1769 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1770 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1771 if(lidx != -1 && ridx != -1)
1773 /* Apply binaural/crossfeed filter */
1774 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1775 device->RealOut.Buffer[ridx], SamplesToDo);
1779 if(buffer)
1781 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1782 ALsizei OutChannels = device->RealOut.NumChannels;
1783 struct OutputLimiter *Limiter = device->Limiter;
1784 DistanceComp *DistComp;
1786 if(Limiter)
1788 const ALfloat AttackRate = powf(0.0001f, 1.0f/(device->Frequency*Limiter->AttackRate));
1789 const ALfloat ReleaseRate = powf(0.0001f, 1.0f/(device->Frequency*Limiter->ReleaseRate));
1791 /* Use NFCtrlData for temp value storage. */
1792 ApplyLimiter(Limiter, OutBuffer, OutChannels,
1793 AttackRate, ReleaseRate, device->NFCtrlData, SamplesToDo
1797 DistComp = device->ChannelDelay;
1798 #define WRITE(T, a, b, c, d, e) do { \
1799 Write_##T(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \
1800 buffer = (T*)buffer + (d)*(e); \
1801 } while(0)
1802 switch(device->FmtType)
1804 case DevFmtByte:
1805 WRITE(ALbyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1806 break;
1807 case DevFmtUByte:
1808 WRITE(ALubyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1809 break;
1810 case DevFmtShort:
1811 WRITE(ALshort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1812 break;
1813 case DevFmtUShort:
1814 WRITE(ALushort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1815 break;
1816 case DevFmtInt:
1817 WRITE(ALint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1818 break;
1819 case DevFmtUInt:
1820 WRITE(ALuint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1821 break;
1822 case DevFmtFloat:
1823 WRITE(ALfloat, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1824 break;
1826 #undef WRITE
1829 size -= SamplesToDo;
1832 RestoreFPUMode(&oldMode);
1836 void aluHandleDisconnect(ALCdevice *device)
1838 ALCcontext *Context;
1840 device->Connected = ALC_FALSE;
1842 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1843 while(Context)
1845 ALvoice **voice, **voice_end;
1847 voice = Context->Voices;
1848 voice_end = voice + Context->VoiceCount;
1849 while(voice != voice_end)
1851 ALsource *source = ATOMIC_EXCHANGE_PTR(&(*voice)->Source, NULL,
1852 almemory_order_acq_rel);
1853 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1855 if(source)
1857 ALenum playing = AL_PLAYING;
1858 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1861 voice++;
1863 Context->VoiceCount = 0;
1865 Context = Context->next;