Avoid duplicating code using a macro
[openal-soft.git] / Alc / ALu.c
blobdc97952231b83f837285de2d9ad4a52ebd42700a
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <math.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
29 #include "alMain.h"
30 #include "alSource.h"
31 #include "alBuffer.h"
32 #include "alListener.h"
33 #include "alAuxEffectSlot.h"
34 #include "alu.h"
35 #include "bs2b.h"
36 #include "hrtf.h"
37 #include "uhjfilter.h"
38 #include "bformatdec.h"
39 #include "static_assert.h"
41 #include "mixer_defs.h"
43 #include "backends/base.h"
46 struct ChanMap {
47 enum Channel channel;
48 ALfloat angle;
49 ALfloat elevation;
52 /* Cone scalar */
53 ALfloat ConeScale = 1.0f;
55 /* Localized Z scalar for mono sources */
56 ALfloat ZScale = 1.0f;
58 extern inline ALfloat minf(ALfloat a, ALfloat b);
59 extern inline ALfloat maxf(ALfloat a, ALfloat b);
60 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
62 extern inline ALdouble mind(ALdouble a, ALdouble b);
63 extern inline ALdouble maxd(ALdouble a, ALdouble b);
64 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
66 extern inline ALuint minu(ALuint a, ALuint b);
67 extern inline ALuint maxu(ALuint a, ALuint b);
68 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
70 extern inline ALint mini(ALint a, ALint b);
71 extern inline ALint maxi(ALint a, ALint b);
72 extern inline ALint clampi(ALint val, ALint min, ALint max);
74 extern inline ALint64 mini64(ALint64 a, ALint64 b);
75 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
76 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
78 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
79 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
80 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
82 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
83 extern inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac);
84 extern inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat val5, ALfloat val6, ALfloat val7, ALuint frac);
86 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
88 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
89 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
90 extern inline void aluMatrixfSet(aluMatrixf *matrix,
91 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
92 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
93 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
94 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
96 const aluMatrixf IdentityMatrixf = {{
97 { 1.0f, 0.0f, 0.0f, 0.0f },
98 { 0.0f, 1.0f, 0.0f, 0.0f },
99 { 0.0f, 0.0f, 1.0f, 0.0f },
100 { 0.0f, 0.0f, 0.0f, 1.0f },
104 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
106 #ifdef HAVE_SSE
107 if((CPUCapFlags&CPU_CAP_SSE))
108 return MixDirectHrtf_SSE;
109 #endif
110 #ifdef HAVE_NEON
111 if((CPUCapFlags&CPU_CAP_NEON))
112 return MixDirectHrtf_Neon;
113 #endif
115 return MixDirectHrtf_C;
119 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
121 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
122 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
123 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
126 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
128 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
131 static ALfloat aluNormalize(ALfloat *vec)
133 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
134 if(length > 0.0f)
136 ALfloat inv_length = 1.0f/length;
137 vec[0] *= inv_length;
138 vec[1] *= inv_length;
139 vec[2] *= inv_length;
141 return length;
144 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
146 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
148 vec[0] = v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0];
149 vec[1] = v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1];
150 vec[2] = v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2];
153 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
155 aluVector v;
156 v.v[0] = vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0];
157 v.v[1] = vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1];
158 v.v[2] = vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2];
159 v.v[3] = vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3];
160 return v;
164 /* Prepares the interpolator for a given rate (determined by increment). A
165 * result of AL_FALSE indicates that the filter output will completely cut
166 * the input signal.
168 * With a bit of work, and a trade of memory for CPU cost, this could be
169 * modified for use with an interpolated increment for buttery-smooth pitch
170 * changes.
172 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
174 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
175 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
176 static const ALuint to[4][BSINC_SCALE_COUNT] =
178 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
179 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
180 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
181 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
183 static const ALuint tm[2][BSINC_SCALE_COUNT] =
185 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
186 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
188 ALfloat sf;
189 ALuint si, pi;
190 ALboolean uncut = AL_TRUE;
192 if(increment > FRACTIONONE)
194 sf = (ALfloat)FRACTIONONE / increment;
195 if(sf < scaleBase)
197 /* Signal has been completely cut. The return result can be used
198 * to skip the filter (and output zeros) as an optimization.
200 sf = 0.0f;
201 si = 0;
202 uncut = AL_FALSE;
204 else
206 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
207 si = fastf2u(sf);
208 /* The interpolation factor is fit to this diagonally-symmetric
209 * curve to reduce the transition ripple caused by interpolating
210 * different scales of the sinc function.
212 sf = 1.0f - cosf(asinf(sf - si));
215 else
217 sf = 0.0f;
218 si = BSINC_SCALE_COUNT - 1;
221 state->sf = sf;
222 state->m = m[si];
223 state->l = -(ALint)((m[si] / 2) - 1);
224 /* The CPU cost of this table re-mapping could be traded for the memory
225 * cost of a complete table map (1024 elements large).
227 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
229 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
230 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
231 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
232 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
234 return uncut;
238 static ALboolean CalcListenerParams(ALCcontext *Context)
240 ALlistener *Listener = Context->Listener;
241 ALfloat N[3], V[3], U[3], P[3];
242 struct ALlistenerProps *props;
243 aluVector vel;
245 props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &Listener->Update, NULL, almemory_order_acq_rel);
246 if(!props) return AL_FALSE;
248 /* AT then UP */
249 N[0] = ATOMIC_LOAD(&props->Forward[0], almemory_order_relaxed);
250 N[1] = ATOMIC_LOAD(&props->Forward[1], almemory_order_relaxed);
251 N[2] = ATOMIC_LOAD(&props->Forward[2], almemory_order_relaxed);
252 aluNormalize(N);
253 V[0] = ATOMIC_LOAD(&props->Up[0], almemory_order_relaxed);
254 V[1] = ATOMIC_LOAD(&props->Up[1], almemory_order_relaxed);
255 V[2] = ATOMIC_LOAD(&props->Up[2], almemory_order_relaxed);
256 aluNormalize(V);
257 /* Build and normalize right-vector */
258 aluCrossproduct(N, V, U);
259 aluNormalize(U);
261 aluMatrixfSet(&Listener->Params.Matrix,
262 U[0], V[0], -N[0], 0.0,
263 U[1], V[1], -N[1], 0.0,
264 U[2], V[2], -N[2], 0.0,
265 0.0, 0.0, 0.0, 1.0
268 P[0] = ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed);
269 P[1] = ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed);
270 P[2] = ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed);
271 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
272 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
274 aluVectorSet(&vel, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
275 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
276 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
277 0.0f);
278 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
280 Listener->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed) * Context->GainBoost;
281 Listener->Params.MetersPerUnit = ATOMIC_LOAD(&props->MetersPerUnit, almemory_order_relaxed);
283 Listener->Params.DopplerFactor = ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
284 Listener->Params.SpeedOfSound = ATOMIC_LOAD(&props->SpeedOfSound, almemory_order_relaxed) *
285 ATOMIC_LOAD(&props->DopplerVelocity, almemory_order_relaxed);
287 Listener->Params.SourceDistanceModel = ATOMIC_LOAD(&props->SourceDistanceModel, almemory_order_relaxed);
288 Listener->Params.DistanceModel = ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed);
290 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
291 return AL_TRUE;
294 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
296 struct ALeffectslotProps *props;
297 ALeffectState *state;
299 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL, almemory_order_acq_rel);
300 if(!props) return AL_FALSE;
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;
318 /* Swap effect states. No need to play with the ref counts since they keep
319 * the same number of refs.
321 state = ATOMIC_EXCHANGE(ALeffectState*, &props->State, slot->Params.EffectState,
322 almemory_order_relaxed);
323 slot->Params.EffectState = state;
325 V(state,update)(device, slot, &props->Props);
327 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
328 return AL_TRUE;
332 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
334 static const struct ChanMap MonoMap[1] = {
335 { FrontCenter, 0.0f, 0.0f }
336 }, RearMap[2] = {
337 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
338 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
339 }, QuadMap[4] = {
340 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
341 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
342 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
343 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
344 }, X51Map[6] = {
345 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
346 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
347 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
348 { LFE, 0.0f, 0.0f },
349 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
350 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
351 }, X61Map[7] = {
352 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
353 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
354 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
355 { LFE, 0.0f, 0.0f },
356 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
357 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
358 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
359 }, X71Map[8] = {
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 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
365 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
366 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
367 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
370 const ALCdevice *Device = ALContext->Device;
371 const ALlistener *Listener = ALContext->Listener;
372 ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
373 ALfloat DryGain, DryGainHF, DryGainLF;
374 ALfloat WetGain[MAX_SENDS];
375 ALfloat WetGainHF[MAX_SENDS];
376 ALfloat WetGainLF[MAX_SENDS];
377 ALeffectslot *SendSlots[MAX_SENDS];
378 ALuint NumSends, Frequency;
379 ALboolean Relative;
380 const struct ChanMap *chans = NULL;
381 struct ChanMap StereoMap[2] = {
382 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
383 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
385 ALuint num_channels = 0;
386 ALboolean DirectChannels;
387 ALboolean isbformat = AL_FALSE;
388 ALfloat Pitch;
389 ALuint i, j, c;
391 /* Get device properties */
392 NumSends = Device->NumAuxSends;
393 Frequency = Device->Frequency;
395 /* Get listener properties */
396 ListenerGain = Listener->Params.Gain;
398 /* Get source properties */
399 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
400 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
401 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
402 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
403 Relative = ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed);
404 DirectChannels = ATOMIC_LOAD(&props->DirectChannels, almemory_order_relaxed);
406 /* Convert counter-clockwise to clockwise. */
407 StereoMap[0].angle = -ATOMIC_LOAD(&props->StereoPan[0], almemory_order_relaxed);
408 StereoMap[1].angle = -ATOMIC_LOAD(&props->StereoPan[1], almemory_order_relaxed);
410 voice->DirectOut.Buffer = Device->Dry.Buffer;
411 voice->DirectOut.Channels = Device->Dry.NumChannels;
412 for(i = 0;i < NumSends;i++)
414 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
415 if(!SendSlots[i] && i == 0)
416 SendSlots[i] = Device->DefaultSlot;
417 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
419 SendSlots[i] = NULL;
420 voice->SendOut[i].Buffer = NULL;
421 voice->SendOut[i].Channels = 0;
423 else
425 voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer;
426 voice->SendOut[i].Channels = SendSlots[i]->NumChannels;
430 /* Calculate the stepping value */
431 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
432 if(Pitch > (ALfloat)MAX_PITCH)
433 voice->Step = MAX_PITCH<<FRACTIONBITS;
434 else
435 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
436 BsincPrepare(voice->Step, &voice->SincState);
438 /* Calculate gains */
439 DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
440 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
441 DryGain = minf(DryGain, GAIN_MIX_MAX);
442 DryGainHF = ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
443 DryGainLF = ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
444 for(i = 0;i < NumSends;i++)
446 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
447 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
448 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
449 WetGainHF[i] = ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
450 WetGainLF[i] = ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
453 switch(ALBuffer->FmtChannels)
455 case FmtMono:
456 chans = MonoMap;
457 num_channels = 1;
458 break;
460 case FmtStereo:
461 chans = StereoMap;
462 num_channels = 2;
463 break;
465 case FmtRear:
466 chans = RearMap;
467 num_channels = 2;
468 break;
470 case FmtQuad:
471 chans = QuadMap;
472 num_channels = 4;
473 break;
475 case FmtX51:
476 chans = X51Map;
477 num_channels = 6;
478 break;
480 case FmtX61:
481 chans = X61Map;
482 num_channels = 7;
483 break;
485 case FmtX71:
486 chans = X71Map;
487 num_channels = 8;
488 break;
490 case FmtBFormat2D:
491 num_channels = 3;
492 isbformat = AL_TRUE;
493 DirectChannels = AL_FALSE;
494 break;
496 case FmtBFormat3D:
497 num_channels = 4;
498 isbformat = AL_TRUE;
499 DirectChannels = AL_FALSE;
500 break;
503 if(isbformat)
505 ALfloat N[3], V[3], U[3];
506 aluMatrixf matrix;
507 ALfloat scale;
509 /* AT then UP */
510 N[0] = ATOMIC_LOAD(&props->Orientation[0][0], almemory_order_relaxed);
511 N[1] = ATOMIC_LOAD(&props->Orientation[0][1], almemory_order_relaxed);
512 N[2] = ATOMIC_LOAD(&props->Orientation[0][2], almemory_order_relaxed);
513 aluNormalize(N);
514 V[0] = ATOMIC_LOAD(&props->Orientation[1][0], almemory_order_relaxed);
515 V[1] = ATOMIC_LOAD(&props->Orientation[1][1], almemory_order_relaxed);
516 V[2] = ATOMIC_LOAD(&props->Orientation[1][2], almemory_order_relaxed);
517 aluNormalize(V);
518 if(!Relative)
520 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
521 aluMatrixfFloat3(N, 0.0f, lmatrix);
522 aluMatrixfFloat3(V, 0.0f, lmatrix);
524 /* Build and normalize right-vector */
525 aluCrossproduct(N, V, U);
526 aluNormalize(U);
528 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
529 scale = 1.732050808f;
530 aluMatrixfSet(&matrix,
531 1.414213562f, 0.0f, 0.0f, 0.0f,
532 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
533 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
534 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
537 voice->DirectOut.Buffer = Device->FOAOut.Buffer;
538 voice->DirectOut.Channels = Device->FOAOut.NumChannels;
539 for(c = 0;c < num_channels;c++)
540 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
541 voice->Chan[c].Direct.Gains.Target);
543 for(i = 0;i < NumSends;i++)
545 if(!SendSlots[i])
547 for(c = 0;c < num_channels;c++)
549 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
550 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
553 else
555 for(c = 0;c < num_channels;c++)
557 const ALeffectslot *Slot = SendSlots[i];
558 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, matrix.m[c],
559 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
564 voice->IsHrtf = AL_FALSE;
566 else
568 ALfloat coeffs[MAX_AMBI_COEFFS];
570 if(DirectChannels)
572 /* Skip the virtual channels and write inputs to the real output. */
573 voice->DirectOut.Buffer = Device->RealOut.Buffer;
574 voice->DirectOut.Channels = Device->RealOut.NumChannels;
575 for(c = 0;c < num_channels;c++)
577 int idx;
578 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
579 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
580 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
581 voice->Chan[c].Direct.Gains.Target[idx] = DryGain;
584 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
585 * channel-match. */
586 for(c = 0;c < num_channels;c++)
588 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
590 for(i = 0;i < NumSends;i++)
592 if(!SendSlots[i])
594 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
595 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
597 else
599 const ALeffectslot *Slot = SendSlots[i];
600 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
601 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
606 voice->IsHrtf = AL_FALSE;
608 else if(Device->Render_Mode == HrtfRender)
610 /* Full HRTF rendering. Skip the virtual channels and render each
611 * input channel to the real outputs.
613 voice->DirectOut.Buffer = Device->RealOut.Buffer;
614 voice->DirectOut.Channels = Device->RealOut.NumChannels;
615 for(c = 0;c < num_channels;c++)
617 if(chans[c].channel == LFE)
619 /* Skip LFE */
620 voice->Chan[c].Direct.Hrtf.Target.Delay[0] = 0;
621 voice->Chan[c].Direct.Hrtf.Target.Delay[1] = 0;
622 for(i = 0;i < HRIR_LENGTH;i++)
624 voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][0] = 0.0f;
625 voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][1] = 0.0f;
628 for(i = 0;i < NumSends;i++)
630 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
631 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
634 continue;
637 /* Get the static HRIR coefficients and delays for this channel. */
638 GetHrtfCoeffs(Device->Hrtf.Handle,
639 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
640 voice->Chan[c].Direct.Hrtf.Target.Coeffs,
641 voice->Chan[c].Direct.Hrtf.Target.Delay
644 /* Normal panning for auxiliary sends. */
645 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
647 for(i = 0;i < NumSends;i++)
649 if(!SendSlots[i])
651 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
652 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
654 else
656 const ALeffectslot *Slot = SendSlots[i];
657 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
658 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
663 voice->IsHrtf = AL_TRUE;
665 else
667 /* Non-HRTF rendering. Use normal panning to the output. */
668 for(c = 0;c < num_channels;c++)
670 /* Special-case LFE */
671 if(chans[c].channel == LFE)
673 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
674 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
675 if(Device->Dry.Buffer == Device->RealOut.Buffer)
677 int idx;
678 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
679 voice->Chan[c].Direct.Gains.Target[idx] = DryGain;
682 for(i = 0;i < NumSends;i++)
684 ALuint j;
685 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
686 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
688 continue;
691 if(Device->Render_Mode == StereoPair)
693 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
694 ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
695 coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
696 voice->Chan[c].Direct.Gains.Target[0] = coeffs[0] * DryGain;
697 voice->Chan[c].Direct.Gains.Target[1] = (1.0f-coeffs[0]) * DryGain;
698 for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
699 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
701 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
703 else
705 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
706 ComputePanningGains(Device->Dry, coeffs, DryGain,
707 voice->Chan[c].Direct.Gains.Target);
710 for(i = 0;i < NumSends;i++)
712 if(!SendSlots[i])
714 ALuint j;
715 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
716 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
718 else
720 const ALeffectslot *Slot = SendSlots[i];
721 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
722 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
727 voice->IsHrtf = AL_FALSE;
732 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
733 Frequency;
734 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
735 Frequency;
736 DryGainHF = maxf(DryGainHF, 0.0001f);
737 DryGainLF = maxf(DryGainLF, 0.0001f);
738 for(c = 0;c < num_channels;c++)
740 voice->Chan[c].Direct.FilterType = AF_None;
741 if(DryGainHF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_LowPass;
742 if(DryGainLF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_HighPass;
743 ALfilterState_setParams(
744 &voice->Chan[c].Direct.LowPass, ALfilterType_HighShelf,
745 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
747 ALfilterState_setParams(
748 &voice->Chan[c].Direct.HighPass, ALfilterType_LowShelf,
749 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
753 for(i = 0;i < NumSends;i++)
755 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
756 Frequency;
757 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
758 Frequency;
759 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
760 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
761 for(c = 0;c < num_channels;c++)
763 voice->Chan[c].Send[i].FilterType = AF_None;
764 if(WetGainHF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_LowPass;
765 if(WetGainLF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_HighPass;
766 ALfilterState_setParams(
767 &voice->Chan[c].Send[i].LowPass, ALfilterType_HighShelf,
768 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
770 ALfilterState_setParams(
771 &voice->Chan[c].Send[i].HighPass, ALfilterType_LowShelf,
772 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
778 static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
780 const ALCdevice *Device = ALContext->Device;
781 const ALlistener *Listener = ALContext->Listener;
782 aluVector Position, Velocity, Direction, SourceToListener;
783 ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
784 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
785 ALfloat SourceVolume,ListenerGain;
786 ALfloat DopplerFactor, SpeedOfSound;
787 ALfloat AirAbsorptionFactor;
788 ALfloat RoomAirAbsorption[MAX_SENDS];
789 ALeffectslot *SendSlots[MAX_SENDS];
790 ALfloat Attenuation;
791 ALfloat RoomAttenuation[MAX_SENDS];
792 ALfloat MetersPerUnit;
793 ALfloat RoomRolloffBase;
794 ALfloat RoomRolloff[MAX_SENDS];
795 ALfloat DecayDistance[MAX_SENDS];
796 ALfloat DryGain;
797 ALfloat DryGainHF;
798 ALfloat DryGainLF;
799 ALboolean DryGainHFAuto;
800 ALfloat WetGain[MAX_SENDS];
801 ALfloat WetGainHF[MAX_SENDS];
802 ALfloat WetGainLF[MAX_SENDS];
803 ALboolean WetGainAuto;
804 ALboolean WetGainHFAuto;
805 ALfloat Pitch;
806 ALuint Frequency;
807 ALint NumSends;
808 ALint i;
810 DryGainHF = 1.0f;
811 DryGainLF = 1.0f;
812 for(i = 0;i < MAX_SENDS;i++)
814 WetGainHF[i] = 1.0f;
815 WetGainLF[i] = 1.0f;
818 /* Get context/device properties */
819 DopplerFactor = Listener->Params.DopplerFactor;
820 SpeedOfSound = Listener->Params.SpeedOfSound;
821 NumSends = Device->NumAuxSends;
822 Frequency = Device->Frequency;
824 /* Get listener properties */
825 ListenerGain = Listener->Params.Gain;
826 MetersPerUnit = Listener->Params.MetersPerUnit;
828 /* Get source properties */
829 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
830 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
831 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
832 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
833 aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed),
834 ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed),
835 ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed),
836 1.0f);
837 aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed),
838 ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed),
839 ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed),
840 0.0f);
841 aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
842 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
843 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
844 0.0f);
845 MinDist = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed);
846 MaxDist = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed);
847 Rolloff = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed);
848 DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
849 InnerAngle = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed);
850 OuterAngle = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed);
851 AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed);
852 DryGainHFAuto = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed);
853 WetGainAuto = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed);
854 WetGainHFAuto = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed);
855 RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed);
857 voice->DirectOut.Buffer = Device->Dry.Buffer;
858 voice->DirectOut.Channels = Device->Dry.NumChannels;
859 for(i = 0;i < NumSends;i++)
861 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
863 if(!SendSlots[i] && i == 0)
864 SendSlots[i] = Device->DefaultSlot;
865 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
867 SendSlots[i] = NULL;
868 RoomRolloff[i] = 0.0f;
869 DecayDistance[i] = 0.0f;
870 RoomAirAbsorption[i] = 1.0f;
872 else if(SendSlots[i]->Params.AuxSendAuto)
874 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
875 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
876 SPEEDOFSOUNDMETRESPERSEC;
877 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
879 else
881 /* If the slot's auxiliary send auto is off, the data sent to the
882 * effect slot is the same as the dry path, sans filter effects */
883 RoomRolloff[i] = Rolloff;
884 DecayDistance[i] = 0.0f;
885 RoomAirAbsorption[i] = AIRABSORBGAINHF;
888 if(!SendSlots[i])
890 voice->SendOut[i].Buffer = NULL;
891 voice->SendOut[i].Channels = 0;
893 else
895 voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer;
896 voice->SendOut[i].Channels = SendSlots[i]->NumChannels;
900 /* Transform source to listener space (convert to head relative) */
901 if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE)
903 const aluMatrixf *Matrix = &Listener->Params.Matrix;
904 /* Transform source vectors */
905 Position = aluMatrixfVector(Matrix, &Position);
906 Velocity = aluMatrixfVector(Matrix, &Velocity);
907 Direction = aluMatrixfVector(Matrix, &Direction);
909 else
911 const aluVector *lvelocity = &Listener->Params.Velocity;
912 /* Offset the source velocity to be relative of the listener velocity */
913 Velocity.v[0] += lvelocity->v[0];
914 Velocity.v[1] += lvelocity->v[1];
915 Velocity.v[2] += lvelocity->v[2];
918 aluNormalize(Direction.v);
919 SourceToListener.v[0] = -Position.v[0];
920 SourceToListener.v[1] = -Position.v[1];
921 SourceToListener.v[2] = -Position.v[2];
922 SourceToListener.v[3] = 0.0f;
923 Distance = aluNormalize(SourceToListener.v);
925 /* Calculate distance attenuation */
926 ClampedDist = Distance;
928 Attenuation = 1.0f;
929 for(i = 0;i < NumSends;i++)
930 RoomAttenuation[i] = 1.0f;
931 switch(Listener->Params.SourceDistanceModel ?
932 ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed) :
933 Listener->Params.DistanceModel)
935 case InverseDistanceClamped:
936 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
937 if(MaxDist < MinDist)
938 break;
939 /*fall-through*/
940 case InverseDistance:
941 if(MinDist > 0.0f)
943 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
944 if(dist > 0.0f) Attenuation = MinDist / dist;
945 for(i = 0;i < NumSends;i++)
947 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
948 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
951 break;
953 case LinearDistanceClamped:
954 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
955 if(MaxDist < MinDist)
956 break;
957 /*fall-through*/
958 case LinearDistance:
959 if(MaxDist != MinDist)
961 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
962 Attenuation = maxf(Attenuation, 0.0f);
963 for(i = 0;i < NumSends;i++)
965 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
966 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
969 break;
971 case ExponentDistanceClamped:
972 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
973 if(MaxDist < MinDist)
974 break;
975 /*fall-through*/
976 case ExponentDistance:
977 if(ClampedDist > 0.0f && MinDist > 0.0f)
979 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
980 for(i = 0;i < NumSends;i++)
981 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
983 break;
985 case DisableDistance:
986 ClampedDist = MinDist;
987 break;
990 /* Source Gain + Attenuation */
991 DryGain = SourceVolume * Attenuation;
992 for(i = 0;i < NumSends;i++)
993 WetGain[i] = SourceVolume * RoomAttenuation[i];
995 /* Distance-based air absorption */
996 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
998 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
999 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
1000 for(i = 0;i < NumSends;i++)
1001 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
1004 if(WetGainAuto)
1006 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1008 /* Apply a decay-time transformation to the wet path, based on the
1009 * attenuation of the dry path.
1011 * Using the apparent distance, based on the distance attenuation, the
1012 * initial decay of the reverb effect is calculated and applied to the
1013 * wet path.
1015 for(i = 0;i < NumSends;i++)
1017 if(DecayDistance[i] > 0.0f)
1018 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1022 /* Calculate directional soundcones */
1023 if(InnerAngle < 360.0f)
1025 ALfloat ConeVolume;
1026 ALfloat ConeHF;
1027 ALfloat Angle;
1028 ALfloat scale;
1030 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1031 if(Angle > InnerAngle)
1033 if(Angle < OuterAngle)
1035 scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1036 ConeVolume = lerp(
1037 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1039 ConeHF = lerp(
1040 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1043 else
1045 ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed);
1046 ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed);
1048 DryGain *= ConeVolume;
1049 if(DryGainHFAuto)
1050 DryGainHF *= ConeHF;
1053 /* Wet path uses the total area of the cone emitter (the room will
1054 * receive the same amount of sound regardless of its direction).
1056 scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
1057 (InnerAngle/360.0f);
1058 if(WetGainAuto)
1060 ConeVolume = lerp(
1061 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1063 for(i = 0;i < NumSends;i++)
1064 WetGain[i] *= ConeVolume;
1066 if(WetGainHFAuto)
1068 ConeHF = lerp(
1069 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1071 for(i = 0;i < NumSends;i++)
1072 WetGainHF[i] *= ConeHF;
1076 /* Apply gain and frequency filters */
1077 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1078 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
1079 DryGain = minf(DryGain, GAIN_MIX_MAX);
1080 DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
1081 DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
1082 for(i = 0;i < NumSends;i++)
1084 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1085 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
1086 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1087 WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
1088 WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
1091 /* Calculate velocity-based doppler effect */
1092 if(DopplerFactor > 0.0f)
1094 const aluVector *lvelocity = &Listener->Params.Velocity;
1095 ALfloat VSS, VLS;
1097 if(SpeedOfSound < 1.0f)
1099 DopplerFactor *= 1.0f/SpeedOfSound;
1100 SpeedOfSound = 1.0f;
1103 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1104 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1106 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1107 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1110 /* Calculate fixed-point stepping value, based on the pitch, buffer
1111 * frequency, and output frequency.
1113 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1114 if(Pitch > (ALfloat)MAX_PITCH)
1115 voice->Step = MAX_PITCH<<FRACTIONBITS;
1116 else
1117 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1118 BsincPrepare(voice->Step, &voice->SincState);
1120 if(Device->Render_Mode == HrtfRender)
1122 /* Full HRTF rendering. Skip the virtual channels and render to the
1123 * real outputs.
1125 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1126 ALfloat ev = 0.0f, az = 0.0f;
1127 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1128 ALfloat coeffs[MAX_AMBI_COEFFS];
1129 ALfloat spread = 0.0f;
1131 voice->DirectOut.Buffer = Device->RealOut.Buffer;
1132 voice->DirectOut.Channels = Device->RealOut.NumChannels;
1134 if(Distance > FLT_EPSILON)
1136 dir[0] = -SourceToListener.v[0];
1137 dir[1] = -SourceToListener.v[1];
1138 dir[2] = -SourceToListener.v[2] * ZScale;
1140 /* Calculate elevation and azimuth only when the source is not at
1141 * the listener. This prevents +0 and -0 Z from producing
1142 * inconsistent panning. Also, clamp Y in case FP precision errors
1143 * cause it to land outside of -1..+1. */
1144 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1145 az = atan2f(dir[0], -dir[2]);
1147 if(radius > Distance)
1148 spread = F_TAU - Distance/radius*F_PI;
1149 else if(Distance > FLT_EPSILON)
1150 spread = asinf(radius / Distance) * 2.0f;
1152 /* Get the HRIR coefficients and delays. */
1153 GetHrtfCoeffs(Device->Hrtf.Handle, ev, az, spread, DryGain,
1154 voice->Chan[0].Direct.Hrtf.Target.Coeffs,
1155 voice->Chan[0].Direct.Hrtf.Target.Delay);
1157 CalcDirectionCoeffs(dir, spread, coeffs);
1159 for(i = 0;i < NumSends;i++)
1161 if(!SendSlots[i])
1163 ALuint j;
1164 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1165 voice->Chan[0].Send[i].Gains.Target[j] = 0.0f;
1167 else
1169 const ALeffectslot *Slot = SendSlots[i];
1170 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1171 WetGain[i], voice->Chan[0].Send[i].Gains.Target);
1175 voice->IsHrtf = AL_TRUE;
1177 else
1179 /* Non-HRTF rendering. */
1180 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1181 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1182 ALfloat coeffs[MAX_AMBI_COEFFS];
1183 ALfloat spread = 0.0f;
1185 /* Get the localized direction, and compute panned gains. */
1186 if(Distance > FLT_EPSILON)
1188 dir[0] = -SourceToListener.v[0];
1189 dir[1] = -SourceToListener.v[1];
1190 dir[2] = -SourceToListener.v[2] * ZScale;
1192 if(radius > Distance)
1193 spread = F_TAU - Distance/radius*F_PI;
1194 else if(Distance > FLT_EPSILON)
1195 spread = asinf(radius / Distance) * 2.0f;
1197 if(Device->Render_Mode == StereoPair)
1199 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
1200 ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
1201 x = clampf(x, -0.5f, 0.5f) + 0.5f;
1202 voice->Chan[0].Direct.Gains.Target[0] = x * DryGain;
1203 voice->Chan[0].Direct.Gains.Target[1] = (1.0f-x) * DryGain;
1204 for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
1205 voice->Chan[0].Direct.Gains.Target[i] = 0.0f;
1207 CalcDirectionCoeffs(dir, spread, coeffs);
1209 else
1211 CalcDirectionCoeffs(dir, spread, coeffs);
1212 ComputePanningGains(Device->Dry, coeffs, DryGain,
1213 voice->Chan[0].Direct.Gains.Target);
1216 for(i = 0;i < NumSends;i++)
1218 if(!SendSlots[i])
1220 ALuint j;
1221 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1222 voice->Chan[0].Send[i].Gains.Target[j] = 0.0f;
1224 else
1226 const ALeffectslot *Slot = SendSlots[i];
1227 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1228 WetGain[i], voice->Chan[0].Send[i].Gains.Target);
1232 voice->IsHrtf = AL_FALSE;
1236 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
1237 Frequency;
1238 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
1239 Frequency;
1240 DryGainHF = maxf(DryGainHF, 0.0001f);
1241 DryGainLF = maxf(DryGainLF, 0.0001f);
1242 voice->Chan[0].Direct.FilterType = AF_None;
1243 if(DryGainHF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_LowPass;
1244 if(DryGainLF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_HighPass;
1245 ALfilterState_setParams(
1246 &voice->Chan[0].Direct.LowPass, ALfilterType_HighShelf,
1247 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
1249 ALfilterState_setParams(
1250 &voice->Chan[0].Direct.HighPass, ALfilterType_LowShelf,
1251 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
1254 for(i = 0;i < NumSends;i++)
1256 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
1257 Frequency;
1258 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
1259 Frequency;
1260 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
1261 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
1262 voice->Chan[0].Send[i].FilterType = AF_None;
1263 if(WetGainHF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_LowPass;
1264 if(WetGainLF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_HighPass;
1265 ALfilterState_setParams(
1266 &voice->Chan[0].Send[i].LowPass, ALfilterType_HighShelf,
1267 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
1269 ALfilterState_setParams(
1270 &voice->Chan[0].Send[i].HighPass, ALfilterType_LowShelf,
1271 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
1276 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1278 ALsource *source = voice->Source;
1279 const ALbufferlistitem *BufferListItem;
1280 struct ALsourceProps *props;
1282 props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
1283 if(!props && !force) return;
1285 if(props)
1287 voice->Props = *props;
1289 ATOMIC_REPLACE_HEAD(struct ALsourceProps*, &source->FreeList, props);
1292 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1293 while(BufferListItem != NULL)
1295 const ALbuffer *buffer;
1296 if((buffer=BufferListItem->buffer) != NULL)
1298 if(buffer->FmtChannels == FmtMono)
1299 CalcAttnSourceParams(voice, &voice->Props, buffer, context);
1300 else
1301 CalcNonAttnSourceParams(voice, &voice->Props, buffer, context);
1302 break;
1304 BufferListItem = BufferListItem->next;
1309 static void UpdateContextSources(ALCcontext *ctx, ALeffectslot *slot)
1311 ALvoice *voice, *voice_end;
1312 ALsource *source;
1314 IncrementRef(&ctx->UpdateCount);
1315 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1317 ALboolean force = CalcListenerParams(ctx);
1318 while(slot)
1320 force |= CalcEffectSlotParams(slot, ctx->Device);
1321 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1324 voice = ctx->Voices;
1325 voice_end = voice + ctx->VoiceCount;
1326 for(;voice != voice_end;++voice)
1328 if(!(source=voice->Source)) continue;
1329 if(source->state != AL_PLAYING && source->state != AL_PAUSED)
1330 voice->Source = NULL;
1331 else
1332 CalcSourceParams(voice, ctx, force);
1335 IncrementRef(&ctx->UpdateCount);
1339 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1340 * converts NaN to 0. */
1341 static inline ALfloat aluClampf(ALfloat val)
1343 if(fabsf(val) <= 1.0f) return val;
1344 return (ALfloat)((0.0f < val) - (val < 0.0f));
1347 static inline ALfloat aluF2F(ALfloat val)
1348 { return val; }
1350 static inline ALint aluF2I(ALfloat val)
1352 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1353 * integer range normalized floats can be safely converted to.
1355 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1357 static inline ALuint aluF2UI(ALfloat val)
1358 { return aluF2I(val)+2147483648u; }
1360 static inline ALshort aluF2S(ALfloat val)
1361 { return fastf2i(aluClampf(val)*32767.0f); }
1362 static inline ALushort aluF2US(ALfloat val)
1363 { return aluF2S(val)+32768; }
1365 static inline ALbyte aluF2B(ALfloat val)
1366 { return fastf2i(aluClampf(val)*127.0f); }
1367 static inline ALubyte aluF2UB(ALfloat val)
1368 { return aluF2B(val)+128; }
1370 #define DECL_TEMPLATE(T, func) \
1371 static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1372 ALuint SamplesToDo, ALuint numchans) \
1374 ALuint i, j; \
1375 for(j = 0;j < numchans;j++) \
1377 const ALfloat *in = InBuffer[j]; \
1378 T *restrict out = (T*)OutBuffer + j; \
1379 for(i = 0;i < SamplesToDo;i++) \
1380 out[i*numchans] = func(in[i]); \
1384 DECL_TEMPLATE(ALfloat, aluF2F)
1385 DECL_TEMPLATE(ALuint, aluF2UI)
1386 DECL_TEMPLATE(ALint, aluF2I)
1387 DECL_TEMPLATE(ALushort, aluF2US)
1388 DECL_TEMPLATE(ALshort, aluF2S)
1389 DECL_TEMPLATE(ALubyte, aluF2UB)
1390 DECL_TEMPLATE(ALbyte, aluF2B)
1392 #undef DECL_TEMPLATE
1395 ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1397 ALuint SamplesToDo;
1398 ALvoice *voice, *voice_end;
1399 ALeffectslot *slot;
1400 ALsource *source;
1401 ALCcontext *ctx;
1402 FPUCtl oldMode;
1403 ALuint i, c;
1405 SetMixerFPUMode(&oldMode);
1407 while(size > 0)
1409 SamplesToDo = minu(size, BUFFERSIZE);
1410 for(c = 0;c < device->Dry.NumChannels;c++)
1411 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1412 if(device->Dry.Buffer != device->RealOut.Buffer)
1413 for(c = 0;c < device->RealOut.NumChannels;c++)
1414 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1415 if(device->Dry.Buffer != device->FOAOut.Buffer)
1416 for(c = 0;c < device->FOAOut.NumChannels;c++)
1417 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1419 IncrementRef(&device->MixCount);
1420 V0(device->Backend,lock)();
1422 if((slot=device->DefaultSlot) != NULL)
1424 CalcEffectSlotParams(device->DefaultSlot, device);
1425 for(i = 0;i < slot->NumChannels;i++)
1426 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1429 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1430 while(ctx)
1432 ALeffectslot *slotroot;
1434 slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList, almemory_order_acquire);
1435 UpdateContextSources(ctx, slotroot);
1437 slot = slotroot;
1438 while(slot)
1440 for(i = 0;i < slot->NumChannels;i++)
1441 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1442 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1445 /* source processing */
1446 voice = ctx->Voices;
1447 voice_end = voice + ctx->VoiceCount;
1448 for(;voice != voice_end;++voice)
1450 ALboolean IsVoiceInit = (voice->Step > 0);
1451 source = voice->Source;
1452 if(source && source->state == AL_PLAYING && IsVoiceInit)
1453 MixSource(voice, source, device, SamplesToDo);
1456 /* effect slot processing */
1457 slot = slotroot;
1458 while(slot)
1460 ALeffectState *state = slot->Params.EffectState;
1461 V(state,process)(SamplesToDo, SAFE_CONST(ALfloatBUFFERSIZE*,slot->WetBuffer),
1462 state->OutBuffer, state->OutChannels);
1463 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1466 ctx = ctx->next;
1469 if(device->DefaultSlot != NULL)
1471 const ALeffectslot *slot = device->DefaultSlot;
1472 ALeffectState *state = slot->Params.EffectState;
1473 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1474 state->OutChannels);
1477 /* Increment the clock time. Every second's worth of samples is
1478 * converted and added to clock base so that large sample counts don't
1479 * overflow during conversion. This also guarantees an exact, stable
1480 * conversion. */
1481 device->SamplesDone += SamplesToDo;
1482 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1483 device->SamplesDone %= device->Frequency;
1484 V0(device->Backend,unlock)();
1485 IncrementRef(&device->MixCount);
1487 if(device->Hrtf.Handle)
1489 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1490 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1491 if(lidx != -1 && ridx != -1)
1493 HrtfDirectMixerFunc HrtfMix = SelectHrtfMixer();
1494 ALuint irsize = device->Hrtf.IrSize;
1495 for(c = 0;c < device->Dry.NumChannels;c++)
1497 HrtfMix(device->RealOut.Buffer, lidx, ridx,
1498 device->Dry.Buffer[c], device->Hrtf.Offset, irsize,
1499 device->Hrtf.Coeffs[c], device->Hrtf.Values[c],
1500 SamplesToDo
1503 device->Hrtf.Offset += SamplesToDo;
1506 else if(device->AmbiDecoder)
1508 if(device->Dry.Buffer != device->FOAOut.Buffer)
1509 bformatdec_upSample(device->AmbiDecoder,
1510 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1511 device->FOAOut.NumChannels, SamplesToDo
1513 bformatdec_process(device->AmbiDecoder,
1514 device->RealOut.Buffer, device->RealOut.NumChannels,
1515 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1518 else if(device->AmbiUp)
1520 ambiup_process(device->AmbiUp,
1521 device->RealOut.Buffer, device->RealOut.NumChannels,
1522 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1525 else if(device->Uhj_Encoder)
1527 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1528 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1529 if(lidx != -1 && ridx != -1)
1531 /* Encode to stereo-compatible 2-channel UHJ output. */
1532 EncodeUhj2(device->Uhj_Encoder,
1533 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1534 device->Dry.Buffer, SamplesToDo
1538 else if(device->Bs2b)
1540 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1541 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1542 if(lidx != -1 && ridx != -1)
1544 /* Apply binaural/crossfeed filter */
1545 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1546 device->RealOut.Buffer[ridx], SamplesToDo);
1550 if(buffer)
1552 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1553 ALuint OutChannels = device->RealOut.NumChannels;
1555 #define WRITE(T, a, b, c, d) do { \
1556 Write_##T((a), (b), (c), (d)); \
1557 buffer = (T*)buffer + (c)*(d); \
1558 } while(0)
1559 switch(device->FmtType)
1561 case DevFmtByte:
1562 WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1563 break;
1564 case DevFmtUByte:
1565 WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1566 break;
1567 case DevFmtShort:
1568 WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
1569 break;
1570 case DevFmtUShort:
1571 WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
1572 break;
1573 case DevFmtInt:
1574 WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
1575 break;
1576 case DevFmtUInt:
1577 WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
1578 break;
1579 case DevFmtFloat:
1580 WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
1581 break;
1583 #undef WRITE
1586 size -= SamplesToDo;
1589 RestoreFPUMode(&oldMode);
1593 ALvoid aluHandleDisconnect(ALCdevice *device)
1595 ALCcontext *Context;
1597 device->Connected = ALC_FALSE;
1599 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1600 while(Context)
1602 ALvoice *voice, *voice_end;
1604 voice = Context->Voices;
1605 voice_end = voice + Context->VoiceCount;
1606 while(voice != voice_end)
1608 ALsource *source = voice->Source;
1609 voice->Source = NULL;
1611 if(source && source->state == AL_PLAYING)
1613 source->state = AL_STOPPED;
1614 ATOMIC_STORE(&source->current_buffer, NULL, almemory_order_relaxed);
1615 ATOMIC_STORE(&source->position, 0, almemory_order_relaxed);
1616 ATOMIC_STORE(&source->position_fraction, 0, almemory_order_release);
1619 voice++;
1621 Context->VoiceCount = 0;
1623 Context = Context->next;