Add 'restrict' to another parameter
[openal-soft.git] / Alc / ALu.c
blob70fe7f49f486f107df255b4440456e126a4f7510
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);
97 static inline HrtfMixerFunc SelectHrtfMixer(void)
99 #ifdef HAVE_SSE
100 if((CPUCapFlags&CPU_CAP_SSE))
101 return MixHrtf_SSE;
102 #endif
103 #ifdef HAVE_NEON
104 if((CPUCapFlags&CPU_CAP_NEON))
105 return MixHrtf_Neon;
106 #endif
108 return MixHrtf_C;
112 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
114 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
115 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
116 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
119 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
121 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
124 static ALfloat aluNormalize(ALfloat *vec)
126 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
127 if(length > 0.0f)
129 ALfloat inv_length = 1.0f/length;
130 vec[0] *= inv_length;
131 vec[1] *= inv_length;
132 vec[2] *= inv_length;
134 return length;
137 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
139 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
141 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];
142 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];
143 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];
146 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
148 aluVector v;
149 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];
150 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];
151 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];
152 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];
153 return v;
157 /* Prepares the interpolator for a given rate (determined by increment). A
158 * result of AL_FALSE indicates that the filter output will completely cut
159 * the input signal.
161 * With a bit of work, and a trade of memory for CPU cost, this could be
162 * modified for use with an interpolated increment for buttery-smooth pitch
163 * changes.
165 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
167 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
168 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
169 static const ALuint to[4][BSINC_SCALE_COUNT] =
171 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
172 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
173 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
174 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
176 static const ALuint tm[2][BSINC_SCALE_COUNT] =
178 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
179 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
181 ALfloat sf;
182 ALuint si, pi;
183 ALboolean uncut = AL_TRUE;
185 if(increment > FRACTIONONE)
187 sf = (ALfloat)FRACTIONONE / increment;
188 if(sf < scaleBase)
190 /* Signal has been completely cut. The return result can be used
191 * to skip the filter (and output zeros) as an optimization.
193 sf = 0.0f;
194 si = 0;
195 uncut = AL_FALSE;
197 else
199 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
200 si = fastf2u(sf);
201 /* The interpolation factor is fit to this diagonally-symmetric
202 * curve to reduce the transition ripple caused by interpolating
203 * different scales of the sinc function.
205 sf = 1.0f - cosf(asinf(sf - si));
208 else
210 sf = 0.0f;
211 si = BSINC_SCALE_COUNT - 1;
214 state->sf = sf;
215 state->m = m[si];
216 state->l = -(ALint)((m[si] / 2) - 1);
217 /* The CPU cost of this table re-mapping could be traded for the memory
218 * cost of a complete table map (1024 elements large).
220 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
222 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
223 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
224 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
225 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
227 return uncut;
231 static void CalcListenerParams(ALCcontext *Context)
233 ALlistener *Listener = Context->Listener;
234 ALfloat N[3], V[3], U[3], P[3];
235 struct ALlistenerProps *first;
236 struct ALlistenerProps *props;
237 aluVector vel;
239 props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &Listener->Update, NULL, almemory_order_acq_rel);
240 if(!props) return;
242 /* AT then UP */
243 N[0] = ATOMIC_LOAD(&props->Forward[0], almemory_order_relaxed);
244 N[1] = ATOMIC_LOAD(&props->Forward[1], almemory_order_relaxed);
245 N[2] = ATOMIC_LOAD(&props->Forward[2], almemory_order_relaxed);
246 aluNormalize(N);
247 V[0] = ATOMIC_LOAD(&props->Up[0], almemory_order_relaxed);
248 V[1] = ATOMIC_LOAD(&props->Up[1], almemory_order_relaxed);
249 V[2] = ATOMIC_LOAD(&props->Up[2], almemory_order_relaxed);
250 aluNormalize(V);
251 /* Build and normalize right-vector */
252 aluCrossproduct(N, V, U);
253 aluNormalize(U);
255 aluMatrixfSet(&Listener->Params.Matrix,
256 U[0], V[0], -N[0], 0.0,
257 U[1], V[1], -N[1], 0.0,
258 U[2], V[2], -N[2], 0.0,
259 0.0, 0.0, 0.0, 1.0
262 P[0] = ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed);
263 P[1] = ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed);
264 P[2] = ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed);
265 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
266 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
268 aluVectorSet(&vel, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
269 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
270 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
271 0.0f);
272 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
274 Listener->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
275 Listener->Params.MetersPerUnit = ATOMIC_LOAD(&props->MetersPerUnit, almemory_order_relaxed);
277 Listener->Params.DopplerFactor = ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
278 Listener->Params.SpeedOfSound = ATOMIC_LOAD(&props->SpeedOfSound, almemory_order_relaxed) *
279 ATOMIC_LOAD(&props->DopplerVelocity, almemory_order_relaxed);
281 /* WARNING: A livelock is theoretically possible if another thread keeps
282 * changing the freelist head without giving this a chance to actually swap
283 * in the old container (practically impossible with this little code,
284 * but...).
286 first = ATOMIC_LOAD(&Listener->FreeList);
287 do {
288 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
289 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*,
290 &Listener->FreeList, &first, props) == 0);
293 static void CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
295 struct ALeffectslotProps *first;
296 struct ALeffectslotProps *props;
297 ALeffectState *state;
299 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL, almemory_order_acq_rel);
300 if(!props) return;
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;
317 state = ATOMIC_EXCHANGE(ALeffectState*, &props->State, NULL, almemory_order_relaxed);
319 /* If the state object is changed, exchange it with the current one so it
320 * remains in the freelist and isn't leaked.
322 if(state != slot->Params.EffectState)
324 ATOMIC_STORE(&props->State, slot->Params.EffectState, almemory_order_relaxed);
325 slot->Params.EffectState = state;
328 V(slot->Params.EffectState,update)(device, slot, &props->Props);
330 /* WARNING: A livelock is theoretically possible if another thread keeps
331 * changing the freelist head without giving this a chance to actually swap
332 * in the old container (practically impossible with this little code,
333 * but...).
335 first = ATOMIC_LOAD(&slot->FreeList);
336 do {
337 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
338 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
339 &slot->FreeList, &first, props) == 0);
343 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
345 static const struct ChanMap MonoMap[1] = {
346 { FrontCenter, 0.0f, 0.0f }
347 }, RearMap[2] = {
348 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
349 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
350 }, QuadMap[4] = {
351 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
352 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
353 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
354 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
355 }, X51Map[6] = {
356 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
357 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
358 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
359 { LFE, 0.0f, 0.0f },
360 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
361 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
362 }, X61Map[7] = {
363 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
364 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
365 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
366 { LFE, 0.0f, 0.0f },
367 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
368 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
369 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
370 }, X71Map[8] = {
371 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
372 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
373 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
374 { LFE, 0.0f, 0.0f },
375 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
376 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
377 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
378 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
381 const ALCdevice *Device = ALContext->Device;
382 const ALlistener *Listener = ALContext->Listener;
383 ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
384 ALfloat DryGain, DryGainHF, DryGainLF;
385 ALfloat WetGain[MAX_SENDS];
386 ALfloat WetGainHF[MAX_SENDS];
387 ALfloat WetGainLF[MAX_SENDS];
388 ALeffectslot *SendSlots[MAX_SENDS];
389 ALuint NumSends, Frequency;
390 ALboolean Relative;
391 const struct ChanMap *chans = NULL;
392 struct ChanMap StereoMap[2] = {
393 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
394 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
396 ALuint num_channels = 0;
397 ALboolean DirectChannels;
398 ALboolean isbformat = AL_FALSE;
399 ALfloat Pitch;
400 ALuint i, j, c;
402 /* Get device properties */
403 NumSends = Device->NumAuxSends;
404 Frequency = Device->Frequency;
406 /* Get listener properties */
407 ListenerGain = Listener->Params.Gain;
409 /* Get source properties */
410 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
411 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
412 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
413 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
414 Relative = ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed);
415 DirectChannels = ATOMIC_LOAD(&props->DirectChannels, almemory_order_relaxed);
417 /* Convert counter-clockwise to clockwise. */
418 StereoMap[0].angle = -ATOMIC_LOAD(&props->StereoPan[0], almemory_order_relaxed);
419 StereoMap[1].angle = -ATOMIC_LOAD(&props->StereoPan[1], almemory_order_relaxed);
421 voice->DirectOut.Buffer = Device->Dry.Buffer;
422 voice->DirectOut.Channels = Device->Dry.NumChannels;
423 for(i = 0;i < NumSends;i++)
425 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
426 if(!SendSlots[i] && i == 0)
427 SendSlots[i] = Device->DefaultSlot;
428 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
430 SendSlots[i] = NULL;
431 voice->SendOut[i].Buffer = NULL;
432 voice->SendOut[i].Channels = 0;
434 else
436 voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer;
437 voice->SendOut[i].Channels = SendSlots[i]->NumChannels;
441 /* Calculate the stepping value */
442 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
443 if(Pitch > (ALfloat)MAX_PITCH)
444 voice->Step = MAX_PITCH<<FRACTIONBITS;
445 else
446 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
447 BsincPrepare(voice->Step, &voice->SincState);
449 /* Calculate gains */
450 DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
451 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
452 DryGainHF = ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
453 DryGainLF = ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
454 for(i = 0;i < NumSends;i++)
456 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
457 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
458 WetGainHF[i] = ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
459 WetGainLF[i] = ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
462 switch(ALBuffer->FmtChannels)
464 case FmtMono:
465 chans = MonoMap;
466 num_channels = 1;
467 break;
469 case FmtStereo:
470 chans = StereoMap;
471 num_channels = 2;
472 break;
474 case FmtRear:
475 chans = RearMap;
476 num_channels = 2;
477 break;
479 case FmtQuad:
480 chans = QuadMap;
481 num_channels = 4;
482 break;
484 case FmtX51:
485 chans = X51Map;
486 num_channels = 6;
487 break;
489 case FmtX61:
490 chans = X61Map;
491 num_channels = 7;
492 break;
494 case FmtX71:
495 chans = X71Map;
496 num_channels = 8;
497 break;
499 case FmtBFormat2D:
500 num_channels = 3;
501 isbformat = AL_TRUE;
502 DirectChannels = AL_FALSE;
503 break;
505 case FmtBFormat3D:
506 num_channels = 4;
507 isbformat = AL_TRUE;
508 DirectChannels = AL_FALSE;
509 break;
512 if(isbformat)
514 ALfloat N[3], V[3], U[3];
515 aluMatrixf matrix;
516 ALfloat scale;
518 /* AT then UP */
519 N[0] = ATOMIC_LOAD(&props->Orientation[0][0], almemory_order_relaxed);
520 N[1] = ATOMIC_LOAD(&props->Orientation[0][1], almemory_order_relaxed);
521 N[2] = ATOMIC_LOAD(&props->Orientation[0][2], almemory_order_relaxed);
522 aluNormalize(N);
523 V[0] = ATOMIC_LOAD(&props->Orientation[1][0], almemory_order_relaxed);
524 V[1] = ATOMIC_LOAD(&props->Orientation[1][1], almemory_order_relaxed);
525 V[2] = ATOMIC_LOAD(&props->Orientation[1][2], almemory_order_relaxed);
526 aluNormalize(V);
527 if(!Relative)
529 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
530 aluMatrixfFloat3(N, 0.0f, lmatrix);
531 aluMatrixfFloat3(V, 0.0f, lmatrix);
533 /* Build and normalize right-vector */
534 aluCrossproduct(N, V, U);
535 aluNormalize(U);
537 /* Build a rotate + conversion matrix (B-Format -> N3D). */
538 scale = 1.732050808f;
539 aluMatrixfSet(&matrix,
540 1.414213562f, 0.0f, 0.0f, 0.0f,
541 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
542 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
543 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
546 voice->DirectOut.Buffer = Device->FOAOut.Buffer;
547 voice->DirectOut.Channels = Device->FOAOut.NumChannels;
548 for(c = 0;c < num_channels;c++)
549 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
550 voice->Chan[c].Direct.Gains.Target);
552 for(i = 0;i < NumSends;i++)
554 if(!SendSlots[i])
556 for(c = 0;c < num_channels;c++)
558 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
559 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
562 else
564 for(c = 0;c < num_channels;c++)
566 const ALeffectslot *Slot = SendSlots[i];
567 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, matrix.m[c],
568 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
573 voice->IsHrtf = AL_FALSE;
575 else
577 ALfloat coeffs[MAX_AMBI_COEFFS];
579 if(DirectChannels)
581 /* Skip the virtual channels and write inputs to the real output. */
582 voice->DirectOut.Buffer = Device->RealOut.Buffer;
583 voice->DirectOut.Channels = Device->RealOut.NumChannels;
584 for(c = 0;c < num_channels;c++)
586 int idx;
587 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
588 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
589 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
590 voice->Chan[c].Direct.Gains.Target[idx] = DryGain;
593 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
594 * channel-match. */
595 for(c = 0;c < num_channels;c++)
597 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
599 for(i = 0;i < NumSends;i++)
601 if(!SendSlots[i])
603 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
604 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
606 else
608 const ALeffectslot *Slot = SendSlots[i];
609 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
610 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
615 voice->IsHrtf = AL_FALSE;
617 else if(Device->Render_Mode == HrtfRender)
619 /* Full HRTF rendering. Skip the virtual channels and render each
620 * input channel to the real outputs.
622 voice->DirectOut.Buffer = Device->RealOut.Buffer;
623 voice->DirectOut.Channels = Device->RealOut.NumChannels;
624 for(c = 0;c < num_channels;c++)
626 if(chans[c].channel == LFE)
628 /* Skip LFE */
629 voice->Chan[c].Direct.Hrtf.Target.Delay[0] = 0;
630 voice->Chan[c].Direct.Hrtf.Target.Delay[1] = 0;
631 for(i = 0;i < HRIR_LENGTH;i++)
633 voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][0] = 0.0f;
634 voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][1] = 0.0f;
637 for(i = 0;i < NumSends;i++)
639 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
640 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
643 continue;
646 /* Get the static HRIR coefficients and delays for this channel. */
647 GetLerpedHrtfCoeffs(Device->Hrtf,
648 chans[c].elevation, chans[c].angle, 0.0f, DryGain,
649 voice->Chan[c].Direct.Hrtf.Target.Coeffs,
650 voice->Chan[c].Direct.Hrtf.Target.Delay
653 /* Normal panning for auxiliary sends. */
654 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
656 for(i = 0;i < NumSends;i++)
658 if(!SendSlots[i])
660 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
661 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
663 else
665 const ALeffectslot *Slot = SendSlots[i];
666 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
667 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
672 voice->IsHrtf = AL_TRUE;
674 else
676 /* Non-HRTF rendering. Use normal panning to the output. */
677 for(c = 0;c < num_channels;c++)
679 /* Special-case LFE */
680 if(chans[c].channel == LFE)
682 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
683 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
684 if(Device->Dry.Buffer == Device->RealOut.Buffer)
686 int idx;
687 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
688 voice->Chan[c].Direct.Gains.Target[idx] = DryGain;
691 for(i = 0;i < NumSends;i++)
693 ALuint j;
694 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
695 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
697 continue;
700 if(Device->Render_Mode == StereoPair)
702 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
703 ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
704 coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
705 voice->Chan[c].Direct.Gains.Target[0] = coeffs[0] * DryGain;
706 voice->Chan[c].Direct.Gains.Target[1] = (1.0f-coeffs[0]) * DryGain;
707 for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
708 voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
710 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
712 else
714 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
715 ComputePanningGains(Device->Dry, coeffs, DryGain,
716 voice->Chan[c].Direct.Gains.Target);
719 for(i = 0;i < NumSends;i++)
721 if(!SendSlots[i])
723 ALuint j;
724 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
725 voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
727 else
729 const ALeffectslot *Slot = SendSlots[i];
730 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
731 WetGain[i], voice->Chan[c].Send[i].Gains.Target);
736 voice->IsHrtf = AL_FALSE;
741 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
742 Frequency;
743 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
744 Frequency;
745 DryGainHF = maxf(DryGainHF, 0.0001f);
746 DryGainLF = maxf(DryGainLF, 0.0001f);
747 for(c = 0;c < num_channels;c++)
749 voice->Chan[c].Direct.FilterType = AF_None;
750 if(DryGainHF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_LowPass;
751 if(DryGainLF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_HighPass;
752 ALfilterState_setParams(
753 &voice->Chan[c].Direct.LowPass, ALfilterType_HighShelf,
754 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
756 ALfilterState_setParams(
757 &voice->Chan[c].Direct.HighPass, ALfilterType_LowShelf,
758 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
762 for(i = 0;i < NumSends;i++)
764 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
765 Frequency;
766 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
767 Frequency;
768 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
769 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
770 for(c = 0;c < num_channels;c++)
772 voice->Chan[c].Send[i].FilterType = AF_None;
773 if(WetGainHF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_LowPass;
774 if(WetGainLF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_HighPass;
775 ALfilterState_setParams(
776 &voice->Chan[c].Send[i].LowPass, ALfilterType_HighShelf,
777 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
779 ALfilterState_setParams(
780 &voice->Chan[c].Send[i].HighPass, ALfilterType_LowShelf,
781 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
787 static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
789 const ALCdevice *Device = ALContext->Device;
790 const ALlistener *Listener = ALContext->Listener;
791 aluVector Position, Velocity, Direction, SourceToListener;
792 ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
793 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
794 ALfloat SourceVolume,ListenerGain;
795 ALfloat DopplerFactor, SpeedOfSound;
796 ALfloat AirAbsorptionFactor;
797 ALfloat RoomAirAbsorption[MAX_SENDS];
798 ALeffectslot *SendSlots[MAX_SENDS];
799 ALfloat Attenuation;
800 ALfloat RoomAttenuation[MAX_SENDS];
801 ALfloat MetersPerUnit;
802 ALfloat RoomRolloffBase;
803 ALfloat RoomRolloff[MAX_SENDS];
804 ALfloat DecayDistance[MAX_SENDS];
805 ALfloat DryGain;
806 ALfloat DryGainHF;
807 ALfloat DryGainLF;
808 ALboolean DryGainHFAuto;
809 ALfloat WetGain[MAX_SENDS];
810 ALfloat WetGainHF[MAX_SENDS];
811 ALfloat WetGainLF[MAX_SENDS];
812 ALboolean WetGainAuto;
813 ALboolean WetGainHFAuto;
814 ALfloat Pitch;
815 ALuint Frequency;
816 ALint NumSends;
817 ALint i;
819 DryGainHF = 1.0f;
820 DryGainLF = 1.0f;
821 for(i = 0;i < MAX_SENDS;i++)
823 WetGainHF[i] = 1.0f;
824 WetGainLF[i] = 1.0f;
827 /* Get context/device properties */
828 DopplerFactor = Listener->Params.DopplerFactor;
829 SpeedOfSound = Listener->Params.SpeedOfSound;
830 NumSends = Device->NumAuxSends;
831 Frequency = Device->Frequency;
833 /* Get listener properties */
834 ListenerGain = Listener->Params.Gain;
835 MetersPerUnit = Listener->Params.MetersPerUnit;
837 /* Get source properties */
838 SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
839 MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
840 MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
841 Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
842 aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed),
843 ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed),
844 ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed),
845 1.0f);
846 aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed),
847 ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed),
848 ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed),
849 0.0f);
850 aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
851 ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
852 ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
853 0.0f);
854 MinDist = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed);
855 MaxDist = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed);
856 Rolloff = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed);
857 DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
858 InnerAngle = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed);
859 OuterAngle = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed);
860 AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed);
861 DryGainHFAuto = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed);
862 WetGainAuto = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed);
863 WetGainHFAuto = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed);
864 RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed);
866 voice->DirectOut.Buffer = Device->Dry.Buffer;
867 voice->DirectOut.Channels = Device->Dry.NumChannels;
868 for(i = 0;i < NumSends;i++)
870 SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
872 if(!SendSlots[i] && i == 0)
873 SendSlots[i] = Device->DefaultSlot;
874 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
876 SendSlots[i] = NULL;
877 RoomRolloff[i] = 0.0f;
878 DecayDistance[i] = 0.0f;
879 RoomAirAbsorption[i] = 1.0f;
881 else if(SendSlots[i]->Params.AuxSendAuto)
883 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
884 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
885 SPEEDOFSOUNDMETRESPERSEC;
886 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
888 else
890 /* If the slot's auxiliary send auto is off, the data sent to the
891 * effect slot is the same as the dry path, sans filter effects */
892 RoomRolloff[i] = Rolloff;
893 DecayDistance[i] = 0.0f;
894 RoomAirAbsorption[i] = AIRABSORBGAINHF;
897 if(!SendSlots[i])
899 voice->SendOut[i].Buffer = NULL;
900 voice->SendOut[i].Channels = 0;
902 else
904 voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer;
905 voice->SendOut[i].Channels = SendSlots[i]->NumChannels;
909 /* Transform source to listener space (convert to head relative) */
910 if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE)
912 const aluMatrixf *Matrix = &Listener->Params.Matrix;
913 /* Transform source vectors */
914 Position = aluMatrixfVector(Matrix, &Position);
915 Velocity = aluMatrixfVector(Matrix, &Velocity);
916 Direction = aluMatrixfVector(Matrix, &Direction);
918 else
920 const aluVector *lvelocity = &Listener->Params.Velocity;
921 /* Offset the source velocity to be relative of the listener velocity */
922 Velocity.v[0] += lvelocity->v[0];
923 Velocity.v[1] += lvelocity->v[1];
924 Velocity.v[2] += lvelocity->v[2];
927 aluNormalize(Direction.v);
928 SourceToListener.v[0] = -Position.v[0];
929 SourceToListener.v[1] = -Position.v[1];
930 SourceToListener.v[2] = -Position.v[2];
931 SourceToListener.v[3] = 0.0f;
932 Distance = aluNormalize(SourceToListener.v);
934 /* Calculate distance attenuation */
935 ClampedDist = Distance;
937 Attenuation = 1.0f;
938 for(i = 0;i < NumSends;i++)
939 RoomAttenuation[i] = 1.0f;
940 switch(ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed))
942 case InverseDistanceClamped:
943 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
944 if(MaxDist < MinDist)
945 break;
946 /*fall-through*/
947 case InverseDistance:
948 if(MinDist > 0.0f)
950 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
951 if(dist > 0.0f) Attenuation = MinDist / dist;
952 for(i = 0;i < NumSends;i++)
954 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
955 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
958 break;
960 case LinearDistanceClamped:
961 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
962 if(MaxDist < MinDist)
963 break;
964 /*fall-through*/
965 case LinearDistance:
966 if(MaxDist != MinDist)
968 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
969 Attenuation = maxf(Attenuation, 0.0f);
970 for(i = 0;i < NumSends;i++)
972 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
973 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
976 break;
978 case ExponentDistanceClamped:
979 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
980 if(MaxDist < MinDist)
981 break;
982 /*fall-through*/
983 case ExponentDistance:
984 if(ClampedDist > 0.0f && MinDist > 0.0f)
986 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
987 for(i = 0;i < NumSends;i++)
988 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
990 break;
992 case DisableDistance:
993 ClampedDist = MinDist;
994 break;
997 /* Source Gain + Attenuation */
998 DryGain = SourceVolume * Attenuation;
999 for(i = 0;i < NumSends;i++)
1000 WetGain[i] = SourceVolume * RoomAttenuation[i];
1002 /* Distance-based air absorption */
1003 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
1005 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
1006 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
1007 for(i = 0;i < NumSends;i++)
1008 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
1011 if(WetGainAuto)
1013 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1015 /* Apply a decay-time transformation to the wet path, based on the
1016 * attenuation of the dry path.
1018 * Using the apparent distance, based on the distance attenuation, the
1019 * initial decay of the reverb effect is calculated and applied to the
1020 * wet path.
1022 for(i = 0;i < NumSends;i++)
1024 if(DecayDistance[i] > 0.0f)
1025 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1029 /* Calculate directional soundcones */
1030 if(InnerAngle < 360.0f)
1032 ALfloat ConeVolume;
1033 ALfloat ConeHF;
1034 ALfloat Angle;
1035 ALfloat scale;
1037 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1038 if(Angle > InnerAngle)
1040 if(Angle < OuterAngle)
1042 scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
1043 ConeVolume = lerp(
1044 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1046 ConeHF = lerp(
1047 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1050 else
1052 ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed);
1053 ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed);
1055 DryGain *= ConeVolume;
1056 if(DryGainHFAuto)
1057 DryGainHF *= ConeHF;
1060 /* Wet path uses the total area of the cone emitter (the room will
1061 * receive the same amount of sound regardless of its direction).
1063 scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
1064 (InnerAngle/360.0f);
1065 if(WetGainAuto)
1067 ConeVolume = lerp(
1068 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
1070 for(i = 0;i < NumSends;i++)
1071 WetGain[i] *= ConeVolume;
1073 if(WetGainHFAuto)
1075 ConeHF = lerp(
1076 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
1078 for(i = 0;i < NumSends;i++)
1079 WetGainHF[i] *= ConeHF;
1083 /* Clamp to Min/Max Gain */
1084 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1085 for(i = 0;i < NumSends;i++)
1086 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1088 /* Apply gain and frequency filters */
1089 DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
1090 DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
1091 DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
1092 for(i = 0;i < NumSends;i++)
1094 WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
1095 WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
1096 WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
1099 /* Calculate velocity-based doppler effect */
1100 if(DopplerFactor > 0.0f)
1102 const aluVector *lvelocity = &Listener->Params.Velocity;
1103 ALfloat VSS, VLS;
1105 if(SpeedOfSound < 1.0f)
1107 DopplerFactor *= 1.0f/SpeedOfSound;
1108 SpeedOfSound = 1.0f;
1111 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1112 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1114 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1115 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1118 /* Calculate fixed-point stepping value, based on the pitch, buffer
1119 * frequency, and output frequency.
1121 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1122 if(Pitch > (ALfloat)MAX_PITCH)
1123 voice->Step = MAX_PITCH<<FRACTIONBITS;
1124 else
1125 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1126 BsincPrepare(voice->Step, &voice->SincState);
1128 if(Device->Render_Mode == HrtfRender)
1130 /* Full HRTF rendering. Skip the virtual channels and render to the
1131 * real outputs.
1133 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1134 ALfloat ev = 0.0f, az = 0.0f;
1135 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1136 ALfloat coeffs[MAX_AMBI_COEFFS];
1137 ALfloat spread = 0.0f;
1139 voice->DirectOut.Buffer = Device->RealOut.Buffer;
1140 voice->DirectOut.Channels = Device->RealOut.NumChannels;
1142 if(Distance > FLT_EPSILON)
1144 dir[0] = -SourceToListener.v[0];
1145 dir[1] = -SourceToListener.v[1];
1146 dir[2] = -SourceToListener.v[2] * ZScale;
1148 /* Calculate elevation and azimuth only when the source is not at
1149 * the listener. This prevents +0 and -0 Z from producing
1150 * inconsistent panning. Also, clamp Y in case FP precision errors
1151 * cause it to land outside of -1..+1. */
1152 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1153 az = atan2f(dir[0], -dir[2]);
1155 if(radius > Distance)
1156 spread = F_TAU - Distance/radius*F_PI;
1157 else if(Distance > FLT_EPSILON)
1158 spread = asinf(radius / Distance) * 2.0f;
1160 /* Get the HRIR coefficients and delays. */
1161 GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, spread, DryGain,
1162 voice->Chan[0].Direct.Hrtf.Target.Coeffs,
1163 voice->Chan[0].Direct.Hrtf.Target.Delay);
1165 CalcDirectionCoeffs(dir, spread, coeffs);
1167 for(i = 0;i < NumSends;i++)
1169 if(!SendSlots[i])
1171 ALuint j;
1172 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1173 voice->Chan[0].Send[i].Gains.Target[j] = 0.0f;
1175 else
1177 const ALeffectslot *Slot = SendSlots[i];
1178 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1179 WetGain[i], voice->Chan[0].Send[i].Gains.Target);
1183 voice->IsHrtf = AL_TRUE;
1185 else
1187 /* Non-HRTF rendering. */
1188 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1189 ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
1190 ALfloat coeffs[MAX_AMBI_COEFFS];
1191 ALfloat spread = 0.0f;
1193 /* Get the localized direction, and compute panned gains. */
1194 if(Distance > FLT_EPSILON)
1196 dir[0] = -SourceToListener.v[0];
1197 dir[1] = -SourceToListener.v[1];
1198 dir[2] = -SourceToListener.v[2] * ZScale;
1200 if(radius > Distance)
1201 spread = F_TAU - Distance/radius*F_PI;
1202 else if(Distance > FLT_EPSILON)
1203 spread = asinf(radius / Distance) * 2.0f;
1205 if(Device->Render_Mode == StereoPair)
1207 /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
1208 ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
1209 x = clampf(x, -0.5f, 0.5f) + 0.5f;
1210 voice->Chan[0].Direct.Gains.Target[0] = x * DryGain;
1211 voice->Chan[0].Direct.Gains.Target[1] = (1.0f-x) * DryGain;
1212 for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
1213 voice->Chan[0].Direct.Gains.Target[i] = 0.0f;
1215 CalcDirectionCoeffs(dir, spread, coeffs);
1217 else
1219 CalcDirectionCoeffs(dir, spread, coeffs);
1220 ComputePanningGains(Device->Dry, coeffs, DryGain,
1221 voice->Chan[0].Direct.Gains.Target);
1224 for(i = 0;i < NumSends;i++)
1226 if(!SendSlots[i])
1228 ALuint j;
1229 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1230 voice->Chan[0].Send[i].Gains.Target[j] = 0.0f;
1232 else
1234 const ALeffectslot *Slot = SendSlots[i];
1235 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
1236 WetGain[i], voice->Chan[0].Send[i].Gains.Target);
1240 voice->IsHrtf = AL_FALSE;
1244 ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
1245 Frequency;
1246 ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
1247 Frequency;
1248 DryGainHF = maxf(DryGainHF, 0.0001f);
1249 DryGainLF = maxf(DryGainLF, 0.0001f);
1250 voice->Chan[0].Direct.FilterType = AF_None;
1251 if(DryGainHF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_LowPass;
1252 if(DryGainLF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_HighPass;
1253 ALfilterState_setParams(
1254 &voice->Chan[0].Direct.LowPass, ALfilterType_HighShelf,
1255 DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
1257 ALfilterState_setParams(
1258 &voice->Chan[0].Direct.HighPass, ALfilterType_LowShelf,
1259 DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
1262 for(i = 0;i < NumSends;i++)
1264 ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
1265 Frequency;
1266 ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
1267 Frequency;
1268 WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
1269 WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
1270 voice->Chan[0].Send[i].FilterType = AF_None;
1271 if(WetGainHF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_LowPass;
1272 if(WetGainLF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_HighPass;
1273 ALfilterState_setParams(
1274 &voice->Chan[0].Send[i].LowPass, ALfilterType_HighShelf,
1275 WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
1277 ALfilterState_setParams(
1278 &voice->Chan[0].Send[i].HighPass, ALfilterType_LowShelf,
1279 WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
1284 static void CalcSourceParams(ALvoice *voice, ALCcontext *context)
1286 ALsource *source = voice->Source;
1287 ALbufferlistitem *BufferListItem;
1288 struct ALsourceProps *first;
1289 struct ALsourceProps *props;
1291 props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
1292 if(!props) return;
1294 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1295 while(BufferListItem != NULL)
1297 ALbuffer *buffer;
1298 if((buffer=BufferListItem->buffer) != NULL)
1300 if(buffer->FmtChannels == FmtMono)
1301 CalcAttnSourceParams(voice, props, buffer, context);
1302 else
1303 CalcNonAttnSourceParams(voice, props, buffer, context);
1304 break;
1306 BufferListItem = BufferListItem->next;
1309 /* WARNING: A livelock is theoretically possible if another thread keeps
1310 * changing the freelist head without giving this a chance to actually swap
1311 * in the old container (practically impossible with this little code,
1312 * but...).
1314 first = ATOMIC_LOAD(&source->FreeList);
1315 do {
1316 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
1317 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*,
1318 &source->FreeList, &first, props) == 0);
1322 static void UpdateContextSources(ALCcontext *ctx, ALeffectslot *slot)
1324 ALvoice *voice, *voice_end;
1325 ALsource *source;
1327 IncrementRef(&ctx->UpdateCount);
1328 if(!ATOMIC_LOAD(&ctx->HoldUpdates))
1330 CalcListenerParams(ctx);
1331 while(slot)
1333 CalcEffectSlotParams(slot, ctx->Device);
1334 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1337 voice = ctx->Voices;
1338 voice_end = voice + ctx->VoiceCount;
1339 for(;voice != voice_end;++voice)
1341 if(!(source=voice->Source)) continue;
1342 if(source->state != AL_PLAYING && source->state != AL_PAUSED)
1343 voice->Source = NULL;
1344 else
1345 CalcSourceParams(voice, ctx);
1348 IncrementRef(&ctx->UpdateCount);
1352 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1353 * converts NaN to 0. */
1354 static inline ALfloat aluClampf(ALfloat val)
1356 if(fabsf(val) <= 1.0f) return val;
1357 return (ALfloat)((0.0f < val) - (val < 0.0f));
1360 static inline ALfloat aluF2F(ALfloat val)
1361 { return val; }
1363 static inline ALint aluF2I(ALfloat val)
1365 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1366 * integer range normalized floats can be safely converted to.
1368 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1370 static inline ALuint aluF2UI(ALfloat val)
1371 { return aluF2I(val)+2147483648u; }
1373 static inline ALshort aluF2S(ALfloat val)
1374 { return fastf2i(aluClampf(val)*32767.0f); }
1375 static inline ALushort aluF2US(ALfloat val)
1376 { return aluF2S(val)+32768; }
1378 static inline ALbyte aluF2B(ALfloat val)
1379 { return fastf2i(aluClampf(val)*127.0f); }
1380 static inline ALubyte aluF2UB(ALfloat val)
1381 { return aluF2B(val)+128; }
1383 #define DECL_TEMPLATE(T, func) \
1384 static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1385 ALuint SamplesToDo, ALuint numchans) \
1387 ALuint i, j; \
1388 for(j = 0;j < numchans;j++) \
1390 const ALfloat *in = InBuffer[j]; \
1391 T *restrict out = (T*)OutBuffer + j; \
1392 for(i = 0;i < SamplesToDo;i++) \
1393 out[i*numchans] = func(in[i]); \
1397 DECL_TEMPLATE(ALfloat, aluF2F)
1398 DECL_TEMPLATE(ALuint, aluF2UI)
1399 DECL_TEMPLATE(ALint, aluF2I)
1400 DECL_TEMPLATE(ALushort, aluF2US)
1401 DECL_TEMPLATE(ALshort, aluF2S)
1402 DECL_TEMPLATE(ALubyte, aluF2UB)
1403 DECL_TEMPLATE(ALbyte, aluF2B)
1405 #undef DECL_TEMPLATE
1408 ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1410 ALuint SamplesToDo;
1411 ALvoice *voice, *voice_end;
1412 ALeffectslot *slot;
1413 ALsource *source;
1414 ALCcontext *ctx;
1415 FPUCtl oldMode;
1416 ALuint i, c;
1418 SetMixerFPUMode(&oldMode);
1420 while(size > 0)
1422 SamplesToDo = minu(size, BUFFERSIZE);
1423 for(c = 0;c < device->Dry.NumChannels;c++)
1424 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1425 if(device->Dry.Buffer != device->RealOut.Buffer)
1426 for(c = 0;c < device->RealOut.NumChannels;c++)
1427 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1428 if(device->Dry.Buffer != device->FOAOut.Buffer)
1429 for(c = 0;c < device->FOAOut.NumChannels;c++)
1430 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1432 IncrementRef(&device->MixCount);
1433 V0(device->Backend,lock)();
1435 if((slot=device->DefaultSlot) != NULL)
1437 CalcEffectSlotParams(device->DefaultSlot, device);
1438 for(i = 0;i < slot->NumChannels;i++)
1439 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1442 ctx = ATOMIC_LOAD(&device->ContextList);
1443 while(ctx)
1445 ALeffectslot *slotroot;
1447 slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList);
1448 UpdateContextSources(ctx, slotroot);
1450 slot = slotroot;
1451 while(slot)
1453 for(i = 0;i < slot->NumChannels;i++)
1454 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1455 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1458 /* source processing */
1459 voice = ctx->Voices;
1460 voice_end = voice + ctx->VoiceCount;
1461 for(;voice != voice_end;++voice)
1463 ALboolean IsVoiceInit = (voice->Step > 0);
1464 source = voice->Source;
1465 if(source && source->state == AL_PLAYING && IsVoiceInit)
1466 MixSource(voice, source, device, SamplesToDo);
1469 /* effect slot processing */
1470 slot = slotroot;
1471 while(slot)
1473 const ALeffectslot *cslot = slot;
1474 ALeffectState *state = cslot->Params.EffectState;
1475 V(state,process)(SamplesToDo, cslot->WetBuffer, state->OutBuffer,
1476 state->OutChannels);
1477 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1480 ctx = ctx->next;
1483 if(device->DefaultSlot != NULL)
1485 const ALeffectslot *slot = device->DefaultSlot;
1486 ALeffectState *state = slot->Params.EffectState;
1487 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1488 state->OutChannels);
1491 /* Increment the clock time. Every second's worth of samples is
1492 * converted and added to clock base so that large sample counts don't
1493 * overflow during conversion. This also guarantees an exact, stable
1494 * conversion. */
1495 device->SamplesDone += SamplesToDo;
1496 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1497 device->SamplesDone %= device->Frequency;
1498 V0(device->Backend,unlock)();
1499 IncrementRef(&device->MixCount);
1501 if(device->Hrtf)
1503 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1504 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1505 if(lidx != -1 && ridx != -1)
1507 HrtfMixerFunc HrtfMix = SelectHrtfMixer();
1508 ALuint irsize = device->Hrtf->irSize;
1509 MixHrtfParams hrtfparams;
1510 memset(&hrtfparams, 0, sizeof(hrtfparams));
1511 for(c = 0;c < device->Dry.NumChannels;c++)
1513 hrtfparams.Current = &device->Hrtf_Params[c];
1514 hrtfparams.Target = &device->Hrtf_Params[c];
1515 HrtfMix(device->RealOut.Buffer, lidx, ridx,
1516 device->Dry.Buffer[c], 0, device->Hrtf_Offset, 0,
1517 irsize, &hrtfparams, &device->Hrtf_State[c], SamplesToDo
1520 device->Hrtf_Offset += SamplesToDo;
1523 else if(device->AmbiDecoder)
1525 if(device->Dry.Buffer != device->FOAOut.Buffer)
1526 bformatdec_upSample(device->AmbiDecoder,
1527 device->Dry.Buffer, device->FOAOut.Buffer,
1528 device->FOAOut.NumChannels, SamplesToDo
1530 bformatdec_process(device->AmbiDecoder,
1531 device->RealOut.Buffer, device->RealOut.NumChannels,
1532 device->Dry.Buffer, SamplesToDo
1535 else if(device->AmbiUp)
1537 ambiup_process(device->AmbiUp,
1538 device->RealOut.Buffer, device->RealOut.NumChannels,
1539 device->FOAOut.Buffer, SamplesToDo
1542 else if(device->Uhj_Encoder)
1544 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1545 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1546 if(lidx != -1 && ridx != -1)
1548 /* Encode to stereo-compatible 2-channel UHJ output. */
1549 EncodeUhj2(device->Uhj_Encoder,
1550 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1551 device->Dry.Buffer, SamplesToDo
1555 else if(device->Bs2b)
1557 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1558 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1559 if(lidx != -1 && ridx != -1)
1561 /* Apply binaural/crossfeed filter */
1562 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1563 device->RealOut.Buffer[ridx], SamplesToDo);
1567 if(buffer)
1569 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1570 ALuint OutChannels = device->RealOut.NumChannels;
1572 #define WRITE(T, a, b, c, d) do { \
1573 Write_##T((a), (b), (c), (d)); \
1574 buffer = (T*)buffer + (c)*(d); \
1575 } while(0)
1576 switch(device->FmtType)
1578 case DevFmtByte:
1579 WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1580 break;
1581 case DevFmtUByte:
1582 WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1583 break;
1584 case DevFmtShort:
1585 WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
1586 break;
1587 case DevFmtUShort:
1588 WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
1589 break;
1590 case DevFmtInt:
1591 WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
1592 break;
1593 case DevFmtUInt:
1594 WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
1595 break;
1596 case DevFmtFloat:
1597 WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
1598 break;
1600 #undef WRITE
1603 size -= SamplesToDo;
1606 RestoreFPUMode(&oldMode);
1610 ALvoid aluHandleDisconnect(ALCdevice *device)
1612 ALCcontext *Context;
1614 device->Connected = ALC_FALSE;
1616 Context = ATOMIC_LOAD(&device->ContextList);
1617 while(Context)
1619 ALvoice *voice, *voice_end;
1621 voice = Context->Voices;
1622 voice_end = voice + Context->VoiceCount;
1623 while(voice != voice_end)
1625 ALsource *source = voice->Source;
1626 voice->Source = NULL;
1628 if(source && source->state == AL_PLAYING)
1630 source->state = AL_STOPPED;
1631 ATOMIC_STORE(&source->current_buffer, NULL);
1632 ATOMIC_STORE(&source->position, 0);
1633 ATOMIC_STORE(&source->position_fraction, 0);
1636 voice++;
1638 Context->VoiceCount = 0;
1640 Context = Context->next;