Make some pointer-to-array parameters const
[openal-soft.git] / Alc / ALu.c
blob5bcf039ea0fc7434b7c4b0fbdd21a02565da1271
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, ALuint frac);
84 extern inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat val5, ALfloat val6, ALfloat val7, ALuint frac);
86 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
88 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
89 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
90 extern inline void aluMatrixfSet(aluMatrixf *matrix,
91 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
92 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
93 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
94 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
96 const aluMatrixf IdentityMatrixf = {{
97 { 1.0f, 0.0f, 0.0f, 0.0f },
98 { 0.0f, 1.0f, 0.0f, 0.0f },
99 { 0.0f, 0.0f, 1.0f, 0.0f },
100 { 0.0f, 0.0f, 0.0f, 1.0f },
104 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
106 #ifdef HAVE_SSE
107 if((CPUCapFlags&CPU_CAP_SSE))
108 return MixDirectHrtf_SSE;
109 #endif
110 #ifdef HAVE_NEON
111 if((CPUCapFlags&CPU_CAP_NEON))
112 return MixDirectHrtf_Neon;
113 #endif
115 return MixDirectHrtf_C;
119 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
121 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
122 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
123 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
126 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
128 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
131 static ALfloat aluNormalize(ALfloat *vec)
133 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
134 if(length > 0.0f)
136 ALfloat inv_length = 1.0f/length;
137 vec[0] *= inv_length;
138 vec[1] *= inv_length;
139 vec[2] *= inv_length;
141 return length;
144 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
146 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
148 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];
149 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];
150 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];
153 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
155 aluVector v;
156 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];
157 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];
158 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];
159 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];
160 return v;
164 /* Prepares the interpolator for a given rate (determined by increment). A
165 * result of AL_FALSE indicates that the filter output will completely cut
166 * the input signal.
168 * With a bit of work, and a trade of memory for CPU cost, this could be
169 * modified for use with an interpolated increment for buttery-smooth pitch
170 * changes.
172 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
174 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
175 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
176 static const ALuint to[4][BSINC_SCALE_COUNT] =
178 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
179 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
180 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
181 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
183 static const ALuint tm[2][BSINC_SCALE_COUNT] =
185 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
186 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
188 ALfloat sf;
189 ALuint si, pi;
190 ALboolean uncut = AL_TRUE;
192 if(increment > FRACTIONONE)
194 sf = (ALfloat)FRACTIONONE / increment;
195 if(sf < scaleBase)
197 /* Signal has been completely cut. The return result can be used
198 * to skip the filter (and output zeros) as an optimization.
200 sf = 0.0f;
201 si = 0;
202 uncut = AL_FALSE;
204 else
206 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
207 si = fastf2u(sf);
208 /* The interpolation factor is fit to this diagonally-symmetric
209 * curve to reduce the transition ripple caused by interpolating
210 * different scales of the sinc function.
212 sf = 1.0f - cosf(asinf(sf - si));
215 else
217 sf = 0.0f;
218 si = BSINC_SCALE_COUNT - 1;
221 state->sf = sf;
222 state->m = m[si];
223 state->l = -(ALint)((m[si] / 2) - 1);
224 /* The CPU cost of this table re-mapping could be traded for the memory
225 * cost of a complete table map (1024 elements large).
227 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
229 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
230 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
231 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
232 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
234 return uncut;
238 static ALboolean CalcListenerParams(ALCcontext *Context)
240 ALlistener *Listener = Context->Listener;
241 ALfloat N[3], V[3], U[3], P[3];
242 struct ALlistenerProps *first;
243 struct ALlistenerProps *props;
244 aluVector vel;
246 props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &Listener->Update, NULL, almemory_order_acq_rel);
247 if(!props) return AL_FALSE;
249 /* AT then UP */
250 N[0] = ATOMIC_LOAD(&props->Forward[0], almemory_order_relaxed);
251 N[1] = ATOMIC_LOAD(&props->Forward[1], almemory_order_relaxed);
252 N[2] = ATOMIC_LOAD(&props->Forward[2], almemory_order_relaxed);
253 aluNormalize(N);
254 V[0] = ATOMIC_LOAD(&props->Up[0], almemory_order_relaxed);
255 V[1] = ATOMIC_LOAD(&props->Up[1], almemory_order_relaxed);
256 V[2] = ATOMIC_LOAD(&props->Up[2], almemory_order_relaxed);
257 aluNormalize(V);
258 /* Build and normalize right-vector */
259 aluCrossproduct(N, V, U);
260 aluNormalize(U);
262 aluMatrixfSet(&Listener->Params.Matrix,
263 U[0], V[0], -N[0], 0.0,
264 U[1], V[1], -N[1], 0.0,
265 U[2], V[2], -N[2], 0.0,
266 0.0, 0.0, 0.0, 1.0
269 P[0] = ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed);
270 P[1] = ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed);
271 P[2] = ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed);
272 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
273 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
275 aluVectorSet(&vel, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
276 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
277 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
278 0.0f);
279 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
281 Listener->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed) * Context->GainBoost;
282 Listener->Params.MetersPerUnit = ATOMIC_LOAD(&props->MetersPerUnit, almemory_order_relaxed);
284 Listener->Params.DopplerFactor = ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
285 Listener->Params.SpeedOfSound = ATOMIC_LOAD(&props->SpeedOfSound, almemory_order_relaxed) *
286 ATOMIC_LOAD(&props->DopplerVelocity, almemory_order_relaxed);
288 Listener->Params.SourceDistanceModel = ATOMIC_LOAD(&props->SourceDistanceModel, almemory_order_relaxed);
289 Listener->Params.DistanceModel = ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed);
291 /* WARNING: A livelock is theoretically possible if another thread keeps
292 * changing the freelist head without giving this a chance to actually swap
293 * in the old container (practically impossible with this little code,
294 * but...).
296 first = ATOMIC_LOAD(&Listener->FreeList);
297 do {
298 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
299 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*,
300 &Listener->FreeList, &first, props) == 0);
302 return AL_TRUE;
305 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
307 struct ALeffectslotProps *first;
308 struct ALeffectslotProps *props;
309 ALeffectState *state;
311 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL, almemory_order_acq_rel);
312 if(!props) return AL_FALSE;
314 slot->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
315 slot->Params.AuxSendAuto = ATOMIC_LOAD(&props->AuxSendAuto, almemory_order_relaxed);
316 slot->Params.EffectType = ATOMIC_LOAD(&props->Type, almemory_order_relaxed);
317 if(IsReverbEffect(slot->Params.EffectType))
319 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
320 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
321 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
323 else
325 slot->Params.RoomRolloff = 0.0f;
326 slot->Params.DecayTime = 0.0f;
327 slot->Params.AirAbsorptionGainHF = 1.0f;
330 /* Swap effect states. No need to play with the ref counts since they keep
331 * the same number of refs.
333 state = ATOMIC_EXCHANGE(ALeffectState*, &props->State, slot->Params.EffectState,
334 almemory_order_relaxed);
335 slot->Params.EffectState = state;
337 V(state,update)(device, slot, &props->Props);
339 /* WARNING: A livelock is theoretically possible if another thread keeps
340 * changing the freelist head without giving this a chance to actually swap
341 * in the old container (practically impossible with this little code,
342 * but...).
344 first = ATOMIC_LOAD(&slot->FreeList);
345 do {
346 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
347 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
348 &slot->FreeList, &first, props) == 0);
350 return AL_TRUE;
354 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
356 static const struct ChanMap MonoMap[1] = {
357 { FrontCenter, 0.0f, 0.0f }
358 }, RearMap[2] = {
359 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
360 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
361 }, QuadMap[4] = {
362 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
363 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
364 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
365 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
366 }, X51Map[6] = {
367 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
368 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
369 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
370 { LFE, 0.0f, 0.0f },
371 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
372 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
373 }, X61Map[7] = {
374 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
375 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
376 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
377 { LFE, 0.0f, 0.0f },
378 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
379 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
380 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
381 }, X71Map[8] = {
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 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
387 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
388 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
389 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
392 const ALCdevice *Device = ALContext->Device;
393 const ALlistener *Listener = ALContext->Listener;
394 ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
395 ALfloat DryGain, DryGainHF, DryGainLF;
396 ALfloat WetGain[MAX_SENDS];
397 ALfloat WetGainHF[MAX_SENDS];
398 ALfloat WetGainLF[MAX_SENDS];
399 ALeffectslot *SendSlots[MAX_SENDS];
400 ALuint NumSends, Frequency;
401 ALboolean Relative;
402 const struct ChanMap *chans = NULL;
403 struct ChanMap StereoMap[2] = {
404 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
405 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
407 ALuint num_channels = 0;
408 ALboolean DirectChannels;
409 ALboolean isbformat = AL_FALSE;
410 ALfloat Pitch;
411 ALuint i, j, c;
413 /* Get device properties */
414 NumSends = Device->NumAuxSends;
415 Frequency = Device->Frequency;
417 /* Get listener properties */
418 ListenerGain = Listener->Params.Gain;
420 /* Get source properties */
421 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
422 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
423 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
424 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
425 Relative = ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed);
426 DirectChannels = ATOMIC_LOAD(&props->DirectChannels, almemory_order_relaxed);
428 /* Convert counter-clockwise to clockwise. */
429 StereoMap[0].angle = -ATOMIC_LOAD(&props->StereoPan[0], almemory_order_relaxed);
430 StereoMap[1].angle = -ATOMIC_LOAD(&props->StereoPan[1], almemory_order_relaxed);
432 voice->DirectOut.Buffer = Device->Dry.Buffer;
433 voice->DirectOut.Channels = Device->Dry.NumChannels;
434 for(i = 0;i < NumSends;i++)
436 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
437 if(!SendSlots[i] && i == 0)
438 SendSlots[i] = Device->DefaultSlot;
439 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
441 SendSlots[i] = NULL;
442 voice->SendOut[i].Buffer = NULL;
443 voice->SendOut[i].Channels = 0;
445 else
447 voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer;
448 voice->SendOut[i].Channels = SendSlots[i]->NumChannels;
452 /* Calculate the stepping value */
453 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
454 if(Pitch > (ALfloat)MAX_PITCH)
455 voice->Step = MAX_PITCH<<FRACTIONBITS;
456 else
457 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
458 BsincPrepare(voice->Step, &voice->SincState);
460 /* Calculate gains */
461 DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
462 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
463 DryGain = minf(DryGain, GAIN_MIX_MAX);
464 DryGainHF = ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
465 DryGainLF = ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
466 for(i = 0;i < NumSends;i++)
468 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
469 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
470 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
471 WetGainHF[i] = ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
472 WetGainLF[i] = ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
475 switch(ALBuffer->FmtChannels)
477 case FmtMono:
478 chans = MonoMap;
479 num_channels = 1;
480 break;
482 case FmtStereo:
483 chans = StereoMap;
484 num_channels = 2;
485 break;
487 case FmtRear:
488 chans = RearMap;
489 num_channels = 2;
490 break;
492 case FmtQuad:
493 chans = QuadMap;
494 num_channels = 4;
495 break;
497 case FmtX51:
498 chans = X51Map;
499 num_channels = 6;
500 break;
502 case FmtX61:
503 chans = X61Map;
504 num_channels = 7;
505 break;
507 case FmtX71:
508 chans = X71Map;
509 num_channels = 8;
510 break;
512 case FmtBFormat2D:
513 num_channels = 3;
514 isbformat = AL_TRUE;
515 DirectChannels = AL_FALSE;
516 break;
518 case FmtBFormat3D:
519 num_channels = 4;
520 isbformat = AL_TRUE;
521 DirectChannels = AL_FALSE;
522 break;
525 if(isbformat)
527 ALfloat N[3], V[3], U[3];
528 aluMatrixf matrix;
529 ALfloat scale;
531 /* AT then UP */
532 N[0] = ATOMIC_LOAD(&props->Orientation[0][0], almemory_order_relaxed);
533 N[1] = ATOMIC_LOAD(&props->Orientation[0][1], almemory_order_relaxed);
534 N[2] = ATOMIC_LOAD(&props->Orientation[0][2], almemory_order_relaxed);
535 aluNormalize(N);
536 V[0] = ATOMIC_LOAD(&props->Orientation[1][0], almemory_order_relaxed);
537 V[1] = ATOMIC_LOAD(&props->Orientation[1][1], almemory_order_relaxed);
538 V[2] = ATOMIC_LOAD(&props->Orientation[1][2], almemory_order_relaxed);
539 aluNormalize(V);
540 if(!Relative)
542 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
543 aluMatrixfFloat3(N, 0.0f, lmatrix);
544 aluMatrixfFloat3(V, 0.0f, lmatrix);
546 /* Build and normalize right-vector */
547 aluCrossproduct(N, V, U);
548 aluNormalize(U);
550 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
551 scale = 1.732050808f;
552 aluMatrixfSet(&matrix,
553 1.414213562f, 0.0f, 0.0f, 0.0f,
554 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
555 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
556 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
559 voice->DirectOut.Buffer = Device->FOAOut.Buffer;
560 voice->DirectOut.Channels = Device->FOAOut.NumChannels;
561 for(c = 0;c < num_channels;c++)
562 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
563 voice->Chan[c].Direct.Gains.Target);
565 for(i = 0;i < NumSends;i++)
567 if(!SendSlots[i])
569 for(c = 0;c < num_channels;c++)
571 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
572 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
575 else
577 for(c = 0;c < num_channels;c++)
579 const ALeffectslot *Slot = SendSlots[i];
580 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, matrix.m[c],
581 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
586 voice->IsHrtf = AL_FALSE;
588 else
590 ALfloat coeffs[MAX_AMBI_COEFFS];
592 if(DirectChannels)
594 /* Skip the virtual channels and write inputs to the real output. */
595 voice->DirectOut.Buffer = Device->RealOut.Buffer;
596 voice->DirectOut.Channels = Device->RealOut.NumChannels;
597 for(c = 0;c < num_channels;c++)
599 int idx;
600 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
601 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
602 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
603 voice->Chan[c].Direct.Gains.Target[idx] = DryGain;
606 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
607 * channel-match. */
608 for(c = 0;c < num_channels;c++)
610 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
612 for(i = 0;i < NumSends;i++)
614 if(!SendSlots[i])
616 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
617 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
619 else
621 const ALeffectslot *Slot = SendSlots[i];
622 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
623 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
628 voice->IsHrtf = AL_FALSE;
630 else if(Device->Render_Mode == HrtfRender)
632 /* Full HRTF rendering. Skip the virtual channels and render each
633 * input channel to the real outputs.
635 voice->DirectOut.Buffer = Device->RealOut.Buffer;
636 voice->DirectOut.Channels = Device->RealOut.NumChannels;
637 for(c = 0;c < num_channels;c++)
639 if(chans[c].channel == LFE)
641 /* Skip LFE */
642 voice->Chan[c].Direct.Hrtf.Target.Delay[0] = 0;
643 voice->Chan[c].Direct.Hrtf.Target.Delay[1] = 0;
644 for(i = 0;i < HRIR_LENGTH;i++)
646 voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][0] = 0.0f;
647 voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][1] = 0.0f;
650 for(i = 0;i < NumSends;i++)
652 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
653 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
656 continue;
659 /* Get the static HRIR coefficients and delays for this channel. */
660 GetLerpedHrtfCoeffs(Device->Hrtf.Handle,
661 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
662 voice->Chan[c].Direct.Hrtf.Target.Coeffs,
663 voice->Chan[c].Direct.Hrtf.Target.Delay
666 /* Normal panning for auxiliary sends. */
667 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
669 for(i = 0;i < NumSends;i++)
671 if(!SendSlots[i])
673 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
674 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
676 else
678 const ALeffectslot *Slot = SendSlots[i];
679 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
680 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
685 voice->IsHrtf = AL_TRUE;
687 else
689 /* Non-HRTF rendering. Use normal panning to the output. */
690 for(c = 0;c < num_channels;c++)
692 /* Special-case LFE */
693 if(chans[c].channel == LFE)
695 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
696 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
697 if(Device->Dry.Buffer == Device->RealOut.Buffer)
699 int idx;
700 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
701 voice->Chan[c].Direct.Gains.Target[idx] = DryGain;
704 for(i = 0;i < NumSends;i++)
706 ALuint j;
707 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
708 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
710 continue;
713 if(Device->Render_Mode == StereoPair)
715 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
716 ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
717 coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
718 voice->Chan[c].Direct.Gains.Target[0] = coeffs[0] * DryGain;
719 voice->Chan[c].Direct.Gains.Target[1] = (1.0f-coeffs[0]) * DryGain;
720 for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
721 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
723 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
725 else
727 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
728 ComputePanningGains(Device->Dry, coeffs, DryGain,
729 voice->Chan[c].Direct.Gains.Target);
732 for(i = 0;i < NumSends;i++)
734 if(!SendSlots[i])
736 ALuint j;
737 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
738 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
740 else
742 const ALeffectslot *Slot = SendSlots[i];
743 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
744 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
749 voice->IsHrtf = AL_FALSE;
754 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
755 Frequency;
756 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
757 Frequency;
758 DryGainHF = maxf(DryGainHF, 0.0001f);
759 DryGainLF = maxf(DryGainLF, 0.0001f);
760 for(c = 0;c < num_channels;c++)
762 voice->Chan[c].Direct.FilterType = AF_None;
763 if(DryGainHF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_LowPass;
764 if(DryGainLF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_HighPass;
765 ALfilterState_setParams(
766 &voice->Chan[c].Direct.LowPass, ALfilterType_HighShelf,
767 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
769 ALfilterState_setParams(
770 &voice->Chan[c].Direct.HighPass, ALfilterType_LowShelf,
771 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
775 for(i = 0;i < NumSends;i++)
777 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
778 Frequency;
779 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
780 Frequency;
781 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
782 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
783 for(c = 0;c < num_channels;c++)
785 voice->Chan[c].Send[i].FilterType = AF_None;
786 if(WetGainHF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_LowPass;
787 if(WetGainLF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_HighPass;
788 ALfilterState_setParams(
789 &voice->Chan[c].Send[i].LowPass, ALfilterType_HighShelf,
790 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
792 ALfilterState_setParams(
793 &voice->Chan[c].Send[i].HighPass, ALfilterType_LowShelf,
794 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
800 static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
802 const ALCdevice *Device = ALContext->Device;
803 const ALlistener *Listener = ALContext->Listener;
804 aluVector Position, Velocity, Direction, SourceToListener;
805 ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
806 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
807 ALfloat SourceVolume,ListenerGain;
808 ALfloat DopplerFactor, SpeedOfSound;
809 ALfloat AirAbsorptionFactor;
810 ALfloat RoomAirAbsorption[MAX_SENDS];
811 ALeffectslot *SendSlots[MAX_SENDS];
812 ALfloat Attenuation;
813 ALfloat RoomAttenuation[MAX_SENDS];
814 ALfloat MetersPerUnit;
815 ALfloat RoomRolloffBase;
816 ALfloat RoomRolloff[MAX_SENDS];
817 ALfloat DecayDistance[MAX_SENDS];
818 ALfloat DryGain;
819 ALfloat DryGainHF;
820 ALfloat DryGainLF;
821 ALboolean DryGainHFAuto;
822 ALfloat WetGain[MAX_SENDS];
823 ALfloat WetGainHF[MAX_SENDS];
824 ALfloat WetGainLF[MAX_SENDS];
825 ALboolean WetGainAuto;
826 ALboolean WetGainHFAuto;
827 ALfloat Pitch;
828 ALuint Frequency;
829 ALint NumSends;
830 ALint i;
832 DryGainHF = 1.0f;
833 DryGainLF = 1.0f;
834 for(i = 0;i < MAX_SENDS;i++)
836 WetGainHF[i] = 1.0f;
837 WetGainLF[i] = 1.0f;
840 /* Get context/device properties */
841 DopplerFactor = Listener->Params.DopplerFactor;
842 SpeedOfSound = Listener->Params.SpeedOfSound;
843 NumSends = Device->NumAuxSends;
844 Frequency = Device->Frequency;
846 /* Get listener properties */
847 ListenerGain = Listener->Params.Gain;
848 MetersPerUnit = Listener->Params.MetersPerUnit;
850 /* Get source properties */
851 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
852 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
853 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
854 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
855 aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed),
856 ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed),
857 ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed),
858 1.0f);
859 aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed),
860 ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed),
861 ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed),
862 0.0f);
863 aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
864 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
865 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
866 0.0f);
867 MinDist = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed);
868 MaxDist = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed);
869 Rolloff = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed);
870 DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
871 InnerAngle = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed);
872 OuterAngle = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed);
873 AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed);
874 DryGainHFAuto = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed);
875 WetGainAuto = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed);
876 WetGainHFAuto = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed);
877 RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed);
879 voice->DirectOut.Buffer = Device->Dry.Buffer;
880 voice->DirectOut.Channels = Device->Dry.NumChannels;
881 for(i = 0;i < NumSends;i++)
883 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
885 if(!SendSlots[i] && i == 0)
886 SendSlots[i] = Device->DefaultSlot;
887 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
889 SendSlots[i] = NULL;
890 RoomRolloff[i] = 0.0f;
891 DecayDistance[i] = 0.0f;
892 RoomAirAbsorption[i] = 1.0f;
894 else if(SendSlots[i]->Params.AuxSendAuto)
896 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
897 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
898 SPEEDOFSOUNDMETRESPERSEC;
899 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
901 else
903 /* If the slot's auxiliary send auto is off, the data sent to the
904 * effect slot is the same as the dry path, sans filter effects */
905 RoomRolloff[i] = Rolloff;
906 DecayDistance[i] = 0.0f;
907 RoomAirAbsorption[i] = AIRABSORBGAINHF;
910 if(!SendSlots[i])
912 voice->SendOut[i].Buffer = NULL;
913 voice->SendOut[i].Channels = 0;
915 else
917 voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer;
918 voice->SendOut[i].Channels = SendSlots[i]->NumChannels;
922 /* Transform source to listener space (convert to head relative) */
923 if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE)
925 const aluMatrixf *Matrix = &Listener->Params.Matrix;
926 /* Transform source vectors */
927 Position = aluMatrixfVector(Matrix, &Position);
928 Velocity = aluMatrixfVector(Matrix, &Velocity);
929 Direction = aluMatrixfVector(Matrix, &Direction);
931 else
933 const aluVector *lvelocity = &Listener->Params.Velocity;
934 /* Offset the source velocity to be relative of the listener velocity */
935 Velocity.v[0] += lvelocity->v[0];
936 Velocity.v[1] += lvelocity->v[1];
937 Velocity.v[2] += lvelocity->v[2];
940 aluNormalize(Direction.v);
941 SourceToListener.v[0] = -Position.v[0];
942 SourceToListener.v[1] = -Position.v[1];
943 SourceToListener.v[2] = -Position.v[2];
944 SourceToListener.v[3] = 0.0f;
945 Distance = aluNormalize(SourceToListener.v);
947 /* Calculate distance attenuation */
948 ClampedDist = Distance;
950 Attenuation = 1.0f;
951 for(i = 0;i < NumSends;i++)
952 RoomAttenuation[i] = 1.0f;
953 switch(Listener->Params.SourceDistanceModel ?
954 ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed) :
955 Listener->Params.DistanceModel)
957 case InverseDistanceClamped:
958 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
959 if(MaxDist < MinDist)
960 break;
961 /*fall-through*/
962 case InverseDistance:
963 if(MinDist > 0.0f)
965 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
966 if(dist > 0.0f) Attenuation = MinDist / dist;
967 for(i = 0;i < NumSends;i++)
969 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
970 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
973 break;
975 case LinearDistanceClamped:
976 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
977 if(MaxDist < MinDist)
978 break;
979 /*fall-through*/
980 case LinearDistance:
981 if(MaxDist != MinDist)
983 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
984 Attenuation = maxf(Attenuation, 0.0f);
985 for(i = 0;i < NumSends;i++)
987 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
988 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
991 break;
993 case ExponentDistanceClamped:
994 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
995 if(MaxDist < MinDist)
996 break;
997 /*fall-through*/
998 case ExponentDistance:
999 if(ClampedDist > 0.0f && MinDist > 0.0f)
1001 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
1002 for(i = 0;i < NumSends;i++)
1003 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
1005 break;
1007 case DisableDistance:
1008 ClampedDist = MinDist;
1009 break;
1012 /* Source Gain + Attenuation */
1013 DryGain = SourceVolume * Attenuation;
1014 for(i = 0;i < NumSends;i++)
1015 WetGain[i] = SourceVolume * RoomAttenuation[i];
1017 /* Distance-based air absorption */
1018 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
1020 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
1021 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
1022 for(i = 0;i < NumSends;i++)
1023 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
1026 if(WetGainAuto)
1028 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1030 /* Apply a decay-time transformation to the wet path, based on the
1031 * attenuation of the dry path.
1033 * Using the apparent distance, based on the distance attenuation, the
1034 * initial decay of the reverb effect is calculated and applied to the
1035 * wet path.
1037 for(i = 0;i < NumSends;i++)
1039 if(DecayDistance[i] > 0.0f)
1040 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1044 /* Calculate directional soundcones */
1045 if(InnerAngle < 360.0f)
1047 ALfloat ConeVolume;
1048 ALfloat ConeHF;
1049 ALfloat Angle;
1050 ALfloat scale;
1052 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1053 if(Angle > InnerAngle)
1055 if(Angle < OuterAngle)
1057 scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1058 ConeVolume = lerp(
1059 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1061 ConeHF = lerp(
1062 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1065 else
1067 ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed);
1068 ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed);
1070 DryGain *= ConeVolume;
1071 if(DryGainHFAuto)
1072 DryGainHF *= ConeHF;
1075 /* Wet path uses the total area of the cone emitter (the room will
1076 * receive the same amount of sound regardless of its direction).
1078 scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
1079 (InnerAngle/360.0f);
1080 if(WetGainAuto)
1082 ConeVolume = lerp(
1083 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1085 for(i = 0;i < NumSends;i++)
1086 WetGain[i] *= ConeVolume;
1088 if(WetGainHFAuto)
1090 ConeHF = lerp(
1091 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1093 for(i = 0;i < NumSends;i++)
1094 WetGainHF[i] *= ConeHF;
1098 /* Apply gain and frequency filters */
1099 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1100 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
1101 DryGain = minf(DryGain, GAIN_MIX_MAX);
1102 DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
1103 DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
1104 for(i = 0;i < NumSends;i++)
1106 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1107 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
1108 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1109 WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
1110 WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
1113 /* Calculate velocity-based doppler effect */
1114 if(DopplerFactor > 0.0f)
1116 const aluVector *lvelocity = &Listener->Params.Velocity;
1117 ALfloat VSS, VLS;
1119 if(SpeedOfSound < 1.0f)
1121 DopplerFactor *= 1.0f/SpeedOfSound;
1122 SpeedOfSound = 1.0f;
1125 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1126 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1128 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1129 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1132 /* Calculate fixed-point stepping value, based on the pitch, buffer
1133 * frequency, and output frequency.
1135 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1136 if(Pitch > (ALfloat)MAX_PITCH)
1137 voice->Step = MAX_PITCH<<FRACTIONBITS;
1138 else
1139 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1140 BsincPrepare(voice->Step, &voice->SincState);
1142 if(Device->Render_Mode == HrtfRender)
1144 /* Full HRTF rendering. Skip the virtual channels and render to the
1145 * real outputs.
1147 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1148 ALfloat ev = 0.0f, az = 0.0f;
1149 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1150 ALfloat coeffs[MAX_AMBI_COEFFS];
1151 ALfloat spread = 0.0f;
1153 voice->DirectOut.Buffer = Device->RealOut.Buffer;
1154 voice->DirectOut.Channels = Device->RealOut.NumChannels;
1156 if(Distance > FLT_EPSILON)
1158 dir[0] = -SourceToListener.v[0];
1159 dir[1] = -SourceToListener.v[1];
1160 dir[2] = -SourceToListener.v[2] * ZScale;
1162 /* Calculate elevation and azimuth only when the source is not at
1163 * the listener. This prevents +0 and -0 Z from producing
1164 * inconsistent panning. Also, clamp Y in case FP precision errors
1165 * cause it to land outside of -1..+1. */
1166 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1167 az = atan2f(dir[0], -dir[2]);
1169 if(radius > Distance)
1170 spread = F_TAU - Distance/radius*F_PI;
1171 else if(Distance > FLT_EPSILON)
1172 spread = asinf(radius / Distance) * 2.0f;
1174 /* Get the HRIR coefficients and delays. */
1175 GetLerpedHrtfCoeffs(Device->Hrtf.Handle, ev, az, spread, DryGain,
1176 voice->Chan[0].Direct.Hrtf.Target.Coeffs,
1177 voice->Chan[0].Direct.Hrtf.Target.Delay);
1179 CalcDirectionCoeffs(dir, spread, coeffs);
1181 for(i = 0;i < NumSends;i++)
1183 if(!SendSlots[i])
1185 ALuint j;
1186 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1187 voice->Chan[0].Send[i].Gains.Target[j] = 0.0f;
1189 else
1191 const ALeffectslot *Slot = SendSlots[i];
1192 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1193 WetGain[i], voice->Chan[0].Send[i].Gains.Target);
1197 voice->IsHrtf = AL_TRUE;
1199 else
1201 /* Non-HRTF rendering. */
1202 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1203 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1204 ALfloat coeffs[MAX_AMBI_COEFFS];
1205 ALfloat spread = 0.0f;
1207 /* Get the localized direction, and compute panned gains. */
1208 if(Distance > FLT_EPSILON)
1210 dir[0] = -SourceToListener.v[0];
1211 dir[1] = -SourceToListener.v[1];
1212 dir[2] = -SourceToListener.v[2] * ZScale;
1214 if(radius > Distance)
1215 spread = F_TAU - Distance/radius*F_PI;
1216 else if(Distance > FLT_EPSILON)
1217 spread = asinf(radius / Distance) * 2.0f;
1219 if(Device->Render_Mode == StereoPair)
1221 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
1222 ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
1223 x = clampf(x, -0.5f, 0.5f) + 0.5f;
1224 voice->Chan[0].Direct.Gains.Target[0] = x * DryGain;
1225 voice->Chan[0].Direct.Gains.Target[1] = (1.0f-x) * DryGain;
1226 for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
1227 voice->Chan[0].Direct.Gains.Target[i] = 0.0f;
1229 CalcDirectionCoeffs(dir, spread, coeffs);
1231 else
1233 CalcDirectionCoeffs(dir, spread, coeffs);
1234 ComputePanningGains(Device->Dry, coeffs, DryGain,
1235 voice->Chan[0].Direct.Gains.Target);
1238 for(i = 0;i < NumSends;i++)
1240 if(!SendSlots[i])
1242 ALuint j;
1243 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1244 voice->Chan[0].Send[i].Gains.Target[j] = 0.0f;
1246 else
1248 const ALeffectslot *Slot = SendSlots[i];
1249 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1250 WetGain[i], voice->Chan[0].Send[i].Gains.Target);
1254 voice->IsHrtf = AL_FALSE;
1258 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
1259 Frequency;
1260 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
1261 Frequency;
1262 DryGainHF = maxf(DryGainHF, 0.0001f);
1263 DryGainLF = maxf(DryGainLF, 0.0001f);
1264 voice->Chan[0].Direct.FilterType = AF_None;
1265 if(DryGainHF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_LowPass;
1266 if(DryGainLF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_HighPass;
1267 ALfilterState_setParams(
1268 &voice->Chan[0].Direct.LowPass, ALfilterType_HighShelf,
1269 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
1271 ALfilterState_setParams(
1272 &voice->Chan[0].Direct.HighPass, ALfilterType_LowShelf,
1273 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
1276 for(i = 0;i < NumSends;i++)
1278 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
1279 Frequency;
1280 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
1281 Frequency;
1282 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
1283 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
1284 voice->Chan[0].Send[i].FilterType = AF_None;
1285 if(WetGainHF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_LowPass;
1286 if(WetGainLF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_HighPass;
1287 ALfilterState_setParams(
1288 &voice->Chan[0].Send[i].LowPass, ALfilterType_HighShelf,
1289 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
1291 ALfilterState_setParams(
1292 &voice->Chan[0].Send[i].HighPass, ALfilterType_LowShelf,
1293 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
1298 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1300 ALsource *source = voice->Source;
1301 const ALbufferlistitem *BufferListItem;
1302 struct ALsourceProps *first;
1303 struct ALsourceProps *props;
1305 props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
1306 if(!props && !force) return;
1308 if(props)
1310 voice->Props = *props;
1312 /* WARNING: A livelock is theoretically possible if another thread
1313 * keeps changing the freelist head without giving this a chance to
1314 * actually swap in the old container (practically impossible with this
1315 * little code, but...).
1317 first = ATOMIC_LOAD(&source->FreeList);
1318 do {
1319 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
1320 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*,
1321 &source->FreeList, &first, props) == 0);
1324 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1325 while(BufferListItem != NULL)
1327 const ALbuffer *buffer;
1328 if((buffer=BufferListItem->buffer) != NULL)
1330 if(buffer->FmtChannels == FmtMono)
1331 CalcAttnSourceParams(voice, &voice->Props, buffer, context);
1332 else
1333 CalcNonAttnSourceParams(voice, &voice->Props, buffer, context);
1334 break;
1336 BufferListItem = BufferListItem->next;
1341 static void UpdateContextSources(ALCcontext *ctx, ALeffectslot *slot)
1343 ALvoice *voice, *voice_end;
1344 ALsource *source;
1346 IncrementRef(&ctx->UpdateCount);
1347 if(!ATOMIC_LOAD(&ctx->HoldUpdates))
1349 ALboolean force = CalcListenerParams(ctx);
1350 while(slot)
1352 force |= CalcEffectSlotParams(slot, ctx->Device);
1353 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1356 voice = ctx->Voices;
1357 voice_end = voice + ctx->VoiceCount;
1358 for(;voice != voice_end;++voice)
1360 if(!(source=voice->Source)) continue;
1361 if(source->state != AL_PLAYING && source->state != AL_PAUSED)
1362 voice->Source = NULL;
1363 else
1364 CalcSourceParams(voice, ctx, force);
1367 IncrementRef(&ctx->UpdateCount);
1371 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1372 * converts NaN to 0. */
1373 static inline ALfloat aluClampf(ALfloat val)
1375 if(fabsf(val) <= 1.0f) return val;
1376 return (ALfloat)((0.0f < val) - (val < 0.0f));
1379 static inline ALfloat aluF2F(ALfloat val)
1380 { return val; }
1382 static inline ALint aluF2I(ALfloat val)
1384 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1385 * integer range normalized floats can be safely converted to.
1387 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1389 static inline ALuint aluF2UI(ALfloat val)
1390 { return aluF2I(val)+2147483648u; }
1392 static inline ALshort aluF2S(ALfloat val)
1393 { return fastf2i(aluClampf(val)*32767.0f); }
1394 static inline ALushort aluF2US(ALfloat val)
1395 { return aluF2S(val)+32768; }
1397 static inline ALbyte aluF2B(ALfloat val)
1398 { return fastf2i(aluClampf(val)*127.0f); }
1399 static inline ALubyte aluF2UB(ALfloat val)
1400 { return aluF2B(val)+128; }
1402 #define DECL_TEMPLATE(T, func) \
1403 static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1404 ALuint SamplesToDo, ALuint numchans) \
1406 ALuint i, j; \
1407 for(j = 0;j < numchans;j++) \
1409 const ALfloat *in = InBuffer[j]; \
1410 T *restrict out = (T*)OutBuffer + j; \
1411 for(i = 0;i < SamplesToDo;i++) \
1412 out[i*numchans] = func(in[i]); \
1416 DECL_TEMPLATE(ALfloat, aluF2F)
1417 DECL_TEMPLATE(ALuint, aluF2UI)
1418 DECL_TEMPLATE(ALint, aluF2I)
1419 DECL_TEMPLATE(ALushort, aluF2US)
1420 DECL_TEMPLATE(ALshort, aluF2S)
1421 DECL_TEMPLATE(ALubyte, aluF2UB)
1422 DECL_TEMPLATE(ALbyte, aluF2B)
1424 #undef DECL_TEMPLATE
1427 ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1429 ALuint SamplesToDo;
1430 ALvoice *voice, *voice_end;
1431 ALeffectslot *slot;
1432 ALsource *source;
1433 ALCcontext *ctx;
1434 FPUCtl oldMode;
1435 ALuint i, c;
1437 SetMixerFPUMode(&oldMode);
1439 while(size > 0)
1441 SamplesToDo = minu(size, BUFFERSIZE);
1442 for(c = 0;c < device->Dry.NumChannels;c++)
1443 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1444 if(device->Dry.Buffer != device->RealOut.Buffer)
1445 for(c = 0;c < device->RealOut.NumChannels;c++)
1446 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1447 if(device->Dry.Buffer != device->FOAOut.Buffer)
1448 for(c = 0;c < device->FOAOut.NumChannels;c++)
1449 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1451 IncrementRef(&device->MixCount);
1452 V0(device->Backend,lock)();
1454 if((slot=device->DefaultSlot) != NULL)
1456 CalcEffectSlotParams(device->DefaultSlot, device);
1457 for(i = 0;i < slot->NumChannels;i++)
1458 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1461 ctx = ATOMIC_LOAD(&device->ContextList);
1462 while(ctx)
1464 ALeffectslot *slotroot;
1466 slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList);
1467 UpdateContextSources(ctx, slotroot);
1469 slot = slotroot;
1470 while(slot)
1472 for(i = 0;i < slot->NumChannels;i++)
1473 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1474 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1477 /* source processing */
1478 voice = ctx->Voices;
1479 voice_end = voice + ctx->VoiceCount;
1480 for(;voice != voice_end;++voice)
1482 ALboolean IsVoiceInit = (voice->Step > 0);
1483 source = voice->Source;
1484 if(source && source->state == AL_PLAYING && IsVoiceInit)
1485 MixSource(voice, source, device, SamplesToDo);
1488 /* effect slot processing */
1489 slot = slotroot;
1490 while(slot)
1492 ALeffectState *state = slot->Params.EffectState;
1493 V(state,process)(SamplesToDo, SAFE_CONST(ALfloatBUFFERSIZE*,slot->WetBuffer),
1494 state->OutBuffer, state->OutChannels);
1495 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1498 ctx = ctx->next;
1501 if(device->DefaultSlot != NULL)
1503 const ALeffectslot *slot = device->DefaultSlot;
1504 ALeffectState *state = slot->Params.EffectState;
1505 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1506 state->OutChannels);
1509 /* Increment the clock time. Every second's worth of samples is
1510 * converted and added to clock base so that large sample counts don't
1511 * overflow during conversion. This also guarantees an exact, stable
1512 * conversion. */
1513 device->SamplesDone += SamplesToDo;
1514 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1515 device->SamplesDone %= device->Frequency;
1516 V0(device->Backend,unlock)();
1517 IncrementRef(&device->MixCount);
1519 if(device->Hrtf.Handle)
1521 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1522 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1523 if(lidx != -1 && ridx != -1)
1525 HrtfDirectMixerFunc HrtfMix = SelectHrtfMixer();
1526 ALuint irsize = device->Hrtf.IrSize;
1527 for(c = 0;c < device->Dry.NumChannels;c++)
1529 HrtfMix(device->RealOut.Buffer, lidx, ridx,
1530 device->Dry.Buffer[c], device->Hrtf.Offset, irsize,
1531 device->Hrtf.Coeffs[c], device->Hrtf.Values[c],
1532 SamplesToDo
1535 device->Hrtf.Offset += SamplesToDo;
1538 else if(device->AmbiDecoder)
1540 if(device->Dry.Buffer != device->FOAOut.Buffer)
1541 bformatdec_upSample(device->AmbiDecoder,
1542 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1543 device->FOAOut.NumChannels, SamplesToDo
1545 bformatdec_process(device->AmbiDecoder,
1546 device->RealOut.Buffer, device->RealOut.NumChannels,
1547 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1550 else if(device->AmbiUp)
1552 ambiup_process(device->AmbiUp,
1553 device->RealOut.Buffer, device->RealOut.NumChannels,
1554 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1557 else if(device->Uhj_Encoder)
1559 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1560 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1561 if(lidx != -1 && ridx != -1)
1563 /* Encode to stereo-compatible 2-channel UHJ output. */
1564 EncodeUhj2(device->Uhj_Encoder,
1565 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1566 device->Dry.Buffer, SamplesToDo
1570 else if(device->Bs2b)
1572 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1573 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1574 if(lidx != -1 && ridx != -1)
1576 /* Apply binaural/crossfeed filter */
1577 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1578 device->RealOut.Buffer[ridx], SamplesToDo);
1582 if(buffer)
1584 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1585 ALuint OutChannels = device->RealOut.NumChannels;
1587 #define WRITE(T, a, b, c, d) do { \
1588 Write_##T((a), (b), (c), (d)); \
1589 buffer = (T*)buffer + (c)*(d); \
1590 } while(0)
1591 switch(device->FmtType)
1593 case DevFmtByte:
1594 WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1595 break;
1596 case DevFmtUByte:
1597 WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1598 break;
1599 case DevFmtShort:
1600 WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
1601 break;
1602 case DevFmtUShort:
1603 WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
1604 break;
1605 case DevFmtInt:
1606 WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
1607 break;
1608 case DevFmtUInt:
1609 WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
1610 break;
1611 case DevFmtFloat:
1612 WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
1613 break;
1615 #undef WRITE
1618 size -= SamplesToDo;
1621 RestoreFPUMode(&oldMode);
1625 ALvoid aluHandleDisconnect(ALCdevice *device)
1627 ALCcontext *Context;
1629 device->Connected = ALC_FALSE;
1631 Context = ATOMIC_LOAD(&device->ContextList);
1632 while(Context)
1634 ALvoice *voice, *voice_end;
1636 voice = Context->Voices;
1637 voice_end = voice + Context->VoiceCount;
1638 while(voice != voice_end)
1640 ALsource *source = voice->Source;
1641 voice->Source = NULL;
1643 if(source && source->state == AL_PLAYING)
1645 source->state = AL_STOPPED;
1646 ATOMIC_STORE(&source->current_buffer, NULL);
1647 ATOMIC_STORE(&source->position, 0);
1648 ATOMIC_STORE(&source->position_fraction, 0);
1651 voice++;
1653 Context->VoiceCount = 0;
1655 Context = Context->next;