Alter how panpot/pair-wise panning works
[openal-soft.git] / Alc / ALu.c
blob5a5e6fdf1e5a09b2a22ae7c4c125f03f4be1b0f9
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, ALCcontext *context, ALboolean force)
1229 ALsource *source = voice->Source;
1230 const ALbufferlistitem *BufferListItem;
1231 struct ALsourceProps *props;
1233 props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
1234 if(!props && !force) return;
1236 if(props)
1238 memcpy(voice->Props, props,
1239 offsetof(struct ALsourceProps, Send[context->Device->NumAuxSends])
1242 ATOMIC_REPLACE_HEAD(struct ALsourceProps*, &source->FreeList, props);
1245 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1246 while(BufferListItem != NULL)
1248 const ALbuffer *buffer;
1249 if((buffer=BufferListItem->buffer) != NULL)
1251 if(buffer->FmtChannels == FmtMono)
1252 CalcAttnSourceParams(voice, voice->Props, buffer, context);
1253 else
1254 CalcNonAttnSourceParams(voice, voice->Props, buffer, context);
1255 break;
1257 BufferListItem = BufferListItem->next;
1262 static void UpdateContextSources(ALCcontext *ctx, ALeffectslot *slot)
1264 ALvoice **voice, **voice_end;
1265 ALsource *source;
1267 IncrementRef(&ctx->UpdateCount);
1268 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1270 ALboolean force = CalcListenerParams(ctx);
1271 while(slot)
1273 force |= CalcEffectSlotParams(slot, ctx->Device);
1274 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1277 voice = ctx->Voices;
1278 voice_end = voice + ctx->VoiceCount;
1279 for(;voice != voice_end;++voice)
1281 if(!(source=(*voice)->Source)) continue;
1282 if(!IsPlayingOrPaused(source))
1283 (*voice)->Source = NULL;
1284 else
1285 CalcSourceParams(*voice, ctx, force);
1288 IncrementRef(&ctx->UpdateCount);
1292 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1293 * converts NaN to 0. */
1294 static inline ALfloat aluClampf(ALfloat val)
1296 if(fabsf(val) <= 1.0f) return val;
1297 return (ALfloat)((0.0f < val) - (val < 0.0f));
1300 static inline ALfloat aluF2F(ALfloat val)
1301 { return val; }
1303 static inline ALint aluF2I(ALfloat val)
1305 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1306 * integer range normalized floats can be safely converted to.
1308 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1310 static inline ALuint aluF2UI(ALfloat val)
1311 { return aluF2I(val)+2147483648u; }
1313 static inline ALshort aluF2S(ALfloat val)
1314 { return fastf2i(aluClampf(val)*32767.0f); }
1315 static inline ALushort aluF2US(ALfloat val)
1316 { return aluF2S(val)+32768; }
1318 static inline ALbyte aluF2B(ALfloat val)
1319 { return fastf2i(aluClampf(val)*127.0f); }
1320 static inline ALubyte aluF2UB(ALfloat val)
1321 { return aluF2B(val)+128; }
1323 #define DECL_TEMPLATE(T, func) \
1324 static void Write_##T(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1325 DistanceComp *distcomp, ALsizei SamplesToDo, \
1326 ALsizei numchans) \
1328 ALsizei i, j; \
1329 for(j = 0;j < numchans;j++) \
1331 const ALfloat *in = InBuffer[j]; \
1332 T *restrict out = (T*)OutBuffer + j; \
1333 const ALfloat gain = distcomp[j].Gain; \
1334 const ALsizei base = distcomp[j].Length; \
1335 if(base > 0 || gain != 1.0f) \
1337 if(SamplesToDo >= base) \
1339 for(i = 0;i < base;i++) \
1340 out[i*numchans] = func(distcomp[j].Buffer[i]*gain); \
1341 for(;i < SamplesToDo;i++) \
1342 out[i*numchans] = func(in[i-base]*gain); \
1343 memcpy(distcomp[j].Buffer, &in[SamplesToDo-base], \
1344 base*sizeof(ALfloat)); \
1346 else \
1348 for(i = 0;i < SamplesToDo;i++) \
1349 out[i*numchans] = func(distcomp[j].Buffer[i]*gain); \
1350 memmove(distcomp[j].Buffer, distcomp[j].Buffer+SamplesToDo, \
1351 (base-SamplesToDo)*sizeof(ALfloat)); \
1352 memcpy(distcomp[j].Buffer+base-SamplesToDo, in, \
1353 SamplesToDo*sizeof(ALfloat)); \
1356 else for(i = 0;i < SamplesToDo;i++) \
1357 out[i*numchans] = func(in[i]); \
1361 DECL_TEMPLATE(ALfloat, aluF2F)
1362 DECL_TEMPLATE(ALuint, aluF2UI)
1363 DECL_TEMPLATE(ALint, aluF2I)
1364 DECL_TEMPLATE(ALushort, aluF2US)
1365 DECL_TEMPLATE(ALshort, aluF2S)
1366 DECL_TEMPLATE(ALubyte, aluF2UB)
1367 DECL_TEMPLATE(ALbyte, aluF2B)
1369 #undef DECL_TEMPLATE
1372 void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1374 ALsizei SamplesToDo;
1375 ALvoice **voice, **voice_end;
1376 ALeffectslot *slot;
1377 ALsource *source;
1378 ALCcontext *ctx;
1379 FPUCtl oldMode;
1380 ALsizei i, c;
1382 SetMixerFPUMode(&oldMode);
1384 while(size > 0)
1386 SamplesToDo = mini(size, BUFFERSIZE);
1387 for(c = 0;c < device->Dry.NumChannels;c++)
1388 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1389 if(device->Dry.Buffer != device->FOAOut.Buffer)
1390 for(c = 0;c < device->FOAOut.NumChannels;c++)
1391 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1392 if(device->Dry.Buffer != device->RealOut.Buffer)
1393 for(c = 0;c < device->RealOut.NumChannels;c++)
1394 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1396 IncrementRef(&device->MixCount);
1398 if((slot=device->DefaultSlot) != NULL)
1400 CalcEffectSlotParams(device->DefaultSlot, device);
1401 for(i = 0;i < slot->NumChannels;i++)
1402 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1405 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1406 while(ctx)
1408 ALeffectslot *slotroot;
1410 slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList, almemory_order_acquire);
1411 UpdateContextSources(ctx, slotroot);
1413 slot = slotroot;
1414 while(slot)
1416 for(i = 0;i < slot->NumChannels;i++)
1417 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1418 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1421 /* source processing */
1422 voice = ctx->Voices;
1423 voice_end = voice + ctx->VoiceCount;
1424 for(;voice != voice_end;++voice)
1426 ALboolean IsVoiceInit = ((*voice)->Step > 0);
1427 source = (*voice)->Source;
1428 if(IsVoiceInit && source &&
1429 ATOMIC_LOAD(&source->state, almemory_order_relaxed) == AL_PLAYING)
1430 MixSource(*voice, source, device, SamplesToDo);
1433 /* effect slot processing */
1434 slot = slotroot;
1435 while(slot)
1437 ALeffectState *state = slot->Params.EffectState;
1438 V(state,process)(SamplesToDo, SAFE_CONST(ALfloatBUFFERSIZE*,slot->WetBuffer),
1439 state->OutBuffer, state->OutChannels);
1440 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1443 ctx = ctx->next;
1446 if(device->DefaultSlot != NULL)
1448 const ALeffectslot *slot = device->DefaultSlot;
1449 ALeffectState *state = slot->Params.EffectState;
1450 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1451 state->OutChannels);
1454 /* Increment the clock time. Every second's worth of samples is
1455 * converted and added to clock base so that large sample counts don't
1456 * overflow during conversion. This also guarantees an exact, stable
1457 * conversion. */
1458 device->SamplesDone += SamplesToDo;
1459 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1460 device->SamplesDone %= device->Frequency;
1461 IncrementRef(&device->MixCount);
1463 if(device->Hrtf.Handle)
1465 HrtfDirectMixerFunc HrtfMix;
1466 ALsizei irsize;
1467 int lidx, ridx;
1469 if(device->AmbiUp)
1470 ambiup_process(device->AmbiUp,
1471 device->Dry.Buffer, device->Dry.NumChannels,
1472 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1475 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1476 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1477 assert(lidx != -1 && ridx != -1);
1479 HrtfMix = SelectHrtfMixer();
1480 irsize = device->Hrtf.IrSize;
1481 for(c = 0;c < device->Dry.NumChannels;c++)
1483 HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1484 device->Dry.Buffer[c], device->Hrtf.Offset, irsize,
1485 device->Hrtf.Coeffs[c], device->Hrtf.Values[c],
1486 SamplesToDo
1489 device->Hrtf.Offset += SamplesToDo;
1491 else if(device->AmbiDecoder)
1493 if(device->Dry.Buffer != device->FOAOut.Buffer)
1494 bformatdec_upSample(device->AmbiDecoder,
1495 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1496 device->FOAOut.NumChannels, SamplesToDo
1498 bformatdec_process(device->AmbiDecoder,
1499 device->RealOut.Buffer, device->RealOut.NumChannels,
1500 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1503 else if(device->AmbiUp)
1505 ambiup_process(device->AmbiUp,
1506 device->RealOut.Buffer, device->RealOut.NumChannels,
1507 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1510 else if(device->Uhj_Encoder)
1512 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1513 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1514 if(lidx != -1 && ridx != -1)
1516 /* Encode to stereo-compatible 2-channel UHJ output. */
1517 EncodeUhj2(device->Uhj_Encoder,
1518 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1519 device->Dry.Buffer, SamplesToDo
1523 else if(device->Bs2b)
1525 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1526 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1527 if(lidx != -1 && ridx != -1)
1529 /* Apply binaural/crossfeed filter */
1530 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1531 device->RealOut.Buffer[ridx], SamplesToDo);
1535 if(buffer)
1537 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1538 ALsizei OutChannels = device->RealOut.NumChannels;
1539 DistanceComp *DistComp = device->ChannelDelay;
1541 #define WRITE(T, a, b, c, d, e) do { \
1542 Write_##T(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \
1543 buffer = (T*)buffer + (d)*(e); \
1544 } while(0)
1545 switch(device->FmtType)
1547 case DevFmtByte:
1548 WRITE(ALbyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1549 break;
1550 case DevFmtUByte:
1551 WRITE(ALubyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1552 break;
1553 case DevFmtShort:
1554 WRITE(ALshort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1555 break;
1556 case DevFmtUShort:
1557 WRITE(ALushort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1558 break;
1559 case DevFmtInt:
1560 WRITE(ALint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1561 break;
1562 case DevFmtUInt:
1563 WRITE(ALuint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1564 break;
1565 case DevFmtFloat:
1566 WRITE(ALfloat, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1567 break;
1569 #undef WRITE
1572 size -= SamplesToDo;
1575 RestoreFPUMode(&oldMode);
1579 void aluHandleDisconnect(ALCdevice *device)
1581 ALCcontext *Context;
1583 device->Connected = ALC_FALSE;
1585 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1586 while(Context)
1588 ALvoice **voice, **voice_end;
1590 voice = Context->Voices;
1591 voice_end = voice + Context->VoiceCount;
1592 while(voice != voice_end)
1594 ALenum playing = AL_PLAYING;
1595 ALsource *source = (*voice)->Source;
1596 (*voice)->Source = NULL;
1598 if(source &&
1599 ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(ALenum, &source->state, &playing, AL_STOPPED))
1601 ATOMIC_STORE(&source->current_buffer, NULL, almemory_order_relaxed);
1602 ATOMIC_STORE(&source->position, 0, almemory_order_relaxed);
1603 ATOMIC_STORE(&source->position_fraction, 0, almemory_order_release);
1606 voice++;
1608 Context->VoiceCount = 0;
1610 Context = Context->next;