Provide (mostly) lockless updates for effect slots
[openal-soft.git] / Alc / ALu.c
blob3b873aa5448c5ebb20bb87013c6706ae2a043e49
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 extern inline void aluMatrixdSetRow(aluMatrixd *matrix, ALuint row,
97 ALdouble m0, ALdouble m1, ALdouble m2, ALdouble m3);
98 extern inline void aluMatrixdSet(aluMatrixd *matrix,
99 ALdouble m00, ALdouble m01, ALdouble m02, ALdouble m03,
100 ALdouble m10, ALdouble m11, ALdouble m12, ALdouble m13,
101 ALdouble m20, ALdouble m21, ALdouble m22, ALdouble m23,
102 ALdouble m30, ALdouble m31, ALdouble m32, ALdouble m33);
105 static inline HrtfMixerFunc SelectHrtfMixer(void)
107 #ifdef HAVE_SSE
108 if((CPUCapFlags&CPU_CAP_SSE))
109 return MixHrtf_SSE;
110 #endif
111 #ifdef HAVE_NEON
112 if((CPUCapFlags&CPU_CAP_NEON))
113 return MixHrtf_Neon;
114 #endif
116 return MixHrtf_C;
120 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
122 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
123 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
124 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
127 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
129 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
132 static inline ALfloat aluNormalize(ALfloat *vec)
134 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
135 if(length > 0.0f)
137 ALfloat inv_length = 1.0f/length;
138 vec[0] *= inv_length;
139 vec[1] *= inv_length;
140 vec[2] *= inv_length;
142 return length;
146 static inline void aluCrossproductd(const ALdouble *inVector1, const ALdouble *inVector2, ALdouble *outVector)
148 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
149 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
150 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
153 static inline ALdouble aluNormalized(ALdouble *vec)
155 ALdouble length = sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
156 if(length > 0.0)
158 ALdouble inv_length = 1.0/length;
159 vec[0] *= inv_length;
160 vec[1] *= inv_length;
161 vec[2] *= inv_length;
163 return length;
166 static inline ALvoid aluMatrixdFloat3(ALfloat *vec, ALfloat w, const aluMatrixd *mtx)
168 ALdouble v[4] = { vec[0], vec[1], vec[2], w };
170 vec[0] = (ALfloat)(v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0]);
171 vec[1] = (ALfloat)(v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1]);
172 vec[2] = (ALfloat)(v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2]);
175 static inline ALvoid aluMatrixdDouble3(ALdouble *vec, ALdouble w, const aluMatrixd *mtx)
177 ALdouble v[4] = { vec[0], vec[1], vec[2], w };
179 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];
180 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];
181 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];
184 static inline aluVector aluMatrixdVector(const aluMatrixd *mtx, const aluVector *vec)
186 aluVector v;
187 v.v[0] = (ALfloat)(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]);
188 v.v[1] = (ALfloat)(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]);
189 v.v[2] = (ALfloat)(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]);
190 v.v[3] = (ALfloat)(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]);
191 return v;
195 /* Prepares the interpolator for a given rate (determined by increment). A
196 * result of AL_FALSE indicates that the filter output will completely cut
197 * the input signal.
199 * With a bit of work, and a trade of memory for CPU cost, this could be
200 * modified for use with an interpolated increment for buttery-smooth pitch
201 * changes.
203 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
205 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
206 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
207 static const ALuint to[4][BSINC_SCALE_COUNT] =
209 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
210 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
211 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
212 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
214 static const ALuint tm[2][BSINC_SCALE_COUNT] =
216 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
217 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
219 ALfloat sf;
220 ALuint si, pi;
221 ALboolean uncut = AL_TRUE;
223 if(increment > FRACTIONONE)
225 sf = (ALfloat)FRACTIONONE / increment;
226 if(sf < scaleBase)
228 /* Signal has been completely cut. The return result can be used
229 * to skip the filter (and output zeros) as an optimization.
231 sf = 0.0f;
232 si = 0;
233 uncut = AL_FALSE;
235 else
237 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
238 si = fastf2u(sf);
239 /* The interpolation factor is fit to this diagonally-symmetric
240 * curve to reduce the transition ripple caused by interpolating
241 * different scales of the sinc function.
243 sf = 1.0f - cosf(asinf(sf - si));
246 else
248 sf = 0.0f;
249 si = BSINC_SCALE_COUNT - 1;
252 state->sf = sf;
253 state->m = m[si];
254 state->l = -(ALint)((m[si] / 2) - 1);
255 /* The CPU cost of this table re-mapping could be traded for the memory
256 * cost of a complete table map (1024 elements large).
258 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
260 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
261 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
262 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
263 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
265 return uncut;
269 static ALboolean CalcListenerParams(ALCcontext *Context)
271 ALlistener *Listener = Context->Listener;
272 ALdouble N[3], V[3], U[3], P[3];
273 struct ALlistenerProps *first;
274 struct ALlistenerProps *props;
275 aluVector vel;
277 props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &Listener->Update, NULL, almemory_order_acq_rel);
278 if(!props) return AL_FALSE;
280 /* AT then UP */
281 N[0] = ATOMIC_LOAD(&props->Forward[0], almemory_order_relaxed);
282 N[1] = ATOMIC_LOAD(&props->Forward[1], almemory_order_relaxed);
283 N[2] = ATOMIC_LOAD(&props->Forward[2], almemory_order_relaxed);
284 aluNormalized(N);
285 V[0] = ATOMIC_LOAD(&props->Up[0], almemory_order_relaxed);
286 V[1] = ATOMIC_LOAD(&props->Up[1], almemory_order_relaxed);
287 V[2] = ATOMIC_LOAD(&props->Up[2], almemory_order_relaxed);
288 aluNormalized(V);
289 /* Build and normalize right-vector */
290 aluCrossproductd(N, V, U);
291 aluNormalized(U);
293 aluMatrixdSet(&Listener->Params.Matrix,
294 U[0], V[0], -N[0], 0.0,
295 U[1], V[1], -N[1], 0.0,
296 U[2], V[2], -N[2], 0.0,
297 0.0, 0.0, 0.0, 1.0
300 P[0] = ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed);
301 P[1] = ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed);
302 P[2] = ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed);
303 aluMatrixdDouble3(P, 1.0, &Listener->Params.Matrix);
304 aluMatrixdSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
306 aluVectorSet(&vel, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
307 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
308 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
309 0.0f);
310 Listener->Params.Velocity = aluMatrixdVector(&Listener->Params.Matrix, &vel);
312 Listener->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
313 Listener->Params.MetersPerUnit = ATOMIC_LOAD(&props->MetersPerUnit, almemory_order_relaxed);
315 Listener->Params.DopplerFactor = ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
316 Listener->Params.SpeedOfSound = ATOMIC_LOAD(&props->SpeedOfSound, almemory_order_relaxed) *
317 ATOMIC_LOAD(&props->DopplerVelocity, almemory_order_relaxed);
319 /* WARNING: A livelock is theoretically possible if another thread keeps
320 * changing the freelist head without giving this a chance to actually swap
321 * in the old container (practically impossible with this little code,
322 * but...).
324 first = ATOMIC_LOAD(&Listener->FreeList);
325 do {
326 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
327 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*,
328 &Listener->FreeList, &first, props) == 0);
330 return AL_TRUE;
333 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
335 struct ALeffectslotProps *first;
336 struct ALeffectslotProps *props;
338 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL, almemory_order_acq_rel);
339 if(!props) return AL_FALSE;
341 slot->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
342 slot->Params.AuxSendAuto = ATOMIC_LOAD(&props->AuxSendAuto, almemory_order_relaxed);
343 slot->Params.EffectType = ATOMIC_LOAD(&props->Type, almemory_order_relaxed);
344 memcpy(&slot->Params.EffectProps, &props->Props, sizeof(props->Props));
345 /* If the existing state object is different from the one being set,
346 * exchange it so it remains in the freelist and isn't leaked.
348 if(slot->Params.EffectState == ATOMIC_LOAD(&props->State, almemory_order_relaxed))
349 slot->Params.EffectState = NULL;
350 slot->Params.EffectState = ATOMIC_EXCHANGE(ALeffectState*,
351 &props->State, slot->Params.EffectState, almemory_order_relaxed
353 if(IsReverbEffect(slot->Params.EffectType))
355 slot->Params.RoomRolloff = slot->Params.EffectProps.Reverb.RoomRolloffFactor;
356 slot->Params.DecayTime = slot->Params.EffectProps.Reverb.DecayTime;
357 slot->Params.AirAbsorptionGainHF = slot->Params.EffectProps.Reverb.AirAbsorptionGainHF;
359 else
361 slot->Params.RoomRolloff = 0.0f;
362 slot->Params.DecayTime = 0.0f;
363 slot->Params.AirAbsorptionGainHF = 1.0f;
366 V(slot->Params.EffectState,update)(device, slot);
368 /* WARNING: A livelock is theoretically possible if another thread keeps
369 * changing the freelist head without giving this a chance to actually swap
370 * in the old container (practically impossible with this little code,
371 * but...).
373 first = ATOMIC_LOAD(&slot->FreeList);
374 do {
375 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
376 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
377 &slot->FreeList, &first, props) == 0);
379 return AL_TRUE;
382 ALvoid CalcNonAttnSourceParams(ALvoice *voice, const ALsource *ALSource, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
384 static const struct ChanMap MonoMap[1] = {
385 { FrontCenter, 0.0f, 0.0f }
386 }, RearMap[2] = {
387 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
388 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
389 }, QuadMap[4] = {
390 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
391 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
392 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
393 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
394 }, X51Map[6] = {
395 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
396 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
397 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
398 { LFE, 0.0f, 0.0f },
399 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
400 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
401 }, X61Map[7] = {
402 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
403 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
404 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
405 { LFE, 0.0f, 0.0f },
406 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
407 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
408 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
409 }, X71Map[8] = {
410 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
411 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
412 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
413 { LFE, 0.0f, 0.0f },
414 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
415 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
416 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
417 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
420 const ALCdevice *Device = ALContext->Device;
421 const ALlistener *Listener = ALContext->Listener;
422 ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
423 ALfloat DryGain, DryGainHF, DryGainLF;
424 ALfloat WetGain[MAX_SENDS];
425 ALfloat WetGainHF[MAX_SENDS];
426 ALfloat WetGainLF[MAX_SENDS];
427 ALeffectslot *SendSlots[MAX_SENDS];
428 ALuint NumSends, Frequency;
429 ALboolean Relative;
430 const struct ChanMap *chans = NULL;
431 struct ChanMap StereoMap[2] = {
432 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
433 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
435 ALuint num_channels = 0;
436 ALboolean DirectChannels;
437 ALboolean isbformat = AL_FALSE;
438 ALfloat Pitch;
439 ALuint i, j, c;
441 /* Get device properties */
442 NumSends = Device->NumAuxSends;
443 Frequency = Device->Frequency;
445 /* Get listener properties */
446 ListenerGain = Listener->Params.Gain;
448 /* Get source properties */
449 SourceVolume = ALSource->Gain;
450 MinVolume = ALSource->MinGain;
451 MaxVolume = ALSource->MaxGain;
452 Pitch = ALSource->Pitch;
453 Relative = ALSource->HeadRelative;
454 DirectChannels = ALSource->DirectChannels;
456 /* Convert counter-clockwise to clockwise. */
457 StereoMap[0].angle = -ALSource->StereoPan[0];
458 StereoMap[1].angle = -ALSource->StereoPan[1];
460 voice->Direct.OutBuffer = Device->Dry.Buffer;
461 voice->Direct.OutChannels = Device->Dry.NumChannels;
462 for(i = 0;i < NumSends;i++)
464 SendSlots[i] = ALSource->Send[i].Slot;
465 if(!SendSlots[i] && i == 0)
466 SendSlots[i] = Device->DefaultSlot;
467 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
469 SendSlots[i] = NULL;
470 voice->Send[i].OutBuffer = NULL;
471 voice->Send[i].OutChannels = 0;
473 else
475 voice->Send[i].OutBuffer = SendSlots[i]->WetBuffer;
476 voice->Send[i].OutChannels = SendSlots[i]->NumChannels;
480 /* Calculate the stepping value */
481 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
482 if(Pitch > (ALfloat)MAX_PITCH)
483 voice->Step = MAX_PITCH<<FRACTIONBITS;
484 else
485 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
486 BsincPrepare(voice->Step, &voice->SincState);
488 /* Calculate gains */
489 DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
490 DryGain *= ALSource->Direct.Gain * ListenerGain;
491 DryGainHF = ALSource->Direct.GainHF;
492 DryGainLF = ALSource->Direct.GainLF;
493 for(i = 0;i < NumSends;i++)
495 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
496 WetGain[i] *= ALSource->Send[i].Gain * ListenerGain;
497 WetGainHF[i] = ALSource->Send[i].GainHF;
498 WetGainLF[i] = ALSource->Send[i].GainLF;
501 switch(ALBuffer->FmtChannels)
503 case FmtMono:
504 chans = MonoMap;
505 num_channels = 1;
506 break;
508 case FmtStereo:
509 chans = StereoMap;
510 num_channels = 2;
511 break;
513 case FmtRear:
514 chans = RearMap;
515 num_channels = 2;
516 break;
518 case FmtQuad:
519 chans = QuadMap;
520 num_channels = 4;
521 break;
523 case FmtX51:
524 chans = X51Map;
525 num_channels = 6;
526 break;
528 case FmtX61:
529 chans = X61Map;
530 num_channels = 7;
531 break;
533 case FmtX71:
534 chans = X71Map;
535 num_channels = 8;
536 break;
538 case FmtBFormat2D:
539 num_channels = 3;
540 isbformat = AL_TRUE;
541 DirectChannels = AL_FALSE;
542 break;
544 case FmtBFormat3D:
545 num_channels = 4;
546 isbformat = AL_TRUE;
547 DirectChannels = AL_FALSE;
548 break;
551 if(isbformat)
553 ALfloat N[3], V[3], U[3];
554 aluMatrixf matrix;
555 ALfloat scale;
557 /* AT then UP */
558 N[0] = ALSource->Orientation[0][0];
559 N[1] = ALSource->Orientation[0][1];
560 N[2] = ALSource->Orientation[0][2];
561 aluNormalize(N);
562 V[0] = ALSource->Orientation[1][0];
563 V[1] = ALSource->Orientation[1][1];
564 V[2] = ALSource->Orientation[1][2];
565 aluNormalize(V);
566 if(!Relative)
568 const aluMatrixd *lmatrix = &Listener->Params.Matrix;
569 aluMatrixdFloat3(N, 0.0f, lmatrix);
570 aluMatrixdFloat3(V, 0.0f, lmatrix);
572 /* Build and normalize right-vector */
573 aluCrossproduct(N, V, U);
574 aluNormalize(U);
576 /* Build a rotate + conversion matrix (B-Format -> N3D). */
577 scale = 1.732050808f;
578 aluMatrixfSet(&matrix,
579 1.414213562f, 0.0f, 0.0f, 0.0f,
580 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
581 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
582 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
585 voice->Direct.OutBuffer = Device->FOAOut.Buffer;
586 voice->Direct.OutChannels = Device->FOAOut.NumChannels;
587 for(c = 0;c < num_channels;c++)
588 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
589 voice->Direct.Gains[c].Target);
591 for(i = 0;i < NumSends;i++)
593 if(!SendSlots[i])
595 for(c = 0;c < num_channels;c++)
597 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
598 voice->Send[i].Gains[c].Target[j] = 0.0f;
601 else
603 for(c = 0;c < num_channels;c++)
605 const ALeffectslot *Slot = SendSlots[i];
606 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, matrix.m[c],
607 WetGain[i], voice->Send[i].Gains[c].Target);
612 voice->IsHrtf = AL_FALSE;
614 else
616 ALfloat coeffs[MAX_AMBI_COEFFS];
618 if(DirectChannels)
620 /* Skip the virtual channels and write inputs to the real output. */
621 voice->Direct.OutBuffer = Device->RealOut.Buffer;
622 voice->Direct.OutChannels = Device->RealOut.NumChannels;
623 for(c = 0;c < num_channels;c++)
625 int idx;
626 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
627 voice->Direct.Gains[c].Target[j] = 0.0f;
628 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
629 voice->Direct.Gains[c].Target[idx] = DryGain;
632 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
633 * channel-match. */
634 for(c = 0;c < num_channels;c++)
636 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
638 for(i = 0;i < NumSends;i++)
640 if(!SendSlots[i])
642 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
643 voice->Send[i].Gains[c].Target[j] = 0.0f;
645 else
647 const ALeffectslot *Slot = SendSlots[i];
648 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
649 WetGain[i], voice->Send[i].Gains[c].Target);
654 voice->IsHrtf = AL_FALSE;
656 else if(Device->Render_Mode == HrtfRender)
658 /* Full HRTF rendering. Skip the virtual channels and render each
659 * input channel to the real outputs.
661 voice->Direct.OutBuffer = Device->RealOut.Buffer;
662 voice->Direct.OutChannels = Device->RealOut.NumChannels;
663 for(c = 0;c < num_channels;c++)
665 if(chans[c].channel == LFE)
667 /* Skip LFE */
668 voice->Direct.Hrtf[c].Target.Delay[0] = 0;
669 voice->Direct.Hrtf[c].Target.Delay[1] = 0;
670 for(i = 0;i < HRIR_LENGTH;i++)
672 voice->Direct.Hrtf[c].Target.Coeffs[i][0] = 0.0f;
673 voice->Direct.Hrtf[c].Target.Coeffs[i][1] = 0.0f;
676 for(i = 0;i < NumSends;i++)
678 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
679 voice->Send[i].Gains[c].Target[j] = 0.0f;
682 continue;
685 /* Get the static HRIR coefficients and delays for this channel. */
686 GetLerpedHrtfCoeffs(Device->Hrtf,
687 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
688 voice->Direct.Hrtf[c].Target.Coeffs,
689 voice->Direct.Hrtf[c].Target.Delay
692 /* Normal panning for auxiliary sends. */
693 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
695 for(i = 0;i < NumSends;i++)
697 if(!SendSlots[i])
699 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
700 voice->Send[i].Gains[c].Target[j] = 0.0f;
702 else
704 const ALeffectslot *Slot = SendSlots[i];
705 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
706 WetGain[i], voice->Send[i].Gains[c].Target);
711 voice->IsHrtf = AL_TRUE;
713 else
715 /* Non-HRTF rendering. Use normal panning to the output. */
716 for(c = 0;c < num_channels;c++)
718 /* Special-case LFE */
719 if(chans[c].channel == LFE)
721 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
722 voice->Direct.Gains[c].Target[j] = 0.0f;
723 if(Device->Dry.Buffer == Device->RealOut.Buffer)
725 int idx;
726 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
727 voice->Direct.Gains[c].Target[idx] = DryGain;
730 for(i = 0;i < NumSends;i++)
732 ALuint j;
733 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
734 voice->Send[i].Gains[c].Target[j] = 0.0f;
736 continue;
739 if(Device->Render_Mode == StereoPair)
741 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
742 ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
743 coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
744 voice->Direct.Gains[c].Target[0] = coeffs[0] * DryGain;
745 voice->Direct.Gains[c].Target[1] = (1.0f-coeffs[0]) * DryGain;
746 for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
747 voice->Direct.Gains[c].Target[j] = 0.0f;
749 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
751 else
753 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
754 ComputePanningGains(Device->Dry, coeffs, DryGain,
755 voice->Direct.Gains[c].Target);
758 for(i = 0;i < NumSends;i++)
760 if(!SendSlots[i])
762 ALuint j;
763 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
764 voice->Send[i].Gains[c].Target[j] = 0.0f;
766 else
768 const ALeffectslot *Slot = SendSlots[i];
769 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
770 WetGain[i], voice->Send[i].Gains[c].Target);
775 voice->IsHrtf = AL_FALSE;
780 ALfloat hfscale = ALSource->Direct.HFReference / Frequency;
781 ALfloat lfscale = ALSource->Direct.LFReference / Frequency;
782 DryGainHF = maxf(DryGainHF, 0.0001f);
783 DryGainLF = maxf(DryGainLF, 0.0001f);
784 for(c = 0;c < num_channels;c++)
786 voice->Direct.Filters[c].ActiveType = AF_None;
787 if(DryGainHF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_LowPass;
788 if(DryGainLF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_HighPass;
789 ALfilterState_setParams(
790 &voice->Direct.Filters[c].LowPass, ALfilterType_HighShelf,
791 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
793 ALfilterState_setParams(
794 &voice->Direct.Filters[c].HighPass, ALfilterType_LowShelf,
795 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
799 for(i = 0;i < NumSends;i++)
801 ALfloat hfscale = ALSource->Send[i].HFReference / Frequency;
802 ALfloat lfscale = ALSource->Send[i].LFReference / Frequency;
803 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
804 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
805 for(c = 0;c < num_channels;c++)
807 voice->Send[i].Filters[c].ActiveType = AF_None;
808 if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_LowPass;
809 if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_HighPass;
810 ALfilterState_setParams(
811 &voice->Send[i].Filters[c].LowPass, ALfilterType_HighShelf,
812 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
814 ALfilterState_setParams(
815 &voice->Send[i].Filters[c].HighPass, ALfilterType_LowShelf,
816 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
822 ALvoid CalcSourceParams(ALvoice *voice, const ALsource *ALSource, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
824 const ALCdevice *Device = ALContext->Device;
825 const ALlistener *Listener = ALContext->Listener;
826 aluVector Position, Velocity, Direction, SourceToListener;
827 ALfloat InnerAngle,OuterAngle,Angle,Distance,ClampedDist;
828 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
829 ALfloat ConeVolume,ConeHF,SourceVolume,ListenerGain;
830 ALfloat DopplerFactor, SpeedOfSound;
831 ALfloat AirAbsorptionFactor;
832 ALfloat RoomAirAbsorption[MAX_SENDS];
833 ALeffectslot *SendSlots[MAX_SENDS];
834 ALfloat Attenuation;
835 ALfloat RoomAttenuation[MAX_SENDS];
836 ALfloat MetersPerUnit;
837 ALfloat RoomRolloffBase;
838 ALfloat RoomRolloff[MAX_SENDS];
839 ALfloat DecayDistance[MAX_SENDS];
840 ALfloat DryGain;
841 ALfloat DryGainHF;
842 ALfloat DryGainLF;
843 ALboolean DryGainHFAuto;
844 ALfloat WetGain[MAX_SENDS];
845 ALfloat WetGainHF[MAX_SENDS];
846 ALfloat WetGainLF[MAX_SENDS];
847 ALboolean WetGainAuto;
848 ALboolean WetGainHFAuto;
849 ALfloat Pitch;
850 ALuint Frequency;
851 ALint NumSends;
852 ALint i;
854 DryGainHF = 1.0f;
855 DryGainLF = 1.0f;
856 for(i = 0;i < MAX_SENDS;i++)
858 WetGainHF[i] = 1.0f;
859 WetGainLF[i] = 1.0f;
862 /* Get context/device properties */
863 DopplerFactor = Listener->Params.DopplerFactor * ALSource->DopplerFactor;
864 SpeedOfSound = Listener->Params.SpeedOfSound;
865 NumSends = Device->NumAuxSends;
866 Frequency = Device->Frequency;
868 /* Get listener properties */
869 ListenerGain = Listener->Params.Gain;
870 MetersPerUnit = Listener->Params.MetersPerUnit;
872 /* Get source properties */
873 SourceVolume = ALSource->Gain;
874 MinVolume = ALSource->MinGain;
875 MaxVolume = ALSource->MaxGain;
876 Pitch = ALSource->Pitch;
877 Position = ALSource->Position;
878 Direction = ALSource->Direction;
879 Velocity = ALSource->Velocity;
880 MinDist = ALSource->RefDistance;
881 MaxDist = ALSource->MaxDistance;
882 Rolloff = ALSource->RollOffFactor;
883 InnerAngle = ALSource->InnerAngle;
884 OuterAngle = ALSource->OuterAngle;
885 AirAbsorptionFactor = ALSource->AirAbsorptionFactor;
886 DryGainHFAuto = ALSource->DryGainHFAuto;
887 WetGainAuto = ALSource->WetGainAuto;
888 WetGainHFAuto = ALSource->WetGainHFAuto;
889 RoomRolloffBase = ALSource->RoomRolloffFactor;
891 voice->Direct.OutBuffer = Device->Dry.Buffer;
892 voice->Direct.OutChannels = Device->Dry.NumChannels;
893 for(i = 0;i < NumSends;i++)
895 SendSlots[i] = ALSource->Send[i].Slot;
897 if(!SendSlots[i] && i == 0)
898 SendSlots[i] = Device->DefaultSlot;
899 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
901 SendSlots[i] = NULL;
902 RoomRolloff[i] = 0.0f;
903 DecayDistance[i] = 0.0f;
904 RoomAirAbsorption[i] = 1.0f;
906 else if(SendSlots[i]->AuxSendAuto)
908 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
909 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
910 SPEEDOFSOUNDMETRESPERSEC;
911 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
913 else
915 /* If the slot's auxiliary send auto is off, the data sent to the
916 * effect slot is the same as the dry path, sans filter effects */
917 RoomRolloff[i] = Rolloff;
918 DecayDistance[i] = 0.0f;
919 RoomAirAbsorption[i] = AIRABSORBGAINHF;
922 if(!SendSlots[i])
924 voice->Send[i].OutBuffer = NULL;
925 voice->Send[i].OutChannels = 0;
927 else
929 voice->Send[i].OutBuffer = SendSlots[i]->WetBuffer;
930 voice->Send[i].OutChannels = SendSlots[i]->NumChannels;
934 /* Transform source to listener space (convert to head relative) */
935 if(ALSource->HeadRelative == AL_FALSE)
937 const aluMatrixd *Matrix = &Listener->Params.Matrix;
938 /* Transform source vectors */
939 Position = aluMatrixdVector(Matrix, &Position);
940 Velocity = aluMatrixdVector(Matrix, &Velocity);
941 Direction = aluMatrixdVector(Matrix, &Direction);
943 else
945 const aluVector *lvelocity = &Listener->Params.Velocity;
946 /* Offset the source velocity to be relative of the listener velocity */
947 Velocity.v[0] += lvelocity->v[0];
948 Velocity.v[1] += lvelocity->v[1];
949 Velocity.v[2] += lvelocity->v[2];
952 aluNormalize(Direction.v);
953 SourceToListener.v[0] = -Position.v[0];
954 SourceToListener.v[1] = -Position.v[1];
955 SourceToListener.v[2] = -Position.v[2];
956 SourceToListener.v[3] = 0.0f;
957 Distance = aluNormalize(SourceToListener.v);
959 /* Calculate distance attenuation */
960 ClampedDist = Distance;
962 Attenuation = 1.0f;
963 for(i = 0;i < NumSends;i++)
964 RoomAttenuation[i] = 1.0f;
965 switch(ALContext->SourceDistanceModel ? ALSource->DistanceModel :
966 ALContext->DistanceModel)
968 case InverseDistanceClamped:
969 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
970 if(MaxDist < MinDist)
971 break;
972 /*fall-through*/
973 case InverseDistance:
974 if(MinDist > 0.0f)
976 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
977 if(dist > 0.0f) Attenuation = MinDist / dist;
978 for(i = 0;i < NumSends;i++)
980 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
981 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
984 break;
986 case LinearDistanceClamped:
987 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
988 if(MaxDist < MinDist)
989 break;
990 /*fall-through*/
991 case LinearDistance:
992 if(MaxDist != MinDist)
994 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
995 Attenuation = maxf(Attenuation, 0.0f);
996 for(i = 0;i < NumSends;i++)
998 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
999 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
1002 break;
1004 case ExponentDistanceClamped:
1005 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
1006 if(MaxDist < MinDist)
1007 break;
1008 /*fall-through*/
1009 case ExponentDistance:
1010 if(ClampedDist > 0.0f && MinDist > 0.0f)
1012 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
1013 for(i = 0;i < NumSends;i++)
1014 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
1016 break;
1018 case DisableDistance:
1019 ClampedDist = MinDist;
1020 break;
1023 /* Source Gain + Attenuation */
1024 DryGain = SourceVolume * Attenuation;
1025 for(i = 0;i < NumSends;i++)
1026 WetGain[i] = SourceVolume * RoomAttenuation[i];
1028 /* Distance-based air absorption */
1029 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
1031 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
1032 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
1033 for(i = 0;i < NumSends;i++)
1034 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
1037 if(WetGainAuto)
1039 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1041 /* Apply a decay-time transformation to the wet path, based on the
1042 * attenuation of the dry path.
1044 * Using the apparent distance, based on the distance attenuation, the
1045 * initial decay of the reverb effect is calculated and applied to the
1046 * wet path.
1048 for(i = 0;i < NumSends;i++)
1050 if(DecayDistance[i] > 0.0f)
1051 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1055 /* Calculate directional soundcones */
1056 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1057 if(Angle > InnerAngle && Angle <= OuterAngle)
1059 ALfloat scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1060 ConeVolume = lerp(1.0f, ALSource->OuterGain, scale);
1061 ConeHF = lerp(1.0f, ALSource->OuterGainHF, scale);
1063 else if(Angle > OuterAngle)
1065 ConeVolume = ALSource->OuterGain;
1066 ConeHF = ALSource->OuterGainHF;
1068 else
1070 ConeVolume = 1.0f;
1071 ConeHF = 1.0f;
1074 DryGain *= ConeVolume;
1075 if(WetGainAuto)
1077 for(i = 0;i < NumSends;i++)
1078 WetGain[i] *= ConeVolume;
1080 if(DryGainHFAuto)
1081 DryGainHF *= ConeHF;
1082 if(WetGainHFAuto)
1084 for(i = 0;i < NumSends;i++)
1085 WetGainHF[i] *= ConeHF;
1088 /* Clamp to Min/Max Gain */
1089 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1090 for(i = 0;i < NumSends;i++)
1091 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1093 /* Apply gain and frequency filters */
1094 DryGain *= ALSource->Direct.Gain * ListenerGain;
1095 DryGainHF *= ALSource->Direct.GainHF;
1096 DryGainLF *= ALSource->Direct.GainLF;
1097 for(i = 0;i < NumSends;i++)
1099 WetGain[i] *= ALSource->Send[i].Gain * ListenerGain;
1100 WetGainHF[i] *= ALSource->Send[i].GainHF;
1101 WetGainLF[i] *= ALSource->Send[i].GainLF;
1104 /* Calculate velocity-based doppler effect */
1105 if(DopplerFactor > 0.0f)
1107 const aluVector *lvelocity = &Listener->Params.Velocity;
1108 ALfloat VSS, VLS;
1110 if(SpeedOfSound < 1.0f)
1112 DopplerFactor *= 1.0f/SpeedOfSound;
1113 SpeedOfSound = 1.0f;
1116 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1117 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1119 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1120 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1123 /* Calculate fixed-point stepping value, based on the pitch, buffer
1124 * frequency, and output frequency.
1126 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1127 if(Pitch > (ALfloat)MAX_PITCH)
1128 voice->Step = MAX_PITCH<<FRACTIONBITS;
1129 else
1130 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1131 BsincPrepare(voice->Step, &voice->SincState);
1133 if(Device->Render_Mode == HrtfRender)
1135 /* Full HRTF rendering. Skip the virtual channels and render to the
1136 * real outputs.
1138 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1139 ALfloat ev = 0.0f, az = 0.0f;
1140 ALfloat radius = ALSource->Radius;
1141 ALfloat coeffs[MAX_AMBI_COEFFS];
1142 ALfloat spread = 0.0f;
1144 voice->Direct.OutBuffer = Device->RealOut.Buffer;
1145 voice->Direct.OutChannels = Device->RealOut.NumChannels;
1147 if(Distance > FLT_EPSILON)
1149 dir[0] = -SourceToListener.v[0];
1150 dir[1] = -SourceToListener.v[1];
1151 dir[2] = -SourceToListener.v[2] * ZScale;
1153 /* Calculate elevation and azimuth only when the source is not at
1154 * the listener. This prevents +0 and -0 Z from producing
1155 * inconsistent panning. Also, clamp Y in case FP precision errors
1156 * cause it to land outside of -1..+1. */
1157 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1158 az = atan2f(dir[0], -dir[2]);
1160 if(radius > Distance)
1161 spread = F_TAU - Distance/radius*F_PI;
1162 else if(Distance > FLT_EPSILON)
1163 spread = asinf(radius / Distance) * 2.0f;
1165 /* Get the HRIR coefficients and delays. */
1166 GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, spread, DryGain,
1167 voice->Direct.Hrtf[0].Target.Coeffs,
1168 voice->Direct.Hrtf[0].Target.Delay);
1170 CalcDirectionCoeffs(dir, spread, coeffs);
1172 for(i = 0;i < NumSends;i++)
1174 if(!SendSlots[i])
1176 ALuint j;
1177 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1178 voice->Send[i].Gains[0].Target[j] = 0.0f;
1180 else
1182 const ALeffectslot *Slot = SendSlots[i];
1183 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1184 WetGain[i], voice->Send[i].Gains[0].Target);
1188 voice->IsHrtf = AL_TRUE;
1190 else
1192 /* Non-HRTF rendering. */
1193 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1194 ALfloat radius = ALSource->Radius;
1195 ALfloat coeffs[MAX_AMBI_COEFFS];
1196 ALfloat spread = 0.0f;
1198 /* Get the localized direction, and compute panned gains. */
1199 if(Distance > FLT_EPSILON)
1201 dir[0] = -SourceToListener.v[0];
1202 dir[1] = -SourceToListener.v[1];
1203 dir[2] = -SourceToListener.v[2] * ZScale;
1205 if(radius > Distance)
1206 spread = F_TAU - Distance/radius*F_PI;
1207 else if(Distance > FLT_EPSILON)
1208 spread = asinf(radius / Distance) * 2.0f;
1210 if(Device->Render_Mode == StereoPair)
1212 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
1213 ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
1214 x = clampf(x, -0.5f, 0.5f) + 0.5f;
1215 voice->Direct.Gains[0].Target[0] = x * DryGain;
1216 voice->Direct.Gains[0].Target[1] = (1.0f-x) * DryGain;
1217 for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
1218 voice->Direct.Gains[0].Target[i] = 0.0f;
1220 CalcDirectionCoeffs(dir, spread, coeffs);
1222 else
1224 CalcDirectionCoeffs(dir, spread, coeffs);
1225 ComputePanningGains(Device->Dry, coeffs, DryGain, voice->Direct.Gains[0].Target);
1228 for(i = 0;i < NumSends;i++)
1230 if(!SendSlots[i])
1232 ALuint j;
1233 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1234 voice->Send[i].Gains[0].Target[j] = 0.0f;
1236 else
1238 const ALeffectslot *Slot = SendSlots[i];
1239 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1240 WetGain[i], voice->Send[i].Gains[0].Target);
1244 voice->IsHrtf = AL_FALSE;
1248 ALfloat hfscale = ALSource->Direct.HFReference / Frequency;
1249 ALfloat lfscale = ALSource->Direct.LFReference / Frequency;
1250 DryGainHF = maxf(DryGainHF, 0.0001f);
1251 DryGainLF = maxf(DryGainLF, 0.0001f);
1252 voice->Direct.Filters[0].ActiveType = AF_None;
1253 if(DryGainHF != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_LowPass;
1254 if(DryGainLF != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_HighPass;
1255 ALfilterState_setParams(
1256 &voice->Direct.Filters[0].LowPass, ALfilterType_HighShelf,
1257 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
1259 ALfilterState_setParams(
1260 &voice->Direct.Filters[0].HighPass, ALfilterType_LowShelf,
1261 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
1264 for(i = 0;i < NumSends;i++)
1266 ALfloat hfscale = ALSource->Send[i].HFReference / Frequency;
1267 ALfloat lfscale = ALSource->Send[i].LFReference / Frequency;
1268 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
1269 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
1270 voice->Send[i].Filters[0].ActiveType = AF_None;
1271 if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_LowPass;
1272 if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_HighPass;
1273 ALfilterState_setParams(
1274 &voice->Send[i].Filters[0].LowPass, ALfilterType_HighShelf,
1275 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
1277 ALfilterState_setParams(
1278 &voice->Send[i].Filters[0].HighPass, ALfilterType_LowShelf,
1279 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
1285 void UpdateContextSources(ALCcontext *ctx)
1287 ALvoice *voice, *voice_end;
1288 ALboolean fullupdate;
1289 ALsource *source;
1291 fullupdate = CalcListenerParams(ctx);
1292 #define UPDATE_SLOT(iter) do { \
1293 if(CalcEffectSlotParams(*iter, ctx->Device)) \
1294 fullupdate = AL_TRUE; \
1295 } while(0)
1296 VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, UPDATE_SLOT);
1297 #undef UPDATE_SLOT
1299 if(fullupdate)
1301 voice = ctx->Voices;
1302 voice_end = voice + ctx->VoiceCount;
1303 for(;voice != voice_end;++voice)
1305 if(!(source=voice->Source)) continue;
1306 if(source->state != AL_PLAYING && source->state != AL_PAUSED)
1307 voice->Source = NULL;
1308 else
1310 ALbufferlistitem *BufferListItem;
1311 BufferListItem = ATOMIC_LOAD(&source->queue);
1312 while(BufferListItem != NULL)
1314 ALbuffer *buffer;
1315 if((buffer=BufferListItem->buffer) != NULL)
1317 ATOMIC_STORE(&source->NeedsUpdate, AL_FALSE);
1318 voice->Update(voice, source, buffer, ctx);
1319 break;
1321 BufferListItem = BufferListItem->next;
1326 else
1328 voice = ctx->Voices;
1329 voice_end = voice + ctx->VoiceCount;
1330 for(;voice != voice_end;++voice)
1332 if(!(source=voice->Source)) continue;
1333 if(source->state != AL_PLAYING && source->state != AL_PAUSED)
1334 voice->Source = NULL;
1335 else if(ATOMIC_EXCHANGE(ALenum, &source->NeedsUpdate, AL_FALSE))
1337 ALbufferlistitem *BufferListItem;
1338 BufferListItem = ATOMIC_LOAD(&source->queue);
1339 while(BufferListItem != NULL)
1341 ALbuffer *buffer;
1342 if((buffer=BufferListItem->buffer) != NULL)
1344 voice->Update(voice, source, buffer, ctx);
1345 break;
1347 BufferListItem = BufferListItem->next;
1355 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1356 * converts NaN to 0. */
1357 static inline ALfloat aluClampf(ALfloat val)
1359 if(fabsf(val) <= 1.0f) return val;
1360 return (ALfloat)((0.0f < val) - (val < 0.0f));
1363 static inline ALfloat aluF2F(ALfloat val)
1364 { return val; }
1366 static inline ALint aluF2I(ALfloat val)
1368 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1369 * integer range normalized floats can be safely converted to.
1371 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1373 static inline ALuint aluF2UI(ALfloat val)
1374 { return aluF2I(val)+2147483648u; }
1376 static inline ALshort aluF2S(ALfloat val)
1377 { return fastf2i(aluClampf(val)*32767.0f); }
1378 static inline ALushort aluF2US(ALfloat val)
1379 { return aluF2S(val)+32768; }
1381 static inline ALbyte aluF2B(ALfloat val)
1382 { return fastf2i(aluClampf(val)*127.0f); }
1383 static inline ALubyte aluF2UB(ALfloat val)
1384 { return aluF2B(val)+128; }
1386 #define DECL_TEMPLATE(T, func) \
1387 static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1388 ALuint SamplesToDo, ALuint numchans) \
1390 ALuint i, j; \
1391 for(j = 0;j < numchans;j++) \
1393 const ALfloat *in = InBuffer[j]; \
1394 T *restrict out = (T*)OutBuffer + j; \
1395 for(i = 0;i < SamplesToDo;i++) \
1396 out[i*numchans] = func(in[i]); \
1400 DECL_TEMPLATE(ALfloat, aluF2F)
1401 DECL_TEMPLATE(ALuint, aluF2UI)
1402 DECL_TEMPLATE(ALint, aluF2I)
1403 DECL_TEMPLATE(ALushort, aluF2US)
1404 DECL_TEMPLATE(ALshort, aluF2S)
1405 DECL_TEMPLATE(ALubyte, aluF2UB)
1406 DECL_TEMPLATE(ALbyte, aluF2B)
1408 #undef DECL_TEMPLATE
1411 ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1413 ALuint SamplesToDo;
1414 ALvoice *voice, *voice_end;
1415 ALeffectslot *slot;
1416 ALsource *source;
1417 ALCcontext *ctx;
1418 FPUCtl oldMode;
1419 ALuint i, c;
1421 SetMixerFPUMode(&oldMode);
1423 while(size > 0)
1425 IncrementRef(&device->MixCount);
1427 SamplesToDo = minu(size, BUFFERSIZE);
1428 for(c = 0;c < device->VirtOut.NumChannels;c++)
1429 memset(device->VirtOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1430 for(c = 0;c < device->RealOut.NumChannels;c++)
1431 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1432 if(device->Dry.Buffer != device->FOAOut.Buffer)
1433 for(c = 0;c < device->FOAOut.NumChannels;c++)
1434 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1436 V0(device->Backend,lock)();
1438 if((slot=device->DefaultSlot) != NULL)
1440 CalcEffectSlotParams(device->DefaultSlot, device);
1441 for(i = 0;i < slot->NumChannels;i++)
1442 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1445 ctx = ATOMIC_LOAD(&device->ContextList);
1446 while(ctx)
1448 if(!ctx->DeferUpdates)
1449 UpdateContextSources(ctx);
1450 #define CLEAR_WET_BUFFER(iter) do { \
1451 for(i = 0;i < (*iter)->NumChannels;i++) \
1452 memset((*iter)->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat)); \
1453 } while(0)
1454 VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, CLEAR_WET_BUFFER);
1455 #undef CLEAR_WET_BUFFER
1457 /* source processing */
1458 voice = ctx->Voices;
1459 voice_end = voice + ctx->VoiceCount;
1460 for(;voice != voice_end;++voice)
1462 source = voice->Source;
1463 if(source && source->state == AL_PLAYING)
1464 MixSource(voice, source, device, SamplesToDo);
1467 /* effect slot processing */
1468 c = VECTOR_SIZE(ctx->ActiveAuxSlots);
1469 for(i = 0;i < c;i++)
1471 const ALeffectslot *slot = VECTOR_ELEM(ctx->ActiveAuxSlots, i);
1472 ALeffectState *state = slot->Params.EffectState;
1473 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1474 state->OutChannels);
1477 ctx = ctx->next;
1480 if(device->DefaultSlot != NULL)
1482 const ALeffectslot *slot = device->DefaultSlot;
1483 ALeffectState *state = slot->Params.EffectState;
1484 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1485 state->OutChannels);
1488 /* Increment the clock time. Every second's worth of samples is
1489 * converted and added to clock base so that large sample counts don't
1490 * overflow during conversion. This also guarantees an exact, stable
1491 * conversion. */
1492 device->SamplesDone += SamplesToDo;
1493 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1494 device->SamplesDone %= device->Frequency;
1495 V0(device->Backend,unlock)();
1497 if(device->Hrtf)
1499 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1500 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1501 if(lidx != -1 && ridx != -1)
1503 HrtfMixerFunc HrtfMix = SelectHrtfMixer();
1504 ALuint irsize = GetHrtfIrSize(device->Hrtf);
1505 MixHrtfParams hrtfparams;
1506 memset(&hrtfparams, 0, sizeof(hrtfparams));
1507 for(c = 0;c < device->VirtOut.NumChannels;c++)
1509 hrtfparams.Current = &device->Hrtf_Params[c];
1510 hrtfparams.Target = &device->Hrtf_Params[c];
1511 HrtfMix(device->RealOut.Buffer, lidx, ridx,
1512 device->VirtOut.Buffer[c], 0, device->Hrtf_Offset, 0,
1513 irsize, &hrtfparams, &device->Hrtf_State[c], SamplesToDo
1516 device->Hrtf_Offset += SamplesToDo;
1519 else if(device->AmbiDecoder)
1521 if(device->VirtOut.Buffer != device->FOAOut.Buffer)
1522 bformatdec_upSample(device->AmbiDecoder,
1523 device->VirtOut.Buffer, device->FOAOut.Buffer,
1524 device->FOAOut.NumChannels, SamplesToDo
1526 bformatdec_process(device->AmbiDecoder,
1527 device->RealOut.Buffer, device->RealOut.NumChannels,
1528 device->VirtOut.Buffer, SamplesToDo
1531 else
1533 if(device->Uhj_Encoder)
1535 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1536 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1537 if(lidx != -1 && ridx != -1)
1539 /* Encode to stereo-compatible 2-channel UHJ output. */
1540 EncodeUhj2(device->Uhj_Encoder,
1541 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1542 device->VirtOut.Buffer, SamplesToDo
1546 if(device->Bs2b)
1548 /* Apply binaural/crossfeed filter */
1549 for(i = 0;i < SamplesToDo;i++)
1551 float samples[2];
1552 samples[0] = device->RealOut.Buffer[0][i];
1553 samples[1] = device->RealOut.Buffer[1][i];
1554 bs2b_cross_feed(device->Bs2b, samples);
1555 device->RealOut.Buffer[0][i] = samples[0];
1556 device->RealOut.Buffer[1][i] = samples[1];
1561 if(buffer)
1563 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1564 ALuint OutChannels = device->RealOut.NumChannels;
1566 #define WRITE(T, a, b, c, d) do { \
1567 Write_##T((a), (b), (c), (d)); \
1568 buffer = (T*)buffer + (c)*(d); \
1569 } while(0)
1570 switch(device->FmtType)
1572 case DevFmtByte:
1573 WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1574 break;
1575 case DevFmtUByte:
1576 WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1577 break;
1578 case DevFmtShort:
1579 WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
1580 break;
1581 case DevFmtUShort:
1582 WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
1583 break;
1584 case DevFmtInt:
1585 WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
1586 break;
1587 case DevFmtUInt:
1588 WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
1589 break;
1590 case DevFmtFloat:
1591 WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
1592 break;
1594 #undef WRITE
1597 size -= SamplesToDo;
1598 IncrementRef(&device->MixCount);
1601 RestoreFPUMode(&oldMode);
1605 ALvoid aluHandleDisconnect(ALCdevice *device)
1607 ALCcontext *Context;
1609 device->Connected = ALC_FALSE;
1611 Context = ATOMIC_LOAD(&device->ContextList);
1612 while(Context)
1614 ALvoice *voice, *voice_end;
1616 voice = Context->Voices;
1617 voice_end = voice + Context->VoiceCount;
1618 while(voice != voice_end)
1620 ALsource *source = voice->Source;
1621 voice->Source = NULL;
1623 if(source && source->state == AL_PLAYING)
1625 source->state = AL_STOPPED;
1626 ATOMIC_STORE(&source->current_buffer, NULL);
1627 source->position = 0;
1628 source->position_fraction = 0;
1631 voice++;
1633 Context->VoiceCount = 0;
1635 Context = Context->next;