Avoid redundantly storing distance model settings
[openal-soft.git] / Alc / ALu.c
blob8be22e66b2d5725a0f532e4bbe3d3ff8e5acdc58
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;
441 /* Calculate the stepping value */
442 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
443 if(Pitch > (ALfloat)MAX_PITCH)
444 voice->Step = MAX_PITCH<<FRACTIONBITS;
445 else
446 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
447 BsincPrepare(voice->Step, &voice->SincState);
449 /* Calculate gains */
450 DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
451 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
452 DryGainHF = ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
453 DryGainLF = ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
454 for(i = 0;i < NumSends;i++)
456 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
457 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
458 WetGainHF[i] = ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
459 WetGainLF[i] = ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
462 switch(ALBuffer->FmtChannels)
464 case FmtMono:
465 chans = MonoMap;
466 num_channels = 1;
467 break;
469 case FmtStereo:
470 chans = StereoMap;
471 num_channels = 2;
472 break;
474 case FmtRear:
475 chans = RearMap;
476 num_channels = 2;
477 break;
479 case FmtQuad:
480 chans = QuadMap;
481 num_channels = 4;
482 break;
484 case FmtX51:
485 chans = X51Map;
486 num_channels = 6;
487 break;
489 case FmtX61:
490 chans = X61Map;
491 num_channels = 7;
492 break;
494 case FmtX71:
495 chans = X71Map;
496 num_channels = 8;
497 break;
499 case FmtBFormat2D:
500 num_channels = 3;
501 isbformat = AL_TRUE;
502 DirectChannels = AL_FALSE;
503 break;
505 case FmtBFormat3D:
506 num_channels = 4;
507 isbformat = AL_TRUE;
508 DirectChannels = AL_FALSE;
509 break;
512 if(isbformat)
514 ALfloat N[3], V[3], U[3];
515 aluMatrixf matrix;
516 ALfloat scale;
518 /* AT then UP */
519 N[0] = ATOMIC_LOAD(&props->Orientation[0][0], almemory_order_relaxed);
520 N[1] = ATOMIC_LOAD(&props->Orientation[0][1], almemory_order_relaxed);
521 N[2] = ATOMIC_LOAD(&props->Orientation[0][2], almemory_order_relaxed);
522 aluNormalize(N);
523 V[0] = ATOMIC_LOAD(&props->Orientation[1][0], almemory_order_relaxed);
524 V[1] = ATOMIC_LOAD(&props->Orientation[1][1], almemory_order_relaxed);
525 V[2] = ATOMIC_LOAD(&props->Orientation[1][2], almemory_order_relaxed);
526 aluNormalize(V);
527 if(!Relative)
529 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
530 aluMatrixfFloat3(N, 0.0f, lmatrix);
531 aluMatrixfFloat3(V, 0.0f, lmatrix);
533 /* Build and normalize right-vector */
534 aluCrossproduct(N, V, U);
535 aluNormalize(U);
537 /* Build a rotate + conversion matrix (B-Format -> N3D). */
538 scale = 1.732050808f;
539 aluMatrixfSet(&matrix,
540 1.414213562f, 0.0f, 0.0f, 0.0f,
541 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
542 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
543 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
546 voice->Direct.OutBuffer = Device->FOAOut.Buffer;
547 voice->Direct.OutChannels = Device->FOAOut.NumChannels;
548 for(c = 0;c < num_channels;c++)
549 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
550 voice->Direct.Gains[c].Target);
552 for(i = 0;i < NumSends;i++)
554 if(!SendSlots[i])
556 for(c = 0;c < num_channels;c++)
558 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
559 voice->Send[i].Gains[c].Target[j] = 0.0f;
562 else
564 for(c = 0;c < num_channels;c++)
566 const ALeffectslot *Slot = SendSlots[i];
567 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, matrix.m[c],
568 WetGain[i], voice->Send[i].Gains[c].Target);
573 voice->IsHrtf = AL_FALSE;
575 else
577 ALfloat coeffs[MAX_AMBI_COEFFS];
579 if(DirectChannels)
581 /* Skip the virtual channels and write inputs to the real output. */
582 voice->Direct.OutBuffer = Device->RealOut.Buffer;
583 voice->Direct.OutChannels = Device->RealOut.NumChannels;
584 for(c = 0;c < num_channels;c++)
586 int idx;
587 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
588 voice->Direct.Gains[c].Target[j] = 0.0f;
589 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
590 voice->Direct.Gains[c].Target[idx] = DryGain;
593 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
594 * channel-match. */
595 for(c = 0;c < num_channels;c++)
597 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
599 for(i = 0;i < NumSends;i++)
601 if(!SendSlots[i])
603 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
604 voice->Send[i].Gains[c].Target[j] = 0.0f;
606 else
608 const ALeffectslot *Slot = SendSlots[i];
609 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
610 WetGain[i], voice->Send[i].Gains[c].Target);
615 voice->IsHrtf = AL_FALSE;
617 else if(Device->Render_Mode == HrtfRender)
619 /* Full HRTF rendering. Skip the virtual channels and render each
620 * input channel to the real outputs.
622 voice->Direct.OutBuffer = Device->RealOut.Buffer;
623 voice->Direct.OutChannels = Device->RealOut.NumChannels;
624 for(c = 0;c < num_channels;c++)
626 if(chans[c].channel == LFE)
628 /* Skip LFE */
629 voice->Direct.Hrtf[c].Target.Delay[0] = 0;
630 voice->Direct.Hrtf[c].Target.Delay[1] = 0;
631 for(i = 0;i < HRIR_LENGTH;i++)
633 voice->Direct.Hrtf[c].Target.Coeffs[i][0] = 0.0f;
634 voice->Direct.Hrtf[c].Target.Coeffs[i][1] = 0.0f;
637 for(i = 0;i < NumSends;i++)
639 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
640 voice->Send[i].Gains[c].Target[j] = 0.0f;
643 continue;
646 /* Get the static HRIR coefficients and delays for this channel. */
647 GetLerpedHrtfCoeffs(Device->Hrtf,
648 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
649 voice->Direct.Hrtf[c].Target.Coeffs,
650 voice->Direct.Hrtf[c].Target.Delay
653 /* Normal panning for auxiliary sends. */
654 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
656 for(i = 0;i < NumSends;i++)
658 if(!SendSlots[i])
660 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
661 voice->Send[i].Gains[c].Target[j] = 0.0f;
663 else
665 const ALeffectslot *Slot = SendSlots[i];
666 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
667 WetGain[i], voice->Send[i].Gains[c].Target);
672 voice->IsHrtf = AL_TRUE;
674 else
676 /* Non-HRTF rendering. Use normal panning to the output. */
677 for(c = 0;c < num_channels;c++)
679 /* Special-case LFE */
680 if(chans[c].channel == LFE)
682 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
683 voice->Direct.Gains[c].Target[j] = 0.0f;
684 if(Device->Dry.Buffer == Device->RealOut.Buffer)
686 int idx;
687 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
688 voice->Direct.Gains[c].Target[idx] = DryGain;
691 for(i = 0;i < NumSends;i++)
693 ALuint j;
694 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
695 voice->Send[i].Gains[c].Target[j] = 0.0f;
697 continue;
700 if(Device->Render_Mode == StereoPair)
702 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
703 ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
704 coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
705 voice->Direct.Gains[c].Target[0] = coeffs[0] * DryGain;
706 voice->Direct.Gains[c].Target[1] = (1.0f-coeffs[0]) * DryGain;
707 for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
708 voice->Direct.Gains[c].Target[j] = 0.0f;
710 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
712 else
714 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
715 ComputePanningGains(Device->Dry, coeffs, DryGain,
716 voice->Direct.Gains[c].Target);
719 for(i = 0;i < NumSends;i++)
721 if(!SendSlots[i])
723 ALuint j;
724 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
725 voice->Send[i].Gains[c].Target[j] = 0.0f;
727 else
729 const ALeffectslot *Slot = SendSlots[i];
730 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
731 WetGain[i], voice->Send[i].Gains[c].Target);
736 voice->IsHrtf = AL_FALSE;
741 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
742 Frequency;
743 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
744 Frequency;
745 DryGainHF = maxf(DryGainHF, 0.0001f);
746 DryGainLF = maxf(DryGainLF, 0.0001f);
747 for(c = 0;c < num_channels;c++)
749 voice->Direct.Filters[c].ActiveType = AF_None;
750 if(DryGainHF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_LowPass;
751 if(DryGainLF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_HighPass;
752 ALfilterState_setParams(
753 &voice->Direct.Filters[c].LowPass, ALfilterType_HighShelf,
754 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
756 ALfilterState_setParams(
757 &voice->Direct.Filters[c].HighPass, ALfilterType_LowShelf,
758 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
762 for(i = 0;i < NumSends;i++)
764 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
765 Frequency;
766 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
767 Frequency;
768 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
769 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
770 for(c = 0;c < num_channels;c++)
772 voice->Send[i].Filters[c].ActiveType = AF_None;
773 if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_LowPass;
774 if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_HighPass;
775 ALfilterState_setParams(
776 &voice->Send[i].Filters[c].LowPass, ALfilterType_HighShelf,
777 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
779 ALfilterState_setParams(
780 &voice->Send[i].Filters[c].HighPass, ALfilterType_LowShelf,
781 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
787 static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
789 const ALCdevice *Device = ALContext->Device;
790 const ALlistener *Listener = ALContext->Listener;
791 aluVector Position, Velocity, Direction, SourceToListener;
792 ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
793 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
794 ALfloat SourceVolume,ListenerGain;
795 ALfloat DopplerFactor, SpeedOfSound;
796 ALfloat AirAbsorptionFactor;
797 ALfloat RoomAirAbsorption[MAX_SENDS];
798 ALeffectslot *SendSlots[MAX_SENDS];
799 ALfloat Attenuation;
800 ALfloat RoomAttenuation[MAX_SENDS];
801 ALfloat MetersPerUnit;
802 ALfloat RoomRolloffBase;
803 ALfloat RoomRolloff[MAX_SENDS];
804 ALfloat DecayDistance[MAX_SENDS];
805 ALfloat DryGain;
806 ALfloat DryGainHF;
807 ALfloat DryGainLF;
808 ALboolean DryGainHFAuto;
809 ALfloat WetGain[MAX_SENDS];
810 ALfloat WetGainHF[MAX_SENDS];
811 ALfloat WetGainLF[MAX_SENDS];
812 ALboolean WetGainAuto;
813 ALboolean WetGainHFAuto;
814 ALfloat Pitch;
815 ALuint Frequency;
816 ALint NumSends;
817 ALint i;
819 DryGainHF = 1.0f;
820 DryGainLF = 1.0f;
821 for(i = 0;i < MAX_SENDS;i++)
823 WetGainHF[i] = 1.0f;
824 WetGainLF[i] = 1.0f;
827 /* Get context/device properties */
828 DopplerFactor = Listener->Params.DopplerFactor;
829 SpeedOfSound = Listener->Params.SpeedOfSound;
830 NumSends = Device->NumAuxSends;
831 Frequency = Device->Frequency;
833 /* Get listener properties */
834 ListenerGain = Listener->Params.Gain;
835 MetersPerUnit = Listener->Params.MetersPerUnit;
837 /* Get source properties */
838 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
839 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
840 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
841 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
842 aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed),
843 ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed),
844 ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed),
845 1.0f);
846 aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed),
847 ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed),
848 ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed),
849 0.0f);
850 aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
851 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
852 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
853 0.0f);
854 MinDist = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed);
855 MaxDist = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed);
856 Rolloff = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed);
857 DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
858 InnerAngle = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed);
859 OuterAngle = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed);
860 AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed);
861 DryGainHFAuto = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed);
862 WetGainAuto = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed);
863 WetGainHFAuto = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed);
864 RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed);
866 voice->Direct.OutBuffer = Device->Dry.Buffer;
867 voice->Direct.OutChannels = Device->Dry.NumChannels;
868 for(i = 0;i < NumSends;i++)
870 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
872 if(!SendSlots[i] && i == 0)
873 SendSlots[i] = Device->DefaultSlot;
874 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
876 SendSlots[i] = NULL;
877 RoomRolloff[i] = 0.0f;
878 DecayDistance[i] = 0.0f;
879 RoomAirAbsorption[i] = 1.0f;
881 else if(SendSlots[i]->Params.AuxSendAuto)
883 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
884 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
885 SPEEDOFSOUNDMETRESPERSEC;
886 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
888 else
890 /* If the slot's auxiliary send auto is off, the data sent to the
891 * effect slot is the same as the dry path, sans filter effects */
892 RoomRolloff[i] = Rolloff;
893 DecayDistance[i] = 0.0f;
894 RoomAirAbsorption[i] = AIRABSORBGAINHF;
897 if(!SendSlots[i])
899 voice->Send[i].OutBuffer = NULL;
900 voice->Send[i].OutChannels = 0;
902 else
904 voice->Send[i].OutBuffer = SendSlots[i]->WetBuffer;
905 voice->Send[i].OutChannels = SendSlots[i]->NumChannels;
909 /* Transform source to listener space (convert to head relative) */
910 if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE)
912 const aluMatrixf *Matrix = &Listener->Params.Matrix;
913 /* Transform source vectors */
914 Position = aluMatrixfVector(Matrix, &Position);
915 Velocity = aluMatrixfVector(Matrix, &Velocity);
916 Direction = aluMatrixfVector(Matrix, &Direction);
918 else
920 const aluVector *lvelocity = &Listener->Params.Velocity;
921 /* Offset the source velocity to be relative of the listener velocity */
922 Velocity.v[0] += lvelocity->v[0];
923 Velocity.v[1] += lvelocity->v[1];
924 Velocity.v[2] += lvelocity->v[2];
927 aluNormalize(Direction.v);
928 SourceToListener.v[0] = -Position.v[0];
929 SourceToListener.v[1] = -Position.v[1];
930 SourceToListener.v[2] = -Position.v[2];
931 SourceToListener.v[3] = 0.0f;
932 Distance = aluNormalize(SourceToListener.v);
934 /* Calculate distance attenuation */
935 ClampedDist = Distance;
937 Attenuation = 1.0f;
938 for(i = 0;i < NumSends;i++)
939 RoomAttenuation[i] = 1.0f;
940 switch(ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed))
942 case InverseDistanceClamped:
943 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
944 if(MaxDist < MinDist)
945 break;
946 /*fall-through*/
947 case InverseDistance:
948 if(MinDist > 0.0f)
950 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
951 if(dist > 0.0f) Attenuation = MinDist / dist;
952 for(i = 0;i < NumSends;i++)
954 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
955 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
958 break;
960 case LinearDistanceClamped:
961 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
962 if(MaxDist < MinDist)
963 break;
964 /*fall-through*/
965 case LinearDistance:
966 if(MaxDist != MinDist)
968 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
969 Attenuation = maxf(Attenuation, 0.0f);
970 for(i = 0;i < NumSends;i++)
972 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
973 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
976 break;
978 case ExponentDistanceClamped:
979 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
980 if(MaxDist < MinDist)
981 break;
982 /*fall-through*/
983 case ExponentDistance:
984 if(ClampedDist > 0.0f && MinDist > 0.0f)
986 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
987 for(i = 0;i < NumSends;i++)
988 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
990 break;
992 case DisableDistance:
993 ClampedDist = MinDist;
994 break;
997 /* Source Gain + Attenuation */
998 DryGain = SourceVolume * Attenuation;
999 for(i = 0;i < NumSends;i++)
1000 WetGain[i] = SourceVolume * RoomAttenuation[i];
1002 /* Distance-based air absorption */
1003 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
1005 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
1006 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
1007 for(i = 0;i < NumSends;i++)
1008 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
1011 if(WetGainAuto)
1013 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1015 /* Apply a decay-time transformation to the wet path, based on the
1016 * attenuation of the dry path.
1018 * Using the apparent distance, based on the distance attenuation, the
1019 * initial decay of the reverb effect is calculated and applied to the
1020 * wet path.
1022 for(i = 0;i < NumSends;i++)
1024 if(DecayDistance[i] > 0.0f)
1025 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1029 /* Calculate directional soundcones */
1030 if(InnerAngle < 360.0f)
1032 ALfloat ConeVolume;
1033 ALfloat ConeHF;
1034 ALfloat Angle;
1035 ALfloat scale;
1037 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1038 if(Angle > InnerAngle)
1040 if(Angle < OuterAngle)
1042 scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1043 ConeVolume = lerp(
1044 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1046 ConeHF = lerp(
1047 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1050 else
1052 ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed);
1053 ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed);
1055 DryGain *= ConeVolume;
1056 if(DryGainHFAuto)
1057 DryGainHF *= ConeHF;
1060 /* Wet path uses the total area of the cone emitter (the room will
1061 * receive the same amount of sound regardless of its direction).
1063 scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
1064 (InnerAngle/360.0f);
1065 if(WetGainAuto)
1067 ConeVolume = lerp(
1068 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1070 for(i = 0;i < NumSends;i++)
1071 WetGain[i] *= ConeVolume;
1073 if(WetGainHFAuto)
1075 ConeHF = lerp(
1076 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1078 for(i = 0;i < NumSends;i++)
1079 WetGainHF[i] *= ConeHF;
1083 /* Clamp to Min/Max Gain */
1084 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1085 for(i = 0;i < NumSends;i++)
1086 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1088 /* Apply gain and frequency filters */
1089 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
1090 DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
1091 DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
1092 for(i = 0;i < NumSends;i++)
1094 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
1095 WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
1096 WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
1099 /* Calculate velocity-based doppler effect */
1100 if(DopplerFactor > 0.0f)
1102 const aluVector *lvelocity = &Listener->Params.Velocity;
1103 ALfloat VSS, VLS;
1105 if(SpeedOfSound < 1.0f)
1107 DopplerFactor *= 1.0f/SpeedOfSound;
1108 SpeedOfSound = 1.0f;
1111 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1112 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1114 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1115 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1118 /* Calculate fixed-point stepping value, based on the pitch, buffer
1119 * frequency, and output frequency.
1121 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1122 if(Pitch > (ALfloat)MAX_PITCH)
1123 voice->Step = MAX_PITCH<<FRACTIONBITS;
1124 else
1125 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1126 BsincPrepare(voice->Step, &voice->SincState);
1128 if(Device->Render_Mode == HrtfRender)
1130 /* Full HRTF rendering. Skip the virtual channels and render to the
1131 * real outputs.
1133 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1134 ALfloat ev = 0.0f, az = 0.0f;
1135 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1136 ALfloat coeffs[MAX_AMBI_COEFFS];
1137 ALfloat spread = 0.0f;
1139 voice->Direct.OutBuffer = Device->RealOut.Buffer;
1140 voice->Direct.OutChannels = Device->RealOut.NumChannels;
1142 if(Distance > FLT_EPSILON)
1144 dir[0] = -SourceToListener.v[0];
1145 dir[1] = -SourceToListener.v[1];
1146 dir[2] = -SourceToListener.v[2] * ZScale;
1148 /* Calculate elevation and azimuth only when the source is not at
1149 * the listener. This prevents +0 and -0 Z from producing
1150 * inconsistent panning. Also, clamp Y in case FP precision errors
1151 * cause it to land outside of -1..+1. */
1152 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1153 az = atan2f(dir[0], -dir[2]);
1155 if(radius > Distance)
1156 spread = F_TAU - Distance/radius*F_PI;
1157 else if(Distance > FLT_EPSILON)
1158 spread = asinf(radius / Distance) * 2.0f;
1160 /* Get the HRIR coefficients and delays. */
1161 GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, spread, DryGain,
1162 voice->Direct.Hrtf[0].Target.Coeffs,
1163 voice->Direct.Hrtf[0].Target.Delay);
1165 CalcDirectionCoeffs(dir, spread, coeffs);
1167 for(i = 0;i < NumSends;i++)
1169 if(!SendSlots[i])
1171 ALuint j;
1172 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1173 voice->Send[i].Gains[0].Target[j] = 0.0f;
1175 else
1177 const ALeffectslot *Slot = SendSlots[i];
1178 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1179 WetGain[i], voice->Send[i].Gains[0].Target);
1183 voice->IsHrtf = AL_TRUE;
1185 else
1187 /* Non-HRTF rendering. */
1188 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1189 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1190 ALfloat coeffs[MAX_AMBI_COEFFS];
1191 ALfloat spread = 0.0f;
1193 /* Get the localized direction, and compute panned gains. */
1194 if(Distance > FLT_EPSILON)
1196 dir[0] = -SourceToListener.v[0];
1197 dir[1] = -SourceToListener.v[1];
1198 dir[2] = -SourceToListener.v[2] * ZScale;
1200 if(radius > Distance)
1201 spread = F_TAU - Distance/radius*F_PI;
1202 else if(Distance > FLT_EPSILON)
1203 spread = asinf(radius / Distance) * 2.0f;
1205 if(Device->Render_Mode == StereoPair)
1207 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
1208 ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
1209 x = clampf(x, -0.5f, 0.5f) + 0.5f;
1210 voice->Direct.Gains[0].Target[0] = x * DryGain;
1211 voice->Direct.Gains[0].Target[1] = (1.0f-x) * DryGain;
1212 for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
1213 voice->Direct.Gains[0].Target[i] = 0.0f;
1215 CalcDirectionCoeffs(dir, spread, coeffs);
1217 else
1219 CalcDirectionCoeffs(dir, spread, coeffs);
1220 ComputePanningGains(Device->Dry, coeffs, DryGain, voice->Direct.Gains[0].Target);
1223 for(i = 0;i < NumSends;i++)
1225 if(!SendSlots[i])
1227 ALuint j;
1228 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1229 voice->Send[i].Gains[0].Target[j] = 0.0f;
1231 else
1233 const ALeffectslot *Slot = SendSlots[i];
1234 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1235 WetGain[i], voice->Send[i].Gains[0].Target);
1239 voice->IsHrtf = AL_FALSE;
1243 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
1244 Frequency;
1245 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
1246 Frequency;
1247 DryGainHF = maxf(DryGainHF, 0.0001f);
1248 DryGainLF = maxf(DryGainLF, 0.0001f);
1249 voice->Direct.Filters[0].ActiveType = AF_None;
1250 if(DryGainHF != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_LowPass;
1251 if(DryGainLF != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_HighPass;
1252 ALfilterState_setParams(
1253 &voice->Direct.Filters[0].LowPass, ALfilterType_HighShelf,
1254 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
1256 ALfilterState_setParams(
1257 &voice->Direct.Filters[0].HighPass, ALfilterType_LowShelf,
1258 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
1261 for(i = 0;i < NumSends;i++)
1263 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
1264 Frequency;
1265 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
1266 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)
1283 static void CalcSourceParams(ALvoice *voice, ALCcontext *context)
1285 ALsource *source = voice->Source;
1286 ALbufferlistitem *BufferListItem;
1287 struct ALsourceProps *first;
1288 struct ALsourceProps *props;
1290 props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
1291 if(!props) return;
1293 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1294 while(BufferListItem != NULL)
1296 ALbuffer *buffer;
1297 if((buffer=BufferListItem->buffer) != NULL)
1299 if(buffer->FmtChannels == FmtMono)
1300 CalcAttnSourceParams(voice, props, buffer, context);
1301 else
1302 CalcNonAttnSourceParams(voice, props, buffer, context);
1303 break;
1305 BufferListItem = BufferListItem->next;
1308 /* WARNING: A livelock is theoretically possible if another thread keeps
1309 * changing the freelist head without giving this a chance to actually swap
1310 * in the old container (practically impossible with this little code,
1311 * but...).
1313 first = ATOMIC_LOAD(&source->FreeList);
1314 do {
1315 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
1316 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*,
1317 &source->FreeList, &first, props) == 0);
1321 static void UpdateContextSources(ALCcontext *ctx)
1323 ALvoice *voice, *voice_end;
1324 ALsource *source;
1326 IncrementRef(&ctx->UpdateCount);
1327 if(!ATOMIC_LOAD(&ctx->HoldUpdates))
1329 CalcListenerParams(ctx);
1330 #define UPDATE_SLOT(iter) CalcEffectSlotParams(*iter, ctx->Device)
1331 VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, UPDATE_SLOT);
1332 #undef UPDATE_SLOT
1334 voice = ctx->Voices;
1335 voice_end = voice + ctx->VoiceCount;
1336 for(;voice != voice_end;++voice)
1338 if(!(source=voice->Source)) continue;
1339 if(source->state != AL_PLAYING && source->state != AL_PAUSED)
1340 voice->Source = NULL;
1341 else
1342 CalcSourceParams(voice, ctx);
1345 IncrementRef(&ctx->UpdateCount);
1349 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1350 * converts NaN to 0. */
1351 static inline ALfloat aluClampf(ALfloat val)
1353 if(fabsf(val) <= 1.0f) return val;
1354 return (ALfloat)((0.0f < val) - (val < 0.0f));
1357 static inline ALfloat aluF2F(ALfloat val)
1358 { return val; }
1360 static inline ALint aluF2I(ALfloat val)
1362 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1363 * integer range normalized floats can be safely converted to.
1365 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1367 static inline ALuint aluF2UI(ALfloat val)
1368 { return aluF2I(val)+2147483648u; }
1370 static inline ALshort aluF2S(ALfloat val)
1371 { return fastf2i(aluClampf(val)*32767.0f); }
1372 static inline ALushort aluF2US(ALfloat val)
1373 { return aluF2S(val)+32768; }
1375 static inline ALbyte aluF2B(ALfloat val)
1376 { return fastf2i(aluClampf(val)*127.0f); }
1377 static inline ALubyte aluF2UB(ALfloat val)
1378 { return aluF2B(val)+128; }
1380 #define DECL_TEMPLATE(T, func) \
1381 static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1382 ALuint SamplesToDo, ALuint numchans) \
1384 ALuint i, j; \
1385 for(j = 0;j < numchans;j++) \
1387 const ALfloat *in = InBuffer[j]; \
1388 T *restrict out = (T*)OutBuffer + j; \
1389 for(i = 0;i < SamplesToDo;i++) \
1390 out[i*numchans] = func(in[i]); \
1394 DECL_TEMPLATE(ALfloat, aluF2F)
1395 DECL_TEMPLATE(ALuint, aluF2UI)
1396 DECL_TEMPLATE(ALint, aluF2I)
1397 DECL_TEMPLATE(ALushort, aluF2US)
1398 DECL_TEMPLATE(ALshort, aluF2S)
1399 DECL_TEMPLATE(ALubyte, aluF2UB)
1400 DECL_TEMPLATE(ALbyte, aluF2B)
1402 #undef DECL_TEMPLATE
1405 ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1407 ALuint SamplesToDo;
1408 ALvoice *voice, *voice_end;
1409 ALeffectslot *slot;
1410 ALsource *source;
1411 ALCcontext *ctx;
1412 FPUCtl oldMode;
1413 ALuint i, c;
1415 SetMixerFPUMode(&oldMode);
1417 while(size > 0)
1419 IncrementRef(&device->MixCount);
1421 SamplesToDo = minu(size, BUFFERSIZE);
1422 for(c = 0;c < device->VirtOut.NumChannels;c++)
1423 memset(device->VirtOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1424 for(c = 0;c < device->RealOut.NumChannels;c++)
1425 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1426 if(device->Dry.Buffer != device->FOAOut.Buffer)
1427 for(c = 0;c < device->FOAOut.NumChannels;c++)
1428 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1430 V0(device->Backend,lock)();
1432 if((slot=device->DefaultSlot) != NULL)
1434 CalcEffectSlotParams(device->DefaultSlot, device);
1435 for(i = 0;i < slot->NumChannels;i++)
1436 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1439 ctx = ATOMIC_LOAD(&device->ContextList);
1440 while(ctx)
1442 UpdateContextSources(ctx);
1443 #define CLEAR_WET_BUFFER(iter) do { \
1444 for(i = 0;i < (*iter)->NumChannels;i++) \
1445 memset((*iter)->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat)); \
1446 } while(0)
1447 VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, CLEAR_WET_BUFFER);
1448 #undef CLEAR_WET_BUFFER
1450 /* source processing */
1451 voice = ctx->Voices;
1452 voice_end = voice + ctx->VoiceCount;
1453 for(;voice != voice_end;++voice)
1455 source = voice->Source;
1456 if(source && source->state == AL_PLAYING)
1457 MixSource(voice, source, device, SamplesToDo);
1460 /* effect slot processing */
1461 c = VECTOR_SIZE(ctx->ActiveAuxSlots);
1462 for(i = 0;i < c;i++)
1464 const ALeffectslot *slot = VECTOR_ELEM(ctx->ActiveAuxSlots, i);
1465 ALeffectState *state = slot->Params.EffectState;
1466 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1467 state->OutChannels);
1470 ctx = ctx->next;
1473 if(device->DefaultSlot != NULL)
1475 const ALeffectslot *slot = device->DefaultSlot;
1476 ALeffectState *state = slot->Params.EffectState;
1477 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1478 state->OutChannels);
1481 /* Increment the clock time. Every second's worth of samples is
1482 * converted and added to clock base so that large sample counts don't
1483 * overflow during conversion. This also guarantees an exact, stable
1484 * conversion. */
1485 device->SamplesDone += SamplesToDo;
1486 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1487 device->SamplesDone %= device->Frequency;
1488 V0(device->Backend,unlock)();
1490 if(device->Hrtf)
1492 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1493 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1494 if(lidx != -1 && ridx != -1)
1496 HrtfMixerFunc HrtfMix = SelectHrtfMixer();
1497 ALuint irsize = GetHrtfIrSize(device->Hrtf);
1498 MixHrtfParams hrtfparams;
1499 memset(&hrtfparams, 0, sizeof(hrtfparams));
1500 for(c = 0;c < device->VirtOut.NumChannels;c++)
1502 hrtfparams.Current = &device->Hrtf_Params[c];
1503 hrtfparams.Target = &device->Hrtf_Params[c];
1504 HrtfMix(device->RealOut.Buffer, lidx, ridx,
1505 device->VirtOut.Buffer[c], 0, device->Hrtf_Offset, 0,
1506 irsize, &hrtfparams, &device->Hrtf_State[c], SamplesToDo
1509 device->Hrtf_Offset += SamplesToDo;
1512 else if(device->AmbiDecoder)
1514 if(device->VirtOut.Buffer != device->FOAOut.Buffer)
1515 bformatdec_upSample(device->AmbiDecoder,
1516 device->VirtOut.Buffer, device->FOAOut.Buffer,
1517 device->FOAOut.NumChannels, SamplesToDo
1519 bformatdec_process(device->AmbiDecoder,
1520 device->RealOut.Buffer, device->RealOut.NumChannels,
1521 device->VirtOut.Buffer, SamplesToDo
1524 else
1526 if(device->Uhj_Encoder)
1528 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1529 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1530 if(lidx != -1 && ridx != -1)
1532 /* Encode to stereo-compatible 2-channel UHJ output. */
1533 EncodeUhj2(device->Uhj_Encoder,
1534 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1535 device->VirtOut.Buffer, SamplesToDo
1539 if(device->Bs2b)
1541 /* Apply binaural/crossfeed filter */
1542 for(i = 0;i < SamplesToDo;i++)
1544 float samples[2];
1545 samples[0] = device->RealOut.Buffer[0][i];
1546 samples[1] = device->RealOut.Buffer[1][i];
1547 bs2b_cross_feed(device->Bs2b, samples);
1548 device->RealOut.Buffer[0][i] = samples[0];
1549 device->RealOut.Buffer[1][i] = samples[1];
1554 if(buffer)
1556 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1557 ALuint OutChannels = device->RealOut.NumChannels;
1559 #define WRITE(T, a, b, c, d) do { \
1560 Write_##T((a), (b), (c), (d)); \
1561 buffer = (T*)buffer + (c)*(d); \
1562 } while(0)
1563 switch(device->FmtType)
1565 case DevFmtByte:
1566 WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1567 break;
1568 case DevFmtUByte:
1569 WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1570 break;
1571 case DevFmtShort:
1572 WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
1573 break;
1574 case DevFmtUShort:
1575 WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
1576 break;
1577 case DevFmtInt:
1578 WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
1579 break;
1580 case DevFmtUInt:
1581 WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
1582 break;
1583 case DevFmtFloat:
1584 WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
1585 break;
1587 #undef WRITE
1590 size -= SamplesToDo;
1591 IncrementRef(&device->MixCount);
1594 RestoreFPUMode(&oldMode);
1598 ALvoid aluHandleDisconnect(ALCdevice *device)
1600 ALCcontext *Context;
1602 device->Connected = ALC_FALSE;
1604 Context = ATOMIC_LOAD(&device->ContextList);
1605 while(Context)
1607 ALvoice *voice, *voice_end;
1609 voice = Context->Voices;
1610 voice_end = voice + Context->VoiceCount;
1611 while(voice != voice_end)
1613 ALsource *source = voice->Source;
1614 voice->Source = NULL;
1616 if(source && source->state == AL_PLAYING)
1618 source->state = AL_STOPPED;
1619 ATOMIC_STORE(&source->current_buffer, NULL);
1620 source->position = 0;
1621 source->position_fraction = 0;
1624 voice++;
1626 Context->VoiceCount = 0;
1628 Context = Context->next;