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