Reorganize ALvoice members
[openal-soft.git] / Alc / ALu.c
blobd5065199782b9342eba3080b87c133e8482d2e3f
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <math.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
29 #include "alMain.h"
30 #include "alSource.h"
31 #include "alBuffer.h"
32 #include "alListener.h"
33 #include "alAuxEffectSlot.h"
34 #include "alu.h"
35 #include "bs2b.h"
36 #include "hrtf.h"
37 #include "uhjfilter.h"
38 #include "bformatdec.h"
39 #include "static_assert.h"
41 #include "mixer_defs.h"
43 #include "backends/base.h"
46 struct ChanMap {
47 enum Channel channel;
48 ALfloat angle;
49 ALfloat elevation;
52 /* Cone scalar */
53 ALfloat ConeScale = 1.0f;
55 /* Localized Z scalar for mono sources */
56 ALfloat ZScale = 1.0f;
58 extern inline ALfloat minf(ALfloat a, ALfloat b);
59 extern inline ALfloat maxf(ALfloat a, ALfloat b);
60 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
62 extern inline ALdouble mind(ALdouble a, ALdouble b);
63 extern inline ALdouble maxd(ALdouble a, ALdouble b);
64 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
66 extern inline ALuint minu(ALuint a, ALuint b);
67 extern inline ALuint maxu(ALuint a, ALuint b);
68 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
70 extern inline ALint mini(ALint a, ALint b);
71 extern inline ALint maxi(ALint a, ALint b);
72 extern inline ALint clampi(ALint val, ALint min, ALint max);
74 extern inline ALint64 mini64(ALint64 a, ALint64 b);
75 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
76 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
78 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
79 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
80 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
82 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
83 extern inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac);
84 extern inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat val5, ALfloat val6, ALfloat val7, ALuint frac);
86 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
88 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
89 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
90 extern inline void aluMatrixfSet(aluMatrixf *matrix,
91 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
92 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
93 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
94 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
96 const aluMatrixf IdentityMatrixf = {{
97 { 1.0f, 0.0f, 0.0f, 0.0f },
98 { 0.0f, 1.0f, 0.0f, 0.0f },
99 { 0.0f, 0.0f, 1.0f, 0.0f },
100 { 0.0f, 0.0f, 0.0f, 1.0f },
104 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
106 #ifdef HAVE_SSE
107 if((CPUCapFlags&CPU_CAP_SSE))
108 return MixDirectHrtf_SSE;
109 #endif
110 #ifdef HAVE_NEON
111 if((CPUCapFlags&CPU_CAP_NEON))
112 return MixDirectHrtf_Neon;
113 #endif
115 return MixDirectHrtf_C;
119 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
121 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
122 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
123 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
126 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
128 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
131 static ALfloat aluNormalize(ALfloat *vec)
133 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
134 if(length > 0.0f)
136 ALfloat inv_length = 1.0f/length;
137 vec[0] *= inv_length;
138 vec[1] *= inv_length;
139 vec[2] *= inv_length;
141 return length;
144 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
146 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
148 vec[0] = v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0];
149 vec[1] = v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1];
150 vec[2] = v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2];
153 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
155 aluVector v;
156 v.v[0] = vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0];
157 v.v[1] = vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1];
158 v.v[2] = vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2];
159 v.v[3] = vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3];
160 return v;
164 /* Prepares the interpolator for a given rate (determined by increment). A
165 * result of AL_FALSE indicates that the filter output will completely cut
166 * the input signal.
168 * With a bit of work, and a trade of memory for CPU cost, this could be
169 * modified for use with an interpolated increment for buttery-smooth pitch
170 * changes.
172 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
174 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
175 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
176 static const ALuint to[4][BSINC_SCALE_COUNT] =
178 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
179 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
180 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
181 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
183 static const ALuint tm[2][BSINC_SCALE_COUNT] =
185 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
186 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
188 ALfloat sf;
189 ALuint si, pi;
190 ALboolean uncut = AL_TRUE;
192 if(increment > FRACTIONONE)
194 sf = (ALfloat)FRACTIONONE / increment;
195 if(sf < scaleBase)
197 /* Signal has been completely cut. The return result can be used
198 * to skip the filter (and output zeros) as an optimization.
200 sf = 0.0f;
201 si = 0;
202 uncut = AL_FALSE;
204 else
206 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
207 si = fastf2u(sf);
208 /* The interpolation factor is fit to this diagonally-symmetric
209 * curve to reduce the transition ripple caused by interpolating
210 * different scales of the sinc function.
212 sf = 1.0f - cosf(asinf(sf - si));
215 else
217 sf = 0.0f;
218 si = BSINC_SCALE_COUNT - 1;
221 state->sf = sf;
222 state->m = m[si];
223 state->l = -(ALint)((m[si] / 2) - 1);
224 /* The CPU cost of this table re-mapping could be traded for the memory
225 * cost of a complete table map (1024 elements large).
227 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
229 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
230 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
231 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
232 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
234 return uncut;
238 static ALboolean CalcListenerParams(ALCcontext *Context)
240 ALlistener *Listener = Context->Listener;
241 ALfloat N[3], V[3], U[3], P[3];
242 struct ALlistenerProps *props;
243 aluVector vel;
245 props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &Listener->Update, NULL, almemory_order_acq_rel);
246 if(!props) return AL_FALSE;
248 /* AT then UP */
249 N[0] = ATOMIC_LOAD(&props->Forward[0], almemory_order_relaxed);
250 N[1] = ATOMIC_LOAD(&props->Forward[1], almemory_order_relaxed);
251 N[2] = ATOMIC_LOAD(&props->Forward[2], almemory_order_relaxed);
252 aluNormalize(N);
253 V[0] = ATOMIC_LOAD(&props->Up[0], almemory_order_relaxed);
254 V[1] = ATOMIC_LOAD(&props->Up[1], almemory_order_relaxed);
255 V[2] = ATOMIC_LOAD(&props->Up[2], almemory_order_relaxed);
256 aluNormalize(V);
257 /* Build and normalize right-vector */
258 aluCrossproduct(N, V, U);
259 aluNormalize(U);
261 aluMatrixfSet(&Listener->Params.Matrix,
262 U[0], V[0], -N[0], 0.0,
263 U[1], V[1], -N[1], 0.0,
264 U[2], V[2], -N[2], 0.0,
265 0.0, 0.0, 0.0, 1.0
268 P[0] = ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed);
269 P[1] = ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed);
270 P[2] = ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed);
271 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
272 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
274 aluVectorSet(&vel, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
275 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
276 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
277 0.0f);
278 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
280 Listener->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed) * Context->GainBoost;
281 Listener->Params.MetersPerUnit = ATOMIC_LOAD(&props->MetersPerUnit, almemory_order_relaxed);
283 Listener->Params.DopplerFactor = ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
284 Listener->Params.SpeedOfSound = ATOMIC_LOAD(&props->SpeedOfSound, almemory_order_relaxed) *
285 ATOMIC_LOAD(&props->DopplerVelocity, almemory_order_relaxed);
287 Listener->Params.SourceDistanceModel = ATOMIC_LOAD(&props->SourceDistanceModel, almemory_order_relaxed);
288 Listener->Params.DistanceModel = ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed);
290 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
291 return AL_TRUE;
294 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
296 struct ALeffectslotProps *props;
297 ALeffectState *state;
299 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL, almemory_order_acq_rel);
300 if(!props) return AL_FALSE;
302 slot->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
303 slot->Params.AuxSendAuto = ATOMIC_LOAD(&props->AuxSendAuto, almemory_order_relaxed);
304 slot->Params.EffectType = ATOMIC_LOAD(&props->Type, almemory_order_relaxed);
305 if(IsReverbEffect(slot->Params.EffectType))
307 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
308 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
309 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
311 else
313 slot->Params.RoomRolloff = 0.0f;
314 slot->Params.DecayTime = 0.0f;
315 slot->Params.AirAbsorptionGainHF = 1.0f;
318 /* Swap effect states. No need to play with the ref counts since they keep
319 * the same number of refs.
321 state = ATOMIC_EXCHANGE(ALeffectState*, &props->State, slot->Params.EffectState,
322 almemory_order_relaxed);
323 slot->Params.EffectState = state;
325 V(state,update)(device, slot, &props->Props);
327 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
328 return AL_TRUE;
332 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
334 static const struct ChanMap MonoMap[1] = {
335 { FrontCenter, 0.0f, 0.0f }
336 }, RearMap[2] = {
337 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
338 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
339 }, QuadMap[4] = {
340 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
341 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
342 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
343 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
344 }, X51Map[6] = {
345 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
346 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
347 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
348 { LFE, 0.0f, 0.0f },
349 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
350 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
351 }, X61Map[7] = {
352 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
353 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
354 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
355 { LFE, 0.0f, 0.0f },
356 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
357 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
358 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
359 }, X71Map[8] = {
360 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
361 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
362 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
363 { LFE, 0.0f, 0.0f },
364 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
365 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
366 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
367 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
370 const ALCdevice *Device = ALContext->Device;
371 const ALlistener *Listener = ALContext->Listener;
372 ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
373 ALfloat DryGain, DryGainHF, DryGainLF;
374 ALfloat WetGain[MAX_SENDS];
375 ALfloat WetGainHF[MAX_SENDS];
376 ALfloat WetGainLF[MAX_SENDS];
377 ALeffectslot *SendSlots[MAX_SENDS];
378 ALuint NumSends, Frequency;
379 ALboolean Relative;
380 const struct ChanMap *chans = NULL;
381 struct ChanMap StereoMap[2] = {
382 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
383 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
385 ALuint num_channels = 0;
386 ALboolean DirectChannels;
387 ALboolean isbformat = AL_FALSE;
388 ALfloat Pitch;
389 ALuint i, j, c;
391 /* Get device properties */
392 NumSends = Device->NumAuxSends;
393 Frequency = Device->Frequency;
395 /* Get listener properties */
396 ListenerGain = Listener->Params.Gain;
398 /* Get source properties */
399 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
400 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
401 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
402 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
403 Relative = ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed);
404 DirectChannels = ATOMIC_LOAD(&props->DirectChannels, almemory_order_relaxed);
406 /* Convert counter-clockwise to clockwise. */
407 StereoMap[0].angle = -ATOMIC_LOAD(&props->StereoPan[0], almemory_order_relaxed);
408 StereoMap[1].angle = -ATOMIC_LOAD(&props->StereoPan[1], almemory_order_relaxed);
410 voice->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 if(!SendSlots[i])
547 for(c = 0;c < num_channels;c++)
549 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
550 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
553 else
555 for(c = 0;c < num_channels;c++)
557 const ALeffectslot *Slot = SendSlots[i];
558 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
559 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
565 voice->IsHrtf = AL_FALSE;
567 else
569 ALfloat coeffs[MAX_AMBI_COEFFS];
571 if(DirectChannels)
573 /* Skip the virtual channels and write inputs to the real output. */
574 voice->Direct.Buffer = Device->RealOut.Buffer;
575 voice->Direct.Channels = Device->RealOut.NumChannels;
576 for(c = 0;c < num_channels;c++)
578 int idx;
579 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
580 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
581 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
582 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
585 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
586 * channel-match. */
587 for(c = 0;c < num_channels;c++)
589 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
591 for(i = 0;i < NumSends;i++)
593 if(!SendSlots[i])
595 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
596 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
598 else
600 const ALeffectslot *Slot = SendSlots[i];
601 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
602 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
608 voice->IsHrtf = AL_FALSE;
610 else if(Device->Render_Mode == HrtfRender)
612 /* Full HRTF rendering. Skip the virtual channels and render each
613 * input channel to the real outputs.
615 voice->Direct.Buffer = Device->RealOut.Buffer;
616 voice->Direct.Channels = Device->RealOut.NumChannels;
617 for(c = 0;c < num_channels;c++)
619 if(chans[c].channel == LFE)
621 /* Skip LFE */
622 voice->Direct.Params[c].Hrtf.Target.Delay[0] = 0;
623 voice->Direct.Params[c].Hrtf.Target.Delay[1] = 0;
624 for(i = 0;i < HRIR_LENGTH;i++)
626 voice->Direct.Params[c].Hrtf.Target.Coeffs[i][0] = 0.0f;
627 voice->Direct.Params[c].Hrtf.Target.Coeffs[i][1] = 0.0f;
630 for(i = 0;i < NumSends;i++)
632 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
633 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
636 continue;
639 /* Get the static HRIR coefficients and delays for this channel. */
640 GetHrtfCoeffs(Device->Hrtf.Handle,
641 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
642 voice->Direct.Params[c].Hrtf.Target.Coeffs,
643 voice->Direct.Params[c].Hrtf.Target.Delay
646 /* Normal panning for auxiliary sends. */
647 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
649 for(i = 0;i < NumSends;i++)
651 if(!SendSlots[i])
653 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
654 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
656 else
658 const ALeffectslot *Slot = SendSlots[i];
659 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
660 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
666 voice->IsHrtf = AL_TRUE;
668 else
670 /* Non-HRTF rendering. Use normal panning to the output. */
671 for(c = 0;c < num_channels;c++)
673 /* Special-case LFE */
674 if(chans[c].channel == LFE)
676 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
677 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
678 if(Device->Dry.Buffer == Device->RealOut.Buffer)
680 int idx;
681 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
682 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
685 for(i = 0;i < NumSends;i++)
687 ALuint j;
688 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
689 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
691 continue;
694 if(Device->Render_Mode == StereoPair)
696 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
697 ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
698 coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
699 voice->Direct.Params[c].Gains.Target[0] = coeffs[0] * DryGain;
700 voice->Direct.Params[c].Gains.Target[1] = (1.0f-coeffs[0]) * DryGain;
701 for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
702 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
704 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
706 else
708 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
709 ComputePanningGains(Device->Dry, coeffs, DryGain,
710 voice->Direct.Params[c].Gains.Target);
713 for(i = 0;i < NumSends;i++)
715 if(!SendSlots[i])
717 ALuint j;
718 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
719 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
721 else
723 const ALeffectslot *Slot = SendSlots[i];
724 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
725 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
731 voice->IsHrtf = AL_FALSE;
736 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
737 Frequency;
738 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
739 Frequency;
740 DryGainHF = maxf(DryGainHF, 0.0001f);
741 DryGainLF = maxf(DryGainLF, 0.0001f);
742 for(c = 0;c < num_channels;c++)
744 voice->Direct.Params[c].FilterType = AF_None;
745 if(DryGainHF != 1.0f) voice->Direct.Params[c].FilterType |= AF_LowPass;
746 if(DryGainLF != 1.0f) voice->Direct.Params[c].FilterType |= AF_HighPass;
747 ALfilterState_setParams(
748 &voice->Direct.Params[c].LowPass, ALfilterType_HighShelf,
749 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
751 ALfilterState_setParams(
752 &voice->Direct.Params[c].HighPass, ALfilterType_LowShelf,
753 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
757 for(i = 0;i < NumSends;i++)
759 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
760 Frequency;
761 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
762 Frequency;
763 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
764 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
765 for(c = 0;c < num_channels;c++)
767 voice->Send[i].Params[c].FilterType = AF_None;
768 if(WetGainHF[i] != 1.0f) voice->Send[i].Params[c].FilterType |= AF_LowPass;
769 if(WetGainLF[i] != 1.0f) voice->Send[i].Params[c].FilterType |= AF_HighPass;
770 ALfilterState_setParams(
771 &voice->Send[i].Params[c].LowPass, ALfilterType_HighShelf,
772 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
774 ALfilterState_setParams(
775 &voice->Send[i].Params[c].HighPass, ALfilterType_LowShelf,
776 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
782 static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
784 const ALCdevice *Device = ALContext->Device;
785 const ALlistener *Listener = ALContext->Listener;
786 aluVector Position, Velocity, Direction, SourceToListener;
787 ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
788 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
789 ALfloat SourceVolume,ListenerGain;
790 ALfloat DopplerFactor, SpeedOfSound;
791 ALfloat AirAbsorptionFactor;
792 ALfloat RoomAirAbsorption[MAX_SENDS];
793 ALeffectslot *SendSlots[MAX_SENDS];
794 ALfloat Attenuation;
795 ALfloat RoomAttenuation[MAX_SENDS];
796 ALfloat MetersPerUnit;
797 ALfloat RoomRolloffBase;
798 ALfloat RoomRolloff[MAX_SENDS];
799 ALfloat DecayDistance[MAX_SENDS];
800 ALfloat DryGain;
801 ALfloat DryGainHF;
802 ALfloat DryGainLF;
803 ALboolean DryGainHFAuto;
804 ALfloat WetGain[MAX_SENDS];
805 ALfloat WetGainHF[MAX_SENDS];
806 ALfloat WetGainLF[MAX_SENDS];
807 ALboolean WetGainAuto;
808 ALboolean WetGainHFAuto;
809 ALfloat Pitch;
810 ALuint Frequency;
811 ALint NumSends;
812 ALint i;
814 /* Get context/device properties */
815 DopplerFactor = Listener->Params.DopplerFactor;
816 SpeedOfSound = Listener->Params.SpeedOfSound;
817 NumSends = Device->NumAuxSends;
818 Frequency = Device->Frequency;
820 /* Get listener properties */
821 ListenerGain = Listener->Params.Gain;
822 MetersPerUnit = Listener->Params.MetersPerUnit;
824 /* Get source properties */
825 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
826 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
827 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
828 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
829 aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed),
830 ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed),
831 ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed),
832 1.0f);
833 aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed),
834 ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed),
835 ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed),
836 0.0f);
837 aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
838 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
839 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
840 0.0f);
841 MinDist = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed);
842 MaxDist = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed);
843 Rolloff = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed);
844 DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
845 InnerAngle = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed);
846 OuterAngle = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed);
847 AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed);
848 DryGainHFAuto = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed);
849 WetGainAuto = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed);
850 WetGainHFAuto = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed);
851 RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed);
853 voice->Direct.Buffer = Device->Dry.Buffer;
854 voice->Direct.Channels = Device->Dry.NumChannels;
855 for(i = 0;i < NumSends;i++)
857 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
859 if(!SendSlots[i] && i == 0)
860 SendSlots[i] = Device->DefaultSlot;
861 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
863 SendSlots[i] = NULL;
864 RoomRolloff[i] = 0.0f;
865 DecayDistance[i] = 0.0f;
866 RoomAirAbsorption[i] = 1.0f;
868 else if(SendSlots[i]->Params.AuxSendAuto)
870 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
871 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
872 SPEEDOFSOUNDMETRESPERSEC;
873 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
875 else
877 /* If the slot's auxiliary send auto is off, the data sent to the
878 * effect slot is the same as the dry path, sans filter effects */
879 RoomRolloff[i] = Rolloff;
880 DecayDistance[i] = 0.0f;
881 RoomAirAbsorption[i] = AIRABSORBGAINHF;
884 if(!SendSlots[i])
886 voice->Send[i].Buffer = NULL;
887 voice->Send[i].Channels = 0;
889 else
891 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
892 voice->Send[i].Channels = SendSlots[i]->NumChannels;
896 /* Transform source to listener space (convert to head relative) */
897 if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE)
899 const aluMatrixf *Matrix = &Listener->Params.Matrix;
900 /* Transform source vectors */
901 Position = aluMatrixfVector(Matrix, &Position);
902 Velocity = aluMatrixfVector(Matrix, &Velocity);
903 Direction = aluMatrixfVector(Matrix, &Direction);
905 else
907 const aluVector *lvelocity = &Listener->Params.Velocity;
908 /* Offset the source velocity to be relative of the listener velocity */
909 Velocity.v[0] += lvelocity->v[0];
910 Velocity.v[1] += lvelocity->v[1];
911 Velocity.v[2] += lvelocity->v[2];
914 aluNormalize(Direction.v);
915 SourceToListener.v[0] = -Position.v[0];
916 SourceToListener.v[1] = -Position.v[1];
917 SourceToListener.v[2] = -Position.v[2];
918 SourceToListener.v[3] = 0.0f;
919 Distance = aluNormalize(SourceToListener.v);
921 /* Calculate distance attenuation */
922 ClampedDist = Distance;
924 Attenuation = 1.0f;
925 for(i = 0;i < NumSends;i++)
926 RoomAttenuation[i] = 1.0f;
927 switch(Listener->Params.SourceDistanceModel ?
928 ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed) :
929 Listener->Params.DistanceModel)
931 case InverseDistanceClamped:
932 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
933 if(MaxDist < MinDist)
934 break;
935 /*fall-through*/
936 case InverseDistance:
937 if(MinDist > 0.0f)
939 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
940 if(dist > 0.0f) Attenuation = MinDist / dist;
941 for(i = 0;i < NumSends;i++)
943 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
944 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
947 break;
949 case LinearDistanceClamped:
950 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
951 if(MaxDist < MinDist)
952 break;
953 /*fall-through*/
954 case LinearDistance:
955 if(MaxDist != MinDist)
957 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
958 Attenuation = maxf(Attenuation, 0.0f);
959 for(i = 0;i < NumSends;i++)
961 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
962 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
965 break;
967 case ExponentDistanceClamped:
968 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
969 if(MaxDist < MinDist)
970 break;
971 /*fall-through*/
972 case ExponentDistance:
973 if(ClampedDist > 0.0f && MinDist > 0.0f)
975 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
976 for(i = 0;i < NumSends;i++)
977 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
979 break;
981 case DisableDistance:
982 ClampedDist = MinDist;
983 break;
986 /* Source Gain + Attenuation */
987 DryGain = SourceVolume * Attenuation;
988 DryGainHF = 1.0f;
989 DryGainLF = 1.0f;
990 for(i = 0;i < NumSends;i++)
992 WetGain[i] = SourceVolume * RoomAttenuation[i];
993 WetGainHF[i] = 1.0f;
994 WetGainLF[i] = 1.0f;
997 /* Distance-based air absorption */
998 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
1000 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
1001 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
1002 for(i = 0;i < NumSends;i++)
1003 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
1006 if(WetGainAuto)
1008 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1010 /* Apply a decay-time transformation to the wet path, based on the
1011 * attenuation of the dry path.
1013 * Using the apparent distance, based on the distance attenuation, the
1014 * initial decay of the reverb effect is calculated and applied to the
1015 * wet path.
1017 for(i = 0;i < NumSends;i++)
1019 if(DecayDistance[i] > 0.0f)
1020 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1024 /* Calculate directional soundcones */
1025 if(InnerAngle < 360.0f)
1027 ALfloat ConeVolume;
1028 ALfloat ConeHF;
1029 ALfloat Angle;
1030 ALfloat scale;
1032 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1033 if(Angle > InnerAngle)
1035 if(Angle < OuterAngle)
1037 scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1038 ConeVolume = lerp(
1039 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1041 ConeHF = lerp(
1042 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1045 else
1047 ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed);
1048 ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed);
1050 DryGain *= ConeVolume;
1051 if(DryGainHFAuto)
1052 DryGainHF *= ConeHF;
1055 /* Wet path uses the total area of the cone emitter (the room will
1056 * receive the same amount of sound regardless of its direction).
1058 scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
1059 (InnerAngle/360.0f);
1060 if(WetGainAuto)
1062 ConeVolume = lerp(
1063 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1065 for(i = 0;i < NumSends;i++)
1066 WetGain[i] *= ConeVolume;
1068 if(WetGainHFAuto)
1070 ConeHF = lerp(
1071 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1073 for(i = 0;i < NumSends;i++)
1074 WetGainHF[i] *= ConeHF;
1078 /* Apply gain and frequency filters */
1079 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1080 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
1081 DryGain = minf(DryGain, GAIN_MIX_MAX);
1082 DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
1083 DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
1084 for(i = 0;i < NumSends;i++)
1086 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1087 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
1088 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1089 WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
1090 WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
1093 /* Calculate velocity-based doppler effect */
1094 if(DopplerFactor > 0.0f)
1096 const aluVector *lvelocity = &Listener->Params.Velocity;
1097 ALfloat VSS, VLS;
1099 if(SpeedOfSound < 1.0f)
1101 DopplerFactor *= 1.0f/SpeedOfSound;
1102 SpeedOfSound = 1.0f;
1105 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1106 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1108 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1109 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1112 /* Calculate fixed-point stepping value, based on the pitch, buffer
1113 * frequency, and output frequency.
1115 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1116 if(Pitch > (ALfloat)MAX_PITCH)
1117 voice->Step = MAX_PITCH<<FRACTIONBITS;
1118 else
1119 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1120 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1122 if(Device->Render_Mode == HrtfRender)
1124 /* Full HRTF rendering. Skip the virtual channels and render to the
1125 * real outputs.
1127 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1128 ALfloat ev = 0.0f, az = 0.0f;
1129 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1130 ALfloat coeffs[MAX_AMBI_COEFFS];
1131 ALfloat spread = 0.0f;
1133 voice->Direct.Buffer = Device->RealOut.Buffer;
1134 voice->Direct.Channels = Device->RealOut.NumChannels;
1136 if(Distance > FLT_EPSILON)
1138 dir[0] = -SourceToListener.v[0];
1139 dir[1] = -SourceToListener.v[1];
1140 dir[2] = -SourceToListener.v[2] * ZScale;
1142 /* Calculate elevation and azimuth only when the source is not at
1143 * the listener. This prevents +0 and -0 Z from producing
1144 * inconsistent panning. Also, clamp Y in case FP precision errors
1145 * cause it to land outside of -1..+1. */
1146 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1147 az = atan2f(dir[0], -dir[2]);
1149 if(radius > Distance)
1150 spread = F_TAU - Distance/radius*F_PI;
1151 else if(Distance > FLT_EPSILON)
1152 spread = asinf(radius / Distance) * 2.0f;
1154 /* Get the HRIR coefficients and delays. */
1155 GetHrtfCoeffs(Device->Hrtf.Handle, ev, az, spread, DryGain,
1156 voice->Direct.Params[0].Hrtf.Target.Coeffs,
1157 voice->Direct.Params[0].Hrtf.Target.Delay);
1159 CalcDirectionCoeffs(dir, spread, coeffs);
1161 for(i = 0;i < NumSends;i++)
1163 if(!SendSlots[i])
1165 ALuint j;
1166 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1167 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
1169 else
1171 const ALeffectslot *Slot = SendSlots[i];
1172 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1173 coeffs, WetGain[i], voice->Send[i].Params[0].Gains.Target
1178 voice->IsHrtf = AL_TRUE;
1180 else
1182 /* Non-HRTF rendering. */
1183 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1184 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1185 ALfloat coeffs[MAX_AMBI_COEFFS];
1186 ALfloat spread = 0.0f;
1188 /* Get the localized direction, and compute panned gains. */
1189 if(Distance > FLT_EPSILON)
1191 dir[0] = -SourceToListener.v[0];
1192 dir[1] = -SourceToListener.v[1];
1193 dir[2] = -SourceToListener.v[2] * ZScale;
1195 if(radius > Distance)
1196 spread = F_TAU - Distance/radius*F_PI;
1197 else if(Distance > FLT_EPSILON)
1198 spread = asinf(radius / Distance) * 2.0f;
1200 if(Device->Render_Mode == StereoPair)
1202 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
1203 ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
1204 x = clampf(x, -0.5f, 0.5f) + 0.5f;
1205 voice->Direct.Params[0].Gains.Target[0] = x * DryGain;
1206 voice->Direct.Params[0].Gains.Target[1] = (1.0f-x) * DryGain;
1207 for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
1208 voice->Direct.Params[0].Gains.Target[i] = 0.0f;
1210 CalcDirectionCoeffs(dir, spread, coeffs);
1212 else
1214 CalcDirectionCoeffs(dir, spread, coeffs);
1215 ComputePanningGains(Device->Dry, coeffs, DryGain,
1216 voice->Direct.Params[0].Gains.Target);
1219 for(i = 0;i < NumSends;i++)
1221 if(!SendSlots[i])
1223 ALuint j;
1224 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1225 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
1227 else
1229 const ALeffectslot *Slot = SendSlots[i];
1230 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1231 coeffs, WetGain[i], voice->Send[i].Params[0].Gains.Target
1236 voice->IsHrtf = AL_FALSE;
1240 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
1241 Frequency;
1242 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
1243 Frequency;
1244 DryGainHF = maxf(DryGainHF, 0.0001f);
1245 DryGainLF = maxf(DryGainLF, 0.0001f);
1246 voice->Direct.Params[0].FilterType = AF_None;
1247 if(DryGainHF != 1.0f) voice->Direct.Params[0].FilterType |= AF_LowPass;
1248 if(DryGainLF != 1.0f) voice->Direct.Params[0].FilterType |= AF_HighPass;
1249 ALfilterState_setParams(
1250 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
1251 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
1253 ALfilterState_setParams(
1254 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
1255 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
1258 for(i = 0;i < NumSends;i++)
1260 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
1261 Frequency;
1262 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
1263 Frequency;
1264 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
1265 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
1266 voice->Send[i].Params[0].FilterType = AF_None;
1267 if(WetGainHF[i] != 1.0f) voice->Send[i].Params[0].FilterType |= AF_LowPass;
1268 if(WetGainLF[i] != 1.0f) voice->Send[i].Params[0].FilterType |= AF_HighPass;
1269 ALfilterState_setParams(
1270 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
1271 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
1273 ALfilterState_setParams(
1274 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
1275 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
1280 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1282 ALsource *source = voice->Source;
1283 const ALbufferlistitem *BufferListItem;
1284 struct ALsourceProps *props;
1286 props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
1287 if(!props && !force) return;
1289 if(props)
1291 memcpy(voice->Props, props,
1292 offsetof(struct ALsourceProps, Send[context->Device->NumAuxSends])
1295 ATOMIC_REPLACE_HEAD(struct ALsourceProps*, &source->FreeList, props);
1298 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1299 while(BufferListItem != NULL)
1301 const ALbuffer *buffer;
1302 if((buffer=BufferListItem->buffer) != NULL)
1304 if(buffer->FmtChannels == FmtMono)
1305 CalcAttnSourceParams(voice, voice->Props, buffer, context);
1306 else
1307 CalcNonAttnSourceParams(voice, voice->Props, buffer, context);
1308 break;
1310 BufferListItem = BufferListItem->next;
1315 static void UpdateContextSources(ALCcontext *ctx, ALeffectslot *slot)
1317 ALvoice *voice, *voice_end;
1318 ALsource *source;
1320 IncrementRef(&ctx->UpdateCount);
1321 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1323 ALboolean force = CalcListenerParams(ctx);
1324 while(slot)
1326 force |= CalcEffectSlotParams(slot, ctx->Device);
1327 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1330 voice = ctx->Voices;
1331 voice_end = voice + ctx->VoiceCount;
1332 for(;voice != voice_end;++voice)
1334 if(!(source=voice->Source)) continue;
1335 if(!IsPlayingOrPaused(source))
1336 voice->Source = NULL;
1337 else
1338 CalcSourceParams(voice, ctx, force);
1341 IncrementRef(&ctx->UpdateCount);
1345 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1346 * converts NaN to 0. */
1347 static inline ALfloat aluClampf(ALfloat val)
1349 if(fabsf(val) <= 1.0f) return val;
1350 return (ALfloat)((0.0f < val) - (val < 0.0f));
1353 static inline ALfloat aluF2F(ALfloat val)
1354 { return val; }
1356 static inline ALint aluF2I(ALfloat val)
1358 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1359 * integer range normalized floats can be safely converted to.
1361 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1363 static inline ALuint aluF2UI(ALfloat val)
1364 { return aluF2I(val)+2147483648u; }
1366 static inline ALshort aluF2S(ALfloat val)
1367 { return fastf2i(aluClampf(val)*32767.0f); }
1368 static inline ALushort aluF2US(ALfloat val)
1369 { return aluF2S(val)+32768; }
1371 static inline ALbyte aluF2B(ALfloat val)
1372 { return fastf2i(aluClampf(val)*127.0f); }
1373 static inline ALubyte aluF2UB(ALfloat val)
1374 { return aluF2B(val)+128; }
1376 #define DECL_TEMPLATE(T, func) \
1377 static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1378 ALsizei SamplesToDo, 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 for(i = 0;i < SamplesToDo;i++) \
1386 out[i*numchans] = func(in[i]); \
1390 DECL_TEMPLATE(ALfloat, aluF2F)
1391 DECL_TEMPLATE(ALuint, aluF2UI)
1392 DECL_TEMPLATE(ALint, aluF2I)
1393 DECL_TEMPLATE(ALushort, aluF2US)
1394 DECL_TEMPLATE(ALshort, aluF2S)
1395 DECL_TEMPLATE(ALubyte, aluF2UB)
1396 DECL_TEMPLATE(ALbyte, aluF2B)
1398 #undef DECL_TEMPLATE
1401 void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1403 ALsizei SamplesToDo;
1404 ALvoice *voice, *voice_end;
1405 ALeffectslot *slot;
1406 ALsource *source;
1407 ALCcontext *ctx;
1408 FPUCtl oldMode;
1409 ALsizei i, c;
1411 SetMixerFPUMode(&oldMode);
1413 while(size > 0)
1415 SamplesToDo = mini(size, BUFFERSIZE);
1416 for(c = 0;c < device->Dry.NumChannels;c++)
1417 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1418 if(device->Dry.Buffer != device->RealOut.Buffer)
1419 for(c = 0;c < device->RealOut.NumChannels;c++)
1420 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1421 if(device->Dry.Buffer != device->FOAOut.Buffer)
1422 for(c = 0;c < device->FOAOut.NumChannels;c++)
1423 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1425 IncrementRef(&device->MixCount);
1426 V0(device->Backend,lock)();
1428 if((slot=device->DefaultSlot) != NULL)
1430 CalcEffectSlotParams(device->DefaultSlot, device);
1431 for(i = 0;i < slot->NumChannels;i++)
1432 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1435 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1436 while(ctx)
1438 ALeffectslot *slotroot;
1440 slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList, almemory_order_acquire);
1441 UpdateContextSources(ctx, slotroot);
1443 slot = slotroot;
1444 while(slot)
1446 for(i = 0;i < slot->NumChannels;i++)
1447 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1448 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1451 /* source processing */
1452 voice = ctx->Voices;
1453 voice_end = voice + ctx->VoiceCount;
1454 for(;voice != voice_end;++voice)
1456 ALboolean IsVoiceInit = (voice->Step > 0);
1457 source = voice->Source;
1458 if(IsVoiceInit && source &&
1459 ATOMIC_LOAD(&source->state, almemory_order_relaxed) == AL_PLAYING)
1460 MixSource(voice, source, device, SamplesToDo);
1463 /* effect slot processing */
1464 slot = slotroot;
1465 while(slot)
1467 ALeffectState *state = slot->Params.EffectState;
1468 V(state,process)(SamplesToDo, SAFE_CONST(ALfloatBUFFERSIZE*,slot->WetBuffer),
1469 state->OutBuffer, state->OutChannels);
1470 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1473 ctx = ctx->next;
1476 if(device->DefaultSlot != NULL)
1478 const ALeffectslot *slot = device->DefaultSlot;
1479 ALeffectState *state = slot->Params.EffectState;
1480 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1481 state->OutChannels);
1484 /* Increment the clock time. Every second's worth of samples is
1485 * converted and added to clock base so that large sample counts don't
1486 * overflow during conversion. This also guarantees an exact, stable
1487 * conversion. */
1488 device->SamplesDone += SamplesToDo;
1489 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1490 device->SamplesDone %= device->Frequency;
1491 V0(device->Backend,unlock)();
1492 IncrementRef(&device->MixCount);
1494 if(device->Hrtf.Handle)
1496 HrtfDirectMixerFunc HrtfMix;
1497 ALsizei irsize;
1498 int lidx, ridx;
1500 if(device->AmbiUp)
1501 ambiup_process(device->AmbiUp,
1502 device->Dry.Buffer, device->Dry.NumChannels,
1503 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1506 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1507 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1508 assert(lidx != -1 && ridx != -1);
1510 HrtfMix = SelectHrtfMixer();
1511 irsize = device->Hrtf.IrSize;
1512 for(c = 0;c < device->Dry.NumChannels;c++)
1514 HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1515 device->Dry.Buffer[c], device->Hrtf.Offset, irsize,
1516 device->Hrtf.Coeffs[c], device->Hrtf.Values[c],
1517 SamplesToDo
1520 device->Hrtf.Offset += SamplesToDo;
1522 else if(device->AmbiDecoder)
1524 if(device->Dry.Buffer != device->FOAOut.Buffer)
1525 bformatdec_upSample(device->AmbiDecoder,
1526 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1527 device->FOAOut.NumChannels, SamplesToDo
1529 bformatdec_process(device->AmbiDecoder,
1530 device->RealOut.Buffer, device->RealOut.NumChannels,
1531 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1534 else if(device->AmbiUp)
1536 ambiup_process(device->AmbiUp,
1537 device->RealOut.Buffer, device->RealOut.NumChannels,
1538 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1541 else if(device->Uhj_Encoder)
1543 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1544 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1545 if(lidx != -1 && ridx != -1)
1547 /* Encode to stereo-compatible 2-channel UHJ output. */
1548 EncodeUhj2(device->Uhj_Encoder,
1549 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1550 device->Dry.Buffer, SamplesToDo
1554 else if(device->Bs2b)
1556 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1557 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1558 if(lidx != -1 && ridx != -1)
1560 /* Apply binaural/crossfeed filter */
1561 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1562 device->RealOut.Buffer[ridx], SamplesToDo);
1566 if(buffer)
1568 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1569 ALsizei OutChannels = device->RealOut.NumChannels;
1571 #define WRITE(T, a, b, c, d) do { \
1572 Write_##T((a), (b), (c), (d)); \
1573 buffer = (T*)buffer + (c)*(d); \
1574 } while(0)
1575 switch(device->FmtType)
1577 case DevFmtByte:
1578 WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1579 break;
1580 case DevFmtUByte:
1581 WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1582 break;
1583 case DevFmtShort:
1584 WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
1585 break;
1586 case DevFmtUShort:
1587 WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
1588 break;
1589 case DevFmtInt:
1590 WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
1591 break;
1592 case DevFmtUInt:
1593 WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
1594 break;
1595 case DevFmtFloat:
1596 WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
1597 break;
1599 #undef WRITE
1602 size -= SamplesToDo;
1605 RestoreFPUMode(&oldMode);
1609 void aluHandleDisconnect(ALCdevice *device)
1611 ALCcontext *Context;
1613 device->Connected = ALC_FALSE;
1615 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1616 while(Context)
1618 ALvoice *voice, *voice_end;
1620 voice = Context->Voices;
1621 voice_end = voice + Context->VoiceCount;
1622 while(voice != voice_end)
1624 ALenum playing = AL_PLAYING;
1625 ALsource *source = voice->Source;
1626 voice->Source = NULL;
1628 if(source &&
1629 ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(ALenum, &source->state, &playing, AL_STOPPED))
1631 ATOMIC_STORE(&source->current_buffer, NULL, almemory_order_relaxed);
1632 ATOMIC_STORE(&source->position, 0, almemory_order_relaxed);
1633 ATOMIC_STORE(&source->position_fraction, 0, almemory_order_release);
1636 voice++;
1638 Context->VoiceCount = 0;
1640 Context = Context->next;