Avoid function calls to get the HRTF sample rate and IR size
[openal-soft.git] / Alc / ALu.c
blobac958d410645704c50db20475de1e8b59965825d
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);
97 static inline HrtfMixerFunc SelectHrtfMixer(void)
99 #ifdef HAVE_SSE
100 if((CPUCapFlags&CPU_CAP_SSE))
101 return MixHrtf_SSE;
102 #endif
103 #ifdef HAVE_NEON
104 if((CPUCapFlags&CPU_CAP_NEON))
105 return MixHrtf_Neon;
106 #endif
108 return MixHrtf_C;
112 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
114 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
115 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
116 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
119 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
121 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
124 static ALfloat aluNormalize(ALfloat *vec)
126 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
127 if(length > 0.0f)
129 ALfloat inv_length = 1.0f/length;
130 vec[0] *= inv_length;
131 vec[1] *= inv_length;
132 vec[2] *= inv_length;
134 return length;
137 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
139 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
141 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];
142 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];
143 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];
146 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
148 aluVector v;
149 v.v[0] = vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0];
150 v.v[1] = vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1];
151 v.v[2] = vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2];
152 v.v[3] = vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3];
153 return v;
157 /* Prepares the interpolator for a given rate (determined by increment). A
158 * result of AL_FALSE indicates that the filter output will completely cut
159 * the input signal.
161 * With a bit of work, and a trade of memory for CPU cost, this could be
162 * modified for use with an interpolated increment for buttery-smooth pitch
163 * changes.
165 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
167 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
168 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
169 static const ALuint to[4][BSINC_SCALE_COUNT] =
171 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
172 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
173 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
174 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
176 static const ALuint tm[2][BSINC_SCALE_COUNT] =
178 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
179 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
181 ALfloat sf;
182 ALuint si, pi;
183 ALboolean uncut = AL_TRUE;
185 if(increment > FRACTIONONE)
187 sf = (ALfloat)FRACTIONONE / increment;
188 if(sf < scaleBase)
190 /* Signal has been completely cut. The return result can be used
191 * to skip the filter (and output zeros) as an optimization.
193 sf = 0.0f;
194 si = 0;
195 uncut = AL_FALSE;
197 else
199 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
200 si = fastf2u(sf);
201 /* The interpolation factor is fit to this diagonally-symmetric
202 * curve to reduce the transition ripple caused by interpolating
203 * different scales of the sinc function.
205 sf = 1.0f - cosf(asinf(sf - si));
208 else
210 sf = 0.0f;
211 si = BSINC_SCALE_COUNT - 1;
214 state->sf = sf;
215 state->m = m[si];
216 state->l = -(ALint)((m[si] / 2) - 1);
217 /* The CPU cost of this table re-mapping could be traded for the memory
218 * cost of a complete table map (1024 elements large).
220 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
222 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
223 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
224 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
225 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
227 return uncut;
231 static void CalcListenerParams(ALCcontext *Context)
233 ALlistener *Listener = Context->Listener;
234 ALfloat N[3], V[3], U[3], P[3];
235 struct ALlistenerProps *first;
236 struct ALlistenerProps *props;
237 aluVector vel;
239 props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &Listener->Update, NULL, almemory_order_acq_rel);
240 if(!props) return;
242 /* AT then UP */
243 N[0] = ATOMIC_LOAD(&props->Forward[0], almemory_order_relaxed);
244 N[1] = ATOMIC_LOAD(&props->Forward[1], almemory_order_relaxed);
245 N[2] = ATOMIC_LOAD(&props->Forward[2], almemory_order_relaxed);
246 aluNormalize(N);
247 V[0] = ATOMIC_LOAD(&props->Up[0], almemory_order_relaxed);
248 V[1] = ATOMIC_LOAD(&props->Up[1], almemory_order_relaxed);
249 V[2] = ATOMIC_LOAD(&props->Up[2], almemory_order_relaxed);
250 aluNormalize(V);
251 /* Build and normalize right-vector */
252 aluCrossproduct(N, V, U);
253 aluNormalize(U);
255 aluMatrixfSet(&Listener->Params.Matrix,
256 U[0], V[0], -N[0], 0.0,
257 U[1], V[1], -N[1], 0.0,
258 U[2], V[2], -N[2], 0.0,
259 0.0, 0.0, 0.0, 1.0
262 P[0] = ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed);
263 P[1] = ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed);
264 P[2] = ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed);
265 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
266 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
268 aluVectorSet(&vel, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
269 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
270 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
271 0.0f);
272 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
274 Listener->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
275 Listener->Params.MetersPerUnit = ATOMIC_LOAD(&props->MetersPerUnit, almemory_order_relaxed);
277 Listener->Params.DopplerFactor = ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
278 Listener->Params.SpeedOfSound = ATOMIC_LOAD(&props->SpeedOfSound, almemory_order_relaxed) *
279 ATOMIC_LOAD(&props->DopplerVelocity, almemory_order_relaxed);
281 /* WARNING: A livelock is theoretically possible if another thread keeps
282 * changing the freelist head without giving this a chance to actually swap
283 * in the old container (practically impossible with this little code,
284 * but...).
286 first = ATOMIC_LOAD(&Listener->FreeList);
287 do {
288 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
289 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*,
290 &Listener->FreeList, &first, props) == 0);
293 static void CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
295 struct ALeffectslotProps *first;
296 struct ALeffectslotProps *props;
297 ALeffectState *state;
299 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL, almemory_order_acq_rel);
300 if(!props) return;
302 slot->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
303 slot->Params.AuxSendAuto = ATOMIC_LOAD(&props->AuxSendAuto, almemory_order_relaxed);
304 slot->Params.EffectType = ATOMIC_LOAD(&props->Type, almemory_order_relaxed);
305 if(IsReverbEffect(slot->Params.EffectType))
307 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
308 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
309 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
311 else
313 slot->Params.RoomRolloff = 0.0f;
314 slot->Params.DecayTime = 0.0f;
315 slot->Params.AirAbsorptionGainHF = 1.0f;
317 state = ATOMIC_EXCHANGE(ALeffectState*, &props->State, NULL, almemory_order_relaxed);
319 /* If the state object is changed, exchange it with the current one so it
320 * remains in the freelist and isn't leaked.
322 if(state != slot->Params.EffectState)
324 ATOMIC_STORE(&props->State, slot->Params.EffectState, almemory_order_relaxed);
325 slot->Params.EffectState = state;
328 V(slot->Params.EffectState,update)(device, slot, &props->Props);
330 /* WARNING: A livelock is theoretically possible if another thread keeps
331 * changing the freelist head without giving this a chance to actually swap
332 * in the old container (practically impossible with this little code,
333 * but...).
335 first = ATOMIC_LOAD(&slot->FreeList);
336 do {
337 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
338 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
339 &slot->FreeList, &first, props) == 0);
343 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
345 static const struct ChanMap MonoMap[1] = {
346 { FrontCenter, 0.0f, 0.0f }
347 }, RearMap[2] = {
348 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
349 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
350 }, QuadMap[4] = {
351 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
352 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
353 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
354 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
355 }, X51Map[6] = {
356 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
357 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
358 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
359 { LFE, 0.0f, 0.0f },
360 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
361 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
362 }, X61Map[7] = {
363 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
364 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
365 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
366 { LFE, 0.0f, 0.0f },
367 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
368 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
369 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
370 }, X71Map[8] = {
371 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
372 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
373 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
374 { LFE, 0.0f, 0.0f },
375 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
376 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
377 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
378 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
381 const ALCdevice *Device = ALContext->Device;
382 const ALlistener *Listener = ALContext->Listener;
383 ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
384 ALfloat DryGain, DryGainHF, DryGainLF;
385 ALfloat WetGain[MAX_SENDS];
386 ALfloat WetGainHF[MAX_SENDS];
387 ALfloat WetGainLF[MAX_SENDS];
388 ALeffectslot *SendSlots[MAX_SENDS];
389 ALuint NumSends, Frequency;
390 ALboolean Relative;
391 const struct ChanMap *chans = NULL;
392 struct ChanMap StereoMap[2] = {
393 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
394 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
396 ALuint num_channels = 0;
397 ALboolean DirectChannels;
398 ALboolean isbformat = AL_FALSE;
399 ALfloat Pitch;
400 ALuint i, j, c;
402 /* Get device properties */
403 NumSends = Device->NumAuxSends;
404 Frequency = Device->Frequency;
406 /* Get listener properties */
407 ListenerGain = Listener->Params.Gain;
409 /* Get source properties */
410 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
411 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
412 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
413 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
414 Relative = ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed);
415 DirectChannels = ATOMIC_LOAD(&props->DirectChannels, almemory_order_relaxed);
417 /* Convert counter-clockwise to clockwise. */
418 StereoMap[0].angle = -ATOMIC_LOAD(&props->StereoPan[0], almemory_order_relaxed);
419 StereoMap[1].angle = -ATOMIC_LOAD(&props->StereoPan[1], almemory_order_relaxed);
421 voice->Direct.OutBuffer = Device->Dry.Buffer;
422 voice->Direct.OutChannels = Device->Dry.NumChannels;
423 for(i = 0;i < NumSends;i++)
425 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
426 if(!SendSlots[i] && i == 0)
427 SendSlots[i] = Device->DefaultSlot;
428 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
430 SendSlots[i] = NULL;
431 voice->Send[i].OutBuffer = NULL;
432 voice->Send[i].OutChannels = 0;
434 else
436 voice->Send[i].OutBuffer = SendSlots[i]->WetBuffer;
437 voice->Send[i].OutChannels = SendSlots[i]->NumChannels;
440 voice->Looping = ATOMIC_LOAD(&props->Looping, almemory_order_relaxed);
442 /* Calculate the stepping value */
443 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
444 if(Pitch > (ALfloat)MAX_PITCH)
445 voice->Step = MAX_PITCH<<FRACTIONBITS;
446 else
447 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
448 BsincPrepare(voice->Step, &voice->SincState);
450 /* Calculate gains */
451 DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
452 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
453 DryGainHF = ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
454 DryGainLF = ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
455 for(i = 0;i < NumSends;i++)
457 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
458 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
459 WetGainHF[i] = ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
460 WetGainLF[i] = ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
463 switch(ALBuffer->FmtChannels)
465 case FmtMono:
466 chans = MonoMap;
467 num_channels = 1;
468 break;
470 case FmtStereo:
471 chans = StereoMap;
472 num_channels = 2;
473 break;
475 case FmtRear:
476 chans = RearMap;
477 num_channels = 2;
478 break;
480 case FmtQuad:
481 chans = QuadMap;
482 num_channels = 4;
483 break;
485 case FmtX51:
486 chans = X51Map;
487 num_channels = 6;
488 break;
490 case FmtX61:
491 chans = X61Map;
492 num_channels = 7;
493 break;
495 case FmtX71:
496 chans = X71Map;
497 num_channels = 8;
498 break;
500 case FmtBFormat2D:
501 num_channels = 3;
502 isbformat = AL_TRUE;
503 DirectChannels = AL_FALSE;
504 break;
506 case FmtBFormat3D:
507 num_channels = 4;
508 isbformat = AL_TRUE;
509 DirectChannels = AL_FALSE;
510 break;
513 if(isbformat)
515 ALfloat N[3], V[3], U[3];
516 aluMatrixf matrix;
517 ALfloat scale;
519 /* AT then UP */
520 N[0] = ATOMIC_LOAD(&props->Orientation[0][0], almemory_order_relaxed);
521 N[1] = ATOMIC_LOAD(&props->Orientation[0][1], almemory_order_relaxed);
522 N[2] = ATOMIC_LOAD(&props->Orientation[0][2], almemory_order_relaxed);
523 aluNormalize(N);
524 V[0] = ATOMIC_LOAD(&props->Orientation[1][0], almemory_order_relaxed);
525 V[1] = ATOMIC_LOAD(&props->Orientation[1][1], almemory_order_relaxed);
526 V[2] = ATOMIC_LOAD(&props->Orientation[1][2], almemory_order_relaxed);
527 aluNormalize(V);
528 if(!Relative)
530 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
531 aluMatrixfFloat3(N, 0.0f, lmatrix);
532 aluMatrixfFloat3(V, 0.0f, lmatrix);
534 /* Build and normalize right-vector */
535 aluCrossproduct(N, V, U);
536 aluNormalize(U);
538 /* Build a rotate + conversion matrix (B-Format -> N3D). */
539 scale = 1.732050808f;
540 aluMatrixfSet(&matrix,
541 1.414213562f, 0.0f, 0.0f, 0.0f,
542 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
543 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
544 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
547 voice->Direct.OutBuffer = Device->FOAOut.Buffer;
548 voice->Direct.OutChannels = Device->FOAOut.NumChannels;
549 for(c = 0;c < num_channels;c++)
550 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
551 voice->Direct.Gains[c].Target);
553 for(i = 0;i < NumSends;i++)
555 if(!SendSlots[i])
557 for(c = 0;c < num_channels;c++)
559 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
560 voice->Send[i].Gains[c].Target[j] = 0.0f;
563 else
565 for(c = 0;c < num_channels;c++)
567 const ALeffectslot *Slot = SendSlots[i];
568 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, matrix.m[c],
569 WetGain[i], voice->Send[i].Gains[c].Target);
574 voice->IsHrtf = AL_FALSE;
576 else
578 ALfloat coeffs[MAX_AMBI_COEFFS];
580 if(DirectChannels)
582 /* Skip the virtual channels and write inputs to the real output. */
583 voice->Direct.OutBuffer = Device->RealOut.Buffer;
584 voice->Direct.OutChannels = Device->RealOut.NumChannels;
585 for(c = 0;c < num_channels;c++)
587 int idx;
588 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
589 voice->Direct.Gains[c].Target[j] = 0.0f;
590 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
591 voice->Direct.Gains[c].Target[idx] = DryGain;
594 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
595 * channel-match. */
596 for(c = 0;c < num_channels;c++)
598 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
600 for(i = 0;i < NumSends;i++)
602 if(!SendSlots[i])
604 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
605 voice->Send[i].Gains[c].Target[j] = 0.0f;
607 else
609 const ALeffectslot *Slot = SendSlots[i];
610 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
611 WetGain[i], voice->Send[i].Gains[c].Target);
616 voice->IsHrtf = AL_FALSE;
618 else if(Device->Render_Mode == HrtfRender)
620 /* Full HRTF rendering. Skip the virtual channels and render each
621 * input channel to the real outputs.
623 voice->Direct.OutBuffer = Device->RealOut.Buffer;
624 voice->Direct.OutChannels = Device->RealOut.NumChannels;
625 for(c = 0;c < num_channels;c++)
627 if(chans[c].channel == LFE)
629 /* Skip LFE */
630 voice->Direct.Hrtf[c].Target.Delay[0] = 0;
631 voice->Direct.Hrtf[c].Target.Delay[1] = 0;
632 for(i = 0;i < HRIR_LENGTH;i++)
634 voice->Direct.Hrtf[c].Target.Coeffs[i][0] = 0.0f;
635 voice->Direct.Hrtf[c].Target.Coeffs[i][1] = 0.0f;
638 for(i = 0;i < NumSends;i++)
640 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
641 voice->Send[i].Gains[c].Target[j] = 0.0f;
644 continue;
647 /* Get the static HRIR coefficients and delays for this channel. */
648 GetLerpedHrtfCoeffs(Device->Hrtf,
649 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
650 voice->Direct.Hrtf[c].Target.Coeffs,
651 voice->Direct.Hrtf[c].Target.Delay
654 /* Normal panning for auxiliary sends. */
655 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
657 for(i = 0;i < NumSends;i++)
659 if(!SendSlots[i])
661 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
662 voice->Send[i].Gains[c].Target[j] = 0.0f;
664 else
666 const ALeffectslot *Slot = SendSlots[i];
667 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
668 WetGain[i], voice->Send[i].Gains[c].Target);
673 voice->IsHrtf = AL_TRUE;
675 else
677 /* Non-HRTF rendering. Use normal panning to the output. */
678 for(c = 0;c < num_channels;c++)
680 /* Special-case LFE */
681 if(chans[c].channel == LFE)
683 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
684 voice->Direct.Gains[c].Target[j] = 0.0f;
685 if(Device->Dry.Buffer == Device->RealOut.Buffer)
687 int idx;
688 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
689 voice->Direct.Gains[c].Target[idx] = DryGain;
692 for(i = 0;i < NumSends;i++)
694 ALuint j;
695 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
696 voice->Send[i].Gains[c].Target[j] = 0.0f;
698 continue;
701 if(Device->Render_Mode == StereoPair)
703 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
704 ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
705 coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
706 voice->Direct.Gains[c].Target[0] = coeffs[0] * DryGain;
707 voice->Direct.Gains[c].Target[1] = (1.0f-coeffs[0]) * DryGain;
708 for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
709 voice->Direct.Gains[c].Target[j] = 0.0f;
711 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
713 else
715 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
716 ComputePanningGains(Device->Dry, coeffs, DryGain,
717 voice->Direct.Gains[c].Target);
720 for(i = 0;i < NumSends;i++)
722 if(!SendSlots[i])
724 ALuint j;
725 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
726 voice->Send[i].Gains[c].Target[j] = 0.0f;
728 else
730 const ALeffectslot *Slot = SendSlots[i];
731 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
732 WetGain[i], voice->Send[i].Gains[c].Target);
737 voice->IsHrtf = AL_FALSE;
742 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
743 Frequency;
744 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
745 Frequency;
746 DryGainHF = maxf(DryGainHF, 0.0001f);
747 DryGainLF = maxf(DryGainLF, 0.0001f);
748 for(c = 0;c < num_channels;c++)
750 voice->Direct.Filters[c].ActiveType = AF_None;
751 if(DryGainHF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_LowPass;
752 if(DryGainLF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_HighPass;
753 ALfilterState_setParams(
754 &voice->Direct.Filters[c].LowPass, ALfilterType_HighShelf,
755 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
757 ALfilterState_setParams(
758 &voice->Direct.Filters[c].HighPass, ALfilterType_LowShelf,
759 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
763 for(i = 0;i < NumSends;i++)
765 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
766 Frequency;
767 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
768 Frequency;
769 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
770 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
771 for(c = 0;c < num_channels;c++)
773 voice->Send[i].Filters[c].ActiveType = AF_None;
774 if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_LowPass;
775 if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_HighPass;
776 ALfilterState_setParams(
777 &voice->Send[i].Filters[c].LowPass, ALfilterType_HighShelf,
778 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
780 ALfilterState_setParams(
781 &voice->Send[i].Filters[c].HighPass, ALfilterType_LowShelf,
782 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
788 static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
790 const ALCdevice *Device = ALContext->Device;
791 const ALlistener *Listener = ALContext->Listener;
792 aluVector Position, Velocity, Direction, SourceToListener;
793 ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
794 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
795 ALfloat SourceVolume,ListenerGain;
796 ALfloat DopplerFactor, SpeedOfSound;
797 ALfloat AirAbsorptionFactor;
798 ALfloat RoomAirAbsorption[MAX_SENDS];
799 ALeffectslot *SendSlots[MAX_SENDS];
800 ALfloat Attenuation;
801 ALfloat RoomAttenuation[MAX_SENDS];
802 ALfloat MetersPerUnit;
803 ALfloat RoomRolloffBase;
804 ALfloat RoomRolloff[MAX_SENDS];
805 ALfloat DecayDistance[MAX_SENDS];
806 ALfloat DryGain;
807 ALfloat DryGainHF;
808 ALfloat DryGainLF;
809 ALboolean DryGainHFAuto;
810 ALfloat WetGain[MAX_SENDS];
811 ALfloat WetGainHF[MAX_SENDS];
812 ALfloat WetGainLF[MAX_SENDS];
813 ALboolean WetGainAuto;
814 ALboolean WetGainHFAuto;
815 ALfloat Pitch;
816 ALuint Frequency;
817 ALint NumSends;
818 ALint i;
820 DryGainHF = 1.0f;
821 DryGainLF = 1.0f;
822 for(i = 0;i < MAX_SENDS;i++)
824 WetGainHF[i] = 1.0f;
825 WetGainLF[i] = 1.0f;
828 /* Get context/device properties */
829 DopplerFactor = Listener->Params.DopplerFactor;
830 SpeedOfSound = Listener->Params.SpeedOfSound;
831 NumSends = Device->NumAuxSends;
832 Frequency = Device->Frequency;
834 /* Get listener properties */
835 ListenerGain = Listener->Params.Gain;
836 MetersPerUnit = Listener->Params.MetersPerUnit;
838 /* Get source properties */
839 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
840 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
841 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
842 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
843 aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed),
844 ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed),
845 ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed),
846 1.0f);
847 aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed),
848 ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed),
849 ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed),
850 0.0f);
851 aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
852 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
853 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
854 0.0f);
855 MinDist = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed);
856 MaxDist = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed);
857 Rolloff = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed);
858 DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
859 InnerAngle = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed);
860 OuterAngle = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed);
861 AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed);
862 DryGainHFAuto = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed);
863 WetGainAuto = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed);
864 WetGainHFAuto = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed);
865 RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed);
867 voice->Direct.OutBuffer = Device->Dry.Buffer;
868 voice->Direct.OutChannels = Device->Dry.NumChannels;
869 for(i = 0;i < NumSends;i++)
871 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
873 if(!SendSlots[i] && i == 0)
874 SendSlots[i] = Device->DefaultSlot;
875 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
877 SendSlots[i] = NULL;
878 RoomRolloff[i] = 0.0f;
879 DecayDistance[i] = 0.0f;
880 RoomAirAbsorption[i] = 1.0f;
882 else if(SendSlots[i]->Params.AuxSendAuto)
884 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
885 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
886 SPEEDOFSOUNDMETRESPERSEC;
887 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
889 else
891 /* If the slot's auxiliary send auto is off, the data sent to the
892 * effect slot is the same as the dry path, sans filter effects */
893 RoomRolloff[i] = Rolloff;
894 DecayDistance[i] = 0.0f;
895 RoomAirAbsorption[i] = AIRABSORBGAINHF;
898 if(!SendSlots[i])
900 voice->Send[i].OutBuffer = NULL;
901 voice->Send[i].OutChannels = 0;
903 else
905 voice->Send[i].OutBuffer = SendSlots[i]->WetBuffer;
906 voice->Send[i].OutChannels = SendSlots[i]->NumChannels;
909 voice->Looping = ATOMIC_LOAD(&props->Looping, almemory_order_relaxed);
911 /* Transform source to listener space (convert to head relative) */
912 if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE)
914 const aluMatrixf *Matrix = &Listener->Params.Matrix;
915 /* Transform source vectors */
916 Position = aluMatrixfVector(Matrix, &Position);
917 Velocity = aluMatrixfVector(Matrix, &Velocity);
918 Direction = aluMatrixfVector(Matrix, &Direction);
920 else
922 const aluVector *lvelocity = &Listener->Params.Velocity;
923 /* Offset the source velocity to be relative of the listener velocity */
924 Velocity.v[0] += lvelocity->v[0];
925 Velocity.v[1] += lvelocity->v[1];
926 Velocity.v[2] += lvelocity->v[2];
929 aluNormalize(Direction.v);
930 SourceToListener.v[0] = -Position.v[0];
931 SourceToListener.v[1] = -Position.v[1];
932 SourceToListener.v[2] = -Position.v[2];
933 SourceToListener.v[3] = 0.0f;
934 Distance = aluNormalize(SourceToListener.v);
936 /* Calculate distance attenuation */
937 ClampedDist = Distance;
939 Attenuation = 1.0f;
940 for(i = 0;i < NumSends;i++)
941 RoomAttenuation[i] = 1.0f;
942 switch(ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed))
944 case InverseDistanceClamped:
945 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
946 if(MaxDist < MinDist)
947 break;
948 /*fall-through*/
949 case InverseDistance:
950 if(MinDist > 0.0f)
952 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
953 if(dist > 0.0f) Attenuation = MinDist / dist;
954 for(i = 0;i < NumSends;i++)
956 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
957 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
960 break;
962 case LinearDistanceClamped:
963 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
964 if(MaxDist < MinDist)
965 break;
966 /*fall-through*/
967 case LinearDistance:
968 if(MaxDist != MinDist)
970 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
971 Attenuation = maxf(Attenuation, 0.0f);
972 for(i = 0;i < NumSends;i++)
974 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
975 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
978 break;
980 case ExponentDistanceClamped:
981 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
982 if(MaxDist < MinDist)
983 break;
984 /*fall-through*/
985 case ExponentDistance:
986 if(ClampedDist > 0.0f && MinDist > 0.0f)
988 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
989 for(i = 0;i < NumSends;i++)
990 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
992 break;
994 case DisableDistance:
995 ClampedDist = MinDist;
996 break;
999 /* Source Gain + Attenuation */
1000 DryGain = SourceVolume * Attenuation;
1001 for(i = 0;i < NumSends;i++)
1002 WetGain[i] = SourceVolume * RoomAttenuation[i];
1004 /* Distance-based air absorption */
1005 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
1007 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
1008 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
1009 for(i = 0;i < NumSends;i++)
1010 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
1013 if(WetGainAuto)
1015 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1017 /* Apply a decay-time transformation to the wet path, based on the
1018 * attenuation of the dry path.
1020 * Using the apparent distance, based on the distance attenuation, the
1021 * initial decay of the reverb effect is calculated and applied to the
1022 * wet path.
1024 for(i = 0;i < NumSends;i++)
1026 if(DecayDistance[i] > 0.0f)
1027 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1031 /* Calculate directional soundcones */
1032 if(InnerAngle < 360.0f)
1034 ALfloat ConeVolume;
1035 ALfloat ConeHF;
1036 ALfloat Angle;
1037 ALfloat scale;
1039 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1040 if(Angle > InnerAngle)
1042 if(Angle < OuterAngle)
1044 scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1045 ConeVolume = lerp(
1046 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1048 ConeHF = lerp(
1049 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1052 else
1054 ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed);
1055 ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed);
1057 DryGain *= ConeVolume;
1058 if(DryGainHFAuto)
1059 DryGainHF *= ConeHF;
1062 /* Wet path uses the total area of the cone emitter (the room will
1063 * receive the same amount of sound regardless of its direction).
1065 scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
1066 (InnerAngle/360.0f);
1067 if(WetGainAuto)
1069 ConeVolume = lerp(
1070 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1072 for(i = 0;i < NumSends;i++)
1073 WetGain[i] *= ConeVolume;
1075 if(WetGainHFAuto)
1077 ConeHF = lerp(
1078 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1080 for(i = 0;i < NumSends;i++)
1081 WetGainHF[i] *= ConeHF;
1085 /* Clamp to Min/Max Gain */
1086 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1087 for(i = 0;i < NumSends;i++)
1088 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1090 /* Apply gain and frequency filters */
1091 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
1092 DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
1093 DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
1094 for(i = 0;i < NumSends;i++)
1096 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
1097 WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
1098 WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
1101 /* Calculate velocity-based doppler effect */
1102 if(DopplerFactor > 0.0f)
1104 const aluVector *lvelocity = &Listener->Params.Velocity;
1105 ALfloat VSS, VLS;
1107 if(SpeedOfSound < 1.0f)
1109 DopplerFactor *= 1.0f/SpeedOfSound;
1110 SpeedOfSound = 1.0f;
1113 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1114 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1116 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1117 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1120 /* Calculate fixed-point stepping value, based on the pitch, buffer
1121 * frequency, and output frequency.
1123 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1124 if(Pitch > (ALfloat)MAX_PITCH)
1125 voice->Step = MAX_PITCH<<FRACTIONBITS;
1126 else
1127 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1128 BsincPrepare(voice->Step, &voice->SincState);
1130 if(Device->Render_Mode == HrtfRender)
1132 /* Full HRTF rendering. Skip the virtual channels and render to the
1133 * real outputs.
1135 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1136 ALfloat ev = 0.0f, az = 0.0f;
1137 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1138 ALfloat coeffs[MAX_AMBI_COEFFS];
1139 ALfloat spread = 0.0f;
1141 voice->Direct.OutBuffer = Device->RealOut.Buffer;
1142 voice->Direct.OutChannels = Device->RealOut.NumChannels;
1144 if(Distance > FLT_EPSILON)
1146 dir[0] = -SourceToListener.v[0];
1147 dir[1] = -SourceToListener.v[1];
1148 dir[2] = -SourceToListener.v[2] * ZScale;
1150 /* Calculate elevation and azimuth only when the source is not at
1151 * the listener. This prevents +0 and -0 Z from producing
1152 * inconsistent panning. Also, clamp Y in case FP precision errors
1153 * cause it to land outside of -1..+1. */
1154 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1155 az = atan2f(dir[0], -dir[2]);
1157 if(radius > Distance)
1158 spread = F_TAU - Distance/radius*F_PI;
1159 else if(Distance > FLT_EPSILON)
1160 spread = asinf(radius / Distance) * 2.0f;
1162 /* Get the HRIR coefficients and delays. */
1163 GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, spread, DryGain,
1164 voice->Direct.Hrtf[0].Target.Coeffs,
1165 voice->Direct.Hrtf[0].Target.Delay);
1167 CalcDirectionCoeffs(dir, spread, coeffs);
1169 for(i = 0;i < NumSends;i++)
1171 if(!SendSlots[i])
1173 ALuint j;
1174 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1175 voice->Send[i].Gains[0].Target[j] = 0.0f;
1177 else
1179 const ALeffectslot *Slot = SendSlots[i];
1180 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1181 WetGain[i], voice->Send[i].Gains[0].Target);
1185 voice->IsHrtf = AL_TRUE;
1187 else
1189 /* Non-HRTF rendering. */
1190 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1191 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1192 ALfloat coeffs[MAX_AMBI_COEFFS];
1193 ALfloat spread = 0.0f;
1195 /* Get the localized direction, and compute panned gains. */
1196 if(Distance > FLT_EPSILON)
1198 dir[0] = -SourceToListener.v[0];
1199 dir[1] = -SourceToListener.v[1];
1200 dir[2] = -SourceToListener.v[2] * ZScale;
1202 if(radius > Distance)
1203 spread = F_TAU - Distance/radius*F_PI;
1204 else if(Distance > FLT_EPSILON)
1205 spread = asinf(radius / Distance) * 2.0f;
1207 if(Device->Render_Mode == StereoPair)
1209 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
1210 ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
1211 x = clampf(x, -0.5f, 0.5f) + 0.5f;
1212 voice->Direct.Gains[0].Target[0] = x * DryGain;
1213 voice->Direct.Gains[0].Target[1] = (1.0f-x) * DryGain;
1214 for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
1215 voice->Direct.Gains[0].Target[i] = 0.0f;
1217 CalcDirectionCoeffs(dir, spread, coeffs);
1219 else
1221 CalcDirectionCoeffs(dir, spread, coeffs);
1222 ComputePanningGains(Device->Dry, coeffs, DryGain, voice->Direct.Gains[0].Target);
1225 for(i = 0;i < NumSends;i++)
1227 if(!SendSlots[i])
1229 ALuint j;
1230 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1231 voice->Send[i].Gains[0].Target[j] = 0.0f;
1233 else
1235 const ALeffectslot *Slot = SendSlots[i];
1236 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1237 WetGain[i], voice->Send[i].Gains[0].Target);
1241 voice->IsHrtf = AL_FALSE;
1245 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
1246 Frequency;
1247 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
1248 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 = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
1266 Frequency;
1267 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
1268 Frequency;
1269 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
1270 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
1271 voice->Send[i].Filters[0].ActiveType = AF_None;
1272 if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_LowPass;
1273 if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_HighPass;
1274 ALfilterState_setParams(
1275 &voice->Send[i].Filters[0].LowPass, ALfilterType_HighShelf,
1276 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
1278 ALfilterState_setParams(
1279 &voice->Send[i].Filters[0].HighPass, ALfilterType_LowShelf,
1280 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
1285 static void CalcSourceParams(ALvoice *voice, ALCcontext *context)
1287 ALsource *source = voice->Source;
1288 ALbufferlistitem *BufferListItem;
1289 struct ALsourceProps *first;
1290 struct ALsourceProps *props;
1292 props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
1293 if(!props) return;
1295 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1296 while(BufferListItem != NULL)
1298 ALbuffer *buffer;
1299 if((buffer=BufferListItem->buffer) != NULL)
1301 if(buffer->FmtChannels == FmtMono)
1302 CalcAttnSourceParams(voice, props, buffer, context);
1303 else
1304 CalcNonAttnSourceParams(voice, props, buffer, context);
1305 break;
1307 BufferListItem = BufferListItem->next;
1310 /* WARNING: A livelock is theoretically possible if another thread keeps
1311 * changing the freelist head without giving this a chance to actually swap
1312 * in the old container (practically impossible with this little code,
1313 * but...).
1315 first = ATOMIC_LOAD(&source->FreeList);
1316 do {
1317 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
1318 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*,
1319 &source->FreeList, &first, props) == 0);
1323 static void UpdateContextSources(ALCcontext *ctx, ALeffectslot *slot)
1325 ALvoice *voice, *voice_end;
1326 ALsource *source;
1328 IncrementRef(&ctx->UpdateCount);
1329 if(!ATOMIC_LOAD(&ctx->HoldUpdates))
1331 CalcListenerParams(ctx);
1332 while(slot)
1334 CalcEffectSlotParams(slot, ctx->Device);
1335 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1338 voice = ctx->Voices;
1339 voice_end = voice + ctx->VoiceCount;
1340 for(;voice != voice_end;++voice)
1342 if(!(source=voice->Source)) continue;
1343 if(source->state != AL_PLAYING && source->state != AL_PAUSED)
1344 voice->Source = NULL;
1345 else
1346 CalcSourceParams(voice, ctx);
1349 IncrementRef(&ctx->UpdateCount);
1353 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1354 * converts NaN to 0. */
1355 static inline ALfloat aluClampf(ALfloat val)
1357 if(fabsf(val) <= 1.0f) return val;
1358 return (ALfloat)((0.0f < val) - (val < 0.0f));
1361 static inline ALfloat aluF2F(ALfloat val)
1362 { return val; }
1364 static inline ALint aluF2I(ALfloat val)
1366 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1367 * integer range normalized floats can be safely converted to.
1369 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1371 static inline ALuint aluF2UI(ALfloat val)
1372 { return aluF2I(val)+2147483648u; }
1374 static inline ALshort aluF2S(ALfloat val)
1375 { return fastf2i(aluClampf(val)*32767.0f); }
1376 static inline ALushort aluF2US(ALfloat val)
1377 { return aluF2S(val)+32768; }
1379 static inline ALbyte aluF2B(ALfloat val)
1380 { return fastf2i(aluClampf(val)*127.0f); }
1381 static inline ALubyte aluF2UB(ALfloat val)
1382 { return aluF2B(val)+128; }
1384 #define DECL_TEMPLATE(T, func) \
1385 static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1386 ALuint SamplesToDo, ALuint numchans) \
1388 ALuint i, j; \
1389 for(j = 0;j < numchans;j++) \
1391 const ALfloat *in = InBuffer[j]; \
1392 T *restrict out = (T*)OutBuffer + j; \
1393 for(i = 0;i < SamplesToDo;i++) \
1394 out[i*numchans] = func(in[i]); \
1398 DECL_TEMPLATE(ALfloat, aluF2F)
1399 DECL_TEMPLATE(ALuint, aluF2UI)
1400 DECL_TEMPLATE(ALint, aluF2I)
1401 DECL_TEMPLATE(ALushort, aluF2US)
1402 DECL_TEMPLATE(ALshort, aluF2S)
1403 DECL_TEMPLATE(ALubyte, aluF2UB)
1404 DECL_TEMPLATE(ALbyte, aluF2B)
1406 #undef DECL_TEMPLATE
1409 ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1411 ALuint SamplesToDo;
1412 ALvoice *voice, *voice_end;
1413 ALeffectslot *slot;
1414 ALsource *source;
1415 ALCcontext *ctx;
1416 FPUCtl oldMode;
1417 ALuint i, c;
1419 SetMixerFPUMode(&oldMode);
1421 while(size > 0)
1423 SamplesToDo = minu(size, BUFFERSIZE);
1424 for(c = 0;c < device->Dry.NumChannels;c++)
1425 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1426 if(device->Dry.Buffer != device->RealOut.Buffer)
1427 for(c = 0;c < device->RealOut.NumChannels;c++)
1428 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1429 if(device->Dry.Buffer != device->FOAOut.Buffer)
1430 for(c = 0;c < device->FOAOut.NumChannels;c++)
1431 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1433 IncrementRef(&device->MixCount);
1434 V0(device->Backend,lock)();
1436 if((slot=device->DefaultSlot) != NULL)
1438 CalcEffectSlotParams(device->DefaultSlot, device);
1439 for(i = 0;i < slot->NumChannels;i++)
1440 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1443 ctx = ATOMIC_LOAD(&device->ContextList);
1444 while(ctx)
1446 ALeffectslot *slotroot;
1448 slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList);
1449 UpdateContextSources(ctx, slotroot);
1451 slot = slotroot;
1452 while(slot)
1454 for(i = 0;i < slot->NumChannels;i++)
1455 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1456 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1459 /* source processing */
1460 voice = ctx->Voices;
1461 voice_end = voice + ctx->VoiceCount;
1462 for(;voice != voice_end;++voice)
1464 ALboolean IsVoiceInit = (voice->Step > 0);
1465 source = voice->Source;
1466 if(source && source->state == AL_PLAYING && IsVoiceInit)
1467 MixSource(voice, source, device, SamplesToDo);
1470 /* effect slot processing */
1471 slot = slotroot;
1472 while(slot)
1474 const ALeffectslot *cslot = slot;
1475 ALeffectState *state = cslot->Params.EffectState;
1476 V(state,process)(SamplesToDo, cslot->WetBuffer, state->OutBuffer,
1477 state->OutChannels);
1478 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1481 ctx = ctx->next;
1484 if(device->DefaultSlot != NULL)
1486 const ALeffectslot *slot = device->DefaultSlot;
1487 ALeffectState *state = slot->Params.EffectState;
1488 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1489 state->OutChannels);
1492 /* Increment the clock time. Every second's worth of samples is
1493 * converted and added to clock base so that large sample counts don't
1494 * overflow during conversion. This also guarantees an exact, stable
1495 * conversion. */
1496 device->SamplesDone += SamplesToDo;
1497 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1498 device->SamplesDone %= device->Frequency;
1499 V0(device->Backend,unlock)();
1500 IncrementRef(&device->MixCount);
1502 if(device->Hrtf)
1504 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1505 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1506 if(lidx != -1 && ridx != -1)
1508 HrtfMixerFunc HrtfMix = SelectHrtfMixer();
1509 ALuint irsize = device->Hrtf->irSize;
1510 MixHrtfParams hrtfparams;
1511 memset(&hrtfparams, 0, sizeof(hrtfparams));
1512 for(c = 0;c < device->Dry.NumChannels;c++)
1514 hrtfparams.Current = &device->Hrtf_Params[c];
1515 hrtfparams.Target = &device->Hrtf_Params[c];
1516 HrtfMix(device->RealOut.Buffer, lidx, ridx,
1517 device->Dry.Buffer[c], 0, device->Hrtf_Offset, 0,
1518 irsize, &hrtfparams, &device->Hrtf_State[c], SamplesToDo
1521 device->Hrtf_Offset += SamplesToDo;
1524 else if(device->AmbiDecoder)
1526 if(device->Dry.Buffer != device->FOAOut.Buffer)
1527 bformatdec_upSample(device->AmbiDecoder,
1528 device->Dry.Buffer, device->FOAOut.Buffer,
1529 device->FOAOut.NumChannels, SamplesToDo
1531 bformatdec_process(device->AmbiDecoder,
1532 device->RealOut.Buffer, device->RealOut.NumChannels,
1533 device->Dry.Buffer, SamplesToDo
1536 else
1538 if(device->Uhj_Encoder)
1540 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1541 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1542 if(lidx != -1 && ridx != -1)
1544 /* Encode to stereo-compatible 2-channel UHJ output. */
1545 EncodeUhj2(device->Uhj_Encoder,
1546 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1547 device->Dry.Buffer, SamplesToDo
1551 if(device->Bs2b)
1553 /* Apply binaural/crossfeed filter */
1554 for(i = 0;i < SamplesToDo;i++)
1556 float samples[2];
1557 samples[0] = device->RealOut.Buffer[0][i];
1558 samples[1] = device->RealOut.Buffer[1][i];
1559 bs2b_cross_feed(device->Bs2b, samples);
1560 device->RealOut.Buffer[0][i] = samples[0];
1561 device->RealOut.Buffer[1][i] = samples[1];
1566 if(buffer)
1568 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1569 ALuint OutChannels = device->RealOut.NumChannels;
1571 #define WRITE(T, a, b, c, d) do { \
1572 Write_##T((a), (b), (c), (d)); \
1573 buffer = (T*)buffer + (c)*(d); \
1574 } while(0)
1575 switch(device->FmtType)
1577 case DevFmtByte:
1578 WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1579 break;
1580 case DevFmtUByte:
1581 WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1582 break;
1583 case DevFmtShort:
1584 WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
1585 break;
1586 case DevFmtUShort:
1587 WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
1588 break;
1589 case DevFmtInt:
1590 WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
1591 break;
1592 case DevFmtUInt:
1593 WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
1594 break;
1595 case DevFmtFloat:
1596 WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
1597 break;
1599 #undef WRITE
1602 size -= SamplesToDo;
1605 RestoreFPUMode(&oldMode);
1609 ALvoid aluHandleDisconnect(ALCdevice *device)
1611 ALCcontext *Context;
1613 device->Connected = ALC_FALSE;
1615 Context = ATOMIC_LOAD(&device->ContextList);
1616 while(Context)
1618 ALvoice *voice, *voice_end;
1620 voice = Context->Voices;
1621 voice_end = voice + Context->VoiceCount;
1622 while(voice != voice_end)
1624 ALsource *source = voice->Source;
1625 voice->Source = NULL;
1627 if(source && source->state == AL_PLAYING)
1629 source->state = AL_STOPPED;
1630 ATOMIC_STORE(&source->current_buffer, NULL);
1631 ATOMIC_STORE(&source->position, 0);
1632 ATOMIC_STORE(&source->position_fraction, 0);
1635 voice++;
1637 Context->VoiceCount = 0;
1639 Context = Context->next;