Make the voice's source pointer atomic
[openal-soft.git] / Alc / ALu.c
blobe14c201303671a973c0947ff7219e119c0e665e2
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);
85 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
87 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
88 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
89 extern inline void aluMatrixfSet(aluMatrixf *matrix,
90 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
91 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
92 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
93 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
95 const aluMatrixf IdentityMatrixf = {{
96 { 1.0f, 0.0f, 0.0f, 0.0f },
97 { 0.0f, 1.0f, 0.0f, 0.0f },
98 { 0.0f, 0.0f, 1.0f, 0.0f },
99 { 0.0f, 0.0f, 0.0f, 1.0f },
103 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
105 #ifdef HAVE_SSE
106 if((CPUCapFlags&CPU_CAP_SSE))
107 return MixDirectHrtf_SSE;
108 #endif
109 #ifdef HAVE_NEON
110 if((CPUCapFlags&CPU_CAP_NEON))
111 return MixDirectHrtf_Neon;
112 #endif
114 return MixDirectHrtf_C;
118 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
120 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
121 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
122 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
125 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
127 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
130 static ALfloat aluNormalize(ALfloat *vec)
132 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
133 if(length > 0.0f)
135 ALfloat inv_length = 1.0f/length;
136 vec[0] *= inv_length;
137 vec[1] *= inv_length;
138 vec[2] *= inv_length;
140 return length;
143 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
145 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
147 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];
148 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];
149 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];
152 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
154 aluVector v;
155 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];
156 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];
157 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];
158 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];
159 return v;
163 /* Prepares the interpolator for a given rate (determined by increment). A
164 * result of AL_FALSE indicates that the filter output will completely cut
165 * the input signal.
167 * With a bit of work, and a trade of memory for CPU cost, this could be
168 * modified for use with an interpolated increment for buttery-smooth pitch
169 * changes.
171 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
173 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
174 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
175 static const ALuint to[4][BSINC_SCALE_COUNT] =
177 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
178 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
179 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
180 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
182 static const ALuint tm[2][BSINC_SCALE_COUNT] =
184 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
185 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
187 ALfloat sf;
188 ALuint si, pi;
189 ALboolean uncut = AL_TRUE;
191 if(increment > FRACTIONONE)
193 sf = (ALfloat)FRACTIONONE / increment;
194 if(sf < scaleBase)
196 /* Signal has been completely cut. The return result can be used
197 * to skip the filter (and output zeros) as an optimization.
199 sf = 0.0f;
200 si = 0;
201 uncut = AL_FALSE;
203 else
205 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
206 si = fastf2u(sf);
207 /* The interpolation factor is fit to this diagonally-symmetric
208 * curve to reduce the transition ripple caused by interpolating
209 * different scales of the sinc function.
211 sf = 1.0f - cosf(asinf(sf - si));
214 else
216 sf = 0.0f;
217 si = BSINC_SCALE_COUNT - 1;
220 state->sf = sf;
221 state->m = m[si];
222 state->l = -(ALint)((m[si] / 2) - 1);
223 /* The CPU cost of this table re-mapping could be traded for the memory
224 * cost of a complete table map (1024 elements large).
226 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
228 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
229 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
230 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
231 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
233 return uncut;
237 static ALboolean CalcListenerParams(ALCcontext *Context)
239 ALlistener *Listener = Context->Listener;
240 ALfloat N[3], V[3], U[3], P[3];
241 struct ALlistenerProps *props;
242 aluVector vel;
244 props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &Listener->Update, NULL, almemory_order_acq_rel);
245 if(!props) return AL_FALSE;
247 /* AT then UP */
248 N[0] = ATOMIC_LOAD(&props->Forward[0], almemory_order_relaxed);
249 N[1] = ATOMIC_LOAD(&props->Forward[1], almemory_order_relaxed);
250 N[2] = ATOMIC_LOAD(&props->Forward[2], almemory_order_relaxed);
251 aluNormalize(N);
252 V[0] = ATOMIC_LOAD(&props->Up[0], almemory_order_relaxed);
253 V[1] = ATOMIC_LOAD(&props->Up[1], almemory_order_relaxed);
254 V[2] = ATOMIC_LOAD(&props->Up[2], almemory_order_relaxed);
255 aluNormalize(V);
256 /* Build and normalize right-vector */
257 aluCrossproduct(N, V, U);
258 aluNormalize(U);
260 aluMatrixfSet(&Listener->Params.Matrix,
261 U[0], V[0], -N[0], 0.0,
262 U[1], V[1], -N[1], 0.0,
263 U[2], V[2], -N[2], 0.0,
264 0.0, 0.0, 0.0, 1.0
267 P[0] = ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed);
268 P[1] = ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed);
269 P[2] = ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed);
270 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
271 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
273 aluVectorSet(&vel, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
274 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
275 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
276 0.0f);
277 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
279 Listener->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed) * Context->GainBoost;
280 Listener->Params.MetersPerUnit = ATOMIC_LOAD(&props->MetersPerUnit, almemory_order_relaxed);
282 Listener->Params.DopplerFactor = ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
283 Listener->Params.SpeedOfSound = ATOMIC_LOAD(&props->SpeedOfSound, almemory_order_relaxed) *
284 ATOMIC_LOAD(&props->DopplerVelocity, almemory_order_relaxed);
286 Listener->Params.SourceDistanceModel = ATOMIC_LOAD(&props->SourceDistanceModel, almemory_order_relaxed);
287 Listener->Params.DistanceModel = ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed);
289 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
290 return AL_TRUE;
293 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
295 struct ALeffectslotProps *props;
296 ALeffectState *state;
298 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL, almemory_order_acq_rel);
299 if(!props) return AL_FALSE;
301 slot->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
302 slot->Params.AuxSendAuto = ATOMIC_LOAD(&props->AuxSendAuto, almemory_order_relaxed);
303 slot->Params.EffectType = ATOMIC_LOAD(&props->Type, almemory_order_relaxed);
304 if(IsReverbEffect(slot->Params.EffectType))
306 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
307 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
308 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
310 else
312 slot->Params.RoomRolloff = 0.0f;
313 slot->Params.DecayTime = 0.0f;
314 slot->Params.AirAbsorptionGainHF = 1.0f;
317 /* Swap effect states. No need to play with the ref counts since they keep
318 * the same number of refs.
320 state = ATOMIC_EXCHANGE(ALeffectState*, &props->State, slot->Params.EffectState,
321 almemory_order_relaxed);
322 slot->Params.EffectState = state;
324 V(state,update)(device, slot, &props->Props);
326 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
327 return AL_TRUE;
331 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
333 static const struct ChanMap MonoMap[1] = {
334 { FrontCenter, 0.0f, 0.0f }
335 }, RearMap[2] = {
336 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
337 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
338 }, QuadMap[4] = {
339 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
340 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
341 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
342 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
343 }, X51Map[6] = {
344 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
345 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
346 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
347 { LFE, 0.0f, 0.0f },
348 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
349 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
350 }, X61Map[7] = {
351 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
352 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
353 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
354 { LFE, 0.0f, 0.0f },
355 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
356 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
357 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
358 }, X71Map[8] = {
359 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
360 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
361 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
362 { LFE, 0.0f, 0.0f },
363 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
364 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
365 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
366 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
369 const ALCdevice *Device = ALContext->Device;
370 const ALlistener *Listener = ALContext->Listener;
371 ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
372 ALfloat DryGain, DryGainHF, DryGainLF;
373 ALfloat WetGain[MAX_SENDS];
374 ALfloat WetGainHF[MAX_SENDS];
375 ALfloat WetGainLF[MAX_SENDS];
376 ALeffectslot *SendSlots[MAX_SENDS];
377 ALfloat HFScale, LFScale;
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->Direct.Buffer = Device->Dry.Buffer;
411 voice->Direct.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->Send[i].Buffer = NULL;
421 voice->Send[i].Channels = 0;
423 else
425 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
426 voice->Send[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->ResampleState.bsinc);
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->Direct.Buffer = Device->FOAOut.Buffer;
538 voice->Direct.Channels = Device->FOAOut.NumChannels;
539 for(c = 0;c < num_channels;c++)
540 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
541 voice->Direct.Params[c].Gains.Target);
543 for(i = 0;i < NumSends;i++)
545 const ALeffectslot *Slot = SendSlots[i];
546 if(Slot)
548 for(c = 0;c < num_channels;c++)
549 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
550 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
553 else
555 for(c = 0;c < num_channels;c++)
556 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
557 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
561 voice->IsHrtf = AL_FALSE;
563 else
565 ALfloat coeffs[MAX_AMBI_COEFFS];
567 if(DirectChannels)
569 /* Skip the virtual channels and write inputs to the real output. */
570 voice->Direct.Buffer = Device->RealOut.Buffer;
571 voice->Direct.Channels = Device->RealOut.NumChannels;
572 for(c = 0;c < num_channels;c++)
574 int idx;
575 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
576 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
577 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
578 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
581 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
582 * channel-match. */
583 for(c = 0;c < num_channels;c++)
585 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
587 for(i = 0;i < NumSends;i++)
589 const ALeffectslot *Slot = SendSlots[i];
590 if(Slot)
591 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
592 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
594 else
595 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
596 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
600 voice->IsHrtf = AL_FALSE;
602 else if(Device->Render_Mode == HrtfRender)
604 /* Full HRTF rendering. Skip the virtual channels and render each
605 * input channel to the real outputs.
607 voice->Direct.Buffer = Device->RealOut.Buffer;
608 voice->Direct.Channels = Device->RealOut.NumChannels;
609 for(c = 0;c < num_channels;c++)
611 if(chans[c].channel == LFE)
613 /* Skip LFE */
614 voice->Direct.Params[c].Hrtf.Target.Delay[0] = 0;
615 voice->Direct.Params[c].Hrtf.Target.Delay[1] = 0;
616 for(i = 0;i < HRIR_LENGTH;i++)
618 voice->Direct.Params[c].Hrtf.Target.Coeffs[i][0] = 0.0f;
619 voice->Direct.Params[c].Hrtf.Target.Coeffs[i][1] = 0.0f;
622 for(i = 0;i < NumSends;i++)
624 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
625 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
628 continue;
631 /* Get the static HRIR coefficients and delays for this channel. */
632 GetHrtfCoeffs(Device->Hrtf.Handle,
633 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
634 voice->Direct.Params[c].Hrtf.Target.Coeffs,
635 voice->Direct.Params[c].Hrtf.Target.Delay
638 /* Normal panning for auxiliary sends. */
639 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
641 for(i = 0;i < NumSends;i++)
643 const ALeffectslot *Slot = SendSlots[i];
644 if(Slot)
645 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
646 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
648 else
649 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
650 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
654 voice->IsHrtf = AL_TRUE;
656 else
658 /* Non-HRTF rendering. Use normal panning to the output. */
659 for(c = 0;c < num_channels;c++)
661 /* Special-case LFE */
662 if(chans[c].channel == LFE)
664 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
665 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
666 if(Device->Dry.Buffer == Device->RealOut.Buffer)
668 int idx;
669 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
670 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
673 for(i = 0;i < NumSends;i++)
675 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
676 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
678 continue;
681 if(Device->Render_Mode == StereoPair)
682 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
683 else
684 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
685 ComputePanningGains(Device->Dry,
686 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
689 for(i = 0;i < NumSends;i++)
691 const ALeffectslot *Slot = SendSlots[i];
692 if(Slot)
693 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
694 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
696 else
697 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
698 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
702 voice->IsHrtf = AL_FALSE;
707 HFScale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) / Frequency;
708 LFScale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) / Frequency;
709 DryGainHF = maxf(DryGainHF, 0.0625f); /* Limit -24dB */
710 DryGainLF = maxf(DryGainLF, 0.0625f);
711 for(c = 0;c < num_channels;c++)
713 voice->Direct.Params[c].FilterType = AF_None;
714 if(DryGainHF != 1.0f) voice->Direct.Params[c].FilterType |= AF_LowPass;
715 if(DryGainLF != 1.0f) voice->Direct.Params[c].FilterType |= AF_HighPass;
716 ALfilterState_setParams(
717 &voice->Direct.Params[c].LowPass, ALfilterType_HighShelf,
718 DryGainHF, HFScale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
720 ALfilterState_setParams(
721 &voice->Direct.Params[c].HighPass, ALfilterType_LowShelf,
722 DryGainLF, LFScale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
726 for(i = 0;i < NumSends;i++)
728 HFScale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) / Frequency;
729 LFScale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) / Frequency;
730 WetGainHF[i] = maxf(WetGainHF[i], 0.0625f);
731 WetGainLF[i] = maxf(WetGainLF[i], 0.0625f);
732 for(c = 0;c < num_channels;c++)
734 voice->Send[i].Params[c].FilterType = AF_None;
735 if(WetGainHF[i] != 1.0f) voice->Send[i].Params[c].FilterType |= AF_LowPass;
736 if(WetGainLF[i] != 1.0f) voice->Send[i].Params[c].FilterType |= AF_HighPass;
737 ALfilterState_setParams(
738 &voice->Send[i].Params[c].LowPass, ALfilterType_HighShelf,
739 WetGainHF[i], HFScale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
741 ALfilterState_setParams(
742 &voice->Send[i].Params[c].HighPass, ALfilterType_LowShelf,
743 WetGainLF[i], LFScale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
749 static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
751 const ALCdevice *Device = ALContext->Device;
752 const ALlistener *Listener = ALContext->Listener;
753 aluVector Position, Velocity, Direction, SourceToListener;
754 ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
755 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
756 ALfloat SourceVolume,ListenerGain;
757 ALfloat DopplerFactor, SpeedOfSound;
758 ALfloat AirAbsorptionFactor;
759 ALfloat RoomAirAbsorption[MAX_SENDS];
760 ALeffectslot *SendSlots[MAX_SENDS];
761 ALfloat Attenuation;
762 ALfloat RoomAttenuation[MAX_SENDS];
763 ALfloat MetersPerUnit;
764 ALfloat RoomRolloffBase;
765 ALfloat RoomRolloff[MAX_SENDS];
766 ALfloat DecayDistance[MAX_SENDS];
767 ALfloat DryGain;
768 ALfloat DryGainHF;
769 ALfloat DryGainLF;
770 ALboolean DryGainHFAuto;
771 ALfloat WetGain[MAX_SENDS];
772 ALfloat WetGainHF[MAX_SENDS];
773 ALfloat WetGainLF[MAX_SENDS];
774 ALfloat HFScale, LFScale;
775 ALboolean WetGainAuto;
776 ALboolean WetGainHFAuto;
777 ALfloat Pitch;
778 ALuint Frequency;
779 ALint NumSends;
780 ALint i, j;
782 /* Get context/device properties */
783 DopplerFactor = Listener->Params.DopplerFactor;
784 SpeedOfSound = Listener->Params.SpeedOfSound;
785 NumSends = Device->NumAuxSends;
786 Frequency = Device->Frequency;
788 /* Get listener properties */
789 ListenerGain = Listener->Params.Gain;
790 MetersPerUnit = Listener->Params.MetersPerUnit;
792 /* Get source properties */
793 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
794 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
795 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
796 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
797 aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed),
798 ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed),
799 ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed),
800 1.0f);
801 aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed),
802 ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed),
803 ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed),
804 0.0f);
805 aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
806 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
807 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
808 0.0f);
809 MinDist = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed);
810 MaxDist = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed);
811 Rolloff = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed);
812 DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
813 InnerAngle = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed);
814 OuterAngle = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed);
815 AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed);
816 DryGainHFAuto = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed);
817 WetGainAuto = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed);
818 WetGainHFAuto = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed);
819 RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed);
821 voice->Direct.Buffer = Device->Dry.Buffer;
822 voice->Direct.Channels = Device->Dry.NumChannels;
823 for(i = 0;i < NumSends;i++)
825 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
827 if(!SendSlots[i] && i == 0)
828 SendSlots[i] = Device->DefaultSlot;
829 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
831 SendSlots[i] = NULL;
832 RoomRolloff[i] = 0.0f;
833 DecayDistance[i] = 0.0f;
834 RoomAirAbsorption[i] = 1.0f;
836 else if(SendSlots[i]->Params.AuxSendAuto)
838 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
839 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
840 SPEEDOFSOUNDMETRESPERSEC;
841 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
843 else
845 /* If the slot's auxiliary send auto is off, the data sent to the
846 * effect slot is the same as the dry path, sans filter effects */
847 RoomRolloff[i] = Rolloff;
848 DecayDistance[i] = 0.0f;
849 RoomAirAbsorption[i] = AIRABSORBGAINHF;
852 if(!SendSlots[i])
854 voice->Send[i].Buffer = NULL;
855 voice->Send[i].Channels = 0;
857 else
859 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
860 voice->Send[i].Channels = SendSlots[i]->NumChannels;
864 /* Transform source to listener space (convert to head relative) */
865 if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE)
867 const aluMatrixf *Matrix = &Listener->Params.Matrix;
868 /* Transform source vectors */
869 Position = aluMatrixfVector(Matrix, &Position);
870 Velocity = aluMatrixfVector(Matrix, &Velocity);
871 Direction = aluMatrixfVector(Matrix, &Direction);
873 else
875 const aluVector *lvelocity = &Listener->Params.Velocity;
876 /* Offset the source velocity to be relative of the listener velocity */
877 Velocity.v[0] += lvelocity->v[0];
878 Velocity.v[1] += lvelocity->v[1];
879 Velocity.v[2] += lvelocity->v[2];
882 aluNormalize(Direction.v);
883 SourceToListener.v[0] = -Position.v[0];
884 SourceToListener.v[1] = -Position.v[1];
885 SourceToListener.v[2] = -Position.v[2];
886 SourceToListener.v[3] = 0.0f;
887 Distance = aluNormalize(SourceToListener.v);
889 /* Calculate distance attenuation */
890 ClampedDist = Distance;
892 Attenuation = 1.0f;
893 for(i = 0;i < NumSends;i++)
894 RoomAttenuation[i] = 1.0f;
895 switch(Listener->Params.SourceDistanceModel ?
896 ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed) :
897 Listener->Params.DistanceModel)
899 case InverseDistanceClamped:
900 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
901 if(MaxDist < MinDist)
902 break;
903 /*fall-through*/
904 case InverseDistance:
905 if(MinDist > 0.0f)
907 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
908 if(dist > 0.0f) Attenuation = MinDist / dist;
909 for(i = 0;i < NumSends;i++)
911 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
912 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
915 break;
917 case LinearDistanceClamped:
918 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
919 if(MaxDist < MinDist)
920 break;
921 /*fall-through*/
922 case LinearDistance:
923 if(MaxDist != MinDist)
925 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
926 Attenuation = maxf(Attenuation, 0.0f);
927 for(i = 0;i < NumSends;i++)
929 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
930 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
933 break;
935 case ExponentDistanceClamped:
936 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
937 if(MaxDist < MinDist)
938 break;
939 /*fall-through*/
940 case ExponentDistance:
941 if(ClampedDist > 0.0f && MinDist > 0.0f)
943 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
944 for(i = 0;i < NumSends;i++)
945 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
947 break;
949 case DisableDistance:
950 ClampedDist = MinDist;
951 break;
954 /* Source Gain + Attenuation */
955 DryGain = SourceVolume * Attenuation;
956 DryGainHF = 1.0f;
957 DryGainLF = 1.0f;
958 for(i = 0;i < NumSends;i++)
960 WetGain[i] = SourceVolume * RoomAttenuation[i];
961 WetGainHF[i] = 1.0f;
962 WetGainLF[i] = 1.0f;
965 /* Distance-based air absorption */
966 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
968 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
969 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
970 for(i = 0;i < NumSends;i++)
971 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
974 if(WetGainAuto)
976 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
978 /* Apply a decay-time transformation to the wet path, based on the
979 * attenuation of the dry path.
981 * Using the apparent distance, based on the distance attenuation, the
982 * initial decay of the reverb effect is calculated and applied to the
983 * wet path.
985 for(i = 0;i < NumSends;i++)
987 if(DecayDistance[i] > 0.0f)
988 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
992 /* Calculate directional soundcones */
993 if(InnerAngle < 360.0f)
995 ALfloat ConeVolume;
996 ALfloat ConeHF;
997 ALfloat Angle;
998 ALfloat scale;
1000 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1001 if(Angle > InnerAngle)
1003 if(Angle < OuterAngle)
1005 scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1006 ConeVolume = lerp(
1007 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1009 ConeHF = lerp(
1010 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1013 else
1015 ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed);
1016 ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed);
1018 DryGain *= ConeVolume;
1019 if(DryGainHFAuto)
1020 DryGainHF *= ConeHF;
1023 /* Wet path uses the total area of the cone emitter (the room will
1024 * receive the same amount of sound regardless of its direction).
1026 scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
1027 (InnerAngle/360.0f);
1028 if(WetGainAuto)
1030 ConeVolume = lerp(
1031 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1033 for(i = 0;i < NumSends;i++)
1034 WetGain[i] *= ConeVolume;
1036 if(WetGainHFAuto)
1038 ConeHF = lerp(
1039 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1041 for(i = 0;i < NumSends;i++)
1042 WetGainHF[i] *= ConeHF;
1046 /* Apply gain and frequency filters */
1047 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1048 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
1049 DryGain = minf(DryGain, GAIN_MIX_MAX);
1050 DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
1051 DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
1052 for(i = 0;i < NumSends;i++)
1054 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1055 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
1056 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1057 WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
1058 WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
1061 /* Calculate velocity-based doppler effect */
1062 if(DopplerFactor > 0.0f)
1064 const aluVector *lvelocity = &Listener->Params.Velocity;
1065 ALfloat VSS, VLS;
1067 if(SpeedOfSound < 1.0f)
1069 DopplerFactor *= 1.0f/SpeedOfSound;
1070 SpeedOfSound = 1.0f;
1073 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1074 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1076 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1077 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1080 /* Calculate fixed-point stepping value, based on the pitch, buffer
1081 * frequency, and output frequency.
1083 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1084 if(Pitch > (ALfloat)MAX_PITCH)
1085 voice->Step = MAX_PITCH<<FRACTIONBITS;
1086 else
1087 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1088 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1090 if(Device->Render_Mode == HrtfRender)
1092 /* Full HRTF rendering. Skip the virtual channels and render to the
1093 * real outputs.
1095 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1096 ALfloat ev = 0.0f, az = 0.0f;
1097 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1098 ALfloat coeffs[MAX_AMBI_COEFFS];
1099 ALfloat spread = 0.0f;
1101 voice->Direct.Buffer = Device->RealOut.Buffer;
1102 voice->Direct.Channels = Device->RealOut.NumChannels;
1104 if(Distance > FLT_EPSILON)
1106 dir[0] = -SourceToListener.v[0];
1107 dir[1] = -SourceToListener.v[1];
1108 dir[2] = -SourceToListener.v[2] * ZScale;
1110 /* Calculate elevation and azimuth only when the source is not at
1111 * the listener. This prevents +0 and -0 Z from producing
1112 * inconsistent panning. Also, clamp Y in case FP precision errors
1113 * cause it to land outside of -1..+1. */
1114 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1115 az = atan2f(dir[0], -dir[2]);
1117 if(radius > Distance)
1118 spread = F_TAU - Distance/radius*F_PI;
1119 else if(Distance > FLT_EPSILON)
1120 spread = asinf(radius / Distance) * 2.0f;
1122 /* Get the HRIR coefficients and delays. */
1123 GetHrtfCoeffs(Device->Hrtf.Handle, ev, az, spread, DryGain,
1124 voice->Direct.Params[0].Hrtf.Target.Coeffs,
1125 voice->Direct.Params[0].Hrtf.Target.Delay);
1127 CalcDirectionCoeffs(dir, spread, coeffs);
1129 for(i = 0;i < NumSends;i++)
1131 const ALeffectslot *Slot = SendSlots[i];
1132 if(Slot)
1133 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1134 coeffs, WetGain[i], voice->Send[i].Params[0].Gains.Target
1136 else
1137 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1138 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
1141 voice->IsHrtf = AL_TRUE;
1143 else
1145 /* Non-HRTF rendering. */
1146 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1147 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1148 ALfloat coeffs[MAX_AMBI_COEFFS];
1149 ALfloat spread = 0.0f;
1151 /* Get the localized direction, and compute panned gains. */
1152 if(Distance > FLT_EPSILON)
1154 dir[0] = -SourceToListener.v[0];
1155 dir[1] = -SourceToListener.v[1];
1156 dir[2] = -SourceToListener.v[2] * ZScale;
1158 if(radius > Distance)
1159 spread = F_TAU - Distance/radius*F_PI;
1160 else if(Distance > FLT_EPSILON)
1161 spread = asinf(radius / Distance) * 2.0f;
1163 if(Device->Render_Mode == StereoPair)
1165 ALfloat ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1166 ALfloat az = atan2f(dir[0], -dir[2]);
1167 CalcAnglePairwiseCoeffs(az, ev, radius, coeffs);
1169 else
1170 CalcDirectionCoeffs(dir, spread, coeffs);
1171 ComputePanningGains(Device->Dry,
1172 coeffs, DryGain, voice->Direct.Params[0].Gains.Target
1175 for(i = 0;i < NumSends;i++)
1177 const ALeffectslot *Slot = SendSlots[i];
1178 if(Slot)
1179 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1180 coeffs, WetGain[i], voice->Send[i].Params[0].Gains.Target
1182 else
1183 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1184 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
1187 voice->IsHrtf = AL_FALSE;
1191 HFScale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) / Frequency;
1192 LFScale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) / Frequency;
1193 DryGainHF = maxf(DryGainHF, 0.0625f); /* Limit -24dB */
1194 DryGainLF = maxf(DryGainLF, 0.0625f);
1195 voice->Direct.Params[0].FilterType = AF_None;
1196 if(DryGainHF != 1.0f) voice->Direct.Params[0].FilterType |= AF_LowPass;
1197 if(DryGainLF != 1.0f) voice->Direct.Params[0].FilterType |= AF_HighPass;
1198 ALfilterState_setParams(
1199 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
1200 DryGainHF, HFScale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
1202 ALfilterState_setParams(
1203 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
1204 DryGainLF, LFScale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
1207 for(i = 0;i < NumSends;i++)
1209 HFScale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) / Frequency;
1210 LFScale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) / Frequency;
1211 WetGainHF[i] = maxf(WetGainHF[i], 0.0625f);
1212 WetGainLF[i] = maxf(WetGainLF[i], 0.0625f);
1213 voice->Send[i].Params[0].FilterType = AF_None;
1214 if(WetGainHF[i] != 1.0f) voice->Send[i].Params[0].FilterType |= AF_LowPass;
1215 if(WetGainLF[i] != 1.0f) voice->Send[i].Params[0].FilterType |= AF_HighPass;
1216 ALfilterState_setParams(
1217 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
1218 WetGainHF[i], HFScale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
1220 ALfilterState_setParams(
1221 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
1222 WetGainLF[i], LFScale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
1227 static void CalcSourceParams(ALvoice *voice, ALsource *source, ALCcontext *context, ALboolean force)
1229 const ALbufferlistitem *BufferListItem;
1230 struct ALsourceProps *props;
1232 props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
1233 if(!props && !force) return;
1235 if(props)
1237 memcpy(voice->Props, props,
1238 offsetof(struct ALsourceProps, Send[context->Device->NumAuxSends])
1241 ATOMIC_REPLACE_HEAD(struct ALsourceProps*, &source->FreeList, props);
1244 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1245 while(BufferListItem != NULL)
1247 const ALbuffer *buffer;
1248 if((buffer=BufferListItem->buffer) != NULL)
1250 if(buffer->FmtChannels == FmtMono)
1251 CalcAttnSourceParams(voice, voice->Props, buffer, context);
1252 else
1253 CalcNonAttnSourceParams(voice, voice->Props, buffer, context);
1254 break;
1256 BufferListItem = BufferListItem->next;
1261 static void UpdateContextSources(ALCcontext *ctx, ALeffectslot *slot)
1263 ALvoice **voice, **voice_end;
1264 ALsource *source;
1266 IncrementRef(&ctx->UpdateCount);
1267 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1269 ALboolean force = CalcListenerParams(ctx);
1270 while(slot)
1272 force |= CalcEffectSlotParams(slot, ctx->Device);
1273 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1276 voice = ctx->Voices;
1277 voice_end = voice + ctx->VoiceCount;
1278 for(;voice != voice_end;++voice)
1280 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1281 if(source) CalcSourceParams(*voice, source, ctx, force);
1284 IncrementRef(&ctx->UpdateCount);
1288 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1289 * converts NaN to 0. */
1290 static inline ALfloat aluClampf(ALfloat val)
1292 if(fabsf(val) <= 1.0f) return val;
1293 return (ALfloat)((0.0f < val) - (val < 0.0f));
1296 static inline ALfloat aluF2F(ALfloat val)
1297 { return val; }
1299 static inline ALint aluF2I(ALfloat val)
1301 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1302 * integer range normalized floats can be safely converted to.
1304 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1306 static inline ALuint aluF2UI(ALfloat val)
1307 { return aluF2I(val)+2147483648u; }
1309 static inline ALshort aluF2S(ALfloat val)
1310 { return fastf2i(aluClampf(val)*32767.0f); }
1311 static inline ALushort aluF2US(ALfloat val)
1312 { return aluF2S(val)+32768; }
1314 static inline ALbyte aluF2B(ALfloat val)
1315 { return fastf2i(aluClampf(val)*127.0f); }
1316 static inline ALubyte aluF2UB(ALfloat val)
1317 { return aluF2B(val)+128; }
1319 #define DECL_TEMPLATE(T, func) \
1320 static void Write_##T(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1321 DistanceComp *distcomp, ALsizei SamplesToDo, \
1322 ALsizei numchans) \
1324 ALsizei i, j; \
1325 for(j = 0;j < numchans;j++) \
1327 const ALfloat *in = InBuffer[j]; \
1328 T *restrict out = (T*)OutBuffer + j; \
1329 const ALfloat gain = distcomp[j].Gain; \
1330 const ALsizei base = distcomp[j].Length; \
1331 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[j].Buffer, 16); \
1332 if(base > 0 || gain != 1.0f) \
1334 if(SamplesToDo >= base) \
1336 for(i = 0;i < base;i++) \
1337 out[i*numchans] = func(distbuf[i]*gain); \
1338 for(;i < SamplesToDo;i++) \
1339 out[i*numchans] = func(in[i-base]*gain); \
1340 memcpy(distbuf, &in[SamplesToDo-base], base*sizeof(ALfloat)); \
1342 else \
1344 for(i = 0;i < SamplesToDo;i++) \
1345 out[i*numchans] = func(distbuf[i]*gain); \
1346 memmove(distbuf, distbuf+SamplesToDo, \
1347 (base-SamplesToDo)*sizeof(ALfloat)); \
1348 memcpy(distbuf+base-SamplesToDo, in, \
1349 SamplesToDo*sizeof(ALfloat)); \
1352 else for(i = 0;i < SamplesToDo;i++) \
1353 out[i*numchans] = func(in[i]); \
1357 DECL_TEMPLATE(ALfloat, aluF2F)
1358 DECL_TEMPLATE(ALuint, aluF2UI)
1359 DECL_TEMPLATE(ALint, aluF2I)
1360 DECL_TEMPLATE(ALushort, aluF2US)
1361 DECL_TEMPLATE(ALshort, aluF2S)
1362 DECL_TEMPLATE(ALubyte, aluF2UB)
1363 DECL_TEMPLATE(ALbyte, aluF2B)
1365 #undef DECL_TEMPLATE
1368 void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1370 ALsizei SamplesToDo;
1371 ALvoice **voice, **voice_end;
1372 ALeffectslot *slot;
1373 ALsource *source;
1374 ALCcontext *ctx;
1375 FPUCtl oldMode;
1376 ALsizei i, c;
1378 SetMixerFPUMode(&oldMode);
1380 while(size > 0)
1382 SamplesToDo = mini(size, BUFFERSIZE);
1383 for(c = 0;c < device->Dry.NumChannels;c++)
1384 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1385 if(device->Dry.Buffer != device->FOAOut.Buffer)
1386 for(c = 0;c < device->FOAOut.NumChannels;c++)
1387 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1388 if(device->Dry.Buffer != device->RealOut.Buffer)
1389 for(c = 0;c < device->RealOut.NumChannels;c++)
1390 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1392 IncrementRef(&device->MixCount);
1394 if((slot=device->DefaultSlot) != NULL)
1396 CalcEffectSlotParams(device->DefaultSlot, device);
1397 for(i = 0;i < slot->NumChannels;i++)
1398 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1401 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1402 while(ctx)
1404 ALeffectslot *slotroot;
1406 slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList, almemory_order_acquire);
1407 UpdateContextSources(ctx, slotroot);
1409 slot = slotroot;
1410 while(slot)
1412 for(i = 0;i < slot->NumChannels;i++)
1413 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1414 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1417 /* source processing */
1418 voice = ctx->Voices;
1419 voice_end = voice + ctx->VoiceCount;
1420 for(;voice != voice_end;++voice)
1422 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1423 if(source && ATOMIC_LOAD(&(*voice)->Playing, almemory_order_relaxed) &&
1424 (*voice)->Step > 0)
1426 if(!MixSource(*voice, source, device, SamplesToDo))
1428 ATOMIC_STORE(&(*voice)->Source, NULL, almemory_order_relaxed);
1429 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1434 /* effect slot processing */
1435 slot = slotroot;
1436 while(slot)
1438 ALeffectState *state = slot->Params.EffectState;
1439 V(state,process)(SamplesToDo, SAFE_CONST(ALfloatBUFFERSIZE*,slot->WetBuffer),
1440 state->OutBuffer, state->OutChannels);
1441 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1444 ctx = ctx->next;
1447 if(device->DefaultSlot != NULL)
1449 const ALeffectslot *slot = device->DefaultSlot;
1450 ALeffectState *state = slot->Params.EffectState;
1451 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1452 state->OutChannels);
1455 /* Increment the clock time. Every second's worth of samples is
1456 * converted and added to clock base so that large sample counts don't
1457 * overflow during conversion. This also guarantees an exact, stable
1458 * conversion. */
1459 device->SamplesDone += SamplesToDo;
1460 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1461 device->SamplesDone %= device->Frequency;
1462 IncrementRef(&device->MixCount);
1464 if(device->Hrtf.Handle)
1466 HrtfDirectMixerFunc HrtfMix;
1467 ALsizei irsize;
1468 int lidx, ridx;
1470 if(device->AmbiUp)
1471 ambiup_process(device->AmbiUp,
1472 device->Dry.Buffer, device->Dry.NumChannels,
1473 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1476 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1477 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1478 assert(lidx != -1 && ridx != -1);
1480 HrtfMix = SelectHrtfMixer();
1481 irsize = device->Hrtf.IrSize;
1482 for(c = 0;c < device->Dry.NumChannels;c++)
1484 HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1485 device->Dry.Buffer[c], device->Hrtf.Offset, irsize,
1486 device->Hrtf.Coeffs[c], device->Hrtf.Values[c],
1487 SamplesToDo
1490 device->Hrtf.Offset += SamplesToDo;
1492 else if(device->AmbiDecoder)
1494 if(device->Dry.Buffer != device->FOAOut.Buffer)
1495 bformatdec_upSample(device->AmbiDecoder,
1496 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1497 device->FOAOut.NumChannels, SamplesToDo
1499 bformatdec_process(device->AmbiDecoder,
1500 device->RealOut.Buffer, device->RealOut.NumChannels,
1501 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1504 else if(device->AmbiUp)
1506 ambiup_process(device->AmbiUp,
1507 device->RealOut.Buffer, device->RealOut.NumChannels,
1508 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1511 else if(device->Uhj_Encoder)
1513 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1514 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1515 if(lidx != -1 && ridx != -1)
1517 /* Encode to stereo-compatible 2-channel UHJ output. */
1518 EncodeUhj2(device->Uhj_Encoder,
1519 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1520 device->Dry.Buffer, SamplesToDo
1524 else if(device->Bs2b)
1526 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1527 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1528 if(lidx != -1 && ridx != -1)
1530 /* Apply binaural/crossfeed filter */
1531 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1532 device->RealOut.Buffer[ridx], SamplesToDo);
1536 if(buffer)
1538 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1539 ALsizei OutChannels = device->RealOut.NumChannels;
1540 DistanceComp *DistComp = device->ChannelDelay;
1542 #define WRITE(T, a, b, c, d, e) do { \
1543 Write_##T(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \
1544 buffer = (T*)buffer + (d)*(e); \
1545 } while(0)
1546 switch(device->FmtType)
1548 case DevFmtByte:
1549 WRITE(ALbyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1550 break;
1551 case DevFmtUByte:
1552 WRITE(ALubyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1553 break;
1554 case DevFmtShort:
1555 WRITE(ALshort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1556 break;
1557 case DevFmtUShort:
1558 WRITE(ALushort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1559 break;
1560 case DevFmtInt:
1561 WRITE(ALint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1562 break;
1563 case DevFmtUInt:
1564 WRITE(ALuint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1565 break;
1566 case DevFmtFloat:
1567 WRITE(ALfloat, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1568 break;
1570 #undef WRITE
1573 size -= SamplesToDo;
1576 RestoreFPUMode(&oldMode);
1580 void aluHandleDisconnect(ALCdevice *device)
1582 ALCcontext *Context;
1584 device->Connected = ALC_FALSE;
1586 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1587 while(Context)
1589 ALvoice **voice, **voice_end;
1591 voice = Context->Voices;
1592 voice_end = voice + Context->VoiceCount;
1593 while(voice != voice_end)
1595 ALsource *source = ATOMIC_EXCHANGE(ALsource*, &(*voice)->Source, NULL,
1596 almemory_order_acq_rel);
1597 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1599 if(source)
1601 ALenum playing = AL_PLAYING;
1602 ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(ALenum, &source->state, &playing, AL_STOPPED);
1605 voice++;
1607 Context->VoiceCount = 0;
1609 Context = Context->next;