Make the voices' Send[] array dynamically sized
[openal-soft.git] / Alc / ALu.c
blobedb231286aa544b6dcc7c65e17d6ae2dd96168d4
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 ALuint NumSends, Frequency;
378 ALboolean Relative;
379 const struct ChanMap *chans = NULL;
380 struct ChanMap StereoMap[2] = {
381 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
382 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
384 ALuint num_channels = 0;
385 ALboolean DirectChannels;
386 ALboolean isbformat = AL_FALSE;
387 ALfloat Pitch;
388 ALuint i, j, c;
390 /* Get device properties */
391 NumSends = Device->NumAuxSends;
392 Frequency = Device->Frequency;
394 /* Get listener properties */
395 ListenerGain = Listener->Params.Gain;
397 /* Get source properties */
398 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
399 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
400 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
401 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
402 Relative = ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed);
403 DirectChannels = ATOMIC_LOAD(&props->DirectChannels, almemory_order_relaxed);
405 /* Convert counter-clockwise to clockwise. */
406 StereoMap[0].angle = -ATOMIC_LOAD(&props->StereoPan[0], almemory_order_relaxed);
407 StereoMap[1].angle = -ATOMIC_LOAD(&props->StereoPan[1], almemory_order_relaxed);
409 voice->Direct.Buffer = Device->Dry.Buffer;
410 voice->Direct.Channels = Device->Dry.NumChannels;
411 for(i = 0;i < NumSends;i++)
413 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
414 if(!SendSlots[i] && i == 0)
415 SendSlots[i] = Device->DefaultSlot;
416 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
418 SendSlots[i] = NULL;
419 voice->Send[i].Buffer = NULL;
420 voice->Send[i].Channels = 0;
422 else
424 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
425 voice->Send[i].Channels = SendSlots[i]->NumChannels;
429 /* Calculate the stepping value */
430 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
431 if(Pitch > (ALfloat)MAX_PITCH)
432 voice->Step = MAX_PITCH<<FRACTIONBITS;
433 else
434 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
435 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
437 /* Calculate gains */
438 DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
439 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
440 DryGain = minf(DryGain, GAIN_MIX_MAX);
441 DryGainHF = ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
442 DryGainLF = ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
443 for(i = 0;i < NumSends;i++)
445 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
446 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
447 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
448 WetGainHF[i] = ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
449 WetGainLF[i] = ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
452 switch(ALBuffer->FmtChannels)
454 case FmtMono:
455 chans = MonoMap;
456 num_channels = 1;
457 break;
459 case FmtStereo:
460 chans = StereoMap;
461 num_channels = 2;
462 break;
464 case FmtRear:
465 chans = RearMap;
466 num_channels = 2;
467 break;
469 case FmtQuad:
470 chans = QuadMap;
471 num_channels = 4;
472 break;
474 case FmtX51:
475 chans = X51Map;
476 num_channels = 6;
477 break;
479 case FmtX61:
480 chans = X61Map;
481 num_channels = 7;
482 break;
484 case FmtX71:
485 chans = X71Map;
486 num_channels = 8;
487 break;
489 case FmtBFormat2D:
490 num_channels = 3;
491 isbformat = AL_TRUE;
492 DirectChannels = AL_FALSE;
493 break;
495 case FmtBFormat3D:
496 num_channels = 4;
497 isbformat = AL_TRUE;
498 DirectChannels = AL_FALSE;
499 break;
502 if(isbformat)
504 ALfloat N[3], V[3], U[3];
505 aluMatrixf matrix;
506 ALfloat scale;
508 /* AT then UP */
509 N[0] = ATOMIC_LOAD(&props->Orientation[0][0], almemory_order_relaxed);
510 N[1] = ATOMIC_LOAD(&props->Orientation[0][1], almemory_order_relaxed);
511 N[2] = ATOMIC_LOAD(&props->Orientation[0][2], almemory_order_relaxed);
512 aluNormalize(N);
513 V[0] = ATOMIC_LOAD(&props->Orientation[1][0], almemory_order_relaxed);
514 V[1] = ATOMIC_LOAD(&props->Orientation[1][1], almemory_order_relaxed);
515 V[2] = ATOMIC_LOAD(&props->Orientation[1][2], almemory_order_relaxed);
516 aluNormalize(V);
517 if(!Relative)
519 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
520 aluMatrixfFloat3(N, 0.0f, lmatrix);
521 aluMatrixfFloat3(V, 0.0f, lmatrix);
523 /* Build and normalize right-vector */
524 aluCrossproduct(N, V, U);
525 aluNormalize(U);
527 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
528 scale = 1.732050808f;
529 aluMatrixfSet(&matrix,
530 1.414213562f, 0.0f, 0.0f, 0.0f,
531 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
532 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
533 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
536 voice->Direct.Buffer = Device->FOAOut.Buffer;
537 voice->Direct.Channels = Device->FOAOut.NumChannels;
538 for(c = 0;c < num_channels;c++)
539 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
540 voice->Direct.Params[c].Gains.Target);
542 for(i = 0;i < NumSends;i++)
544 if(!SendSlots[i])
546 for(c = 0;c < num_channels;c++)
548 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
549 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
552 else
554 for(c = 0;c < num_channels;c++)
556 const ALeffectslot *Slot = SendSlots[i];
557 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
558 matrix.m[c], WetGain[i], voice->Send[i].Params[c].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->Direct.Buffer = Device->RealOut.Buffer;
574 voice->Direct.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->Direct.Params[c].Gains.Target[j] = 0.0f;
580 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
581 voice->Direct.Params[c].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->Send[i].Params[c].Gains.Target[j] = 0.0f;
597 else
599 const ALeffectslot *Slot = SendSlots[i];
600 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
601 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
607 voice->IsHrtf = AL_FALSE;
609 else if(Device->Render_Mode == HrtfRender)
611 /* Full HRTF rendering. Skip the virtual channels and render each
612 * input channel to the real outputs.
614 voice->Direct.Buffer = Device->RealOut.Buffer;
615 voice->Direct.Channels = Device->RealOut.NumChannels;
616 for(c = 0;c < num_channels;c++)
618 if(chans[c].channel == LFE)
620 /* Skip LFE */
621 voice->Direct.Params[c].Hrtf.Target.Delay[0] = 0;
622 voice->Direct.Params[c].Hrtf.Target.Delay[1] = 0;
623 for(i = 0;i < HRIR_LENGTH;i++)
625 voice->Direct.Params[c].Hrtf.Target.Coeffs[i][0] = 0.0f;
626 voice->Direct.Params[c].Hrtf.Target.Coeffs[i][1] = 0.0f;
629 for(i = 0;i < NumSends;i++)
631 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
632 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
635 continue;
638 /* Get the static HRIR coefficients and delays for this channel. */
639 GetHrtfCoeffs(Device->Hrtf.Handle,
640 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
641 voice->Direct.Params[c].Hrtf.Target.Coeffs,
642 voice->Direct.Params[c].Hrtf.Target.Delay
645 /* Normal panning for auxiliary sends. */
646 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
648 for(i = 0;i < NumSends;i++)
650 if(!SendSlots[i])
652 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
653 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
655 else
657 const ALeffectslot *Slot = SendSlots[i];
658 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
659 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
665 voice->IsHrtf = AL_TRUE;
667 else
669 /* Non-HRTF rendering. Use normal panning to the output. */
670 for(c = 0;c < num_channels;c++)
672 /* Special-case LFE */
673 if(chans[c].channel == LFE)
675 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
676 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
677 if(Device->Dry.Buffer == Device->RealOut.Buffer)
679 int idx;
680 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
681 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
684 for(i = 0;i < NumSends;i++)
686 ALuint j;
687 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
688 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
690 continue;
693 if(Device->Render_Mode == StereoPair)
695 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
696 ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
697 coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
698 voice->Direct.Params[c].Gains.Target[0] = coeffs[0] * DryGain;
699 voice->Direct.Params[c].Gains.Target[1] = (1.0f-coeffs[0]) * DryGain;
700 for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
701 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
703 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
705 else
707 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
708 ComputePanningGains(Device->Dry, coeffs, DryGain,
709 voice->Direct.Params[c].Gains.Target);
712 for(i = 0;i < NumSends;i++)
714 if(!SendSlots[i])
716 ALuint j;
717 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
718 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
720 else
722 const ALeffectslot *Slot = SendSlots[i];
723 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
724 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
730 voice->IsHrtf = AL_FALSE;
735 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
736 Frequency;
737 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
738 Frequency;
739 DryGainHF = maxf(DryGainHF, 0.0001f);
740 DryGainLF = maxf(DryGainLF, 0.0001f);
741 for(c = 0;c < num_channels;c++)
743 voice->Direct.Params[c].FilterType = AF_None;
744 if(DryGainHF != 1.0f) voice->Direct.Params[c].FilterType |= AF_LowPass;
745 if(DryGainLF != 1.0f) voice->Direct.Params[c].FilterType |= AF_HighPass;
746 ALfilterState_setParams(
747 &voice->Direct.Params[c].LowPass, ALfilterType_HighShelf,
748 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
750 ALfilterState_setParams(
751 &voice->Direct.Params[c].HighPass, ALfilterType_LowShelf,
752 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
756 for(i = 0;i < NumSends;i++)
758 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
759 Frequency;
760 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
761 Frequency;
762 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
763 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
764 for(c = 0;c < num_channels;c++)
766 voice->Send[i].Params[c].FilterType = AF_None;
767 if(WetGainHF[i] != 1.0f) voice->Send[i].Params[c].FilterType |= AF_LowPass;
768 if(WetGainLF[i] != 1.0f) voice->Send[i].Params[c].FilterType |= AF_HighPass;
769 ALfilterState_setParams(
770 &voice->Send[i].Params[c].LowPass, ALfilterType_HighShelf,
771 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
773 ALfilterState_setParams(
774 &voice->Send[i].Params[c].HighPass, ALfilterType_LowShelf,
775 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
781 static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
783 const ALCdevice *Device = ALContext->Device;
784 const ALlistener *Listener = ALContext->Listener;
785 aluVector Position, Velocity, Direction, SourceToListener;
786 ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
787 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
788 ALfloat SourceVolume,ListenerGain;
789 ALfloat DopplerFactor, SpeedOfSound;
790 ALfloat AirAbsorptionFactor;
791 ALfloat RoomAirAbsorption[MAX_SENDS];
792 ALeffectslot *SendSlots[MAX_SENDS];
793 ALfloat Attenuation;
794 ALfloat RoomAttenuation[MAX_SENDS];
795 ALfloat MetersPerUnit;
796 ALfloat RoomRolloffBase;
797 ALfloat RoomRolloff[MAX_SENDS];
798 ALfloat DecayDistance[MAX_SENDS];
799 ALfloat DryGain;
800 ALfloat DryGainHF;
801 ALfloat DryGainLF;
802 ALboolean DryGainHFAuto;
803 ALfloat WetGain[MAX_SENDS];
804 ALfloat WetGainHF[MAX_SENDS];
805 ALfloat WetGainLF[MAX_SENDS];
806 ALboolean WetGainAuto;
807 ALboolean WetGainHFAuto;
808 ALfloat Pitch;
809 ALuint Frequency;
810 ALint NumSends;
811 ALint i;
813 /* Get context/device properties */
814 DopplerFactor = Listener->Params.DopplerFactor;
815 SpeedOfSound = Listener->Params.SpeedOfSound;
816 NumSends = Device->NumAuxSends;
817 Frequency = Device->Frequency;
819 /* Get listener properties */
820 ListenerGain = Listener->Params.Gain;
821 MetersPerUnit = Listener->Params.MetersPerUnit;
823 /* Get source properties */
824 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
825 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
826 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
827 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
828 aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed),
829 ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed),
830 ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed),
831 1.0f);
832 aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed),
833 ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed),
834 ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed),
835 0.0f);
836 aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
837 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
838 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
839 0.0f);
840 MinDist = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed);
841 MaxDist = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed);
842 Rolloff = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed);
843 DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
844 InnerAngle = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed);
845 OuterAngle = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed);
846 AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed);
847 DryGainHFAuto = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed);
848 WetGainAuto = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed);
849 WetGainHFAuto = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed);
850 RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed);
852 voice->Direct.Buffer = Device->Dry.Buffer;
853 voice->Direct.Channels = Device->Dry.NumChannels;
854 for(i = 0;i < NumSends;i++)
856 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
858 if(!SendSlots[i] && i == 0)
859 SendSlots[i] = Device->DefaultSlot;
860 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
862 SendSlots[i] = NULL;
863 RoomRolloff[i] = 0.0f;
864 DecayDistance[i] = 0.0f;
865 RoomAirAbsorption[i] = 1.0f;
867 else if(SendSlots[i]->Params.AuxSendAuto)
869 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
870 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
871 SPEEDOFSOUNDMETRESPERSEC;
872 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
874 else
876 /* If the slot's auxiliary send auto is off, the data sent to the
877 * effect slot is the same as the dry path, sans filter effects */
878 RoomRolloff[i] = Rolloff;
879 DecayDistance[i] = 0.0f;
880 RoomAirAbsorption[i] = AIRABSORBGAINHF;
883 if(!SendSlots[i])
885 voice->Send[i].Buffer = NULL;
886 voice->Send[i].Channels = 0;
888 else
890 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
891 voice->Send[i].Channels = SendSlots[i]->NumChannels;
895 /* Transform source to listener space (convert to head relative) */
896 if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE)
898 const aluMatrixf *Matrix = &Listener->Params.Matrix;
899 /* Transform source vectors */
900 Position = aluMatrixfVector(Matrix, &Position);
901 Velocity = aluMatrixfVector(Matrix, &Velocity);
902 Direction = aluMatrixfVector(Matrix, &Direction);
904 else
906 const aluVector *lvelocity = &Listener->Params.Velocity;
907 /* Offset the source velocity to be relative of the listener velocity */
908 Velocity.v[0] += lvelocity->v[0];
909 Velocity.v[1] += lvelocity->v[1];
910 Velocity.v[2] += lvelocity->v[2];
913 aluNormalize(Direction.v);
914 SourceToListener.v[0] = -Position.v[0];
915 SourceToListener.v[1] = -Position.v[1];
916 SourceToListener.v[2] = -Position.v[2];
917 SourceToListener.v[3] = 0.0f;
918 Distance = aluNormalize(SourceToListener.v);
920 /* Calculate distance attenuation */
921 ClampedDist = Distance;
923 Attenuation = 1.0f;
924 for(i = 0;i < NumSends;i++)
925 RoomAttenuation[i] = 1.0f;
926 switch(Listener->Params.SourceDistanceModel ?
927 ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed) :
928 Listener->Params.DistanceModel)
930 case InverseDistanceClamped:
931 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
932 if(MaxDist < MinDist)
933 break;
934 /*fall-through*/
935 case InverseDistance:
936 if(MinDist > 0.0f)
938 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
939 if(dist > 0.0f) Attenuation = MinDist / dist;
940 for(i = 0;i < NumSends;i++)
942 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
943 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
946 break;
948 case LinearDistanceClamped:
949 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
950 if(MaxDist < MinDist)
951 break;
952 /*fall-through*/
953 case LinearDistance:
954 if(MaxDist != MinDist)
956 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
957 Attenuation = maxf(Attenuation, 0.0f);
958 for(i = 0;i < NumSends;i++)
960 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
961 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
964 break;
966 case ExponentDistanceClamped:
967 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
968 if(MaxDist < MinDist)
969 break;
970 /*fall-through*/
971 case ExponentDistance:
972 if(ClampedDist > 0.0f && MinDist > 0.0f)
974 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
975 for(i = 0;i < NumSends;i++)
976 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
978 break;
980 case DisableDistance:
981 ClampedDist = MinDist;
982 break;
985 /* Source Gain + Attenuation */
986 DryGain = SourceVolume * Attenuation;
987 DryGainHF = 1.0f;
988 DryGainLF = 1.0f;
989 for(i = 0;i < NumSends;i++)
991 WetGain[i] = SourceVolume * RoomAttenuation[i];
992 WetGainHF[i] = 1.0f;
993 WetGainLF[i] = 1.0f;
996 /* Distance-based air absorption */
997 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
999 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
1000 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
1001 for(i = 0;i < NumSends;i++)
1002 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
1005 if(WetGainAuto)
1007 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1009 /* Apply a decay-time transformation to the wet path, based on the
1010 * attenuation of the dry path.
1012 * Using the apparent distance, based on the distance attenuation, the
1013 * initial decay of the reverb effect is calculated and applied to the
1014 * wet path.
1016 for(i = 0;i < NumSends;i++)
1018 if(DecayDistance[i] > 0.0f)
1019 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1023 /* Calculate directional soundcones */
1024 if(InnerAngle < 360.0f)
1026 ALfloat ConeVolume;
1027 ALfloat ConeHF;
1028 ALfloat Angle;
1029 ALfloat scale;
1031 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1032 if(Angle > InnerAngle)
1034 if(Angle < OuterAngle)
1036 scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1037 ConeVolume = lerp(
1038 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1040 ConeHF = lerp(
1041 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1044 else
1046 ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed);
1047 ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed);
1049 DryGain *= ConeVolume;
1050 if(DryGainHFAuto)
1051 DryGainHF *= ConeHF;
1054 /* Wet path uses the total area of the cone emitter (the room will
1055 * receive the same amount of sound regardless of its direction).
1057 scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
1058 (InnerAngle/360.0f);
1059 if(WetGainAuto)
1061 ConeVolume = lerp(
1062 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1064 for(i = 0;i < NumSends;i++)
1065 WetGain[i] *= ConeVolume;
1067 if(WetGainHFAuto)
1069 ConeHF = lerp(
1070 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1072 for(i = 0;i < NumSends;i++)
1073 WetGainHF[i] *= ConeHF;
1077 /* Apply gain and frequency filters */
1078 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1079 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
1080 DryGain = minf(DryGain, GAIN_MIX_MAX);
1081 DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
1082 DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
1083 for(i = 0;i < NumSends;i++)
1085 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1086 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
1087 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1088 WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
1089 WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
1092 /* Calculate velocity-based doppler effect */
1093 if(DopplerFactor > 0.0f)
1095 const aluVector *lvelocity = &Listener->Params.Velocity;
1096 ALfloat VSS, VLS;
1098 if(SpeedOfSound < 1.0f)
1100 DopplerFactor *= 1.0f/SpeedOfSound;
1101 SpeedOfSound = 1.0f;
1104 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1105 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1107 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1108 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1111 /* Calculate fixed-point stepping value, based on the pitch, buffer
1112 * frequency, and output frequency.
1114 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1115 if(Pitch > (ALfloat)MAX_PITCH)
1116 voice->Step = MAX_PITCH<<FRACTIONBITS;
1117 else
1118 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1119 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1121 if(Device->Render_Mode == HrtfRender)
1123 /* Full HRTF rendering. Skip the virtual channels and render to the
1124 * real outputs.
1126 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1127 ALfloat ev = 0.0f, az = 0.0f;
1128 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1129 ALfloat coeffs[MAX_AMBI_COEFFS];
1130 ALfloat spread = 0.0f;
1132 voice->Direct.Buffer = Device->RealOut.Buffer;
1133 voice->Direct.Channels = Device->RealOut.NumChannels;
1135 if(Distance > FLT_EPSILON)
1137 dir[0] = -SourceToListener.v[0];
1138 dir[1] = -SourceToListener.v[1];
1139 dir[2] = -SourceToListener.v[2] * ZScale;
1141 /* Calculate elevation and azimuth only when the source is not at
1142 * the listener. This prevents +0 and -0 Z from producing
1143 * inconsistent panning. Also, clamp Y in case FP precision errors
1144 * cause it to land outside of -1..+1. */
1145 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1146 az = atan2f(dir[0], -dir[2]);
1148 if(radius > Distance)
1149 spread = F_TAU - Distance/radius*F_PI;
1150 else if(Distance > FLT_EPSILON)
1151 spread = asinf(radius / Distance) * 2.0f;
1153 /* Get the HRIR coefficients and delays. */
1154 GetHrtfCoeffs(Device->Hrtf.Handle, ev, az, spread, DryGain,
1155 voice->Direct.Params[0].Hrtf.Target.Coeffs,
1156 voice->Direct.Params[0].Hrtf.Target.Delay);
1158 CalcDirectionCoeffs(dir, spread, coeffs);
1160 for(i = 0;i < NumSends;i++)
1162 if(!SendSlots[i])
1164 ALuint j;
1165 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1166 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
1168 else
1170 const ALeffectslot *Slot = SendSlots[i];
1171 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1172 coeffs, WetGain[i], voice->Send[i].Params[0].Gains.Target
1177 voice->IsHrtf = AL_TRUE;
1179 else
1181 /* Non-HRTF rendering. */
1182 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1183 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1184 ALfloat coeffs[MAX_AMBI_COEFFS];
1185 ALfloat spread = 0.0f;
1187 /* Get the localized direction, and compute panned gains. */
1188 if(Distance > FLT_EPSILON)
1190 dir[0] = -SourceToListener.v[0];
1191 dir[1] = -SourceToListener.v[1];
1192 dir[2] = -SourceToListener.v[2] * ZScale;
1194 if(radius > Distance)
1195 spread = F_TAU - Distance/radius*F_PI;
1196 else if(Distance > FLT_EPSILON)
1197 spread = asinf(radius / Distance) * 2.0f;
1199 if(Device->Render_Mode == StereoPair)
1201 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
1202 ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
1203 x = clampf(x, -0.5f, 0.5f) + 0.5f;
1204 voice->Direct.Params[0].Gains.Target[0] = x * DryGain;
1205 voice->Direct.Params[0].Gains.Target[1] = (1.0f-x) * DryGain;
1206 for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
1207 voice->Direct.Params[0].Gains.Target[i] = 0.0f;
1209 CalcDirectionCoeffs(dir, spread, coeffs);
1211 else
1213 CalcDirectionCoeffs(dir, spread, coeffs);
1214 ComputePanningGains(Device->Dry, coeffs, DryGain,
1215 voice->Direct.Params[0].Gains.Target);
1218 for(i = 0;i < NumSends;i++)
1220 if(!SendSlots[i])
1222 ALuint j;
1223 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1224 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
1226 else
1228 const ALeffectslot *Slot = SendSlots[i];
1229 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1230 coeffs, WetGain[i], voice->Send[i].Params[0].Gains.Target
1235 voice->IsHrtf = AL_FALSE;
1239 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
1240 Frequency;
1241 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
1242 Frequency;
1243 DryGainHF = maxf(DryGainHF, 0.0001f);
1244 DryGainLF = maxf(DryGainLF, 0.0001f);
1245 voice->Direct.Params[0].FilterType = AF_None;
1246 if(DryGainHF != 1.0f) voice->Direct.Params[0].FilterType |= AF_LowPass;
1247 if(DryGainLF != 1.0f) voice->Direct.Params[0].FilterType |= AF_HighPass;
1248 ALfilterState_setParams(
1249 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
1250 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
1252 ALfilterState_setParams(
1253 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
1254 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
1257 for(i = 0;i < NumSends;i++)
1259 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
1260 Frequency;
1261 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
1262 Frequency;
1263 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
1264 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
1265 voice->Send[i].Params[0].FilterType = AF_None;
1266 if(WetGainHF[i] != 1.0f) voice->Send[i].Params[0].FilterType |= AF_LowPass;
1267 if(WetGainLF[i] != 1.0f) voice->Send[i].Params[0].FilterType |= AF_HighPass;
1268 ALfilterState_setParams(
1269 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
1270 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
1272 ALfilterState_setParams(
1273 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
1274 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
1279 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1281 ALsource *source = voice->Source;
1282 const ALbufferlistitem *BufferListItem;
1283 struct ALsourceProps *props;
1285 props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
1286 if(!props && !force) return;
1288 if(props)
1290 memcpy(voice->Props, props,
1291 offsetof(struct ALsourceProps, Send[context->Device->NumAuxSends])
1294 ATOMIC_REPLACE_HEAD(struct ALsourceProps*, &source->FreeList, props);
1297 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1298 while(BufferListItem != NULL)
1300 const ALbuffer *buffer;
1301 if((buffer=BufferListItem->buffer) != NULL)
1303 if(buffer->FmtChannels == FmtMono)
1304 CalcAttnSourceParams(voice, voice->Props, buffer, context);
1305 else
1306 CalcNonAttnSourceParams(voice, voice->Props, buffer, context);
1307 break;
1309 BufferListItem = BufferListItem->next;
1314 static void UpdateContextSources(ALCcontext *ctx, ALeffectslot *slot)
1316 ALvoice **voice, **voice_end;
1317 ALsource *source;
1319 IncrementRef(&ctx->UpdateCount);
1320 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1322 ALboolean force = CalcListenerParams(ctx);
1323 while(slot)
1325 force |= CalcEffectSlotParams(slot, ctx->Device);
1326 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1329 voice = ctx->Voices;
1330 voice_end = voice + ctx->VoiceCount;
1331 for(;voice != voice_end;++voice)
1333 if(!(source=(*voice)->Source)) continue;
1334 if(!IsPlayingOrPaused(source))
1335 (*voice)->Source = NULL;
1336 else
1337 CalcSourceParams(*voice, ctx, force);
1340 IncrementRef(&ctx->UpdateCount);
1344 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1345 * converts NaN to 0. */
1346 static inline ALfloat aluClampf(ALfloat val)
1348 if(fabsf(val) <= 1.0f) return val;
1349 return (ALfloat)((0.0f < val) - (val < 0.0f));
1352 static inline ALfloat aluF2F(ALfloat val)
1353 { return val; }
1355 static inline ALint aluF2I(ALfloat val)
1357 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1358 * integer range normalized floats can be safely converted to.
1360 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1362 static inline ALuint aluF2UI(ALfloat val)
1363 { return aluF2I(val)+2147483648u; }
1365 static inline ALshort aluF2S(ALfloat val)
1366 { return fastf2i(aluClampf(val)*32767.0f); }
1367 static inline ALushort aluF2US(ALfloat val)
1368 { return aluF2S(val)+32768; }
1370 static inline ALbyte aluF2B(ALfloat val)
1371 { return fastf2i(aluClampf(val)*127.0f); }
1372 static inline ALubyte aluF2UB(ALfloat val)
1373 { return aluF2B(val)+128; }
1375 #define DECL_TEMPLATE(T, func) \
1376 static void Write_##T(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1377 DistanceComp *distcomp, ALsizei SamplesToDo, \
1378 ALsizei numchans) \
1380 ALsizei i, j; \
1381 for(j = 0;j < numchans;j++) \
1383 const ALfloat *in = InBuffer[j]; \
1384 T *restrict out = (T*)OutBuffer + j; \
1385 const ALfloat gain = distcomp[j].Gain; \
1386 const ALsizei base = distcomp[j].Length; \
1387 if(base > 0 || gain != 1.0f) \
1389 if(SamplesToDo >= base) \
1391 for(i = 0;i < base;i++) \
1392 out[i*numchans] = func(distcomp[j].Buffer[i]*gain); \
1393 for(;i < SamplesToDo;i++) \
1394 out[i*numchans] = func(in[i-base]*gain); \
1395 memcpy(distcomp[j].Buffer, &in[SamplesToDo-base], \
1396 base*sizeof(ALfloat)); \
1398 else \
1400 for(i = 0;i < SamplesToDo;i++) \
1401 out[i*numchans] = func(distcomp[j].Buffer[i]*gain); \
1402 memmove(distcomp[j].Buffer, distcomp[j].Buffer+SamplesToDo, \
1403 (base-SamplesToDo)*sizeof(ALfloat)); \
1404 memcpy(distcomp[j].Buffer+base-SamplesToDo, in, \
1405 SamplesToDo*sizeof(ALfloat)); \
1408 else for(i = 0;i < SamplesToDo;i++) \
1409 out[i*numchans] = func(in[i]); \
1413 DECL_TEMPLATE(ALfloat, aluF2F)
1414 DECL_TEMPLATE(ALuint, aluF2UI)
1415 DECL_TEMPLATE(ALint, aluF2I)
1416 DECL_TEMPLATE(ALushort, aluF2US)
1417 DECL_TEMPLATE(ALshort, aluF2S)
1418 DECL_TEMPLATE(ALubyte, aluF2UB)
1419 DECL_TEMPLATE(ALbyte, aluF2B)
1421 #undef DECL_TEMPLATE
1424 void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1426 ALsizei SamplesToDo;
1427 ALvoice **voice, **voice_end;
1428 ALeffectslot *slot;
1429 ALsource *source;
1430 ALCcontext *ctx;
1431 FPUCtl oldMode;
1432 ALsizei i, c;
1434 SetMixerFPUMode(&oldMode);
1436 while(size > 0)
1438 SamplesToDo = mini(size, BUFFERSIZE);
1439 for(c = 0;c < device->Dry.NumChannels;c++)
1440 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1441 if(device->Dry.Buffer != device->FOAOut.Buffer)
1442 for(c = 0;c < device->FOAOut.NumChannels;c++)
1443 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1444 if(device->Dry.Buffer != device->RealOut.Buffer)
1445 for(c = 0;c < device->RealOut.NumChannels;c++)
1446 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1448 IncrementRef(&device->MixCount);
1450 if((slot=device->DefaultSlot) != NULL)
1452 CalcEffectSlotParams(device->DefaultSlot, device);
1453 for(i = 0;i < slot->NumChannels;i++)
1454 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1457 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1458 while(ctx)
1460 ALeffectslot *slotroot;
1462 slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList, almemory_order_acquire);
1463 UpdateContextSources(ctx, slotroot);
1465 slot = slotroot;
1466 while(slot)
1468 for(i = 0;i < slot->NumChannels;i++)
1469 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1470 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1473 /* source processing */
1474 voice = ctx->Voices;
1475 voice_end = voice + ctx->VoiceCount;
1476 for(;voice != voice_end;++voice)
1478 ALboolean IsVoiceInit = ((*voice)->Step > 0);
1479 source = (*voice)->Source;
1480 if(IsVoiceInit && source &&
1481 ATOMIC_LOAD(&source->state, almemory_order_relaxed) == AL_PLAYING)
1482 MixSource(*voice, source, device, SamplesToDo);
1485 /* effect slot processing */
1486 slot = slotroot;
1487 while(slot)
1489 ALeffectState *state = slot->Params.EffectState;
1490 V(state,process)(SamplesToDo, SAFE_CONST(ALfloatBUFFERSIZE*,slot->WetBuffer),
1491 state->OutBuffer, state->OutChannels);
1492 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1495 ctx = ctx->next;
1498 if(device->DefaultSlot != NULL)
1500 const ALeffectslot *slot = device->DefaultSlot;
1501 ALeffectState *state = slot->Params.EffectState;
1502 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1503 state->OutChannels);
1506 /* Increment the clock time. Every second's worth of samples is
1507 * converted and added to clock base so that large sample counts don't
1508 * overflow during conversion. This also guarantees an exact, stable
1509 * conversion. */
1510 device->SamplesDone += SamplesToDo;
1511 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1512 device->SamplesDone %= device->Frequency;
1513 IncrementRef(&device->MixCount);
1515 if(device->Hrtf.Handle)
1517 HrtfDirectMixerFunc HrtfMix;
1518 ALsizei irsize;
1519 int lidx, ridx;
1521 if(device->AmbiUp)
1522 ambiup_process(device->AmbiUp,
1523 device->Dry.Buffer, device->Dry.NumChannels,
1524 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1527 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1528 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1529 assert(lidx != -1 && ridx != -1);
1531 HrtfMix = SelectHrtfMixer();
1532 irsize = device->Hrtf.IrSize;
1533 for(c = 0;c < device->Dry.NumChannels;c++)
1535 HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1536 device->Dry.Buffer[c], device->Hrtf.Offset, irsize,
1537 device->Hrtf.Coeffs[c], device->Hrtf.Values[c],
1538 SamplesToDo
1541 device->Hrtf.Offset += SamplesToDo;
1543 else if(device->AmbiDecoder)
1545 if(device->Dry.Buffer != device->FOAOut.Buffer)
1546 bformatdec_upSample(device->AmbiDecoder,
1547 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1548 device->FOAOut.NumChannels, SamplesToDo
1550 bformatdec_process(device->AmbiDecoder,
1551 device->RealOut.Buffer, device->RealOut.NumChannels,
1552 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1555 else if(device->AmbiUp)
1557 ambiup_process(device->AmbiUp,
1558 device->RealOut.Buffer, device->RealOut.NumChannels,
1559 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1562 else if(device->Uhj_Encoder)
1564 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1565 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1566 if(lidx != -1 && ridx != -1)
1568 /* Encode to stereo-compatible 2-channel UHJ output. */
1569 EncodeUhj2(device->Uhj_Encoder,
1570 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1571 device->Dry.Buffer, SamplesToDo
1575 else if(device->Bs2b)
1577 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1578 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1579 if(lidx != -1 && ridx != -1)
1581 /* Apply binaural/crossfeed filter */
1582 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1583 device->RealOut.Buffer[ridx], SamplesToDo);
1587 if(buffer)
1589 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1590 ALsizei OutChannels = device->RealOut.NumChannels;
1591 DistanceComp *DistComp = device->ChannelDelay;
1593 #define WRITE(T, a, b, c, d, e) do { \
1594 Write_##T(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \
1595 buffer = (T*)buffer + (d)*(e); \
1596 } while(0)
1597 switch(device->FmtType)
1599 case DevFmtByte:
1600 WRITE(ALbyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1601 break;
1602 case DevFmtUByte:
1603 WRITE(ALubyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1604 break;
1605 case DevFmtShort:
1606 WRITE(ALshort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1607 break;
1608 case DevFmtUShort:
1609 WRITE(ALushort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1610 break;
1611 case DevFmtInt:
1612 WRITE(ALint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1613 break;
1614 case DevFmtUInt:
1615 WRITE(ALuint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1616 break;
1617 case DevFmtFloat:
1618 WRITE(ALfloat, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1619 break;
1621 #undef WRITE
1624 size -= SamplesToDo;
1627 RestoreFPUMode(&oldMode);
1631 void aluHandleDisconnect(ALCdevice *device)
1633 ALCcontext *Context;
1635 device->Connected = ALC_FALSE;
1637 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1638 while(Context)
1640 ALvoice **voice, **voice_end;
1642 voice = Context->Voices;
1643 voice_end = voice + Context->VoiceCount;
1644 while(voice != voice_end)
1646 ALenum playing = AL_PLAYING;
1647 ALsource *source = (*voice)->Source;
1648 (*voice)->Source = NULL;
1650 if(source &&
1651 ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(ALenum, &source->state, &playing, AL_STOPPED))
1653 ATOMIC_STORE(&source->current_buffer, NULL, almemory_order_relaxed);
1654 ATOMIC_STORE(&source->position, 0, almemory_order_relaxed);
1655 ATOMIC_STORE(&source->position_fraction, 0, almemory_order_release);
1658 voice++;
1660 Context->VoiceCount = 0;
1662 Context = Context->next;