Print whether direct channels are off or on to stdout in alffplay
[openal-soft.git] / Alc / ALu.c
blobfc49c60ffa8339a6b03497480c93b3bdda61ed14
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 HrtfDirectMixerFunc SelectHrtfMixer(void)
99 #ifdef HAVE_SSE
100 if((CPUCapFlags&CPU_CAP_SSE))
101 return MixDirectHrtf_SSE;
102 #endif
103 #ifdef HAVE_NEON
104 if((CPUCapFlags&CPU_CAP_NEON))
105 return MixDirectHrtf_Neon;
106 #endif
108 return MixDirectHrtf_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 ALboolean 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 AL_FALSE;
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, almemory_order_relaxed);
282 Listener->Params.DistanceModel = ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed);
284 /* WARNING: A livelock is theoretically possible if another thread keeps
285 * changing the freelist head without giving this a chance to actually swap
286 * in the old container (practically impossible with this little code,
287 * but...).
289 first = ATOMIC_LOAD(&Listener->FreeList);
290 do {
291 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
292 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*,
293 &Listener->FreeList, &first, props) == 0);
295 return AL_TRUE;
298 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
300 struct ALeffectslotProps *first;
301 struct ALeffectslotProps *props;
302 ALeffectState *state;
304 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL, almemory_order_acq_rel);
305 if(!props) return AL_FALSE;
307 slot->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
308 slot->Params.AuxSendAuto = ATOMIC_LOAD(&props->AuxSendAuto, almemory_order_relaxed);
309 slot->Params.EffectType = ATOMIC_LOAD(&props->Type, almemory_order_relaxed);
310 if(IsReverbEffect(slot->Params.EffectType))
312 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
313 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
314 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
316 else
318 slot->Params.RoomRolloff = 0.0f;
319 slot->Params.DecayTime = 0.0f;
320 slot->Params.AirAbsorptionGainHF = 1.0f;
323 /* Swap effect states. No need to play with the ref counts since they keep
324 * the same number of refs.
326 state = ATOMIC_EXCHANGE(ALeffectState*, &props->State, slot->Params.EffectState,
327 almemory_order_relaxed);
328 slot->Params.EffectState = state;
330 V(state,update)(device, slot, &props->Props);
332 /* WARNING: A livelock is theoretically possible if another thread keeps
333 * changing the freelist head without giving this a chance to actually swap
334 * in the old container (practically impossible with this little code,
335 * but...).
337 first = ATOMIC_LOAD(&slot->FreeList);
338 do {
339 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
340 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
341 &slot->FreeList, &first, props) == 0);
343 return AL_TRUE;
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->DirectOut.Buffer = Device->Dry.Buffer;
426 voice->DirectOut.Channels = 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->SendOut[i].Buffer = NULL;
436 voice->SendOut[i].Channels = 0;
438 else
440 voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer;
441 voice->SendOut[i].Channels = 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 DryGain = minf(DryGain, GAIN_MIX_MAX);
457 DryGainHF = ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
458 DryGainLF = ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
459 for(i = 0;i < NumSends;i++)
461 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
462 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
463 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
464 WetGainHF[i] = ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
465 WetGainLF[i] = ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
468 switch(ALBuffer->FmtChannels)
470 case FmtMono:
471 chans = MonoMap;
472 num_channels = 1;
473 break;
475 case FmtStereo:
476 chans = StereoMap;
477 num_channels = 2;
478 break;
480 case FmtRear:
481 chans = RearMap;
482 num_channels = 2;
483 break;
485 case FmtQuad:
486 chans = QuadMap;
487 num_channels = 4;
488 break;
490 case FmtX51:
491 chans = X51Map;
492 num_channels = 6;
493 break;
495 case FmtX61:
496 chans = X61Map;
497 num_channels = 7;
498 break;
500 case FmtX71:
501 chans = X71Map;
502 num_channels = 8;
503 break;
505 case FmtBFormat2D:
506 num_channels = 3;
507 isbformat = AL_TRUE;
508 DirectChannels = AL_FALSE;
509 break;
511 case FmtBFormat3D:
512 num_channels = 4;
513 isbformat = AL_TRUE;
514 DirectChannels = AL_FALSE;
515 break;
518 if(isbformat)
520 ALfloat N[3], V[3], U[3];
521 aluMatrixf matrix;
522 ALfloat scale;
524 /* AT then UP */
525 N[0] = ATOMIC_LOAD(&props->Orientation[0][0], almemory_order_relaxed);
526 N[1] = ATOMIC_LOAD(&props->Orientation[0][1], almemory_order_relaxed);
527 N[2] = ATOMIC_LOAD(&props->Orientation[0][2], almemory_order_relaxed);
528 aluNormalize(N);
529 V[0] = ATOMIC_LOAD(&props->Orientation[1][0], almemory_order_relaxed);
530 V[1] = ATOMIC_LOAD(&props->Orientation[1][1], almemory_order_relaxed);
531 V[2] = ATOMIC_LOAD(&props->Orientation[1][2], almemory_order_relaxed);
532 aluNormalize(V);
533 if(!Relative)
535 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
536 aluMatrixfFloat3(N, 0.0f, lmatrix);
537 aluMatrixfFloat3(V, 0.0f, lmatrix);
539 /* Build and normalize right-vector */
540 aluCrossproduct(N, V, U);
541 aluNormalize(U);
543 /* Build a rotate + conversion matrix (B-Format -> N3D). */
544 scale = 1.732050808f;
545 aluMatrixfSet(&matrix,
546 1.414213562f, 0.0f, 0.0f, 0.0f,
547 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
548 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
549 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
552 voice->DirectOut.Buffer = Device->FOAOut.Buffer;
553 voice->DirectOut.Channels = Device->FOAOut.NumChannels;
554 for(c = 0;c < num_channels;c++)
555 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
556 voice->Chan[c].Direct.Gains.Target);
558 for(i = 0;i < NumSends;i++)
560 if(!SendSlots[i])
562 for(c = 0;c < num_channels;c++)
564 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
565 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
568 else
570 for(c = 0;c < num_channels;c++)
572 const ALeffectslot *Slot = SendSlots[i];
573 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, matrix.m[c],
574 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
579 voice->IsHrtf = AL_FALSE;
581 else
583 ALfloat coeffs[MAX_AMBI_COEFFS];
585 if(DirectChannels)
587 /* Skip the virtual channels and write inputs to the real output. */
588 voice->DirectOut.Buffer = Device->RealOut.Buffer;
589 voice->DirectOut.Channels = Device->RealOut.NumChannels;
590 for(c = 0;c < num_channels;c++)
592 int idx;
593 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
594 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
595 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
596 voice->Chan[c].Direct.Gains.Target[idx] = DryGain;
599 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
600 * channel-match. */
601 for(c = 0;c < num_channels;c++)
603 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
605 for(i = 0;i < NumSends;i++)
607 if(!SendSlots[i])
609 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
610 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
612 else
614 const ALeffectslot *Slot = SendSlots[i];
615 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
616 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
621 voice->IsHrtf = AL_FALSE;
623 else if(Device->Render_Mode == HrtfRender)
625 /* Full HRTF rendering. Skip the virtual channels and render each
626 * input channel to the real outputs.
628 voice->DirectOut.Buffer = Device->RealOut.Buffer;
629 voice->DirectOut.Channels = Device->RealOut.NumChannels;
630 for(c = 0;c < num_channels;c++)
632 if(chans[c].channel == LFE)
634 /* Skip LFE */
635 voice->Chan[c].Direct.Hrtf.Target.Delay[0] = 0;
636 voice->Chan[c].Direct.Hrtf.Target.Delay[1] = 0;
637 for(i = 0;i < HRIR_LENGTH;i++)
639 voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][0] = 0.0f;
640 voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][1] = 0.0f;
643 for(i = 0;i < NumSends;i++)
645 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
646 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
649 continue;
652 /* Get the static HRIR coefficients and delays for this channel. */
653 GetLerpedHrtfCoeffs(Device->Hrtf.Handle,
654 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
655 voice->Chan[c].Direct.Hrtf.Target.Coeffs,
656 voice->Chan[c].Direct.Hrtf.Target.Delay
659 /* Normal panning for auxiliary sends. */
660 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
662 for(i = 0;i < NumSends;i++)
664 if(!SendSlots[i])
666 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
667 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
669 else
671 const ALeffectslot *Slot = SendSlots[i];
672 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
673 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
678 voice->IsHrtf = AL_TRUE;
680 else
682 /* Non-HRTF rendering. Use normal panning to the output. */
683 for(c = 0;c < num_channels;c++)
685 /* Special-case LFE */
686 if(chans[c].channel == LFE)
688 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
689 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
690 if(Device->Dry.Buffer == Device->RealOut.Buffer)
692 int idx;
693 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
694 voice->Chan[c].Direct.Gains.Target[idx] = DryGain;
697 for(i = 0;i < NumSends;i++)
699 ALuint j;
700 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
701 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
703 continue;
706 if(Device->Render_Mode == StereoPair)
708 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
709 ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
710 coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
711 voice->Chan[c].Direct.Gains.Target[0] = coeffs[0] * DryGain;
712 voice->Chan[c].Direct.Gains.Target[1] = (1.0f-coeffs[0]) * DryGain;
713 for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
714 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
716 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
718 else
720 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
721 ComputePanningGains(Device->Dry, coeffs, DryGain,
722 voice->Chan[c].Direct.Gains.Target);
725 for(i = 0;i < NumSends;i++)
727 if(!SendSlots[i])
729 ALuint j;
730 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
731 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
733 else
735 const ALeffectslot *Slot = SendSlots[i];
736 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
737 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
742 voice->IsHrtf = AL_FALSE;
747 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
748 Frequency;
749 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
750 Frequency;
751 DryGainHF = maxf(DryGainHF, 0.0001f);
752 DryGainLF = maxf(DryGainLF, 0.0001f);
753 for(c = 0;c < num_channels;c++)
755 voice->Chan[c].Direct.FilterType = AF_None;
756 if(DryGainHF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_LowPass;
757 if(DryGainLF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_HighPass;
758 ALfilterState_setParams(
759 &voice->Chan[c].Direct.LowPass, ALfilterType_HighShelf,
760 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
762 ALfilterState_setParams(
763 &voice->Chan[c].Direct.HighPass, ALfilterType_LowShelf,
764 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
768 for(i = 0;i < NumSends;i++)
770 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
771 Frequency;
772 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
773 Frequency;
774 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
775 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
776 for(c = 0;c < num_channels;c++)
778 voice->Chan[c].Send[i].FilterType = AF_None;
779 if(WetGainHF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_LowPass;
780 if(WetGainLF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_HighPass;
781 ALfilterState_setParams(
782 &voice->Chan[c].Send[i].LowPass, ALfilterType_HighShelf,
783 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
785 ALfilterState_setParams(
786 &voice->Chan[c].Send[i].HighPass, ALfilterType_LowShelf,
787 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
793 static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
795 const ALCdevice *Device = ALContext->Device;
796 const ALlistener *Listener = ALContext->Listener;
797 aluVector Position, Velocity, Direction, SourceToListener;
798 ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
799 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
800 ALfloat SourceVolume,ListenerGain;
801 ALfloat DopplerFactor, SpeedOfSound;
802 ALfloat AirAbsorptionFactor;
803 ALfloat RoomAirAbsorption[MAX_SENDS];
804 ALeffectslot *SendSlots[MAX_SENDS];
805 ALfloat Attenuation;
806 ALfloat RoomAttenuation[MAX_SENDS];
807 ALfloat MetersPerUnit;
808 ALfloat RoomRolloffBase;
809 ALfloat RoomRolloff[MAX_SENDS];
810 ALfloat DecayDistance[MAX_SENDS];
811 ALfloat DryGain;
812 ALfloat DryGainHF;
813 ALfloat DryGainLF;
814 ALboolean DryGainHFAuto;
815 ALfloat WetGain[MAX_SENDS];
816 ALfloat WetGainHF[MAX_SENDS];
817 ALfloat WetGainLF[MAX_SENDS];
818 ALboolean WetGainAuto;
819 ALboolean WetGainHFAuto;
820 ALfloat Pitch;
821 ALuint Frequency;
822 ALint NumSends;
823 ALint i;
825 DryGainHF = 1.0f;
826 DryGainLF = 1.0f;
827 for(i = 0;i < MAX_SENDS;i++)
829 WetGainHF[i] = 1.0f;
830 WetGainLF[i] = 1.0f;
833 /* Get context/device properties */
834 DopplerFactor = Listener->Params.DopplerFactor;
835 SpeedOfSound = Listener->Params.SpeedOfSound;
836 NumSends = Device->NumAuxSends;
837 Frequency = Device->Frequency;
839 /* Get listener properties */
840 ListenerGain = Listener->Params.Gain;
841 MetersPerUnit = Listener->Params.MetersPerUnit;
843 /* Get source properties */
844 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
845 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
846 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
847 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
848 aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed),
849 ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed),
850 ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed),
851 1.0f);
852 aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed),
853 ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed),
854 ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed),
855 0.0f);
856 aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
857 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
858 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
859 0.0f);
860 MinDist = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed);
861 MaxDist = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed);
862 Rolloff = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed);
863 DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
864 InnerAngle = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed);
865 OuterAngle = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed);
866 AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed);
867 DryGainHFAuto = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed);
868 WetGainAuto = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed);
869 WetGainHFAuto = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed);
870 RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed);
872 voice->DirectOut.Buffer = Device->Dry.Buffer;
873 voice->DirectOut.Channels = Device->Dry.NumChannels;
874 for(i = 0;i < NumSends;i++)
876 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
878 if(!SendSlots[i] && i == 0)
879 SendSlots[i] = Device->DefaultSlot;
880 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
882 SendSlots[i] = NULL;
883 RoomRolloff[i] = 0.0f;
884 DecayDistance[i] = 0.0f;
885 RoomAirAbsorption[i] = 1.0f;
887 else if(SendSlots[i]->Params.AuxSendAuto)
889 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
890 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
891 SPEEDOFSOUNDMETRESPERSEC;
892 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
894 else
896 /* If the slot's auxiliary send auto is off, the data sent to the
897 * effect slot is the same as the dry path, sans filter effects */
898 RoomRolloff[i] = Rolloff;
899 DecayDistance[i] = 0.0f;
900 RoomAirAbsorption[i] = AIRABSORBGAINHF;
903 if(!SendSlots[i])
905 voice->SendOut[i].Buffer = NULL;
906 voice->SendOut[i].Channels = 0;
908 else
910 voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer;
911 voice->SendOut[i].Channels = SendSlots[i]->NumChannels;
915 /* Transform source to listener space (convert to head relative) */
916 if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE)
918 const aluMatrixf *Matrix = &Listener->Params.Matrix;
919 /* Transform source vectors */
920 Position = aluMatrixfVector(Matrix, &Position);
921 Velocity = aluMatrixfVector(Matrix, &Velocity);
922 Direction = aluMatrixfVector(Matrix, &Direction);
924 else
926 const aluVector *lvelocity = &Listener->Params.Velocity;
927 /* Offset the source velocity to be relative of the listener velocity */
928 Velocity.v[0] += lvelocity->v[0];
929 Velocity.v[1] += lvelocity->v[1];
930 Velocity.v[2] += lvelocity->v[2];
933 aluNormalize(Direction.v);
934 SourceToListener.v[0] = -Position.v[0];
935 SourceToListener.v[1] = -Position.v[1];
936 SourceToListener.v[2] = -Position.v[2];
937 SourceToListener.v[3] = 0.0f;
938 Distance = aluNormalize(SourceToListener.v);
940 /* Calculate distance attenuation */
941 ClampedDist = Distance;
943 Attenuation = 1.0f;
944 for(i = 0;i < NumSends;i++)
945 RoomAttenuation[i] = 1.0f;
946 switch(Listener->Params.SourceDistanceModel ?
947 ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed) :
948 Listener->Params.DistanceModel)
950 case InverseDistanceClamped:
951 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
952 if(MaxDist < MinDist)
953 break;
954 /*fall-through*/
955 case InverseDistance:
956 if(MinDist > 0.0f)
958 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
959 if(dist > 0.0f) Attenuation = MinDist / dist;
960 for(i = 0;i < NumSends;i++)
962 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
963 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
966 break;
968 case LinearDistanceClamped:
969 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
970 if(MaxDist < MinDist)
971 break;
972 /*fall-through*/
973 case LinearDistance:
974 if(MaxDist != MinDist)
976 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
977 Attenuation = maxf(Attenuation, 0.0f);
978 for(i = 0;i < NumSends;i++)
980 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
981 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
984 break;
986 case ExponentDistanceClamped:
987 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
988 if(MaxDist < MinDist)
989 break;
990 /*fall-through*/
991 case ExponentDistance:
992 if(ClampedDist > 0.0f && MinDist > 0.0f)
994 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
995 for(i = 0;i < NumSends;i++)
996 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
998 break;
1000 case DisableDistance:
1001 ClampedDist = MinDist;
1002 break;
1005 /* Source Gain + Attenuation */
1006 DryGain = SourceVolume * Attenuation;
1007 for(i = 0;i < NumSends;i++)
1008 WetGain[i] = SourceVolume * RoomAttenuation[i];
1010 /* Distance-based air absorption */
1011 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
1013 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
1014 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
1015 for(i = 0;i < NumSends;i++)
1016 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
1019 if(WetGainAuto)
1021 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1023 /* Apply a decay-time transformation to the wet path, based on the
1024 * attenuation of the dry path.
1026 * Using the apparent distance, based on the distance attenuation, the
1027 * initial decay of the reverb effect is calculated and applied to the
1028 * wet path.
1030 for(i = 0;i < NumSends;i++)
1032 if(DecayDistance[i] > 0.0f)
1033 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1037 /* Calculate directional soundcones */
1038 if(InnerAngle < 360.0f)
1040 ALfloat ConeVolume;
1041 ALfloat ConeHF;
1042 ALfloat Angle;
1043 ALfloat scale;
1045 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1046 if(Angle > InnerAngle)
1048 if(Angle < OuterAngle)
1050 scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1051 ConeVolume = lerp(
1052 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1054 ConeHF = lerp(
1055 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1058 else
1060 ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed);
1061 ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed);
1063 DryGain *= ConeVolume;
1064 if(DryGainHFAuto)
1065 DryGainHF *= ConeHF;
1068 /* Wet path uses the total area of the cone emitter (the room will
1069 * receive the same amount of sound regardless of its direction).
1071 scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
1072 (InnerAngle/360.0f);
1073 if(WetGainAuto)
1075 ConeVolume = lerp(
1076 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1078 for(i = 0;i < NumSends;i++)
1079 WetGain[i] *= ConeVolume;
1081 if(WetGainHFAuto)
1083 ConeHF = lerp(
1084 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1086 for(i = 0;i < NumSends;i++)
1087 WetGainHF[i] *= ConeHF;
1091 /* Apply gain and frequency filters */
1092 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1093 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
1094 DryGain = minf(DryGain, GAIN_MIX_MAX);
1095 DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
1096 DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
1097 for(i = 0;i < NumSends;i++)
1099 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1100 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
1101 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1102 WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
1103 WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
1106 /* Calculate velocity-based doppler effect */
1107 if(DopplerFactor > 0.0f)
1109 const aluVector *lvelocity = &Listener->Params.Velocity;
1110 ALfloat VSS, VLS;
1112 if(SpeedOfSound < 1.0f)
1114 DopplerFactor *= 1.0f/SpeedOfSound;
1115 SpeedOfSound = 1.0f;
1118 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1119 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1121 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1122 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1125 /* Calculate fixed-point stepping value, based on the pitch, buffer
1126 * frequency, and output frequency.
1128 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1129 if(Pitch > (ALfloat)MAX_PITCH)
1130 voice->Step = MAX_PITCH<<FRACTIONBITS;
1131 else
1132 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1133 BsincPrepare(voice->Step, &voice->SincState);
1135 if(Device->Render_Mode == HrtfRender)
1137 /* Full HRTF rendering. Skip the virtual channels and render to the
1138 * real outputs.
1140 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1141 ALfloat ev = 0.0f, az = 0.0f;
1142 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1143 ALfloat coeffs[MAX_AMBI_COEFFS];
1144 ALfloat spread = 0.0f;
1146 voice->DirectOut.Buffer = Device->RealOut.Buffer;
1147 voice->DirectOut.Channels = Device->RealOut.NumChannels;
1149 if(Distance > FLT_EPSILON)
1151 dir[0] = -SourceToListener.v[0];
1152 dir[1] = -SourceToListener.v[1];
1153 dir[2] = -SourceToListener.v[2] * ZScale;
1155 /* Calculate elevation and azimuth only when the source is not at
1156 * the listener. This prevents +0 and -0 Z from producing
1157 * inconsistent panning. Also, clamp Y in case FP precision errors
1158 * cause it to land outside of -1..+1. */
1159 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1160 az = atan2f(dir[0], -dir[2]);
1162 if(radius > Distance)
1163 spread = F_TAU - Distance/radius*F_PI;
1164 else if(Distance > FLT_EPSILON)
1165 spread = asinf(radius / Distance) * 2.0f;
1167 /* Get the HRIR coefficients and delays. */
1168 GetLerpedHrtfCoeffs(Device->Hrtf.Handle, ev, az, spread, DryGain,
1169 voice->Chan[0].Direct.Hrtf.Target.Coeffs,
1170 voice->Chan[0].Direct.Hrtf.Target.Delay);
1172 CalcDirectionCoeffs(dir, spread, coeffs);
1174 for(i = 0;i < NumSends;i++)
1176 if(!SendSlots[i])
1178 ALuint j;
1179 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1180 voice->Chan[0].Send[i].Gains.Target[j] = 0.0f;
1182 else
1184 const ALeffectslot *Slot = SendSlots[i];
1185 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1186 WetGain[i], voice->Chan[0].Send[i].Gains.Target);
1190 voice->IsHrtf = AL_TRUE;
1192 else
1194 /* Non-HRTF rendering. */
1195 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1196 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1197 ALfloat coeffs[MAX_AMBI_COEFFS];
1198 ALfloat spread = 0.0f;
1200 /* Get the localized direction, and compute panned gains. */
1201 if(Distance > FLT_EPSILON)
1203 dir[0] = -SourceToListener.v[0];
1204 dir[1] = -SourceToListener.v[1];
1205 dir[2] = -SourceToListener.v[2] * ZScale;
1207 if(radius > Distance)
1208 spread = F_TAU - Distance/radius*F_PI;
1209 else if(Distance > FLT_EPSILON)
1210 spread = asinf(radius / Distance) * 2.0f;
1212 if(Device->Render_Mode == StereoPair)
1214 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
1215 ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
1216 x = clampf(x, -0.5f, 0.5f) + 0.5f;
1217 voice->Chan[0].Direct.Gains.Target[0] = x * DryGain;
1218 voice->Chan[0].Direct.Gains.Target[1] = (1.0f-x) * DryGain;
1219 for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
1220 voice->Chan[0].Direct.Gains.Target[i] = 0.0f;
1222 CalcDirectionCoeffs(dir, spread, coeffs);
1224 else
1226 CalcDirectionCoeffs(dir, spread, coeffs);
1227 ComputePanningGains(Device->Dry, coeffs, DryGain,
1228 voice->Chan[0].Direct.Gains.Target);
1231 for(i = 0;i < NumSends;i++)
1233 if(!SendSlots[i])
1235 ALuint j;
1236 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1237 voice->Chan[0].Send[i].Gains.Target[j] = 0.0f;
1239 else
1241 const ALeffectslot *Slot = SendSlots[i];
1242 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1243 WetGain[i], voice->Chan[0].Send[i].Gains.Target);
1247 voice->IsHrtf = AL_FALSE;
1251 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
1252 Frequency;
1253 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
1254 Frequency;
1255 DryGainHF = maxf(DryGainHF, 0.0001f);
1256 DryGainLF = maxf(DryGainLF, 0.0001f);
1257 voice->Chan[0].Direct.FilterType = AF_None;
1258 if(DryGainHF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_LowPass;
1259 if(DryGainLF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_HighPass;
1260 ALfilterState_setParams(
1261 &voice->Chan[0].Direct.LowPass, ALfilterType_HighShelf,
1262 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
1264 ALfilterState_setParams(
1265 &voice->Chan[0].Direct.HighPass, ALfilterType_LowShelf,
1266 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
1269 for(i = 0;i < NumSends;i++)
1271 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
1272 Frequency;
1273 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
1274 Frequency;
1275 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
1276 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
1277 voice->Chan[0].Send[i].FilterType = AF_None;
1278 if(WetGainHF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_LowPass;
1279 if(WetGainLF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_HighPass;
1280 ALfilterState_setParams(
1281 &voice->Chan[0].Send[i].LowPass, ALfilterType_HighShelf,
1282 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
1284 ALfilterState_setParams(
1285 &voice->Chan[0].Send[i].HighPass, ALfilterType_LowShelf,
1286 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
1291 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1293 ALsource *source = voice->Source;
1294 const ALbufferlistitem *BufferListItem;
1295 struct ALsourceProps *first;
1296 struct ALsourceProps *props;
1298 props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
1299 if(!props && !force) return;
1301 if(props)
1303 voice->Props = *props;
1305 /* WARNING: A livelock is theoretically possible if another thread
1306 * keeps changing the freelist head without giving this a chance to
1307 * actually swap in the old container (practically impossible with this
1308 * little code, but...).
1310 first = ATOMIC_LOAD(&source->FreeList);
1311 do {
1312 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
1313 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*,
1314 &source->FreeList, &first, props) == 0);
1317 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1318 while(BufferListItem != NULL)
1320 const ALbuffer *buffer;
1321 if((buffer=BufferListItem->buffer) != NULL)
1323 if(buffer->FmtChannels == FmtMono)
1324 CalcAttnSourceParams(voice, &voice->Props, buffer, context);
1325 else
1326 CalcNonAttnSourceParams(voice, &voice->Props, buffer, context);
1327 break;
1329 BufferListItem = BufferListItem->next;
1334 static void UpdateContextSources(ALCcontext *ctx, ALeffectslot *slot)
1336 ALvoice *voice, *voice_end;
1337 ALsource *source;
1339 IncrementRef(&ctx->UpdateCount);
1340 if(!ATOMIC_LOAD(&ctx->HoldUpdates))
1342 ALboolean force = CalcListenerParams(ctx);
1343 while(slot)
1345 force |= CalcEffectSlotParams(slot, ctx->Device);
1346 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1349 voice = ctx->Voices;
1350 voice_end = voice + ctx->VoiceCount;
1351 for(;voice != voice_end;++voice)
1353 if(!(source=voice->Source)) continue;
1354 if(source->state != AL_PLAYING && source->state != AL_PAUSED)
1355 voice->Source = NULL;
1356 else
1357 CalcSourceParams(voice, ctx, force);
1360 IncrementRef(&ctx->UpdateCount);
1364 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1365 * converts NaN to 0. */
1366 static inline ALfloat aluClampf(ALfloat val)
1368 if(fabsf(val) <= 1.0f) return val;
1369 return (ALfloat)((0.0f < val) - (val < 0.0f));
1372 static inline ALfloat aluF2F(ALfloat val)
1373 { return val; }
1375 static inline ALint aluF2I(ALfloat val)
1377 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1378 * integer range normalized floats can be safely converted to.
1380 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1382 static inline ALuint aluF2UI(ALfloat val)
1383 { return aluF2I(val)+2147483648u; }
1385 static inline ALshort aluF2S(ALfloat val)
1386 { return fastf2i(aluClampf(val)*32767.0f); }
1387 static inline ALushort aluF2US(ALfloat val)
1388 { return aluF2S(val)+32768; }
1390 static inline ALbyte aluF2B(ALfloat val)
1391 { return fastf2i(aluClampf(val)*127.0f); }
1392 static inline ALubyte aluF2UB(ALfloat val)
1393 { return aluF2B(val)+128; }
1395 #define DECL_TEMPLATE(T, func) \
1396 static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1397 ALuint SamplesToDo, ALuint numchans) \
1399 ALuint i, j; \
1400 for(j = 0;j < numchans;j++) \
1402 const ALfloat *in = InBuffer[j]; \
1403 T *restrict out = (T*)OutBuffer + j; \
1404 for(i = 0;i < SamplesToDo;i++) \
1405 out[i*numchans] = func(in[i]); \
1409 DECL_TEMPLATE(ALfloat, aluF2F)
1410 DECL_TEMPLATE(ALuint, aluF2UI)
1411 DECL_TEMPLATE(ALint, aluF2I)
1412 DECL_TEMPLATE(ALushort, aluF2US)
1413 DECL_TEMPLATE(ALshort, aluF2S)
1414 DECL_TEMPLATE(ALubyte, aluF2UB)
1415 DECL_TEMPLATE(ALbyte, aluF2B)
1417 #undef DECL_TEMPLATE
1420 ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1422 ALuint SamplesToDo;
1423 ALvoice *voice, *voice_end;
1424 ALeffectslot *slot;
1425 ALsource *source;
1426 ALCcontext *ctx;
1427 FPUCtl oldMode;
1428 ALuint i, c;
1430 SetMixerFPUMode(&oldMode);
1432 while(size > 0)
1434 SamplesToDo = minu(size, BUFFERSIZE);
1435 for(c = 0;c < device->Dry.NumChannels;c++)
1436 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1437 if(device->Dry.Buffer != device->RealOut.Buffer)
1438 for(c = 0;c < device->RealOut.NumChannels;c++)
1439 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1440 if(device->Dry.Buffer != device->FOAOut.Buffer)
1441 for(c = 0;c < device->FOAOut.NumChannels;c++)
1442 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1444 IncrementRef(&device->MixCount);
1445 V0(device->Backend,lock)();
1447 if((slot=device->DefaultSlot) != NULL)
1449 CalcEffectSlotParams(device->DefaultSlot, device);
1450 for(i = 0;i < slot->NumChannels;i++)
1451 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1454 ctx = ATOMIC_LOAD(&device->ContextList);
1455 while(ctx)
1457 ALeffectslot *slotroot;
1459 slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList);
1460 UpdateContextSources(ctx, slotroot);
1462 slot = slotroot;
1463 while(slot)
1465 for(i = 0;i < slot->NumChannels;i++)
1466 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1467 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1470 /* source processing */
1471 voice = ctx->Voices;
1472 voice_end = voice + ctx->VoiceCount;
1473 for(;voice != voice_end;++voice)
1475 ALboolean IsVoiceInit = (voice->Step > 0);
1476 source = voice->Source;
1477 if(source && source->state == AL_PLAYING && IsVoiceInit)
1478 MixSource(voice, source, device, SamplesToDo);
1481 /* effect slot processing */
1482 slot = slotroot;
1483 while(slot)
1485 const ALeffectslot *cslot = slot;
1486 ALeffectState *state = cslot->Params.EffectState;
1487 V(state,process)(SamplesToDo, cslot->WetBuffer, state->OutBuffer,
1488 state->OutChannels);
1489 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1492 ctx = ctx->next;
1495 if(device->DefaultSlot != NULL)
1497 const ALeffectslot *slot = device->DefaultSlot;
1498 ALeffectState *state = slot->Params.EffectState;
1499 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1500 state->OutChannels);
1503 /* Increment the clock time. Every second's worth of samples is
1504 * converted and added to clock base so that large sample counts don't
1505 * overflow during conversion. This also guarantees an exact, stable
1506 * conversion. */
1507 device->SamplesDone += SamplesToDo;
1508 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1509 device->SamplesDone %= device->Frequency;
1510 V0(device->Backend,unlock)();
1511 IncrementRef(&device->MixCount);
1513 if(device->Hrtf.Handle)
1515 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1516 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1517 if(lidx != -1 && ridx != -1)
1519 HrtfDirectMixerFunc HrtfMix = SelectHrtfMixer();
1520 ALuint irsize = device->Hrtf.IrSize;
1521 for(c = 0;c < device->Dry.NumChannels;c++)
1523 HrtfMix(device->RealOut.Buffer, lidx, ridx,
1524 device->Dry.Buffer[c], device->Hrtf.Offset, irsize,
1525 device->Hrtf.Coeffs[c], device->Hrtf.Values[c],
1526 SamplesToDo
1529 device->Hrtf.Offset += SamplesToDo;
1532 else if(device->AmbiDecoder)
1534 if(device->Dry.Buffer != device->FOAOut.Buffer)
1535 bformatdec_upSample(device->AmbiDecoder,
1536 device->Dry.Buffer, device->FOAOut.Buffer,
1537 device->FOAOut.NumChannels, SamplesToDo
1539 bformatdec_process(device->AmbiDecoder,
1540 device->RealOut.Buffer, device->RealOut.NumChannels,
1541 device->Dry.Buffer, SamplesToDo
1544 else if(device->AmbiUp)
1546 ambiup_process(device->AmbiUp,
1547 device->RealOut.Buffer, device->RealOut.NumChannels,
1548 device->FOAOut.Buffer, SamplesToDo
1551 else if(device->Uhj_Encoder)
1553 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1554 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1555 if(lidx != -1 && ridx != -1)
1557 /* Encode to stereo-compatible 2-channel UHJ output. */
1558 EncodeUhj2(device->Uhj_Encoder,
1559 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1560 device->Dry.Buffer, SamplesToDo
1564 else if(device->Bs2b)
1566 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1567 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1568 if(lidx != -1 && ridx != -1)
1570 /* Apply binaural/crossfeed filter */
1571 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1572 device->RealOut.Buffer[ridx], SamplesToDo);
1576 if(buffer)
1578 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1579 ALuint OutChannels = device->RealOut.NumChannels;
1581 #define WRITE(T, a, b, c, d) do { \
1582 Write_##T((a), (b), (c), (d)); \
1583 buffer = (T*)buffer + (c)*(d); \
1584 } while(0)
1585 switch(device->FmtType)
1587 case DevFmtByte:
1588 WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1589 break;
1590 case DevFmtUByte:
1591 WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1592 break;
1593 case DevFmtShort:
1594 WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
1595 break;
1596 case DevFmtUShort:
1597 WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
1598 break;
1599 case DevFmtInt:
1600 WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
1601 break;
1602 case DevFmtUInt:
1603 WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
1604 break;
1605 case DevFmtFloat:
1606 WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
1607 break;
1609 #undef WRITE
1612 size -= SamplesToDo;
1615 RestoreFPUMode(&oldMode);
1619 ALvoid aluHandleDisconnect(ALCdevice *device)
1621 ALCcontext *Context;
1623 device->Connected = ALC_FALSE;
1625 Context = ATOMIC_LOAD(&device->ContextList);
1626 while(Context)
1628 ALvoice *voice, *voice_end;
1630 voice = Context->Voices;
1631 voice_end = voice + Context->VoiceCount;
1632 while(voice != voice_end)
1634 ALsource *source = voice->Source;
1635 voice->Source = NULL;
1637 if(source && source->state == AL_PLAYING)
1639 source->state = AL_STOPPED;
1640 ATOMIC_STORE(&source->current_buffer, NULL);
1641 ATOMIC_STORE(&source->position, 0);
1642 ATOMIC_STORE(&source->position_fraction, 0);
1645 voice++;
1647 Context->VoiceCount = 0;
1649 Context = Context->next;