Ignore the listening angle for the wet path sound cones
[openal-soft.git] / Alc / ALu.c
blob4c7000d7dc5e6d4caeba68a8a275ad4cf7c64fcf
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 Listener->Params.SourceDistanceModel = ATOMIC_LOAD(&props->SourceDistanceModel,
282 almemory_order_relaxed);
283 Listener->Params.DistanceModel = ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed);
285 /* WARNING: A livelock is theoretically possible if another thread keeps
286 * changing the freelist head without giving this a chance to actually swap
287 * in the old container (practically impossible with this little code,
288 * but...).
290 first = ATOMIC_LOAD(&Listener->FreeList);
291 do {
292 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
293 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*,
294 &Listener->FreeList, &first, props) == 0);
297 static void CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
299 struct ALeffectslotProps *first;
300 struct ALeffectslotProps *props;
301 ALeffectState *state;
303 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL, almemory_order_acq_rel);
304 if(!props) return;
306 slot->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
307 slot->Params.AuxSendAuto = ATOMIC_LOAD(&props->AuxSendAuto, almemory_order_relaxed);
308 slot->Params.EffectType = ATOMIC_LOAD(&props->Type, almemory_order_relaxed);
309 if(IsReverbEffect(slot->Params.EffectType))
311 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
312 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
313 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
315 else
317 slot->Params.RoomRolloff = 0.0f;
318 slot->Params.DecayTime = 0.0f;
319 slot->Params.AirAbsorptionGainHF = 1.0f;
321 state = ATOMIC_EXCHANGE(ALeffectState*, &props->State, NULL, almemory_order_relaxed);
323 /* If the state object is changed, exchange it with the current one so it
324 * remains in the freelist and isn't leaked.
326 if(state != slot->Params.EffectState)
328 ATOMIC_STORE(&props->State, slot->Params.EffectState, almemory_order_relaxed);
329 slot->Params.EffectState = state;
332 V(slot->Params.EffectState,update)(device, slot, &props->Props);
334 /* WARNING: A livelock is theoretically possible if another thread keeps
335 * changing the freelist head without giving this a chance to actually swap
336 * in the old container (practically impossible with this little code,
337 * but...).
339 first = ATOMIC_LOAD(&slot->FreeList);
340 do {
341 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
342 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
343 &slot->FreeList, &first, props) == 0);
347 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
349 static const struct ChanMap MonoMap[1] = {
350 { FrontCenter, 0.0f, 0.0f }
351 }, RearMap[2] = {
352 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
353 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
354 }, QuadMap[4] = {
355 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
356 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
357 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
358 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
359 }, X51Map[6] = {
360 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
361 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
362 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
363 { LFE, 0.0f, 0.0f },
364 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
365 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
366 }, X61Map[7] = {
367 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
368 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
369 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
370 { LFE, 0.0f, 0.0f },
371 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
372 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
373 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
374 }, X71Map[8] = {
375 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
376 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
377 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
378 { LFE, 0.0f, 0.0f },
379 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
380 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
381 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
382 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
385 const ALCdevice *Device = ALContext->Device;
386 const ALlistener *Listener = ALContext->Listener;
387 ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
388 ALfloat DryGain, DryGainHF, DryGainLF;
389 ALfloat WetGain[MAX_SENDS];
390 ALfloat WetGainHF[MAX_SENDS];
391 ALfloat WetGainLF[MAX_SENDS];
392 ALeffectslot *SendSlots[MAX_SENDS];
393 ALuint NumSends, Frequency;
394 ALboolean Relative;
395 const struct ChanMap *chans = NULL;
396 struct ChanMap StereoMap[2] = {
397 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
398 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
400 ALuint num_channels = 0;
401 ALboolean DirectChannels;
402 ALboolean isbformat = AL_FALSE;
403 ALfloat Pitch;
404 ALuint i, j, c;
406 /* Get device properties */
407 NumSends = Device->NumAuxSends;
408 Frequency = Device->Frequency;
410 /* Get listener properties */
411 ListenerGain = Listener->Params.Gain;
413 /* Get source properties */
414 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
415 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
416 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
417 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
418 Relative = ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed);
419 DirectChannels = ATOMIC_LOAD(&props->DirectChannels, almemory_order_relaxed);
421 /* Convert counter-clockwise to clockwise. */
422 StereoMap[0].angle = -ATOMIC_LOAD(&props->StereoPan[0], almemory_order_relaxed);
423 StereoMap[1].angle = -ATOMIC_LOAD(&props->StereoPan[1], almemory_order_relaxed);
425 voice->Direct.OutBuffer = Device->Dry.Buffer;
426 voice->Direct.OutChannels = Device->Dry.NumChannels;
427 for(i = 0;i < NumSends;i++)
429 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
430 if(!SendSlots[i] && i == 0)
431 SendSlots[i] = Device->DefaultSlot;
432 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
434 SendSlots[i] = NULL;
435 voice->Send[i].OutBuffer = NULL;
436 voice->Send[i].OutChannels = 0;
438 else
440 voice->Send[i].OutBuffer = SendSlots[i]->WetBuffer;
441 voice->Send[i].OutChannels = SendSlots[i]->NumChannels;
445 /* Calculate the stepping value */
446 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
447 if(Pitch > (ALfloat)MAX_PITCH)
448 voice->Step = MAX_PITCH<<FRACTIONBITS;
449 else
450 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
451 BsincPrepare(voice->Step, &voice->SincState);
453 /* Calculate gains */
454 DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
455 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
456 DryGainHF = ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
457 DryGainLF = ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
458 for(i = 0;i < NumSends;i++)
460 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
461 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
462 WetGainHF[i] = ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
463 WetGainLF[i] = ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
466 switch(ALBuffer->FmtChannels)
468 case FmtMono:
469 chans = MonoMap;
470 num_channels = 1;
471 break;
473 case FmtStereo:
474 chans = StereoMap;
475 num_channels = 2;
476 break;
478 case FmtRear:
479 chans = RearMap;
480 num_channels = 2;
481 break;
483 case FmtQuad:
484 chans = QuadMap;
485 num_channels = 4;
486 break;
488 case FmtX51:
489 chans = X51Map;
490 num_channels = 6;
491 break;
493 case FmtX61:
494 chans = X61Map;
495 num_channels = 7;
496 break;
498 case FmtX71:
499 chans = X71Map;
500 num_channels = 8;
501 break;
503 case FmtBFormat2D:
504 num_channels = 3;
505 isbformat = AL_TRUE;
506 DirectChannels = AL_FALSE;
507 break;
509 case FmtBFormat3D:
510 num_channels = 4;
511 isbformat = AL_TRUE;
512 DirectChannels = AL_FALSE;
513 break;
516 if(isbformat)
518 ALfloat N[3], V[3], U[3];
519 aluMatrixf matrix;
520 ALfloat scale;
522 /* AT then UP */
523 N[0] = ATOMIC_LOAD(&props->Orientation[0][0], almemory_order_relaxed);
524 N[1] = ATOMIC_LOAD(&props->Orientation[0][1], almemory_order_relaxed);
525 N[2] = ATOMIC_LOAD(&props->Orientation[0][2], almemory_order_relaxed);
526 aluNormalize(N);
527 V[0] = ATOMIC_LOAD(&props->Orientation[1][0], almemory_order_relaxed);
528 V[1] = ATOMIC_LOAD(&props->Orientation[1][1], almemory_order_relaxed);
529 V[2] = ATOMIC_LOAD(&props->Orientation[1][2], almemory_order_relaxed);
530 aluNormalize(V);
531 if(!Relative)
533 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
534 aluMatrixfFloat3(N, 0.0f, lmatrix);
535 aluMatrixfFloat3(V, 0.0f, lmatrix);
537 /* Build and normalize right-vector */
538 aluCrossproduct(N, V, U);
539 aluNormalize(U);
541 /* Build a rotate + conversion matrix (B-Format -> N3D). */
542 scale = 1.732050808f;
543 aluMatrixfSet(&matrix,
544 1.414213562f, 0.0f, 0.0f, 0.0f,
545 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
546 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
547 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
550 voice->Direct.OutBuffer = Device->FOAOut.Buffer;
551 voice->Direct.OutChannels = Device->FOAOut.NumChannels;
552 for(c = 0;c < num_channels;c++)
553 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
554 voice->Direct.Gains[c].Target);
556 for(i = 0;i < NumSends;i++)
558 if(!SendSlots[i])
560 for(c = 0;c < num_channels;c++)
562 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
563 voice->Send[i].Gains[c].Target[j] = 0.0f;
566 else
568 for(c = 0;c < num_channels;c++)
570 const ALeffectslot *Slot = SendSlots[i];
571 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, matrix.m[c],
572 WetGain[i], voice->Send[i].Gains[c].Target);
577 voice->IsHrtf = AL_FALSE;
579 else
581 ALfloat coeffs[MAX_AMBI_COEFFS];
583 if(DirectChannels)
585 /* Skip the virtual channels and write inputs to the real output. */
586 voice->Direct.OutBuffer = Device->RealOut.Buffer;
587 voice->Direct.OutChannels = Device->RealOut.NumChannels;
588 for(c = 0;c < num_channels;c++)
590 int idx;
591 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
592 voice->Direct.Gains[c].Target[j] = 0.0f;
593 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
594 voice->Direct.Gains[c].Target[idx] = DryGain;
597 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
598 * channel-match. */
599 for(c = 0;c < num_channels;c++)
601 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
603 for(i = 0;i < NumSends;i++)
605 if(!SendSlots[i])
607 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
608 voice->Send[i].Gains[c].Target[j] = 0.0f;
610 else
612 const ALeffectslot *Slot = SendSlots[i];
613 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
614 WetGain[i], voice->Send[i].Gains[c].Target);
619 voice->IsHrtf = AL_FALSE;
621 else if(Device->Render_Mode == HrtfRender)
623 /* Full HRTF rendering. Skip the virtual channels and render each
624 * input channel to the real outputs.
626 voice->Direct.OutBuffer = Device->RealOut.Buffer;
627 voice->Direct.OutChannels = Device->RealOut.NumChannels;
628 for(c = 0;c < num_channels;c++)
630 if(chans[c].channel == LFE)
632 /* Skip LFE */
633 voice->Direct.Hrtf[c].Target.Delay[0] = 0;
634 voice->Direct.Hrtf[c].Target.Delay[1] = 0;
635 for(i = 0;i < HRIR_LENGTH;i++)
637 voice->Direct.Hrtf[c].Target.Coeffs[i][0] = 0.0f;
638 voice->Direct.Hrtf[c].Target.Coeffs[i][1] = 0.0f;
641 for(i = 0;i < NumSends;i++)
643 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
644 voice->Send[i].Gains[c].Target[j] = 0.0f;
647 continue;
650 /* Get the static HRIR coefficients and delays for this channel. */
651 GetLerpedHrtfCoeffs(Device->Hrtf,
652 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
653 voice->Direct.Hrtf[c].Target.Coeffs,
654 voice->Direct.Hrtf[c].Target.Delay
657 /* Normal panning for auxiliary sends. */
658 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
660 for(i = 0;i < NumSends;i++)
662 if(!SendSlots[i])
664 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
665 voice->Send[i].Gains[c].Target[j] = 0.0f;
667 else
669 const ALeffectslot *Slot = SendSlots[i];
670 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
671 WetGain[i], voice->Send[i].Gains[c].Target);
676 voice->IsHrtf = AL_TRUE;
678 else
680 /* Non-HRTF rendering. Use normal panning to the output. */
681 for(c = 0;c < num_channels;c++)
683 /* Special-case LFE */
684 if(chans[c].channel == LFE)
686 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
687 voice->Direct.Gains[c].Target[j] = 0.0f;
688 if(Device->Dry.Buffer == Device->RealOut.Buffer)
690 int idx;
691 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
692 voice->Direct.Gains[c].Target[idx] = DryGain;
695 for(i = 0;i < NumSends;i++)
697 ALuint j;
698 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
699 voice->Send[i].Gains[c].Target[j] = 0.0f;
701 continue;
704 if(Device->Render_Mode == StereoPair)
706 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
707 ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
708 coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
709 voice->Direct.Gains[c].Target[0] = coeffs[0] * DryGain;
710 voice->Direct.Gains[c].Target[1] = (1.0f-coeffs[0]) * DryGain;
711 for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
712 voice->Direct.Gains[c].Target[j] = 0.0f;
714 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
716 else
718 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
719 ComputePanningGains(Device->Dry, coeffs, DryGain,
720 voice->Direct.Gains[c].Target);
723 for(i = 0;i < NumSends;i++)
725 if(!SendSlots[i])
727 ALuint j;
728 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
729 voice->Send[i].Gains[c].Target[j] = 0.0f;
731 else
733 const ALeffectslot *Slot = SendSlots[i];
734 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
735 WetGain[i], voice->Send[i].Gains[c].Target);
740 voice->IsHrtf = AL_FALSE;
745 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
746 Frequency;
747 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
748 Frequency;
749 DryGainHF = maxf(DryGainHF, 0.0001f);
750 DryGainLF = maxf(DryGainLF, 0.0001f);
751 for(c = 0;c < num_channels;c++)
753 voice->Direct.Filters[c].ActiveType = AF_None;
754 if(DryGainHF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_LowPass;
755 if(DryGainLF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_HighPass;
756 ALfilterState_setParams(
757 &voice->Direct.Filters[c].LowPass, ALfilterType_HighShelf,
758 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
760 ALfilterState_setParams(
761 &voice->Direct.Filters[c].HighPass, ALfilterType_LowShelf,
762 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
766 for(i = 0;i < NumSends;i++)
768 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
769 Frequency;
770 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
771 Frequency;
772 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
773 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
774 for(c = 0;c < num_channels;c++)
776 voice->Send[i].Filters[c].ActiveType = AF_None;
777 if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_LowPass;
778 if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_HighPass;
779 ALfilterState_setParams(
780 &voice->Send[i].Filters[c].LowPass, ALfilterType_HighShelf,
781 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
783 ALfilterState_setParams(
784 &voice->Send[i].Filters[c].HighPass, ALfilterType_LowShelf,
785 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
791 static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
793 const ALCdevice *Device = ALContext->Device;
794 const ALlistener *Listener = ALContext->Listener;
795 aluVector Position, Velocity, Direction, SourceToListener;
796 ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
797 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
798 ALfloat SourceVolume,ListenerGain;
799 ALfloat DopplerFactor, SpeedOfSound;
800 ALfloat AirAbsorptionFactor;
801 ALfloat RoomAirAbsorption[MAX_SENDS];
802 ALeffectslot *SendSlots[MAX_SENDS];
803 ALfloat Attenuation;
804 ALfloat RoomAttenuation[MAX_SENDS];
805 ALfloat MetersPerUnit;
806 ALfloat RoomRolloffBase;
807 ALfloat RoomRolloff[MAX_SENDS];
808 ALfloat DecayDistance[MAX_SENDS];
809 ALfloat DryGain;
810 ALfloat DryGainHF;
811 ALfloat DryGainLF;
812 ALboolean DryGainHFAuto;
813 ALfloat WetGain[MAX_SENDS];
814 ALfloat WetGainHF[MAX_SENDS];
815 ALfloat WetGainLF[MAX_SENDS];
816 ALboolean WetGainAuto;
817 ALboolean WetGainHFAuto;
818 ALfloat Pitch;
819 ALuint Frequency;
820 ALint NumSends;
821 ALint i;
823 DryGainHF = 1.0f;
824 DryGainLF = 1.0f;
825 for(i = 0;i < MAX_SENDS;i++)
827 WetGainHF[i] = 1.0f;
828 WetGainLF[i] = 1.0f;
831 /* Get context/device properties */
832 DopplerFactor = Listener->Params.DopplerFactor;
833 SpeedOfSound = Listener->Params.SpeedOfSound;
834 NumSends = Device->NumAuxSends;
835 Frequency = Device->Frequency;
837 /* Get listener properties */
838 ListenerGain = Listener->Params.Gain;
839 MetersPerUnit = Listener->Params.MetersPerUnit;
841 /* Get source properties */
842 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
843 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
844 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
845 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
846 aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed),
847 ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed),
848 ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed),
849 1.0f);
850 aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed),
851 ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed),
852 ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed),
853 0.0f);
854 aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
855 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
856 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
857 0.0f);
858 MinDist = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed);
859 MaxDist = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed);
860 Rolloff = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed);
861 DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
862 InnerAngle = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed);
863 OuterAngle = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed);
864 AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed);
865 DryGainHFAuto = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed);
866 WetGainAuto = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed);
867 WetGainHFAuto = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed);
868 RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed);
870 voice->Direct.OutBuffer = Device->Dry.Buffer;
871 voice->Direct.OutChannels = Device->Dry.NumChannels;
872 for(i = 0;i < NumSends;i++)
874 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
876 if(!SendSlots[i] && i == 0)
877 SendSlots[i] = Device->DefaultSlot;
878 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
880 SendSlots[i] = NULL;
881 RoomRolloff[i] = 0.0f;
882 DecayDistance[i] = 0.0f;
883 RoomAirAbsorption[i] = 1.0f;
885 else if(SendSlots[i]->Params.AuxSendAuto)
887 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
888 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
889 SPEEDOFSOUNDMETRESPERSEC;
890 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
892 else
894 /* If the slot's auxiliary send auto is off, the data sent to the
895 * effect slot is the same as the dry path, sans filter effects */
896 RoomRolloff[i] = Rolloff;
897 DecayDistance[i] = 0.0f;
898 RoomAirAbsorption[i] = AIRABSORBGAINHF;
901 if(!SendSlots[i])
903 voice->Send[i].OutBuffer = NULL;
904 voice->Send[i].OutChannels = 0;
906 else
908 voice->Send[i].OutBuffer = SendSlots[i]->WetBuffer;
909 voice->Send[i].OutChannels = SendSlots[i]->NumChannels;
913 /* Transform source to listener space (convert to head relative) */
914 if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE)
916 const aluMatrixf *Matrix = &Listener->Params.Matrix;
917 /* Transform source vectors */
918 Position = aluMatrixfVector(Matrix, &Position);
919 Velocity = aluMatrixfVector(Matrix, &Velocity);
920 Direction = aluMatrixfVector(Matrix, &Direction);
922 else
924 const aluVector *lvelocity = &Listener->Params.Velocity;
925 /* Offset the source velocity to be relative of the listener velocity */
926 Velocity.v[0] += lvelocity->v[0];
927 Velocity.v[1] += lvelocity->v[1];
928 Velocity.v[2] += lvelocity->v[2];
931 aluNormalize(Direction.v);
932 SourceToListener.v[0] = -Position.v[0];
933 SourceToListener.v[1] = -Position.v[1];
934 SourceToListener.v[2] = -Position.v[2];
935 SourceToListener.v[3] = 0.0f;
936 Distance = aluNormalize(SourceToListener.v);
938 /* Calculate distance attenuation */
939 ClampedDist = Distance;
941 Attenuation = 1.0f;
942 for(i = 0;i < NumSends;i++)
943 RoomAttenuation[i] = 1.0f;
944 switch(Listener->Params.SourceDistanceModel ?
945 ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed) :
946 Listener->Params.DistanceModel)
948 case InverseDistanceClamped:
949 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
950 if(MaxDist < MinDist)
951 break;
952 /*fall-through*/
953 case InverseDistance:
954 if(MinDist > 0.0f)
956 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
957 if(dist > 0.0f) Attenuation = MinDist / dist;
958 for(i = 0;i < NumSends;i++)
960 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
961 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
964 break;
966 case LinearDistanceClamped:
967 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
968 if(MaxDist < MinDist)
969 break;
970 /*fall-through*/
971 case LinearDistance:
972 if(MaxDist != MinDist)
974 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
975 Attenuation = maxf(Attenuation, 0.0f);
976 for(i = 0;i < NumSends;i++)
978 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
979 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
982 break;
984 case ExponentDistanceClamped:
985 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
986 if(MaxDist < MinDist)
987 break;
988 /*fall-through*/
989 case ExponentDistance:
990 if(ClampedDist > 0.0f && MinDist > 0.0f)
992 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
993 for(i = 0;i < NumSends;i++)
994 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
996 break;
998 case DisableDistance:
999 ClampedDist = MinDist;
1000 break;
1003 /* Source Gain + Attenuation */
1004 DryGain = SourceVolume * Attenuation;
1005 for(i = 0;i < NumSends;i++)
1006 WetGain[i] = SourceVolume * RoomAttenuation[i];
1008 /* Distance-based air absorption */
1009 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
1011 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
1012 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
1013 for(i = 0;i < NumSends;i++)
1014 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
1017 if(WetGainAuto)
1019 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1021 /* Apply a decay-time transformation to the wet path, based on the
1022 * attenuation of the dry path.
1024 * Using the apparent distance, based on the distance attenuation, the
1025 * initial decay of the reverb effect is calculated and applied to the
1026 * wet path.
1028 for(i = 0;i < NumSends;i++)
1030 if(DecayDistance[i] > 0.0f)
1031 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1035 /* Calculate directional soundcones */
1036 if(InnerAngle < 360.0f)
1038 ALfloat ConeVolume;
1039 ALfloat ConeHF;
1040 ALfloat Angle;
1041 ALfloat scale;
1043 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1044 if(Angle > InnerAngle)
1046 if(Angle < OuterAngle)
1048 scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1049 ConeVolume = lerp(
1050 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1052 ConeHF = lerp(
1053 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1056 else
1058 ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed);
1059 ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed);
1061 DryGain *= ConeVolume;
1062 if(DryGainHFAuto)
1063 DryGainHF *= ConeHF;
1066 /* Wet path uses the total area of the cone emitter (the room will
1067 * receive the same amount of sound regardless of its direction).
1069 scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
1070 (InnerAngle/360.0f);
1071 if(WetGainAuto)
1073 ConeVolume = lerp(
1074 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1076 for(i = 0;i < NumSends;i++)
1077 WetGain[i] *= ConeVolume;
1079 if(WetGainHFAuto)
1081 ConeHF = lerp(
1082 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1084 for(i = 0;i < NumSends;i++)
1085 WetGainHF[i] *= ConeHF;
1089 /* Clamp to Min/Max Gain */
1090 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1091 for(i = 0;i < NumSends;i++)
1092 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1094 /* Apply gain and frequency filters */
1095 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
1096 DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
1097 DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
1098 for(i = 0;i < NumSends;i++)
1100 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
1101 WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
1102 WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
1105 /* Calculate velocity-based doppler effect */
1106 if(DopplerFactor > 0.0f)
1108 const aluVector *lvelocity = &Listener->Params.Velocity;
1109 ALfloat VSS, VLS;
1111 if(SpeedOfSound < 1.0f)
1113 DopplerFactor *= 1.0f/SpeedOfSound;
1114 SpeedOfSound = 1.0f;
1117 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1118 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1120 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1121 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1124 /* Calculate fixed-point stepping value, based on the pitch, buffer
1125 * frequency, and output frequency.
1127 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1128 if(Pitch > (ALfloat)MAX_PITCH)
1129 voice->Step = MAX_PITCH<<FRACTIONBITS;
1130 else
1131 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1132 BsincPrepare(voice->Step, &voice->SincState);
1134 if(Device->Render_Mode == HrtfRender)
1136 /* Full HRTF rendering. Skip the virtual channels and render to the
1137 * real outputs.
1139 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1140 ALfloat ev = 0.0f, az = 0.0f;
1141 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1142 ALfloat coeffs[MAX_AMBI_COEFFS];
1143 ALfloat spread = 0.0f;
1145 voice->Direct.OutBuffer = Device->RealOut.Buffer;
1146 voice->Direct.OutChannels = Device->RealOut.NumChannels;
1148 if(Distance > FLT_EPSILON)
1150 dir[0] = -SourceToListener.v[0];
1151 dir[1] = -SourceToListener.v[1];
1152 dir[2] = -SourceToListener.v[2] * ZScale;
1154 /* Calculate elevation and azimuth only when the source is not at
1155 * the listener. This prevents +0 and -0 Z from producing
1156 * inconsistent panning. Also, clamp Y in case FP precision errors
1157 * cause it to land outside of -1..+1. */
1158 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1159 az = atan2f(dir[0], -dir[2]);
1161 if(radius > Distance)
1162 spread = F_TAU - Distance/radius*F_PI;
1163 else if(Distance > FLT_EPSILON)
1164 spread = asinf(radius / Distance) * 2.0f;
1166 /* Get the HRIR coefficients and delays. */
1167 GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, spread, DryGain,
1168 voice->Direct.Hrtf[0].Target.Coeffs,
1169 voice->Direct.Hrtf[0].Target.Delay);
1171 CalcDirectionCoeffs(dir, spread, coeffs);
1173 for(i = 0;i < NumSends;i++)
1175 if(!SendSlots[i])
1177 ALuint j;
1178 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1179 voice->Send[i].Gains[0].Target[j] = 0.0f;
1181 else
1183 const ALeffectslot *Slot = SendSlots[i];
1184 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1185 WetGain[i], voice->Send[i].Gains[0].Target);
1189 voice->IsHrtf = AL_TRUE;
1191 else
1193 /* Non-HRTF rendering. */
1194 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1195 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1196 ALfloat coeffs[MAX_AMBI_COEFFS];
1197 ALfloat spread = 0.0f;
1199 /* Get the localized direction, and compute panned gains. */
1200 if(Distance > FLT_EPSILON)
1202 dir[0] = -SourceToListener.v[0];
1203 dir[1] = -SourceToListener.v[1];
1204 dir[2] = -SourceToListener.v[2] * ZScale;
1206 if(radius > Distance)
1207 spread = F_TAU - Distance/radius*F_PI;
1208 else if(Distance > FLT_EPSILON)
1209 spread = asinf(radius / Distance) * 2.0f;
1211 if(Device->Render_Mode == StereoPair)
1213 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
1214 ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
1215 x = clampf(x, -0.5f, 0.5f) + 0.5f;
1216 voice->Direct.Gains[0].Target[0] = x * DryGain;
1217 voice->Direct.Gains[0].Target[1] = (1.0f-x) * DryGain;
1218 for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
1219 voice->Direct.Gains[0].Target[i] = 0.0f;
1221 CalcDirectionCoeffs(dir, spread, coeffs);
1223 else
1225 CalcDirectionCoeffs(dir, spread, coeffs);
1226 ComputePanningGains(Device->Dry, coeffs, DryGain, voice->Direct.Gains[0].Target);
1229 for(i = 0;i < NumSends;i++)
1231 if(!SendSlots[i])
1233 ALuint j;
1234 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1235 voice->Send[i].Gains[0].Target[j] = 0.0f;
1237 else
1239 const ALeffectslot *Slot = SendSlots[i];
1240 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1241 WetGain[i], voice->Send[i].Gains[0].Target);
1245 voice->IsHrtf = AL_FALSE;
1249 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
1250 Frequency;
1251 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
1252 Frequency;
1253 DryGainHF = maxf(DryGainHF, 0.0001f);
1254 DryGainLF = maxf(DryGainLF, 0.0001f);
1255 voice->Direct.Filters[0].ActiveType = AF_None;
1256 if(DryGainHF != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_LowPass;
1257 if(DryGainLF != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_HighPass;
1258 ALfilterState_setParams(
1259 &voice->Direct.Filters[0].LowPass, ALfilterType_HighShelf,
1260 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
1262 ALfilterState_setParams(
1263 &voice->Direct.Filters[0].HighPass, ALfilterType_LowShelf,
1264 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
1267 for(i = 0;i < NumSends;i++)
1269 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
1270 Frequency;
1271 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
1272 Frequency;
1273 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
1274 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
1275 voice->Send[i].Filters[0].ActiveType = AF_None;
1276 if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_LowPass;
1277 if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_HighPass;
1278 ALfilterState_setParams(
1279 &voice->Send[i].Filters[0].LowPass, ALfilterType_HighShelf,
1280 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
1282 ALfilterState_setParams(
1283 &voice->Send[i].Filters[0].HighPass, ALfilterType_LowShelf,
1284 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
1289 static void CalcSourceParams(ALvoice *voice, ALCcontext *context)
1291 ALsource *source = voice->Source;
1292 ALbufferlistitem *BufferListItem;
1293 struct ALsourceProps *first;
1294 struct ALsourceProps *props;
1296 props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
1297 if(!props) return;
1299 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1300 while(BufferListItem != NULL)
1302 ALbuffer *buffer;
1303 if((buffer=BufferListItem->buffer) != NULL)
1305 if(buffer->FmtChannels == FmtMono)
1306 CalcAttnSourceParams(voice, props, buffer, context);
1307 else
1308 CalcNonAttnSourceParams(voice, props, buffer, context);
1309 break;
1311 BufferListItem = BufferListItem->next;
1314 /* WARNING: A livelock is theoretically possible if another thread keeps
1315 * changing the freelist head without giving this a chance to actually swap
1316 * in the old container (practically impossible with this little code,
1317 * but...).
1319 first = ATOMIC_LOAD(&source->FreeList);
1320 do {
1321 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
1322 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*,
1323 &source->FreeList, &first, props) == 0);
1327 static void UpdateContextSources(ALCcontext *ctx)
1329 ALvoice *voice, *voice_end;
1330 ALsource *source;
1332 IncrementRef(&ctx->UpdateCount);
1333 if(!ATOMIC_LOAD(&ctx->HoldUpdates))
1335 CalcListenerParams(ctx);
1336 #define UPDATE_SLOT(iter) CalcEffectSlotParams(*iter, ctx->Device)
1337 VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, UPDATE_SLOT);
1338 #undef UPDATE_SLOT
1340 voice = ctx->Voices;
1341 voice_end = voice + ctx->VoiceCount;
1342 for(;voice != voice_end;++voice)
1344 if(!(source=voice->Source)) continue;
1345 if(source->state != AL_PLAYING && source->state != AL_PAUSED)
1346 voice->Source = NULL;
1347 else
1348 CalcSourceParams(voice, ctx);
1351 IncrementRef(&ctx->UpdateCount);
1355 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1356 * converts NaN to 0. */
1357 static inline ALfloat aluClampf(ALfloat val)
1359 if(fabsf(val) <= 1.0f) return val;
1360 return (ALfloat)((0.0f < val) - (val < 0.0f));
1363 static inline ALfloat aluF2F(ALfloat val)
1364 { return val; }
1366 static inline ALint aluF2I(ALfloat val)
1368 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1369 * integer range normalized floats can be safely converted to.
1371 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1373 static inline ALuint aluF2UI(ALfloat val)
1374 { return aluF2I(val)+2147483648u; }
1376 static inline ALshort aluF2S(ALfloat val)
1377 { return fastf2i(aluClampf(val)*32767.0f); }
1378 static inline ALushort aluF2US(ALfloat val)
1379 { return aluF2S(val)+32768; }
1381 static inline ALbyte aluF2B(ALfloat val)
1382 { return fastf2i(aluClampf(val)*127.0f); }
1383 static inline ALubyte aluF2UB(ALfloat val)
1384 { return aluF2B(val)+128; }
1386 #define DECL_TEMPLATE(T, func) \
1387 static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1388 ALuint SamplesToDo, ALuint numchans) \
1390 ALuint i, j; \
1391 for(j = 0;j < numchans;j++) \
1393 const ALfloat *in = InBuffer[j]; \
1394 T *restrict out = (T*)OutBuffer + j; \
1395 for(i = 0;i < SamplesToDo;i++) \
1396 out[i*numchans] = func(in[i]); \
1400 DECL_TEMPLATE(ALfloat, aluF2F)
1401 DECL_TEMPLATE(ALuint, aluF2UI)
1402 DECL_TEMPLATE(ALint, aluF2I)
1403 DECL_TEMPLATE(ALushort, aluF2US)
1404 DECL_TEMPLATE(ALshort, aluF2S)
1405 DECL_TEMPLATE(ALubyte, aluF2UB)
1406 DECL_TEMPLATE(ALbyte, aluF2B)
1408 #undef DECL_TEMPLATE
1411 ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1413 ALuint SamplesToDo;
1414 ALvoice *voice, *voice_end;
1415 ALeffectslot *slot;
1416 ALsource *source;
1417 ALCcontext *ctx;
1418 FPUCtl oldMode;
1419 ALuint i, c;
1421 SetMixerFPUMode(&oldMode);
1423 while(size > 0)
1425 IncrementRef(&device->MixCount);
1427 SamplesToDo = minu(size, BUFFERSIZE);
1428 for(c = 0;c < device->VirtOut.NumChannels;c++)
1429 memset(device->VirtOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1430 for(c = 0;c < device->RealOut.NumChannels;c++)
1431 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1432 if(device->Dry.Buffer != device->FOAOut.Buffer)
1433 for(c = 0;c < device->FOAOut.NumChannels;c++)
1434 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1436 V0(device->Backend,lock)();
1438 if((slot=device->DefaultSlot) != NULL)
1440 CalcEffectSlotParams(device->DefaultSlot, device);
1441 for(i = 0;i < slot->NumChannels;i++)
1442 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1445 ctx = ATOMIC_LOAD(&device->ContextList);
1446 while(ctx)
1448 UpdateContextSources(ctx);
1449 #define CLEAR_WET_BUFFER(iter) do { \
1450 for(i = 0;i < (*iter)->NumChannels;i++) \
1451 memset((*iter)->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat)); \
1452 } while(0)
1453 VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, CLEAR_WET_BUFFER);
1454 #undef CLEAR_WET_BUFFER
1456 /* source processing */
1457 voice = ctx->Voices;
1458 voice_end = voice + ctx->VoiceCount;
1459 for(;voice != voice_end;++voice)
1461 source = voice->Source;
1462 if(source && source->state == AL_PLAYING)
1463 MixSource(voice, source, device, SamplesToDo);
1466 /* effect slot processing */
1467 c = VECTOR_SIZE(ctx->ActiveAuxSlots);
1468 for(i = 0;i < c;i++)
1470 const ALeffectslot *slot = VECTOR_ELEM(ctx->ActiveAuxSlots, i);
1471 ALeffectState *state = slot->Params.EffectState;
1472 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1473 state->OutChannels);
1476 ctx = ctx->next;
1479 if(device->DefaultSlot != NULL)
1481 const ALeffectslot *slot = device->DefaultSlot;
1482 ALeffectState *state = slot->Params.EffectState;
1483 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1484 state->OutChannels);
1487 /* Increment the clock time. Every second's worth of samples is
1488 * converted and added to clock base so that large sample counts don't
1489 * overflow during conversion. This also guarantees an exact, stable
1490 * conversion. */
1491 device->SamplesDone += SamplesToDo;
1492 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1493 device->SamplesDone %= device->Frequency;
1494 V0(device->Backend,unlock)();
1496 if(device->Hrtf)
1498 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1499 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1500 if(lidx != -1 && ridx != -1)
1502 HrtfMixerFunc HrtfMix = SelectHrtfMixer();
1503 ALuint irsize = GetHrtfIrSize(device->Hrtf);
1504 MixHrtfParams hrtfparams;
1505 memset(&hrtfparams, 0, sizeof(hrtfparams));
1506 for(c = 0;c < device->VirtOut.NumChannels;c++)
1508 hrtfparams.Current = &device->Hrtf_Params[c];
1509 hrtfparams.Target = &device->Hrtf_Params[c];
1510 HrtfMix(device->RealOut.Buffer, lidx, ridx,
1511 device->VirtOut.Buffer[c], 0, device->Hrtf_Offset, 0,
1512 irsize, &hrtfparams, &device->Hrtf_State[c], SamplesToDo
1515 device->Hrtf_Offset += SamplesToDo;
1518 else if(device->AmbiDecoder)
1520 if(device->VirtOut.Buffer != device->FOAOut.Buffer)
1521 bformatdec_upSample(device->AmbiDecoder,
1522 device->VirtOut.Buffer, device->FOAOut.Buffer,
1523 device->FOAOut.NumChannels, SamplesToDo
1525 bformatdec_process(device->AmbiDecoder,
1526 device->RealOut.Buffer, device->RealOut.NumChannels,
1527 device->VirtOut.Buffer, SamplesToDo
1530 else
1532 if(device->Uhj_Encoder)
1534 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1535 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1536 if(lidx != -1 && ridx != -1)
1538 /* Encode to stereo-compatible 2-channel UHJ output. */
1539 EncodeUhj2(device->Uhj_Encoder,
1540 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1541 device->VirtOut.Buffer, SamplesToDo
1545 if(device->Bs2b)
1547 /* Apply binaural/crossfeed filter */
1548 for(i = 0;i < SamplesToDo;i++)
1550 float samples[2];
1551 samples[0] = device->RealOut.Buffer[0][i];
1552 samples[1] = device->RealOut.Buffer[1][i];
1553 bs2b_cross_feed(device->Bs2b, samples);
1554 device->RealOut.Buffer[0][i] = samples[0];
1555 device->RealOut.Buffer[1][i] = samples[1];
1560 if(buffer)
1562 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1563 ALuint OutChannels = device->RealOut.NumChannels;
1565 #define WRITE(T, a, b, c, d) do { \
1566 Write_##T((a), (b), (c), (d)); \
1567 buffer = (T*)buffer + (c)*(d); \
1568 } while(0)
1569 switch(device->FmtType)
1571 case DevFmtByte:
1572 WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1573 break;
1574 case DevFmtUByte:
1575 WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1576 break;
1577 case DevFmtShort:
1578 WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
1579 break;
1580 case DevFmtUShort:
1581 WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
1582 break;
1583 case DevFmtInt:
1584 WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
1585 break;
1586 case DevFmtUInt:
1587 WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
1588 break;
1589 case DevFmtFloat:
1590 WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
1591 break;
1593 #undef WRITE
1596 size -= SamplesToDo;
1597 IncrementRef(&device->MixCount);
1600 RestoreFPUMode(&oldMode);
1604 ALvoid aluHandleDisconnect(ALCdevice *device)
1606 ALCcontext *Context;
1608 device->Connected = ALC_FALSE;
1610 Context = ATOMIC_LOAD(&device->ContextList);
1611 while(Context)
1613 ALvoice *voice, *voice_end;
1615 voice = Context->Voices;
1616 voice_end = voice + Context->VoiceCount;
1617 while(voice != voice_end)
1619 ALsource *source = voice->Source;
1620 voice->Source = NULL;
1622 if(source && source->state == AL_PLAYING)
1624 source->state = AL_STOPPED;
1625 ATOMIC_STORE(&source->current_buffer, NULL);
1626 source->position = 0;
1627 source->position_fraction = 0;
1630 voice++;
1632 Context->VoiceCount = 0;
1634 Context = Context->next;