Avoid updating the effect state object if it's not changed
[openal-soft.git] / Alc / ALu.c
bloba555b8345769c90f764a4954ca68bf72f4beef50
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(IsReverbEffect(slot->Params.EffectType))
347 slot->Params.RoomRolloff = slot->Params.EffectProps.Reverb.RoomRolloffFactor;
348 slot->Params.DecayTime = slot->Params.EffectProps.Reverb.DecayTime;
349 slot->Params.AirAbsorptionGainHF = slot->Params.EffectProps.Reverb.AirAbsorptionGainHF;
351 else
353 slot->Params.RoomRolloff = 0.0f;
354 slot->Params.DecayTime = 0.0f;
355 slot->Params.AirAbsorptionGainHF = 1.0f;
357 /* If the state object is changed, exchange it with the current one so it
358 * remains in the freelist and isn't leaked.
360 if(ATOMIC_LOAD(&props->UpdateState, almemory_order_relaxed))
361 slot->Params.EffectState = ATOMIC_EXCHANGE(ALeffectState*,
362 &props->State, slot->Params.EffectState, almemory_order_relaxed
365 V(slot->Params.EffectState,update)(device, slot);
367 /* WARNING: A livelock is theoretically possible if another thread keeps
368 * changing the freelist head without giving this a chance to actually swap
369 * in the old container (practically impossible with this little code,
370 * but...).
372 first = ATOMIC_LOAD(&slot->FreeList);
373 do {
374 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
375 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
376 &slot->FreeList, &first, props) == 0);
378 return AL_TRUE;
381 ALvoid CalcNonAttnSourceParams(ALvoice *voice, const ALsource *ALSource, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
383 static const struct ChanMap MonoMap[1] = {
384 { FrontCenter, 0.0f, 0.0f }
385 }, RearMap[2] = {
386 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
387 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
388 }, QuadMap[4] = {
389 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
390 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
391 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
392 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
393 }, X51Map[6] = {
394 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
395 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
396 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
397 { LFE, 0.0f, 0.0f },
398 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
399 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
400 }, X61Map[7] = {
401 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
402 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
403 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
404 { LFE, 0.0f, 0.0f },
405 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
406 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
407 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
408 }, X71Map[8] = {
409 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
410 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
411 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
412 { LFE, 0.0f, 0.0f },
413 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
414 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
415 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
416 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
419 const ALCdevice *Device = ALContext->Device;
420 const ALlistener *Listener = ALContext->Listener;
421 ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
422 ALfloat DryGain, DryGainHF, DryGainLF;
423 ALfloat WetGain[MAX_SENDS];
424 ALfloat WetGainHF[MAX_SENDS];
425 ALfloat WetGainLF[MAX_SENDS];
426 ALeffectslot *SendSlots[MAX_SENDS];
427 ALuint NumSends, Frequency;
428 ALboolean Relative;
429 const struct ChanMap *chans = NULL;
430 struct ChanMap StereoMap[2] = {
431 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
432 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
434 ALuint num_channels = 0;
435 ALboolean DirectChannels;
436 ALboolean isbformat = AL_FALSE;
437 ALfloat Pitch;
438 ALuint i, j, c;
440 /* Get device properties */
441 NumSends = Device->NumAuxSends;
442 Frequency = Device->Frequency;
444 /* Get listener properties */
445 ListenerGain = Listener->Params.Gain;
447 /* Get source properties */
448 SourceVolume = ALSource->Gain;
449 MinVolume = ALSource->MinGain;
450 MaxVolume = ALSource->MaxGain;
451 Pitch = ALSource->Pitch;
452 Relative = ALSource->HeadRelative;
453 DirectChannels = ALSource->DirectChannels;
455 /* Convert counter-clockwise to clockwise. */
456 StereoMap[0].angle = -ALSource->StereoPan[0];
457 StereoMap[1].angle = -ALSource->StereoPan[1];
459 voice->Direct.OutBuffer = Device->Dry.Buffer;
460 voice->Direct.OutChannels = Device->Dry.NumChannels;
461 for(i = 0;i < NumSends;i++)
463 SendSlots[i] = ALSource->Send[i].Slot;
464 if(!SendSlots[i] && i == 0)
465 SendSlots[i] = Device->DefaultSlot;
466 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
468 SendSlots[i] = NULL;
469 voice->Send[i].OutBuffer = NULL;
470 voice->Send[i].OutChannels = 0;
472 else
474 voice->Send[i].OutBuffer = SendSlots[i]->WetBuffer;
475 voice->Send[i].OutChannels = SendSlots[i]->NumChannels;
479 /* Calculate the stepping value */
480 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
481 if(Pitch > (ALfloat)MAX_PITCH)
482 voice->Step = MAX_PITCH<<FRACTIONBITS;
483 else
484 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
485 BsincPrepare(voice->Step, &voice->SincState);
487 /* Calculate gains */
488 DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
489 DryGain *= ALSource->Direct.Gain * ListenerGain;
490 DryGainHF = ALSource->Direct.GainHF;
491 DryGainLF = ALSource->Direct.GainLF;
492 for(i = 0;i < NumSends;i++)
494 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
495 WetGain[i] *= ALSource->Send[i].Gain * ListenerGain;
496 WetGainHF[i] = ALSource->Send[i].GainHF;
497 WetGainLF[i] = ALSource->Send[i].GainLF;
500 switch(ALBuffer->FmtChannels)
502 case FmtMono:
503 chans = MonoMap;
504 num_channels = 1;
505 break;
507 case FmtStereo:
508 chans = StereoMap;
509 num_channels = 2;
510 break;
512 case FmtRear:
513 chans = RearMap;
514 num_channels = 2;
515 break;
517 case FmtQuad:
518 chans = QuadMap;
519 num_channels = 4;
520 break;
522 case FmtX51:
523 chans = X51Map;
524 num_channels = 6;
525 break;
527 case FmtX61:
528 chans = X61Map;
529 num_channels = 7;
530 break;
532 case FmtX71:
533 chans = X71Map;
534 num_channels = 8;
535 break;
537 case FmtBFormat2D:
538 num_channels = 3;
539 isbformat = AL_TRUE;
540 DirectChannels = AL_FALSE;
541 break;
543 case FmtBFormat3D:
544 num_channels = 4;
545 isbformat = AL_TRUE;
546 DirectChannels = AL_FALSE;
547 break;
550 if(isbformat)
552 ALfloat N[3], V[3], U[3];
553 aluMatrixf matrix;
554 ALfloat scale;
556 /* AT then UP */
557 N[0] = ALSource->Orientation[0][0];
558 N[1] = ALSource->Orientation[0][1];
559 N[2] = ALSource->Orientation[0][2];
560 aluNormalize(N);
561 V[0] = ALSource->Orientation[1][0];
562 V[1] = ALSource->Orientation[1][1];
563 V[2] = ALSource->Orientation[1][2];
564 aluNormalize(V);
565 if(!Relative)
567 const aluMatrixd *lmatrix = &Listener->Params.Matrix;
568 aluMatrixdFloat3(N, 0.0f, lmatrix);
569 aluMatrixdFloat3(V, 0.0f, lmatrix);
571 /* Build and normalize right-vector */
572 aluCrossproduct(N, V, U);
573 aluNormalize(U);
575 /* Build a rotate + conversion matrix (B-Format -> N3D). */
576 scale = 1.732050808f;
577 aluMatrixfSet(&matrix,
578 1.414213562f, 0.0f, 0.0f, 0.0f,
579 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
580 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
581 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
584 voice->Direct.OutBuffer = Device->FOAOut.Buffer;
585 voice->Direct.OutChannels = Device->FOAOut.NumChannels;
586 for(c = 0;c < num_channels;c++)
587 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
588 voice->Direct.Gains[c].Target);
590 for(i = 0;i < NumSends;i++)
592 if(!SendSlots[i])
594 for(c = 0;c < num_channels;c++)
596 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
597 voice->Send[i].Gains[c].Target[j] = 0.0f;
600 else
602 for(c = 0;c < num_channels;c++)
604 const ALeffectslot *Slot = SendSlots[i];
605 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, matrix.m[c],
606 WetGain[i], voice->Send[i].Gains[c].Target);
611 voice->IsHrtf = AL_FALSE;
613 else
615 ALfloat coeffs[MAX_AMBI_COEFFS];
617 if(DirectChannels)
619 /* Skip the virtual channels and write inputs to the real output. */
620 voice->Direct.OutBuffer = Device->RealOut.Buffer;
621 voice->Direct.OutChannels = Device->RealOut.NumChannels;
622 for(c = 0;c < num_channels;c++)
624 int idx;
625 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
626 voice->Direct.Gains[c].Target[j] = 0.0f;
627 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
628 voice->Direct.Gains[c].Target[idx] = DryGain;
631 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
632 * channel-match. */
633 for(c = 0;c < num_channels;c++)
635 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
637 for(i = 0;i < NumSends;i++)
639 if(!SendSlots[i])
641 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
642 voice->Send[i].Gains[c].Target[j] = 0.0f;
644 else
646 const ALeffectslot *Slot = SendSlots[i];
647 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
648 WetGain[i], voice->Send[i].Gains[c].Target);
653 voice->IsHrtf = AL_FALSE;
655 else if(Device->Render_Mode == HrtfRender)
657 /* Full HRTF rendering. Skip the virtual channels and render each
658 * input channel to the real outputs.
660 voice->Direct.OutBuffer = Device->RealOut.Buffer;
661 voice->Direct.OutChannels = Device->RealOut.NumChannels;
662 for(c = 0;c < num_channels;c++)
664 if(chans[c].channel == LFE)
666 /* Skip LFE */
667 voice->Direct.Hrtf[c].Target.Delay[0] = 0;
668 voice->Direct.Hrtf[c].Target.Delay[1] = 0;
669 for(i = 0;i < HRIR_LENGTH;i++)
671 voice->Direct.Hrtf[c].Target.Coeffs[i][0] = 0.0f;
672 voice->Direct.Hrtf[c].Target.Coeffs[i][1] = 0.0f;
675 for(i = 0;i < NumSends;i++)
677 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
678 voice->Send[i].Gains[c].Target[j] = 0.0f;
681 continue;
684 /* Get the static HRIR coefficients and delays for this channel. */
685 GetLerpedHrtfCoeffs(Device->Hrtf,
686 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
687 voice->Direct.Hrtf[c].Target.Coeffs,
688 voice->Direct.Hrtf[c].Target.Delay
691 /* Normal panning for auxiliary sends. */
692 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
694 for(i = 0;i < NumSends;i++)
696 if(!SendSlots[i])
698 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
699 voice->Send[i].Gains[c].Target[j] = 0.0f;
701 else
703 const ALeffectslot *Slot = SendSlots[i];
704 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
705 WetGain[i], voice->Send[i].Gains[c].Target);
710 voice->IsHrtf = AL_TRUE;
712 else
714 /* Non-HRTF rendering. Use normal panning to the output. */
715 for(c = 0;c < num_channels;c++)
717 /* Special-case LFE */
718 if(chans[c].channel == LFE)
720 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
721 voice->Direct.Gains[c].Target[j] = 0.0f;
722 if(Device->Dry.Buffer == Device->RealOut.Buffer)
724 int idx;
725 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
726 voice->Direct.Gains[c].Target[idx] = DryGain;
729 for(i = 0;i < NumSends;i++)
731 ALuint j;
732 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
733 voice->Send[i].Gains[c].Target[j] = 0.0f;
735 continue;
738 if(Device->Render_Mode == StereoPair)
740 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
741 ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
742 coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
743 voice->Direct.Gains[c].Target[0] = coeffs[0] * DryGain;
744 voice->Direct.Gains[c].Target[1] = (1.0f-coeffs[0]) * DryGain;
745 for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
746 voice->Direct.Gains[c].Target[j] = 0.0f;
748 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
750 else
752 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
753 ComputePanningGains(Device->Dry, coeffs, DryGain,
754 voice->Direct.Gains[c].Target);
757 for(i = 0;i < NumSends;i++)
759 if(!SendSlots[i])
761 ALuint j;
762 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
763 voice->Send[i].Gains[c].Target[j] = 0.0f;
765 else
767 const ALeffectslot *Slot = SendSlots[i];
768 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
769 WetGain[i], voice->Send[i].Gains[c].Target);
774 voice->IsHrtf = AL_FALSE;
779 ALfloat hfscale = ALSource->Direct.HFReference / Frequency;
780 ALfloat lfscale = ALSource->Direct.LFReference / Frequency;
781 DryGainHF = maxf(DryGainHF, 0.0001f);
782 DryGainLF = maxf(DryGainLF, 0.0001f);
783 for(c = 0;c < num_channels;c++)
785 voice->Direct.Filters[c].ActiveType = AF_None;
786 if(DryGainHF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_LowPass;
787 if(DryGainLF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_HighPass;
788 ALfilterState_setParams(
789 &voice->Direct.Filters[c].LowPass, ALfilterType_HighShelf,
790 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
792 ALfilterState_setParams(
793 &voice->Direct.Filters[c].HighPass, ALfilterType_LowShelf,
794 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
798 for(i = 0;i < NumSends;i++)
800 ALfloat hfscale = ALSource->Send[i].HFReference / Frequency;
801 ALfloat lfscale = ALSource->Send[i].LFReference / Frequency;
802 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
803 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
804 for(c = 0;c < num_channels;c++)
806 voice->Send[i].Filters[c].ActiveType = AF_None;
807 if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_LowPass;
808 if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_HighPass;
809 ALfilterState_setParams(
810 &voice->Send[i].Filters[c].LowPass, ALfilterType_HighShelf,
811 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
813 ALfilterState_setParams(
814 &voice->Send[i].Filters[c].HighPass, ALfilterType_LowShelf,
815 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
821 ALvoid CalcSourceParams(ALvoice *voice, const ALsource *ALSource, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
823 const ALCdevice *Device = ALContext->Device;
824 const ALlistener *Listener = ALContext->Listener;
825 aluVector Position, Velocity, Direction, SourceToListener;
826 ALfloat InnerAngle,OuterAngle,Angle,Distance,ClampedDist;
827 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
828 ALfloat ConeVolume,ConeHF,SourceVolume,ListenerGain;
829 ALfloat DopplerFactor, SpeedOfSound;
830 ALfloat AirAbsorptionFactor;
831 ALfloat RoomAirAbsorption[MAX_SENDS];
832 ALeffectslot *SendSlots[MAX_SENDS];
833 ALfloat Attenuation;
834 ALfloat RoomAttenuation[MAX_SENDS];
835 ALfloat MetersPerUnit;
836 ALfloat RoomRolloffBase;
837 ALfloat RoomRolloff[MAX_SENDS];
838 ALfloat DecayDistance[MAX_SENDS];
839 ALfloat DryGain;
840 ALfloat DryGainHF;
841 ALfloat DryGainLF;
842 ALboolean DryGainHFAuto;
843 ALfloat WetGain[MAX_SENDS];
844 ALfloat WetGainHF[MAX_SENDS];
845 ALfloat WetGainLF[MAX_SENDS];
846 ALboolean WetGainAuto;
847 ALboolean WetGainHFAuto;
848 ALfloat Pitch;
849 ALuint Frequency;
850 ALint NumSends;
851 ALint i;
853 DryGainHF = 1.0f;
854 DryGainLF = 1.0f;
855 for(i = 0;i < MAX_SENDS;i++)
857 WetGainHF[i] = 1.0f;
858 WetGainLF[i] = 1.0f;
861 /* Get context/device properties */
862 DopplerFactor = Listener->Params.DopplerFactor * ALSource->DopplerFactor;
863 SpeedOfSound = Listener->Params.SpeedOfSound;
864 NumSends = Device->NumAuxSends;
865 Frequency = Device->Frequency;
867 /* Get listener properties */
868 ListenerGain = Listener->Params.Gain;
869 MetersPerUnit = Listener->Params.MetersPerUnit;
871 /* Get source properties */
872 SourceVolume = ALSource->Gain;
873 MinVolume = ALSource->MinGain;
874 MaxVolume = ALSource->MaxGain;
875 Pitch = ALSource->Pitch;
876 Position = ALSource->Position;
877 Direction = ALSource->Direction;
878 Velocity = ALSource->Velocity;
879 MinDist = ALSource->RefDistance;
880 MaxDist = ALSource->MaxDistance;
881 Rolloff = ALSource->RollOffFactor;
882 InnerAngle = ALSource->InnerAngle;
883 OuterAngle = ALSource->OuterAngle;
884 AirAbsorptionFactor = ALSource->AirAbsorptionFactor;
885 DryGainHFAuto = ALSource->DryGainHFAuto;
886 WetGainAuto = ALSource->WetGainAuto;
887 WetGainHFAuto = ALSource->WetGainHFAuto;
888 RoomRolloffBase = ALSource->RoomRolloffFactor;
890 voice->Direct.OutBuffer = Device->Dry.Buffer;
891 voice->Direct.OutChannels = Device->Dry.NumChannels;
892 for(i = 0;i < NumSends;i++)
894 SendSlots[i] = ALSource->Send[i].Slot;
896 if(!SendSlots[i] && i == 0)
897 SendSlots[i] = Device->DefaultSlot;
898 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
900 SendSlots[i] = NULL;
901 RoomRolloff[i] = 0.0f;
902 DecayDistance[i] = 0.0f;
903 RoomAirAbsorption[i] = 1.0f;
905 else if(SendSlots[i]->AuxSendAuto)
907 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
908 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
909 SPEEDOFSOUNDMETRESPERSEC;
910 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
912 else
914 /* If the slot's auxiliary send auto is off, the data sent to the
915 * effect slot is the same as the dry path, sans filter effects */
916 RoomRolloff[i] = Rolloff;
917 DecayDistance[i] = 0.0f;
918 RoomAirAbsorption[i] = AIRABSORBGAINHF;
921 if(!SendSlots[i])
923 voice->Send[i].OutBuffer = NULL;
924 voice->Send[i].OutChannels = 0;
926 else
928 voice->Send[i].OutBuffer = SendSlots[i]->WetBuffer;
929 voice->Send[i].OutChannels = SendSlots[i]->NumChannels;
933 /* Transform source to listener space (convert to head relative) */
934 if(ALSource->HeadRelative == AL_FALSE)
936 const aluMatrixd *Matrix = &Listener->Params.Matrix;
937 /* Transform source vectors */
938 Position = aluMatrixdVector(Matrix, &Position);
939 Velocity = aluMatrixdVector(Matrix, &Velocity);
940 Direction = aluMatrixdVector(Matrix, &Direction);
942 else
944 const aluVector *lvelocity = &Listener->Params.Velocity;
945 /* Offset the source velocity to be relative of the listener velocity */
946 Velocity.v[0] += lvelocity->v[0];
947 Velocity.v[1] += lvelocity->v[1];
948 Velocity.v[2] += lvelocity->v[2];
951 aluNormalize(Direction.v);
952 SourceToListener.v[0] = -Position.v[0];
953 SourceToListener.v[1] = -Position.v[1];
954 SourceToListener.v[2] = -Position.v[2];
955 SourceToListener.v[3] = 0.0f;
956 Distance = aluNormalize(SourceToListener.v);
958 /* Calculate distance attenuation */
959 ClampedDist = Distance;
961 Attenuation = 1.0f;
962 for(i = 0;i < NumSends;i++)
963 RoomAttenuation[i] = 1.0f;
964 switch(ALContext->SourceDistanceModel ? ALSource->DistanceModel :
965 ALContext->DistanceModel)
967 case InverseDistanceClamped:
968 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
969 if(MaxDist < MinDist)
970 break;
971 /*fall-through*/
972 case InverseDistance:
973 if(MinDist > 0.0f)
975 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
976 if(dist > 0.0f) Attenuation = MinDist / dist;
977 for(i = 0;i < NumSends;i++)
979 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
980 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
983 break;
985 case LinearDistanceClamped:
986 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
987 if(MaxDist < MinDist)
988 break;
989 /*fall-through*/
990 case LinearDistance:
991 if(MaxDist != MinDist)
993 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
994 Attenuation = maxf(Attenuation, 0.0f);
995 for(i = 0;i < NumSends;i++)
997 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
998 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
1001 break;
1003 case ExponentDistanceClamped:
1004 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
1005 if(MaxDist < MinDist)
1006 break;
1007 /*fall-through*/
1008 case ExponentDistance:
1009 if(ClampedDist > 0.0f && MinDist > 0.0f)
1011 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
1012 for(i = 0;i < NumSends;i++)
1013 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
1015 break;
1017 case DisableDistance:
1018 ClampedDist = MinDist;
1019 break;
1022 /* Source Gain + Attenuation */
1023 DryGain = SourceVolume * Attenuation;
1024 for(i = 0;i < NumSends;i++)
1025 WetGain[i] = SourceVolume * RoomAttenuation[i];
1027 /* Distance-based air absorption */
1028 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
1030 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
1031 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
1032 for(i = 0;i < NumSends;i++)
1033 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
1036 if(WetGainAuto)
1038 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1040 /* Apply a decay-time transformation to the wet path, based on the
1041 * attenuation of the dry path.
1043 * Using the apparent distance, based on the distance attenuation, the
1044 * initial decay of the reverb effect is calculated and applied to the
1045 * wet path.
1047 for(i = 0;i < NumSends;i++)
1049 if(DecayDistance[i] > 0.0f)
1050 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1054 /* Calculate directional soundcones */
1055 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1056 if(Angle > InnerAngle && Angle <= OuterAngle)
1058 ALfloat scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1059 ConeVolume = lerp(1.0f, ALSource->OuterGain, scale);
1060 ConeHF = lerp(1.0f, ALSource->OuterGainHF, scale);
1062 else if(Angle > OuterAngle)
1064 ConeVolume = ALSource->OuterGain;
1065 ConeHF = ALSource->OuterGainHF;
1067 else
1069 ConeVolume = 1.0f;
1070 ConeHF = 1.0f;
1073 DryGain *= ConeVolume;
1074 if(WetGainAuto)
1076 for(i = 0;i < NumSends;i++)
1077 WetGain[i] *= ConeVolume;
1079 if(DryGainHFAuto)
1080 DryGainHF *= ConeHF;
1081 if(WetGainHFAuto)
1083 for(i = 0;i < NumSends;i++)
1084 WetGainHF[i] *= ConeHF;
1087 /* Clamp to Min/Max Gain */
1088 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1089 for(i = 0;i < NumSends;i++)
1090 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1092 /* Apply gain and frequency filters */
1093 DryGain *= ALSource->Direct.Gain * ListenerGain;
1094 DryGainHF *= ALSource->Direct.GainHF;
1095 DryGainLF *= ALSource->Direct.GainLF;
1096 for(i = 0;i < NumSends;i++)
1098 WetGain[i] *= ALSource->Send[i].Gain * ListenerGain;
1099 WetGainHF[i] *= ALSource->Send[i].GainHF;
1100 WetGainLF[i] *= ALSource->Send[i].GainLF;
1103 /* Calculate velocity-based doppler effect */
1104 if(DopplerFactor > 0.0f)
1106 const aluVector *lvelocity = &Listener->Params.Velocity;
1107 ALfloat VSS, VLS;
1109 if(SpeedOfSound < 1.0f)
1111 DopplerFactor *= 1.0f/SpeedOfSound;
1112 SpeedOfSound = 1.0f;
1115 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1116 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1118 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1119 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1122 /* Calculate fixed-point stepping value, based on the pitch, buffer
1123 * frequency, and output frequency.
1125 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1126 if(Pitch > (ALfloat)MAX_PITCH)
1127 voice->Step = MAX_PITCH<<FRACTIONBITS;
1128 else
1129 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1130 BsincPrepare(voice->Step, &voice->SincState);
1132 if(Device->Render_Mode == HrtfRender)
1134 /* Full HRTF rendering. Skip the virtual channels and render to the
1135 * real outputs.
1137 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1138 ALfloat ev = 0.0f, az = 0.0f;
1139 ALfloat radius = ALSource->Radius;
1140 ALfloat coeffs[MAX_AMBI_COEFFS];
1141 ALfloat spread = 0.0f;
1143 voice->Direct.OutBuffer = Device->RealOut.Buffer;
1144 voice->Direct.OutChannels = Device->RealOut.NumChannels;
1146 if(Distance > FLT_EPSILON)
1148 dir[0] = -SourceToListener.v[0];
1149 dir[1] = -SourceToListener.v[1];
1150 dir[2] = -SourceToListener.v[2] * ZScale;
1152 /* Calculate elevation and azimuth only when the source is not at
1153 * the listener. This prevents +0 and -0 Z from producing
1154 * inconsistent panning. Also, clamp Y in case FP precision errors
1155 * cause it to land outside of -1..+1. */
1156 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1157 az = atan2f(dir[0], -dir[2]);
1159 if(radius > Distance)
1160 spread = F_TAU - Distance/radius*F_PI;
1161 else if(Distance > FLT_EPSILON)
1162 spread = asinf(radius / Distance) * 2.0f;
1164 /* Get the HRIR coefficients and delays. */
1165 GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, spread, DryGain,
1166 voice->Direct.Hrtf[0].Target.Coeffs,
1167 voice->Direct.Hrtf[0].Target.Delay);
1169 CalcDirectionCoeffs(dir, spread, coeffs);
1171 for(i = 0;i < NumSends;i++)
1173 if(!SendSlots[i])
1175 ALuint j;
1176 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1177 voice->Send[i].Gains[0].Target[j] = 0.0f;
1179 else
1181 const ALeffectslot *Slot = SendSlots[i];
1182 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1183 WetGain[i], voice->Send[i].Gains[0].Target);
1187 voice->IsHrtf = AL_TRUE;
1189 else
1191 /* Non-HRTF rendering. */
1192 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1193 ALfloat radius = ALSource->Radius;
1194 ALfloat coeffs[MAX_AMBI_COEFFS];
1195 ALfloat spread = 0.0f;
1197 /* Get the localized direction, and compute panned gains. */
1198 if(Distance > FLT_EPSILON)
1200 dir[0] = -SourceToListener.v[0];
1201 dir[1] = -SourceToListener.v[1];
1202 dir[2] = -SourceToListener.v[2] * ZScale;
1204 if(radius > Distance)
1205 spread = F_TAU - Distance/radius*F_PI;
1206 else if(Distance > FLT_EPSILON)
1207 spread = asinf(radius / Distance) * 2.0f;
1209 if(Device->Render_Mode == StereoPair)
1211 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
1212 ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
1213 x = clampf(x, -0.5f, 0.5f) + 0.5f;
1214 voice->Direct.Gains[0].Target[0] = x * DryGain;
1215 voice->Direct.Gains[0].Target[1] = (1.0f-x) * DryGain;
1216 for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
1217 voice->Direct.Gains[0].Target[i] = 0.0f;
1219 CalcDirectionCoeffs(dir, spread, coeffs);
1221 else
1223 CalcDirectionCoeffs(dir, spread, coeffs);
1224 ComputePanningGains(Device->Dry, coeffs, DryGain, voice->Direct.Gains[0].Target);
1227 for(i = 0;i < NumSends;i++)
1229 if(!SendSlots[i])
1231 ALuint j;
1232 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1233 voice->Send[i].Gains[0].Target[j] = 0.0f;
1235 else
1237 const ALeffectslot *Slot = SendSlots[i];
1238 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1239 WetGain[i], voice->Send[i].Gains[0].Target);
1243 voice->IsHrtf = AL_FALSE;
1247 ALfloat hfscale = ALSource->Direct.HFReference / Frequency;
1248 ALfloat lfscale = ALSource->Direct.LFReference / Frequency;
1249 DryGainHF = maxf(DryGainHF, 0.0001f);
1250 DryGainLF = maxf(DryGainLF, 0.0001f);
1251 voice->Direct.Filters[0].ActiveType = AF_None;
1252 if(DryGainHF != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_LowPass;
1253 if(DryGainLF != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_HighPass;
1254 ALfilterState_setParams(
1255 &voice->Direct.Filters[0].LowPass, ALfilterType_HighShelf,
1256 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
1258 ALfilterState_setParams(
1259 &voice->Direct.Filters[0].HighPass, ALfilterType_LowShelf,
1260 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
1263 for(i = 0;i < NumSends;i++)
1265 ALfloat hfscale = ALSource->Send[i].HFReference / Frequency;
1266 ALfloat lfscale = ALSource->Send[i].LFReference / Frequency;
1267 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
1268 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
1269 voice->Send[i].Filters[0].ActiveType = AF_None;
1270 if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_LowPass;
1271 if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_HighPass;
1272 ALfilterState_setParams(
1273 &voice->Send[i].Filters[0].LowPass, ALfilterType_HighShelf,
1274 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
1276 ALfilterState_setParams(
1277 &voice->Send[i].Filters[0].HighPass, ALfilterType_LowShelf,
1278 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
1284 void UpdateContextSources(ALCcontext *ctx)
1286 ALvoice *voice, *voice_end;
1287 ALboolean fullupdate;
1288 ALsource *source;
1290 fullupdate = CalcListenerParams(ctx);
1291 #define UPDATE_SLOT(iter) do { \
1292 if(CalcEffectSlotParams(*iter, ctx->Device)) \
1293 fullupdate = AL_TRUE; \
1294 } while(0)
1295 VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, UPDATE_SLOT);
1296 #undef UPDATE_SLOT
1298 if(fullupdate)
1300 voice = ctx->Voices;
1301 voice_end = voice + ctx->VoiceCount;
1302 for(;voice != voice_end;++voice)
1304 if(!(source=voice->Source)) continue;
1305 if(source->state != AL_PLAYING && source->state != AL_PAUSED)
1306 voice->Source = NULL;
1307 else
1309 ALbufferlistitem *BufferListItem;
1310 BufferListItem = ATOMIC_LOAD(&source->queue);
1311 while(BufferListItem != NULL)
1313 ALbuffer *buffer;
1314 if((buffer=BufferListItem->buffer) != NULL)
1316 ATOMIC_STORE(&source->NeedsUpdate, AL_FALSE);
1317 voice->Update(voice, source, buffer, ctx);
1318 break;
1320 BufferListItem = BufferListItem->next;
1325 else
1327 voice = ctx->Voices;
1328 voice_end = voice + ctx->VoiceCount;
1329 for(;voice != voice_end;++voice)
1331 if(!(source=voice->Source)) continue;
1332 if(source->state != AL_PLAYING && source->state != AL_PAUSED)
1333 voice->Source = NULL;
1334 else if(ATOMIC_EXCHANGE(ALenum, &source->NeedsUpdate, AL_FALSE))
1336 ALbufferlistitem *BufferListItem;
1337 BufferListItem = ATOMIC_LOAD(&source->queue);
1338 while(BufferListItem != NULL)
1340 ALbuffer *buffer;
1341 if((buffer=BufferListItem->buffer) != NULL)
1343 voice->Update(voice, source, buffer, ctx);
1344 break;
1346 BufferListItem = BufferListItem->next;
1354 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1355 * converts NaN to 0. */
1356 static inline ALfloat aluClampf(ALfloat val)
1358 if(fabsf(val) <= 1.0f) return val;
1359 return (ALfloat)((0.0f < val) - (val < 0.0f));
1362 static inline ALfloat aluF2F(ALfloat val)
1363 { return val; }
1365 static inline ALint aluF2I(ALfloat val)
1367 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1368 * integer range normalized floats can be safely converted to.
1370 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1372 static inline ALuint aluF2UI(ALfloat val)
1373 { return aluF2I(val)+2147483648u; }
1375 static inline ALshort aluF2S(ALfloat val)
1376 { return fastf2i(aluClampf(val)*32767.0f); }
1377 static inline ALushort aluF2US(ALfloat val)
1378 { return aluF2S(val)+32768; }
1380 static inline ALbyte aluF2B(ALfloat val)
1381 { return fastf2i(aluClampf(val)*127.0f); }
1382 static inline ALubyte aluF2UB(ALfloat val)
1383 { return aluF2B(val)+128; }
1385 #define DECL_TEMPLATE(T, func) \
1386 static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1387 ALuint SamplesToDo, ALuint numchans) \
1389 ALuint i, j; \
1390 for(j = 0;j < numchans;j++) \
1392 const ALfloat *in = InBuffer[j]; \
1393 T *restrict out = (T*)OutBuffer + j; \
1394 for(i = 0;i < SamplesToDo;i++) \
1395 out[i*numchans] = func(in[i]); \
1399 DECL_TEMPLATE(ALfloat, aluF2F)
1400 DECL_TEMPLATE(ALuint, aluF2UI)
1401 DECL_TEMPLATE(ALint, aluF2I)
1402 DECL_TEMPLATE(ALushort, aluF2US)
1403 DECL_TEMPLATE(ALshort, aluF2S)
1404 DECL_TEMPLATE(ALubyte, aluF2UB)
1405 DECL_TEMPLATE(ALbyte, aluF2B)
1407 #undef DECL_TEMPLATE
1410 ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1412 ALuint SamplesToDo;
1413 ALvoice *voice, *voice_end;
1414 ALeffectslot *slot;
1415 ALsource *source;
1416 ALCcontext *ctx;
1417 FPUCtl oldMode;
1418 ALuint i, c;
1420 SetMixerFPUMode(&oldMode);
1422 while(size > 0)
1424 IncrementRef(&device->MixCount);
1426 SamplesToDo = minu(size, BUFFERSIZE);
1427 for(c = 0;c < device->VirtOut.NumChannels;c++)
1428 memset(device->VirtOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1429 for(c = 0;c < device->RealOut.NumChannels;c++)
1430 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1431 if(device->Dry.Buffer != device->FOAOut.Buffer)
1432 for(c = 0;c < device->FOAOut.NumChannels;c++)
1433 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1435 V0(device->Backend,lock)();
1437 if((slot=device->DefaultSlot) != NULL)
1439 CalcEffectSlotParams(device->DefaultSlot, device);
1440 for(i = 0;i < slot->NumChannels;i++)
1441 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1444 ctx = ATOMIC_LOAD(&device->ContextList);
1445 while(ctx)
1447 if(!ctx->DeferUpdates)
1448 UpdateContextSources(ctx);
1449 #define CLEAR_WET_BUFFER(iter) do { \
1450 for(i = 0;i < (*iter)->NumChannels;i++) \
1451 memset((*iter)->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat)); \
1452 } while(0)
1453 VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, CLEAR_WET_BUFFER);
1454 #undef CLEAR_WET_BUFFER
1456 /* source processing */
1457 voice = ctx->Voices;
1458 voice_end = voice + ctx->VoiceCount;
1459 for(;voice != voice_end;++voice)
1461 source = voice->Source;
1462 if(source && source->state == AL_PLAYING)
1463 MixSource(voice, source, device, SamplesToDo);
1466 /* effect slot processing */
1467 c = VECTOR_SIZE(ctx->ActiveAuxSlots);
1468 for(i = 0;i < c;i++)
1470 const ALeffectslot *slot = VECTOR_ELEM(ctx->ActiveAuxSlots, i);
1471 ALeffectState *state = slot->Params.EffectState;
1472 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1473 state->OutChannels);
1476 ctx = ctx->next;
1479 if(device->DefaultSlot != NULL)
1481 const ALeffectslot *slot = device->DefaultSlot;
1482 ALeffectState *state = slot->Params.EffectState;
1483 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1484 state->OutChannels);
1487 /* Increment the clock time. Every second's worth of samples is
1488 * converted and added to clock base so that large sample counts don't
1489 * overflow during conversion. This also guarantees an exact, stable
1490 * conversion. */
1491 device->SamplesDone += SamplesToDo;
1492 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1493 device->SamplesDone %= device->Frequency;
1494 V0(device->Backend,unlock)();
1496 if(device->Hrtf)
1498 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1499 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1500 if(lidx != -1 && ridx != -1)
1502 HrtfMixerFunc HrtfMix = SelectHrtfMixer();
1503 ALuint irsize = GetHrtfIrSize(device->Hrtf);
1504 MixHrtfParams hrtfparams;
1505 memset(&hrtfparams, 0, sizeof(hrtfparams));
1506 for(c = 0;c < device->VirtOut.NumChannels;c++)
1508 hrtfparams.Current = &device->Hrtf_Params[c];
1509 hrtfparams.Target = &device->Hrtf_Params[c];
1510 HrtfMix(device->RealOut.Buffer, lidx, ridx,
1511 device->VirtOut.Buffer[c], 0, device->Hrtf_Offset, 0,
1512 irsize, &hrtfparams, &device->Hrtf_State[c], SamplesToDo
1515 device->Hrtf_Offset += SamplesToDo;
1518 else if(device->AmbiDecoder)
1520 if(device->VirtOut.Buffer != device->FOAOut.Buffer)
1521 bformatdec_upSample(device->AmbiDecoder,
1522 device->VirtOut.Buffer, device->FOAOut.Buffer,
1523 device->FOAOut.NumChannels, SamplesToDo
1525 bformatdec_process(device->AmbiDecoder,
1526 device->RealOut.Buffer, device->RealOut.NumChannels,
1527 device->VirtOut.Buffer, SamplesToDo
1530 else
1532 if(device->Uhj_Encoder)
1534 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1535 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1536 if(lidx != -1 && ridx != -1)
1538 /* Encode to stereo-compatible 2-channel UHJ output. */
1539 EncodeUhj2(device->Uhj_Encoder,
1540 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1541 device->VirtOut.Buffer, SamplesToDo
1545 if(device->Bs2b)
1547 /* Apply binaural/crossfeed filter */
1548 for(i = 0;i < SamplesToDo;i++)
1550 float samples[2];
1551 samples[0] = device->RealOut.Buffer[0][i];
1552 samples[1] = device->RealOut.Buffer[1][i];
1553 bs2b_cross_feed(device->Bs2b, samples);
1554 device->RealOut.Buffer[0][i] = samples[0];
1555 device->RealOut.Buffer[1][i] = samples[1];
1560 if(buffer)
1562 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1563 ALuint OutChannels = device->RealOut.NumChannels;
1565 #define WRITE(T, a, b, c, d) do { \
1566 Write_##T((a), (b), (c), (d)); \
1567 buffer = (T*)buffer + (c)*(d); \
1568 } while(0)
1569 switch(device->FmtType)
1571 case DevFmtByte:
1572 WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1573 break;
1574 case DevFmtUByte:
1575 WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1576 break;
1577 case DevFmtShort:
1578 WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
1579 break;
1580 case DevFmtUShort:
1581 WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
1582 break;
1583 case DevFmtInt:
1584 WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
1585 break;
1586 case DevFmtUInt:
1587 WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
1588 break;
1589 case DevFmtFloat:
1590 WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
1591 break;
1593 #undef WRITE
1596 size -= SamplesToDo;
1597 IncrementRef(&device->MixCount);
1600 RestoreFPUMode(&oldMode);
1604 ALvoid aluHandleDisconnect(ALCdevice *device)
1606 ALCcontext *Context;
1608 device->Connected = ALC_FALSE;
1610 Context = ATOMIC_LOAD(&device->ContextList);
1611 while(Context)
1613 ALvoice *voice, *voice_end;
1615 voice = Context->Voices;
1616 voice_end = voice + Context->VoiceCount;
1617 while(voice != voice_end)
1619 ALsource *source = voice->Source;
1620 voice->Source = NULL;
1622 if(source && source->state == AL_PLAYING)
1624 source->state = AL_STOPPED;
1625 ATOMIC_STORE(&source->current_buffer, NULL);
1626 source->position = 0;
1627 source->position_fraction = 0;
1630 voice++;
1632 Context->VoiceCount = 0;
1634 Context = Context->next;