Use separate macros for atomics that don't take a memory order
[openal-soft.git] / Alc / ALu.c
blobdb0320572666d45bc65f3046a3da83124a885de1
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <math.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
29 #include "alMain.h"
30 #include "alSource.h"
31 #include "alBuffer.h"
32 #include "alListener.h"
33 #include "alAuxEffectSlot.h"
34 #include "alu.h"
35 #include "bs2b.h"
36 #include "hrtf.h"
37 #include "uhjfilter.h"
38 #include "bformatdec.h"
39 #include "static_assert.h"
41 #include "mixer_defs.h"
43 #include "backends/base.h"
46 struct ChanMap {
47 enum Channel channel;
48 ALfloat angle;
49 ALfloat elevation;
52 /* Cone scalar */
53 ALfloat ConeScale = 1.0f;
55 /* Localized Z scalar for mono sources */
56 ALfloat ZScale = 1.0f;
58 extern inline ALfloat minf(ALfloat a, ALfloat b);
59 extern inline ALfloat maxf(ALfloat a, ALfloat b);
60 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
62 extern inline ALdouble mind(ALdouble a, ALdouble b);
63 extern inline ALdouble maxd(ALdouble a, ALdouble b);
64 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
66 extern inline ALuint minu(ALuint a, ALuint b);
67 extern inline ALuint maxu(ALuint a, ALuint b);
68 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
70 extern inline ALint mini(ALint a, ALint b);
71 extern inline ALint maxi(ALint a, ALint b);
72 extern inline ALint clampi(ALint val, ALint min, ALint max);
74 extern inline ALint64 mini64(ALint64 a, ALint64 b);
75 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
76 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
78 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
79 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
80 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
82 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
83 extern inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac);
84 extern inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat val5, ALfloat val6, ALfloat val7, ALuint frac);
86 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
88 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
89 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
90 extern inline void aluMatrixfSet(aluMatrixf *matrix,
91 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
92 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
93 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
94 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
96 const aluMatrixf IdentityMatrixf = {{
97 { 1.0f, 0.0f, 0.0f, 0.0f },
98 { 0.0f, 1.0f, 0.0f, 0.0f },
99 { 0.0f, 0.0f, 1.0f, 0.0f },
100 { 0.0f, 0.0f, 0.0f, 1.0f },
104 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
106 #ifdef HAVE_SSE
107 if((CPUCapFlags&CPU_CAP_SSE))
108 return MixDirectHrtf_SSE;
109 #endif
110 #ifdef HAVE_NEON
111 if((CPUCapFlags&CPU_CAP_NEON))
112 return MixDirectHrtf_Neon;
113 #endif
115 return MixDirectHrtf_C;
119 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
121 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
122 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
123 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
126 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
128 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
131 static ALfloat aluNormalize(ALfloat *vec)
133 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
134 if(length > 0.0f)
136 ALfloat inv_length = 1.0f/length;
137 vec[0] *= inv_length;
138 vec[1] *= inv_length;
139 vec[2] *= inv_length;
141 return length;
144 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
146 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
148 vec[0] = v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0];
149 vec[1] = v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1];
150 vec[2] = v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2];
153 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
155 aluVector v;
156 v.v[0] = vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0];
157 v.v[1] = vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1];
158 v.v[2] = vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2];
159 v.v[3] = vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3];
160 return v;
164 /* Prepares the interpolator for a given rate (determined by increment). A
165 * result of AL_FALSE indicates that the filter output will completely cut
166 * the input signal.
168 * With a bit of work, and a trade of memory for CPU cost, this could be
169 * modified for use with an interpolated increment for buttery-smooth pitch
170 * changes.
172 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
174 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
175 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
176 static const ALuint to[4][BSINC_SCALE_COUNT] =
178 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
179 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
180 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
181 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
183 static const ALuint tm[2][BSINC_SCALE_COUNT] =
185 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
186 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
188 ALfloat sf;
189 ALuint si, pi;
190 ALboolean uncut = AL_TRUE;
192 if(increment > FRACTIONONE)
194 sf = (ALfloat)FRACTIONONE / increment;
195 if(sf < scaleBase)
197 /* Signal has been completely cut. The return result can be used
198 * to skip the filter (and output zeros) as an optimization.
200 sf = 0.0f;
201 si = 0;
202 uncut = AL_FALSE;
204 else
206 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
207 si = fastf2u(sf);
208 /* The interpolation factor is fit to this diagonally-symmetric
209 * curve to reduce the transition ripple caused by interpolating
210 * different scales of the sinc function.
212 sf = 1.0f - cosf(asinf(sf - si));
215 else
217 sf = 0.0f;
218 si = BSINC_SCALE_COUNT - 1;
221 state->sf = sf;
222 state->m = m[si];
223 state->l = -(ALint)((m[si] / 2) - 1);
224 /* The CPU cost of this table re-mapping could be traded for the memory
225 * cost of a complete table map (1024 elements large).
227 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
229 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
230 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
231 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
232 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
234 return uncut;
238 static ALboolean CalcListenerParams(ALCcontext *Context)
240 ALlistener *Listener = Context->Listener;
241 ALfloat N[3], V[3], U[3], P[3];
242 struct ALlistenerProps *first;
243 struct ALlistenerProps *props;
244 aluVector vel;
246 props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &Listener->Update, NULL, almemory_order_acq_rel);
247 if(!props) return AL_FALSE;
249 /* AT then UP */
250 N[0] = ATOMIC_LOAD(&props->Forward[0], almemory_order_relaxed);
251 N[1] = ATOMIC_LOAD(&props->Forward[1], almemory_order_relaxed);
252 N[2] = ATOMIC_LOAD(&props->Forward[2], almemory_order_relaxed);
253 aluNormalize(N);
254 V[0] = ATOMIC_LOAD(&props->Up[0], almemory_order_relaxed);
255 V[1] = ATOMIC_LOAD(&props->Up[1], almemory_order_relaxed);
256 V[2] = ATOMIC_LOAD(&props->Up[2], almemory_order_relaxed);
257 aluNormalize(V);
258 /* Build and normalize right-vector */
259 aluCrossproduct(N, V, U);
260 aluNormalize(U);
262 aluMatrixfSet(&Listener->Params.Matrix,
263 U[0], V[0], -N[0], 0.0,
264 U[1], V[1], -N[1], 0.0,
265 U[2], V[2], -N[2], 0.0,
266 0.0, 0.0, 0.0, 1.0
269 P[0] = ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed);
270 P[1] = ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed);
271 P[2] = ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed);
272 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
273 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
275 aluVectorSet(&vel, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
276 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
277 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
278 0.0f);
279 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
281 Listener->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed) * Context->GainBoost;
282 Listener->Params.MetersPerUnit = ATOMIC_LOAD(&props->MetersPerUnit, almemory_order_relaxed);
284 Listener->Params.DopplerFactor = ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
285 Listener->Params.SpeedOfSound = ATOMIC_LOAD(&props->SpeedOfSound, almemory_order_relaxed) *
286 ATOMIC_LOAD(&props->DopplerVelocity, almemory_order_relaxed);
288 Listener->Params.SourceDistanceModel = ATOMIC_LOAD(&props->SourceDistanceModel, almemory_order_relaxed);
289 Listener->Params.DistanceModel = ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed);
291 /* WARNING: A livelock is theoretically possible if another thread keeps
292 * changing the freelist head without giving this a chance to actually swap
293 * in the old container (practically impossible with this little code,
294 * but...).
296 first = ATOMIC_LOAD(&Listener->FreeList, almemory_order_acquire);
297 do {
298 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
299 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*,
300 &Listener->FreeList, &first, props, almemory_order_acq_rel,
301 almemory_order_acquire) == 0);
303 return AL_TRUE;
306 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
308 struct ALeffectslotProps *first;
309 struct ALeffectslotProps *props;
310 ALeffectState *state;
312 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL, almemory_order_acq_rel);
313 if(!props) return AL_FALSE;
315 slot->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
316 slot->Params.AuxSendAuto = ATOMIC_LOAD(&props->AuxSendAuto, almemory_order_relaxed);
317 slot->Params.EffectType = ATOMIC_LOAD(&props->Type, almemory_order_relaxed);
318 if(IsReverbEffect(slot->Params.EffectType))
320 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
321 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
322 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
324 else
326 slot->Params.RoomRolloff = 0.0f;
327 slot->Params.DecayTime = 0.0f;
328 slot->Params.AirAbsorptionGainHF = 1.0f;
331 /* Swap effect states. No need to play with the ref counts since they keep
332 * the same number of refs.
334 state = ATOMIC_EXCHANGE(ALeffectState*, &props->State, slot->Params.EffectState,
335 almemory_order_relaxed);
336 slot->Params.EffectState = state;
338 V(state,update)(device, slot, &props->Props);
340 /* WARNING: A livelock is theoretically possible if another thread keeps
341 * changing the freelist head without giving this a chance to actually swap
342 * in the old container (practically impossible with this little code,
343 * but...).
345 first = ATOMIC_LOAD(&slot->FreeList, almemory_order_acquire);
346 do {
347 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
348 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
349 &slot->FreeList, &first, props, almemory_order_acq_rel,
350 almemory_order_acquire) == 0);
352 return AL_TRUE;
356 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
358 static const struct ChanMap MonoMap[1] = {
359 { FrontCenter, 0.0f, 0.0f }
360 }, RearMap[2] = {
361 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
362 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
363 }, QuadMap[4] = {
364 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
365 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
366 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
367 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
368 }, X51Map[6] = {
369 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
370 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
371 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
372 { LFE, 0.0f, 0.0f },
373 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
374 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
375 }, X61Map[7] = {
376 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
377 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
378 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
379 { LFE, 0.0f, 0.0f },
380 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
381 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
382 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
383 }, X71Map[8] = {
384 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
385 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
386 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
387 { LFE, 0.0f, 0.0f },
388 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
389 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
390 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
391 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
394 const ALCdevice *Device = ALContext->Device;
395 const ALlistener *Listener = ALContext->Listener;
396 ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
397 ALfloat DryGain, DryGainHF, DryGainLF;
398 ALfloat WetGain[MAX_SENDS];
399 ALfloat WetGainHF[MAX_SENDS];
400 ALfloat WetGainLF[MAX_SENDS];
401 ALeffectslot *SendSlots[MAX_SENDS];
402 ALuint NumSends, Frequency;
403 ALboolean Relative;
404 const struct ChanMap *chans = NULL;
405 struct ChanMap StereoMap[2] = {
406 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
407 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
409 ALuint num_channels = 0;
410 ALboolean DirectChannels;
411 ALboolean isbformat = AL_FALSE;
412 ALfloat Pitch;
413 ALuint i, j, c;
415 /* Get device properties */
416 NumSends = Device->NumAuxSends;
417 Frequency = Device->Frequency;
419 /* Get listener properties */
420 ListenerGain = Listener->Params.Gain;
422 /* Get source properties */
423 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
424 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
425 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
426 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
427 Relative = ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed);
428 DirectChannels = ATOMIC_LOAD(&props->DirectChannels, almemory_order_relaxed);
430 /* Convert counter-clockwise to clockwise. */
431 StereoMap[0].angle = -ATOMIC_LOAD(&props->StereoPan[0], almemory_order_relaxed);
432 StereoMap[1].angle = -ATOMIC_LOAD(&props->StereoPan[1], almemory_order_relaxed);
434 voice->DirectOut.Buffer = Device->Dry.Buffer;
435 voice->DirectOut.Channels = Device->Dry.NumChannels;
436 for(i = 0;i < NumSends;i++)
438 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
439 if(!SendSlots[i] && i == 0)
440 SendSlots[i] = Device->DefaultSlot;
441 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
443 SendSlots[i] = NULL;
444 voice->SendOut[i].Buffer = NULL;
445 voice->SendOut[i].Channels = 0;
447 else
449 voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer;
450 voice->SendOut[i].Channels = SendSlots[i]->NumChannels;
454 /* Calculate the stepping value */
455 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
456 if(Pitch > (ALfloat)MAX_PITCH)
457 voice->Step = MAX_PITCH<<FRACTIONBITS;
458 else
459 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
460 BsincPrepare(voice->Step, &voice->SincState);
462 /* Calculate gains */
463 DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
464 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
465 DryGain = minf(DryGain, GAIN_MIX_MAX);
466 DryGainHF = ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
467 DryGainLF = ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
468 for(i = 0;i < NumSends;i++)
470 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
471 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
472 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
473 WetGainHF[i] = ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
474 WetGainLF[i] = ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
477 switch(ALBuffer->FmtChannels)
479 case FmtMono:
480 chans = MonoMap;
481 num_channels = 1;
482 break;
484 case FmtStereo:
485 chans = StereoMap;
486 num_channels = 2;
487 break;
489 case FmtRear:
490 chans = RearMap;
491 num_channels = 2;
492 break;
494 case FmtQuad:
495 chans = QuadMap;
496 num_channels = 4;
497 break;
499 case FmtX51:
500 chans = X51Map;
501 num_channels = 6;
502 break;
504 case FmtX61:
505 chans = X61Map;
506 num_channels = 7;
507 break;
509 case FmtX71:
510 chans = X71Map;
511 num_channels = 8;
512 break;
514 case FmtBFormat2D:
515 num_channels = 3;
516 isbformat = AL_TRUE;
517 DirectChannels = AL_FALSE;
518 break;
520 case FmtBFormat3D:
521 num_channels = 4;
522 isbformat = AL_TRUE;
523 DirectChannels = AL_FALSE;
524 break;
527 if(isbformat)
529 ALfloat N[3], V[3], U[3];
530 aluMatrixf matrix;
531 ALfloat scale;
533 /* AT then UP */
534 N[0] = ATOMIC_LOAD(&props->Orientation[0][0], almemory_order_relaxed);
535 N[1] = ATOMIC_LOAD(&props->Orientation[0][1], almemory_order_relaxed);
536 N[2] = ATOMIC_LOAD(&props->Orientation[0][2], almemory_order_relaxed);
537 aluNormalize(N);
538 V[0] = ATOMIC_LOAD(&props->Orientation[1][0], almemory_order_relaxed);
539 V[1] = ATOMIC_LOAD(&props->Orientation[1][1], almemory_order_relaxed);
540 V[2] = ATOMIC_LOAD(&props->Orientation[1][2], almemory_order_relaxed);
541 aluNormalize(V);
542 if(!Relative)
544 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
545 aluMatrixfFloat3(N, 0.0f, lmatrix);
546 aluMatrixfFloat3(V, 0.0f, lmatrix);
548 /* Build and normalize right-vector */
549 aluCrossproduct(N, V, U);
550 aluNormalize(U);
552 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
553 scale = 1.732050808f;
554 aluMatrixfSet(&matrix,
555 1.414213562f, 0.0f, 0.0f, 0.0f,
556 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
557 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
558 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
561 voice->DirectOut.Buffer = Device->FOAOut.Buffer;
562 voice->DirectOut.Channels = Device->FOAOut.NumChannels;
563 for(c = 0;c < num_channels;c++)
564 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
565 voice->Chan[c].Direct.Gains.Target);
567 for(i = 0;i < NumSends;i++)
569 if(!SendSlots[i])
571 for(c = 0;c < num_channels;c++)
573 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
574 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
577 else
579 for(c = 0;c < num_channels;c++)
581 const ALeffectslot *Slot = SendSlots[i];
582 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, matrix.m[c],
583 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
588 voice->IsHrtf = AL_FALSE;
590 else
592 ALfloat coeffs[MAX_AMBI_COEFFS];
594 if(DirectChannels)
596 /* Skip the virtual channels and write inputs to the real output. */
597 voice->DirectOut.Buffer = Device->RealOut.Buffer;
598 voice->DirectOut.Channels = Device->RealOut.NumChannels;
599 for(c = 0;c < num_channels;c++)
601 int idx;
602 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
603 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
604 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
605 voice->Chan[c].Direct.Gains.Target[idx] = DryGain;
608 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
609 * channel-match. */
610 for(c = 0;c < num_channels;c++)
612 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
614 for(i = 0;i < NumSends;i++)
616 if(!SendSlots[i])
618 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
619 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
621 else
623 const ALeffectslot *Slot = SendSlots[i];
624 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
625 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
630 voice->IsHrtf = AL_FALSE;
632 else if(Device->Render_Mode == HrtfRender)
634 /* Full HRTF rendering. Skip the virtual channels and render each
635 * input channel to the real outputs.
637 voice->DirectOut.Buffer = Device->RealOut.Buffer;
638 voice->DirectOut.Channels = Device->RealOut.NumChannels;
639 for(c = 0;c < num_channels;c++)
641 if(chans[c].channel == LFE)
643 /* Skip LFE */
644 voice->Chan[c].Direct.Hrtf.Target.Delay[0] = 0;
645 voice->Chan[c].Direct.Hrtf.Target.Delay[1] = 0;
646 for(i = 0;i < HRIR_LENGTH;i++)
648 voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][0] = 0.0f;
649 voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][1] = 0.0f;
652 for(i = 0;i < NumSends;i++)
654 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
655 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
658 continue;
661 /* Get the static HRIR coefficients and delays for this channel. */
662 GetHrtfCoeffs(Device->Hrtf.Handle,
663 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
664 voice->Chan[c].Direct.Hrtf.Target.Coeffs,
665 voice->Chan[c].Direct.Hrtf.Target.Delay
668 /* Normal panning for auxiliary sends. */
669 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
671 for(i = 0;i < NumSends;i++)
673 if(!SendSlots[i])
675 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
676 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
678 else
680 const ALeffectslot *Slot = SendSlots[i];
681 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
682 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
687 voice->IsHrtf = AL_TRUE;
689 else
691 /* Non-HRTF rendering. Use normal panning to the output. */
692 for(c = 0;c < num_channels;c++)
694 /* Special-case LFE */
695 if(chans[c].channel == LFE)
697 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
698 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
699 if(Device->Dry.Buffer == Device->RealOut.Buffer)
701 int idx;
702 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
703 voice->Chan[c].Direct.Gains.Target[idx] = DryGain;
706 for(i = 0;i < NumSends;i++)
708 ALuint j;
709 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
710 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
712 continue;
715 if(Device->Render_Mode == StereoPair)
717 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
718 ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
719 coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
720 voice->Chan[c].Direct.Gains.Target[0] = coeffs[0] * DryGain;
721 voice->Chan[c].Direct.Gains.Target[1] = (1.0f-coeffs[0]) * DryGain;
722 for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
723 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
725 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
727 else
729 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
730 ComputePanningGains(Device->Dry, coeffs, DryGain,
731 voice->Chan[c].Direct.Gains.Target);
734 for(i = 0;i < NumSends;i++)
736 if(!SendSlots[i])
738 ALuint j;
739 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
740 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
742 else
744 const ALeffectslot *Slot = SendSlots[i];
745 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
746 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
751 voice->IsHrtf = AL_FALSE;
756 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
757 Frequency;
758 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
759 Frequency;
760 DryGainHF = maxf(DryGainHF, 0.0001f);
761 DryGainLF = maxf(DryGainLF, 0.0001f);
762 for(c = 0;c < num_channels;c++)
764 voice->Chan[c].Direct.FilterType = AF_None;
765 if(DryGainHF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_LowPass;
766 if(DryGainLF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_HighPass;
767 ALfilterState_setParams(
768 &voice->Chan[c].Direct.LowPass, ALfilterType_HighShelf,
769 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
771 ALfilterState_setParams(
772 &voice->Chan[c].Direct.HighPass, ALfilterType_LowShelf,
773 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
777 for(i = 0;i < NumSends;i++)
779 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
780 Frequency;
781 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
782 Frequency;
783 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
784 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
785 for(c = 0;c < num_channels;c++)
787 voice->Chan[c].Send[i].FilterType = AF_None;
788 if(WetGainHF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_LowPass;
789 if(WetGainLF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_HighPass;
790 ALfilterState_setParams(
791 &voice->Chan[c].Send[i].LowPass, ALfilterType_HighShelf,
792 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
794 ALfilterState_setParams(
795 &voice->Chan[c].Send[i].HighPass, ALfilterType_LowShelf,
796 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
802 static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
804 const ALCdevice *Device = ALContext->Device;
805 const ALlistener *Listener = ALContext->Listener;
806 aluVector Position, Velocity, Direction, SourceToListener;
807 ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
808 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
809 ALfloat SourceVolume,ListenerGain;
810 ALfloat DopplerFactor, SpeedOfSound;
811 ALfloat AirAbsorptionFactor;
812 ALfloat RoomAirAbsorption[MAX_SENDS];
813 ALeffectslot *SendSlots[MAX_SENDS];
814 ALfloat Attenuation;
815 ALfloat RoomAttenuation[MAX_SENDS];
816 ALfloat MetersPerUnit;
817 ALfloat RoomRolloffBase;
818 ALfloat RoomRolloff[MAX_SENDS];
819 ALfloat DecayDistance[MAX_SENDS];
820 ALfloat DryGain;
821 ALfloat DryGainHF;
822 ALfloat DryGainLF;
823 ALboolean DryGainHFAuto;
824 ALfloat WetGain[MAX_SENDS];
825 ALfloat WetGainHF[MAX_SENDS];
826 ALfloat WetGainLF[MAX_SENDS];
827 ALboolean WetGainAuto;
828 ALboolean WetGainHFAuto;
829 ALfloat Pitch;
830 ALuint Frequency;
831 ALint NumSends;
832 ALint i;
834 DryGainHF = 1.0f;
835 DryGainLF = 1.0f;
836 for(i = 0;i < MAX_SENDS;i++)
838 WetGainHF[i] = 1.0f;
839 WetGainLF[i] = 1.0f;
842 /* Get context/device properties */
843 DopplerFactor = Listener->Params.DopplerFactor;
844 SpeedOfSound = Listener->Params.SpeedOfSound;
845 NumSends = Device->NumAuxSends;
846 Frequency = Device->Frequency;
848 /* Get listener properties */
849 ListenerGain = Listener->Params.Gain;
850 MetersPerUnit = Listener->Params.MetersPerUnit;
852 /* Get source properties */
853 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
854 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
855 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
856 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
857 aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed),
858 ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed),
859 ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed),
860 1.0f);
861 aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed),
862 ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed),
863 ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed),
864 0.0f);
865 aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
866 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
867 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
868 0.0f);
869 MinDist = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed);
870 MaxDist = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed);
871 Rolloff = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed);
872 DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
873 InnerAngle = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed);
874 OuterAngle = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed);
875 AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed);
876 DryGainHFAuto = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed);
877 WetGainAuto = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed);
878 WetGainHFAuto = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed);
879 RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed);
881 voice->DirectOut.Buffer = Device->Dry.Buffer;
882 voice->DirectOut.Channels = Device->Dry.NumChannels;
883 for(i = 0;i < NumSends;i++)
885 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
887 if(!SendSlots[i] && i == 0)
888 SendSlots[i] = Device->DefaultSlot;
889 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
891 SendSlots[i] = NULL;
892 RoomRolloff[i] = 0.0f;
893 DecayDistance[i] = 0.0f;
894 RoomAirAbsorption[i] = 1.0f;
896 else if(SendSlots[i]->Params.AuxSendAuto)
898 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
899 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
900 SPEEDOFSOUNDMETRESPERSEC;
901 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
903 else
905 /* If the slot's auxiliary send auto is off, the data sent to the
906 * effect slot is the same as the dry path, sans filter effects */
907 RoomRolloff[i] = Rolloff;
908 DecayDistance[i] = 0.0f;
909 RoomAirAbsorption[i] = AIRABSORBGAINHF;
912 if(!SendSlots[i])
914 voice->SendOut[i].Buffer = NULL;
915 voice->SendOut[i].Channels = 0;
917 else
919 voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer;
920 voice->SendOut[i].Channels = SendSlots[i]->NumChannels;
924 /* Transform source to listener space (convert to head relative) */
925 if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE)
927 const aluMatrixf *Matrix = &Listener->Params.Matrix;
928 /* Transform source vectors */
929 Position = aluMatrixfVector(Matrix, &Position);
930 Velocity = aluMatrixfVector(Matrix, &Velocity);
931 Direction = aluMatrixfVector(Matrix, &Direction);
933 else
935 const aluVector *lvelocity = &Listener->Params.Velocity;
936 /* Offset the source velocity to be relative of the listener velocity */
937 Velocity.v[0] += lvelocity->v[0];
938 Velocity.v[1] += lvelocity->v[1];
939 Velocity.v[2] += lvelocity->v[2];
942 aluNormalize(Direction.v);
943 SourceToListener.v[0] = -Position.v[0];
944 SourceToListener.v[1] = -Position.v[1];
945 SourceToListener.v[2] = -Position.v[2];
946 SourceToListener.v[3] = 0.0f;
947 Distance = aluNormalize(SourceToListener.v);
949 /* Calculate distance attenuation */
950 ClampedDist = Distance;
952 Attenuation = 1.0f;
953 for(i = 0;i < NumSends;i++)
954 RoomAttenuation[i] = 1.0f;
955 switch(Listener->Params.SourceDistanceModel ?
956 ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed) :
957 Listener->Params.DistanceModel)
959 case InverseDistanceClamped:
960 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
961 if(MaxDist < MinDist)
962 break;
963 /*fall-through*/
964 case InverseDistance:
965 if(MinDist > 0.0f)
967 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
968 if(dist > 0.0f) Attenuation = MinDist / dist;
969 for(i = 0;i < NumSends;i++)
971 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
972 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
975 break;
977 case LinearDistanceClamped:
978 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
979 if(MaxDist < MinDist)
980 break;
981 /*fall-through*/
982 case LinearDistance:
983 if(MaxDist != MinDist)
985 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
986 Attenuation = maxf(Attenuation, 0.0f);
987 for(i = 0;i < NumSends;i++)
989 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
990 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
993 break;
995 case ExponentDistanceClamped:
996 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
997 if(MaxDist < MinDist)
998 break;
999 /*fall-through*/
1000 case ExponentDistance:
1001 if(ClampedDist > 0.0f && MinDist > 0.0f)
1003 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
1004 for(i = 0;i < NumSends;i++)
1005 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
1007 break;
1009 case DisableDistance:
1010 ClampedDist = MinDist;
1011 break;
1014 /* Source Gain + Attenuation */
1015 DryGain = SourceVolume * Attenuation;
1016 for(i = 0;i < NumSends;i++)
1017 WetGain[i] = SourceVolume * RoomAttenuation[i];
1019 /* Distance-based air absorption */
1020 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
1022 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
1023 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
1024 for(i = 0;i < NumSends;i++)
1025 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
1028 if(WetGainAuto)
1030 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1032 /* Apply a decay-time transformation to the wet path, based on the
1033 * attenuation of the dry path.
1035 * Using the apparent distance, based on the distance attenuation, the
1036 * initial decay of the reverb effect is calculated and applied to the
1037 * wet path.
1039 for(i = 0;i < NumSends;i++)
1041 if(DecayDistance[i] > 0.0f)
1042 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1046 /* Calculate directional soundcones */
1047 if(InnerAngle < 360.0f)
1049 ALfloat ConeVolume;
1050 ALfloat ConeHF;
1051 ALfloat Angle;
1052 ALfloat scale;
1054 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1055 if(Angle > InnerAngle)
1057 if(Angle < OuterAngle)
1059 scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1060 ConeVolume = lerp(
1061 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1063 ConeHF = lerp(
1064 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1067 else
1069 ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed);
1070 ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed);
1072 DryGain *= ConeVolume;
1073 if(DryGainHFAuto)
1074 DryGainHF *= ConeHF;
1077 /* Wet path uses the total area of the cone emitter (the room will
1078 * receive the same amount of sound regardless of its direction).
1080 scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
1081 (InnerAngle/360.0f);
1082 if(WetGainAuto)
1084 ConeVolume = lerp(
1085 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1087 for(i = 0;i < NumSends;i++)
1088 WetGain[i] *= ConeVolume;
1090 if(WetGainHFAuto)
1092 ConeHF = lerp(
1093 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1095 for(i = 0;i < NumSends;i++)
1096 WetGainHF[i] *= ConeHF;
1100 /* Apply gain and frequency filters */
1101 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1102 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
1103 DryGain = minf(DryGain, GAIN_MIX_MAX);
1104 DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
1105 DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
1106 for(i = 0;i < NumSends;i++)
1108 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1109 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
1110 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1111 WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
1112 WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
1115 /* Calculate velocity-based doppler effect */
1116 if(DopplerFactor > 0.0f)
1118 const aluVector *lvelocity = &Listener->Params.Velocity;
1119 ALfloat VSS, VLS;
1121 if(SpeedOfSound < 1.0f)
1123 DopplerFactor *= 1.0f/SpeedOfSound;
1124 SpeedOfSound = 1.0f;
1127 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1128 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1130 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1131 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1134 /* Calculate fixed-point stepping value, based on the pitch, buffer
1135 * frequency, and output frequency.
1137 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1138 if(Pitch > (ALfloat)MAX_PITCH)
1139 voice->Step = MAX_PITCH<<FRACTIONBITS;
1140 else
1141 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1142 BsincPrepare(voice->Step, &voice->SincState);
1144 if(Device->Render_Mode == HrtfRender)
1146 /* Full HRTF rendering. Skip the virtual channels and render to the
1147 * real outputs.
1149 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1150 ALfloat ev = 0.0f, az = 0.0f;
1151 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1152 ALfloat coeffs[MAX_AMBI_COEFFS];
1153 ALfloat spread = 0.0f;
1155 voice->DirectOut.Buffer = Device->RealOut.Buffer;
1156 voice->DirectOut.Channels = Device->RealOut.NumChannels;
1158 if(Distance > FLT_EPSILON)
1160 dir[0] = -SourceToListener.v[0];
1161 dir[1] = -SourceToListener.v[1];
1162 dir[2] = -SourceToListener.v[2] * ZScale;
1164 /* Calculate elevation and azimuth only when the source is not at
1165 * the listener. This prevents +0 and -0 Z from producing
1166 * inconsistent panning. Also, clamp Y in case FP precision errors
1167 * cause it to land outside of -1..+1. */
1168 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1169 az = atan2f(dir[0], -dir[2]);
1171 if(radius > Distance)
1172 spread = F_TAU - Distance/radius*F_PI;
1173 else if(Distance > FLT_EPSILON)
1174 spread = asinf(radius / Distance) * 2.0f;
1176 /* Get the HRIR coefficients and delays. */
1177 GetHrtfCoeffs(Device->Hrtf.Handle, ev, az, spread, DryGain,
1178 voice->Chan[0].Direct.Hrtf.Target.Coeffs,
1179 voice->Chan[0].Direct.Hrtf.Target.Delay);
1181 CalcDirectionCoeffs(dir, spread, coeffs);
1183 for(i = 0;i < NumSends;i++)
1185 if(!SendSlots[i])
1187 ALuint j;
1188 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1189 voice->Chan[0].Send[i].Gains.Target[j] = 0.0f;
1191 else
1193 const ALeffectslot *Slot = SendSlots[i];
1194 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1195 WetGain[i], voice->Chan[0].Send[i].Gains.Target);
1199 voice->IsHrtf = AL_TRUE;
1201 else
1203 /* Non-HRTF rendering. */
1204 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1205 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1206 ALfloat coeffs[MAX_AMBI_COEFFS];
1207 ALfloat spread = 0.0f;
1209 /* Get the localized direction, and compute panned gains. */
1210 if(Distance > FLT_EPSILON)
1212 dir[0] = -SourceToListener.v[0];
1213 dir[1] = -SourceToListener.v[1];
1214 dir[2] = -SourceToListener.v[2] * ZScale;
1216 if(radius > Distance)
1217 spread = F_TAU - Distance/radius*F_PI;
1218 else if(Distance > FLT_EPSILON)
1219 spread = asinf(radius / Distance) * 2.0f;
1221 if(Device->Render_Mode == StereoPair)
1223 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
1224 ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
1225 x = clampf(x, -0.5f, 0.5f) + 0.5f;
1226 voice->Chan[0].Direct.Gains.Target[0] = x * DryGain;
1227 voice->Chan[0].Direct.Gains.Target[1] = (1.0f-x) * DryGain;
1228 for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
1229 voice->Chan[0].Direct.Gains.Target[i] = 0.0f;
1231 CalcDirectionCoeffs(dir, spread, coeffs);
1233 else
1235 CalcDirectionCoeffs(dir, spread, coeffs);
1236 ComputePanningGains(Device->Dry, coeffs, DryGain,
1237 voice->Chan[0].Direct.Gains.Target);
1240 for(i = 0;i < NumSends;i++)
1242 if(!SendSlots[i])
1244 ALuint j;
1245 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1246 voice->Chan[0].Send[i].Gains.Target[j] = 0.0f;
1248 else
1250 const ALeffectslot *Slot = SendSlots[i];
1251 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1252 WetGain[i], voice->Chan[0].Send[i].Gains.Target);
1256 voice->IsHrtf = AL_FALSE;
1260 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
1261 Frequency;
1262 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
1263 Frequency;
1264 DryGainHF = maxf(DryGainHF, 0.0001f);
1265 DryGainLF = maxf(DryGainLF, 0.0001f);
1266 voice->Chan[0].Direct.FilterType = AF_None;
1267 if(DryGainHF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_LowPass;
1268 if(DryGainLF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_HighPass;
1269 ALfilterState_setParams(
1270 &voice->Chan[0].Direct.LowPass, ALfilterType_HighShelf,
1271 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
1273 ALfilterState_setParams(
1274 &voice->Chan[0].Direct.HighPass, ALfilterType_LowShelf,
1275 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
1278 for(i = 0;i < NumSends;i++)
1280 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
1281 Frequency;
1282 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
1283 Frequency;
1284 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
1285 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
1286 voice->Chan[0].Send[i].FilterType = AF_None;
1287 if(WetGainHF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_LowPass;
1288 if(WetGainLF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_HighPass;
1289 ALfilterState_setParams(
1290 &voice->Chan[0].Send[i].LowPass, ALfilterType_HighShelf,
1291 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
1293 ALfilterState_setParams(
1294 &voice->Chan[0].Send[i].HighPass, ALfilterType_LowShelf,
1295 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
1300 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1302 ALsource *source = voice->Source;
1303 const ALbufferlistitem *BufferListItem;
1304 struct ALsourceProps *first;
1305 struct ALsourceProps *props;
1307 props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
1308 if(!props && !force) return;
1310 if(props)
1312 voice->Props = *props;
1314 /* WARNING: A livelock is theoretically possible if another thread
1315 * keeps changing the freelist head without giving this a chance to
1316 * actually swap in the old container (practically impossible with this
1317 * little code, but...).
1319 first = ATOMIC_LOAD(&source->FreeList, almemory_order_acquire);
1320 do {
1321 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
1322 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*,
1323 &source->FreeList, &first, props, almemory_order_acq_rel,
1324 almemory_order_acquire) == 0);
1327 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1328 while(BufferListItem != NULL)
1330 const ALbuffer *buffer;
1331 if((buffer=BufferListItem->buffer) != NULL)
1333 if(buffer->FmtChannels == FmtMono)
1334 CalcAttnSourceParams(voice, &voice->Props, buffer, context);
1335 else
1336 CalcNonAttnSourceParams(voice, &voice->Props, buffer, context);
1337 break;
1339 BufferListItem = BufferListItem->next;
1344 static void UpdateContextSources(ALCcontext *ctx, ALeffectslot *slot)
1346 ALvoice *voice, *voice_end;
1347 ALsource *source;
1349 IncrementRef(&ctx->UpdateCount);
1350 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1352 ALboolean force = CalcListenerParams(ctx);
1353 while(slot)
1355 force |= CalcEffectSlotParams(slot, ctx->Device);
1356 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1359 voice = ctx->Voices;
1360 voice_end = voice + ctx->VoiceCount;
1361 for(;voice != voice_end;++voice)
1363 if(!(source=voice->Source)) continue;
1364 if(source->state != AL_PLAYING && source->state != AL_PAUSED)
1365 voice->Source = NULL;
1366 else
1367 CalcSourceParams(voice, ctx, force);
1370 IncrementRef(&ctx->UpdateCount);
1374 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1375 * converts NaN to 0. */
1376 static inline ALfloat aluClampf(ALfloat val)
1378 if(fabsf(val) <= 1.0f) return val;
1379 return (ALfloat)((0.0f < val) - (val < 0.0f));
1382 static inline ALfloat aluF2F(ALfloat val)
1383 { return val; }
1385 static inline ALint aluF2I(ALfloat val)
1387 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1388 * integer range normalized floats can be safely converted to.
1390 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1392 static inline ALuint aluF2UI(ALfloat val)
1393 { return aluF2I(val)+2147483648u; }
1395 static inline ALshort aluF2S(ALfloat val)
1396 { return fastf2i(aluClampf(val)*32767.0f); }
1397 static inline ALushort aluF2US(ALfloat val)
1398 { return aluF2S(val)+32768; }
1400 static inline ALbyte aluF2B(ALfloat val)
1401 { return fastf2i(aluClampf(val)*127.0f); }
1402 static inline ALubyte aluF2UB(ALfloat val)
1403 { return aluF2B(val)+128; }
1405 #define DECL_TEMPLATE(T, func) \
1406 static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1407 ALuint SamplesToDo, ALuint numchans) \
1409 ALuint i, j; \
1410 for(j = 0;j < numchans;j++) \
1412 const ALfloat *in = InBuffer[j]; \
1413 T *restrict out = (T*)OutBuffer + j; \
1414 for(i = 0;i < SamplesToDo;i++) \
1415 out[i*numchans] = func(in[i]); \
1419 DECL_TEMPLATE(ALfloat, aluF2F)
1420 DECL_TEMPLATE(ALuint, aluF2UI)
1421 DECL_TEMPLATE(ALint, aluF2I)
1422 DECL_TEMPLATE(ALushort, aluF2US)
1423 DECL_TEMPLATE(ALshort, aluF2S)
1424 DECL_TEMPLATE(ALubyte, aluF2UB)
1425 DECL_TEMPLATE(ALbyte, aluF2B)
1427 #undef DECL_TEMPLATE
1430 ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1432 ALuint SamplesToDo;
1433 ALvoice *voice, *voice_end;
1434 ALeffectslot *slot;
1435 ALsource *source;
1436 ALCcontext *ctx;
1437 FPUCtl oldMode;
1438 ALuint i, c;
1440 SetMixerFPUMode(&oldMode);
1442 while(size > 0)
1444 SamplesToDo = minu(size, BUFFERSIZE);
1445 for(c = 0;c < device->Dry.NumChannels;c++)
1446 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1447 if(device->Dry.Buffer != device->RealOut.Buffer)
1448 for(c = 0;c < device->RealOut.NumChannels;c++)
1449 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1450 if(device->Dry.Buffer != device->FOAOut.Buffer)
1451 for(c = 0;c < device->FOAOut.NumChannels;c++)
1452 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1454 IncrementRef(&device->MixCount);
1455 V0(device->Backend,lock)();
1457 if((slot=device->DefaultSlot) != NULL)
1459 CalcEffectSlotParams(device->DefaultSlot, device);
1460 for(i = 0;i < slot->NumChannels;i++)
1461 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1464 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1465 while(ctx)
1467 ALeffectslot *slotroot;
1469 slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList, almemory_order_acquire);
1470 UpdateContextSources(ctx, slotroot);
1472 slot = slotroot;
1473 while(slot)
1475 for(i = 0;i < slot->NumChannels;i++)
1476 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1477 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1480 /* source processing */
1481 voice = ctx->Voices;
1482 voice_end = voice + ctx->VoiceCount;
1483 for(;voice != voice_end;++voice)
1485 ALboolean IsVoiceInit = (voice->Step > 0);
1486 source = voice->Source;
1487 if(source && source->state == AL_PLAYING && IsVoiceInit)
1488 MixSource(voice, source, device, SamplesToDo);
1491 /* effect slot processing */
1492 slot = slotroot;
1493 while(slot)
1495 ALeffectState *state = slot->Params.EffectState;
1496 V(state,process)(SamplesToDo, SAFE_CONST(ALfloatBUFFERSIZE*,slot->WetBuffer),
1497 state->OutBuffer, state->OutChannels);
1498 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1501 ctx = ctx->next;
1504 if(device->DefaultSlot != NULL)
1506 const ALeffectslot *slot = device->DefaultSlot;
1507 ALeffectState *state = slot->Params.EffectState;
1508 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1509 state->OutChannels);
1512 /* Increment the clock time. Every second's worth of samples is
1513 * converted and added to clock base so that large sample counts don't
1514 * overflow during conversion. This also guarantees an exact, stable
1515 * conversion. */
1516 device->SamplesDone += SamplesToDo;
1517 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1518 device->SamplesDone %= device->Frequency;
1519 V0(device->Backend,unlock)();
1520 IncrementRef(&device->MixCount);
1522 if(device->Hrtf.Handle)
1524 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1525 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1526 if(lidx != -1 && ridx != -1)
1528 HrtfDirectMixerFunc HrtfMix = SelectHrtfMixer();
1529 ALuint irsize = device->Hrtf.IrSize;
1530 for(c = 0;c < device->Dry.NumChannels;c++)
1532 HrtfMix(device->RealOut.Buffer, lidx, ridx,
1533 device->Dry.Buffer[c], device->Hrtf.Offset, irsize,
1534 device->Hrtf.Coeffs[c], device->Hrtf.Values[c],
1535 SamplesToDo
1538 device->Hrtf.Offset += SamplesToDo;
1541 else if(device->AmbiDecoder)
1543 if(device->Dry.Buffer != device->FOAOut.Buffer)
1544 bformatdec_upSample(device->AmbiDecoder,
1545 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1546 device->FOAOut.NumChannels, SamplesToDo
1548 bformatdec_process(device->AmbiDecoder,
1549 device->RealOut.Buffer, device->RealOut.NumChannels,
1550 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1553 else if(device->AmbiUp)
1555 ambiup_process(device->AmbiUp,
1556 device->RealOut.Buffer, device->RealOut.NumChannels,
1557 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1560 else if(device->Uhj_Encoder)
1562 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1563 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1564 if(lidx != -1 && ridx != -1)
1566 /* Encode to stereo-compatible 2-channel UHJ output. */
1567 EncodeUhj2(device->Uhj_Encoder,
1568 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1569 device->Dry.Buffer, SamplesToDo
1573 else if(device->Bs2b)
1575 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1576 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1577 if(lidx != -1 && ridx != -1)
1579 /* Apply binaural/crossfeed filter */
1580 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1581 device->RealOut.Buffer[ridx], SamplesToDo);
1585 if(buffer)
1587 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1588 ALuint OutChannels = device->RealOut.NumChannels;
1590 #define WRITE(T, a, b, c, d) do { \
1591 Write_##T((a), (b), (c), (d)); \
1592 buffer = (T*)buffer + (c)*(d); \
1593 } while(0)
1594 switch(device->FmtType)
1596 case DevFmtByte:
1597 WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1598 break;
1599 case DevFmtUByte:
1600 WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1601 break;
1602 case DevFmtShort:
1603 WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
1604 break;
1605 case DevFmtUShort:
1606 WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
1607 break;
1608 case DevFmtInt:
1609 WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
1610 break;
1611 case DevFmtUInt:
1612 WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
1613 break;
1614 case DevFmtFloat:
1615 WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
1616 break;
1618 #undef WRITE
1621 size -= SamplesToDo;
1624 RestoreFPUMode(&oldMode);
1628 ALvoid aluHandleDisconnect(ALCdevice *device)
1630 ALCcontext *Context;
1632 device->Connected = ALC_FALSE;
1634 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1635 while(Context)
1637 ALvoice *voice, *voice_end;
1639 voice = Context->Voices;
1640 voice_end = voice + Context->VoiceCount;
1641 while(voice != voice_end)
1643 ALsource *source = voice->Source;
1644 voice->Source = NULL;
1646 if(source && source->state == AL_PLAYING)
1648 source->state = AL_STOPPED;
1649 ATOMIC_STORE(&source->current_buffer, NULL, almemory_order_relaxed);
1650 ATOMIC_STORE(&source->position, 0, almemory_order_relaxed);
1651 ATOMIC_STORE(&source->position_fraction, 0, almemory_order_release);
1654 voice++;
1656 Context->VoiceCount = 0;
1658 Context = Context->next;