Use separate atomic macros for pointers
[openal-soft.git] / Alc / ALu.c
blob89689fbaafd12991fc5f5fa9ab15103466bf6151
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, ALsizei frac);
85 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
87 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
88 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
89 extern inline void aluMatrixfSet(aluMatrixf *matrix,
90 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
91 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
92 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
93 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
95 const aluMatrixf IdentityMatrixf = {{
96 { 1.0f, 0.0f, 0.0f, 0.0f },
97 { 0.0f, 1.0f, 0.0f, 0.0f },
98 { 0.0f, 0.0f, 1.0f, 0.0f },
99 { 0.0f, 0.0f, 0.0f, 1.0f },
103 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
105 #ifdef HAVE_NEON
106 if((CPUCapFlags&CPU_CAP_NEON))
107 return MixDirectHrtf_Neon;
108 #endif
109 #ifdef HAVE_SSE
110 if((CPUCapFlags&CPU_CAP_SSE))
111 return MixDirectHrtf_SSE;
112 #endif
114 return MixDirectHrtf_C;
118 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
120 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
121 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
122 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
125 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
127 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
130 static ALfloat aluNormalize(ALfloat *vec)
132 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
133 if(length > 0.0f)
135 ALfloat inv_length = 1.0f/length;
136 vec[0] *= inv_length;
137 vec[1] *= inv_length;
138 vec[2] *= inv_length;
140 return length;
143 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
145 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
147 vec[0] = v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0];
148 vec[1] = v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1];
149 vec[2] = v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2];
152 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
154 aluVector v;
155 v.v[0] = vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0];
156 v.v[1] = vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1];
157 v.v[2] = vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2];
158 v.v[3] = vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3];
159 return v;
163 /* Prepares the interpolator for a given rate (determined by increment). A
164 * result of AL_FALSE indicates that the filter output will completely cut
165 * the input signal.
167 * With a bit of work, and a trade of memory for CPU cost, this could be
168 * modified for use with an interpolated increment for buttery-smooth pitch
169 * changes.
171 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
173 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
174 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
175 static const ALuint to[4][BSINC_SCALE_COUNT] =
177 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
178 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
179 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
180 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
182 static const ALuint tm[2][BSINC_SCALE_COUNT] =
184 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
185 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
187 ALfloat sf;
188 ALuint si, pi;
189 ALboolean uncut = AL_TRUE;
191 if(increment > FRACTIONONE)
193 sf = (ALfloat)FRACTIONONE / increment;
194 if(sf < scaleBase)
196 /* Signal has been completely cut. The return result can be used
197 * to skip the filter (and output zeros) as an optimization.
199 sf = 0.0f;
200 si = 0;
201 uncut = AL_FALSE;
203 else
205 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
206 si = fastf2u(sf);
207 /* The interpolation factor is fit to this diagonally-symmetric
208 * curve to reduce the transition ripple caused by interpolating
209 * different scales of the sinc function.
211 sf = 1.0f - cosf(asinf(sf - si));
214 else
216 sf = 0.0f;
217 si = BSINC_SCALE_COUNT - 1;
220 state->sf = sf;
221 state->m = m[si];
222 state->l = -(ALint)((m[si] / 2) - 1);
223 /* The CPU cost of this table re-mapping could be traded for the memory
224 * cost of a complete table map (1024 elements large).
226 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
228 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
229 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
230 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
231 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
233 return uncut;
237 static ALboolean CalcListenerParams(ALCcontext *Context)
239 ALlistener *Listener = Context->Listener;
240 ALfloat N[3], V[3], U[3], P[3];
241 struct ALlistenerProps *props;
242 aluVector vel;
244 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
245 if(!props) return AL_FALSE;
247 /* AT then UP */
248 N[0] = props->Forward[0];
249 N[1] = props->Forward[1];
250 N[2] = props->Forward[2];
251 aluNormalize(N);
252 V[0] = props->Up[0];
253 V[1] = props->Up[1];
254 V[2] = props->Up[2];
255 aluNormalize(V);
256 /* Build and normalize right-vector */
257 aluCrossproduct(N, V, U);
258 aluNormalize(U);
260 aluMatrixfSet(&Listener->Params.Matrix,
261 U[0], V[0], -N[0], 0.0,
262 U[1], V[1], -N[1], 0.0,
263 U[2], V[2], -N[2], 0.0,
264 0.0, 0.0, 0.0, 1.0
267 P[0] = props->Position[0];
268 P[1] = props->Position[1];
269 P[2] = props->Position[2];
270 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
271 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
273 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
274 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
276 Listener->Params.Gain = props->Gain * Context->GainBoost;
277 Listener->Params.MetersPerUnit = props->MetersPerUnit;
279 Listener->Params.DopplerFactor = props->DopplerFactor;
280 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
282 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
283 Listener->Params.DistanceModel = props->DistanceModel;
285 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
286 return AL_TRUE;
289 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
291 struct ALeffectslotProps *props;
292 ALeffectState *state;
294 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
295 if(!props) return AL_FALSE;
297 slot->Params.Gain = props->Gain;
298 slot->Params.AuxSendAuto = props->AuxSendAuto;
299 slot->Params.EffectType = props->Type;
300 if(IsReverbEffect(slot->Params.EffectType))
302 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
303 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
304 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
306 else
308 slot->Params.RoomRolloff = 0.0f;
309 slot->Params.DecayTime = 0.0f;
310 slot->Params.AirAbsorptionGainHF = 1.0f;
313 /* Swap effect states. No need to play with the ref counts since they keep
314 * the same number of refs.
316 state = props->State;
317 props->State = slot->Params.EffectState;
318 slot->Params.EffectState = state;
320 V(state,update)(device, slot, &props->Props);
322 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
323 return AL_TRUE;
327 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
329 static const struct ChanMap MonoMap[1] = {
330 { FrontCenter, 0.0f, 0.0f }
331 }, RearMap[2] = {
332 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
333 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
334 }, QuadMap[4] = {
335 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
336 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
337 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
338 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
339 }, X51Map[6] = {
340 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
341 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
342 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
343 { LFE, 0.0f, 0.0f },
344 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
345 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
346 }, X61Map[7] = {
347 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
348 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
349 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
350 { LFE, 0.0f, 0.0f },
351 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
352 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
353 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
354 }, X71Map[8] = {
355 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
356 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
357 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
358 { LFE, 0.0f, 0.0f },
359 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
360 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
361 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
362 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
365 const ALCdevice *Device = ALContext->Device;
366 const ALlistener *Listener = ALContext->Listener;
367 ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
368 ALfloat DryGain, DryGainHF, DryGainLF;
369 ALfloat WetGain[MAX_SENDS];
370 ALfloat WetGainHF[MAX_SENDS];
371 ALfloat WetGainLF[MAX_SENDS];
372 ALeffectslot *SendSlots[MAX_SENDS];
373 ALfloat HFScale, LFScale;
374 ALuint NumSends, Frequency;
375 ALboolean Relative;
376 const struct ChanMap *chans = NULL;
377 struct ChanMap StereoMap[2] = {
378 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
379 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
381 ALuint num_channels = 0;
382 ALboolean DirectChannels;
383 ALboolean isbformat = AL_FALSE;
384 ALfloat Pitch;
385 ALuint i, j, c;
387 /* Get device properties */
388 NumSends = Device->NumAuxSends;
389 Frequency = Device->Frequency;
391 /* Get listener properties */
392 ListenerGain = Listener->Params.Gain;
394 /* Get source properties */
395 SourceVolume = props->Gain;
396 MinVolume = props->MinGain;
397 MaxVolume = props->MaxGain;
398 Pitch = props->Pitch;
399 Relative = props->HeadRelative;
400 DirectChannels = props->DirectChannels;
402 /* Convert counter-clockwise to clockwise. */
403 StereoMap[0].angle = -props->StereoPan[0];
404 StereoMap[1].angle = -props->StereoPan[1];
406 voice->Direct.Buffer = Device->Dry.Buffer;
407 voice->Direct.Channels = Device->Dry.NumChannels;
408 for(i = 0;i < NumSends;i++)
410 SendSlots[i] = props->Send[i].Slot;
411 if(!SendSlots[i] && i == 0)
412 SendSlots[i] = Device->DefaultSlot;
413 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
415 SendSlots[i] = NULL;
416 voice->Send[i].Buffer = NULL;
417 voice->Send[i].Channels = 0;
419 else
421 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
422 voice->Send[i].Channels = SendSlots[i]->NumChannels;
426 /* Calculate the stepping value */
427 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
428 if(Pitch > (ALfloat)MAX_PITCH)
429 voice->Step = MAX_PITCH<<FRACTIONBITS;
430 else
431 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
432 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
434 /* Calculate gains */
435 DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
436 DryGain *= props->Direct.Gain * ListenerGain;
437 DryGain = minf(DryGain, GAIN_MIX_MAX);
438 DryGainHF = props->Direct.GainHF;
439 DryGainLF = props->Direct.GainLF;
440 for(i = 0;i < NumSends;i++)
442 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
443 WetGain[i] *= props->Send[i].Gain * ListenerGain;
444 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
445 WetGainHF[i] = props->Send[i].GainHF;
446 WetGainLF[i] = props->Send[i].GainLF;
449 switch(ALBuffer->FmtChannels)
451 case FmtMono:
452 chans = MonoMap;
453 num_channels = 1;
454 break;
456 case FmtStereo:
457 chans = StereoMap;
458 num_channels = 2;
459 break;
461 case FmtRear:
462 chans = RearMap;
463 num_channels = 2;
464 break;
466 case FmtQuad:
467 chans = QuadMap;
468 num_channels = 4;
469 break;
471 case FmtX51:
472 chans = X51Map;
473 num_channels = 6;
474 break;
476 case FmtX61:
477 chans = X61Map;
478 num_channels = 7;
479 break;
481 case FmtX71:
482 chans = X71Map;
483 num_channels = 8;
484 break;
486 case FmtBFormat2D:
487 num_channels = 3;
488 isbformat = AL_TRUE;
489 DirectChannels = AL_FALSE;
490 break;
492 case FmtBFormat3D:
493 num_channels = 4;
494 isbformat = AL_TRUE;
495 DirectChannels = AL_FALSE;
496 break;
499 voice->Flags &= ~(VOICE_IS_HRTF | VOICE_HAS_NFC);
500 if(isbformat)
502 ALfloat N[3], V[3], U[3];
503 aluMatrixf matrix;
504 ALfloat scale;
506 /* AT then UP */
507 N[0] = props->Orientation[0][0];
508 N[1] = props->Orientation[0][1];
509 N[2] = props->Orientation[0][2];
510 aluNormalize(N);
511 V[0] = props->Orientation[1][0];
512 V[1] = props->Orientation[1][1];
513 V[2] = props->Orientation[1][2];
514 aluNormalize(V);
515 if(!Relative)
517 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
518 aluMatrixfFloat3(N, 0.0f, lmatrix);
519 aluMatrixfFloat3(V, 0.0f, lmatrix);
521 /* Build and normalize right-vector */
522 aluCrossproduct(N, V, U);
523 aluNormalize(U);
525 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
526 scale = 1.732050808f;
527 aluMatrixfSet(&matrix,
528 1.414213562f, 0.0f, 0.0f, 0.0f,
529 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
530 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
531 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
534 voice->Direct.Buffer = Device->FOAOut.Buffer;
535 voice->Direct.Channels = Device->FOAOut.NumChannels;
536 for(c = 0;c < num_channels;c++)
537 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
538 voice->Direct.Params[c].Gains.Target);
539 if(Device->AvgSpeakerDist > 0.0f)
541 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which is
542 * what we want for FOA input. So there's nothing to adjust.
544 voice->Direct.ChannelsPerOrder[0] = 1;
545 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
546 voice->Direct.ChannelsPerOrder[2] = 0;
547 voice->Direct.ChannelsPerOrder[3] = 0;
548 voice->Flags |= VOICE_HAS_NFC;
551 for(i = 0;i < NumSends;i++)
553 const ALeffectslot *Slot = SendSlots[i];
554 if(Slot)
556 for(c = 0;c < num_channels;c++)
557 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
558 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
561 else
563 for(c = 0;c < num_channels;c++)
564 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
565 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
569 else
571 ALfloat coeffs[MAX_AMBI_COEFFS];
573 if(DirectChannels)
575 /* Skip the virtual channels and write inputs to the real output. */
576 voice->Direct.Buffer = Device->RealOut.Buffer;
577 voice->Direct.Channels = Device->RealOut.NumChannels;
578 for(c = 0;c < num_channels;c++)
580 int idx;
581 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
582 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
583 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
584 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
587 /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
588 * channel-match. */
589 for(c = 0;c < num_channels;c++)
591 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
593 for(i = 0;i < NumSends;i++)
595 const ALeffectslot *Slot = SendSlots[i];
596 if(Slot)
597 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
598 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
600 else
601 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
602 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
606 else if(Device->Render_Mode == HrtfRender)
608 /* Full HRTF rendering. Skip the virtual channels and render each
609 * input channel to the real outputs.
611 voice->Direct.Buffer = Device->RealOut.Buffer;
612 voice->Direct.Channels = Device->RealOut.NumChannels;
613 for(c = 0;c < num_channels;c++)
615 if(chans[c].channel == LFE)
617 /* Skip LFE */
618 voice->Direct.Params[c].Hrtf.Target.Delay[0] = 0;
619 voice->Direct.Params[c].Hrtf.Target.Delay[1] = 0;
620 for(i = 0;i < HRIR_LENGTH;i++)
622 voice->Direct.Params[c].Hrtf.Target.Coeffs[i][0] = 0.0f;
623 voice->Direct.Params[c].Hrtf.Target.Coeffs[i][1] = 0.0f;
626 for(i = 0;i < NumSends;i++)
628 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
629 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
632 continue;
635 /* Get the static HRIR coefficients and delays for this channel. */
636 GetHrtfCoeffs(Device->HrtfHandle,
637 chans[c].elevation, chans[c].angle, 0.0f,
638 voice->Direct.Params[c].Hrtf.Target.Coeffs,
639 voice->Direct.Params[c].Hrtf.Target.Delay
641 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
643 /* Normal panning for auxiliary sends. */
644 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
646 for(i = 0;i < NumSends;i++)
648 const ALeffectslot *Slot = SendSlots[i];
649 if(Slot)
650 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
651 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
653 else
654 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
655 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
659 voice->Flags |= VOICE_IS_HRTF;
661 else
663 /* Non-HRTF rendering. Use normal panning to the output. */
664 for(c = 0;c < num_channels;c++)
666 /* Special-case LFE */
667 if(chans[c].channel == LFE)
669 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
670 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
671 if(Device->Dry.Buffer == Device->RealOut.Buffer)
673 int idx;
674 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
675 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
678 for(i = 0;i < NumSends;i++)
680 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
681 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
683 continue;
686 if(Device->Render_Mode == StereoPair)
687 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
688 else
689 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
690 ComputePanningGains(Device->Dry,
691 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
694 for(i = 0;i < NumSends;i++)
696 const ALeffectslot *Slot = SendSlots[i];
697 if(Slot)
698 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
699 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
701 else
702 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
703 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
710 HFScale = props->Direct.HFReference / Frequency;
711 LFScale = props->Direct.LFReference / Frequency;
712 DryGainHF = maxf(DryGainHF, 0.0625f); /* Limit -24dB */
713 DryGainLF = maxf(DryGainLF, 0.0625f);
714 for(c = 0;c < num_channels;c++)
716 voice->Direct.Params[c].FilterType = AF_None;
717 if(DryGainHF != 1.0f) voice->Direct.Params[c].FilterType |= AF_LowPass;
718 if(DryGainLF != 1.0f) voice->Direct.Params[c].FilterType |= AF_HighPass;
719 ALfilterState_setParams(
720 &voice->Direct.Params[c].LowPass, ALfilterType_HighShelf,
721 DryGainHF, HFScale, calc_rcpQ_from_slope(DryGainHF, 1.0f)
723 ALfilterState_setParams(
724 &voice->Direct.Params[c].HighPass, ALfilterType_LowShelf,
725 DryGainLF, LFScale, calc_rcpQ_from_slope(DryGainLF, 1.0f)
729 for(i = 0;i < NumSends;i++)
731 HFScale = props->Send[i].HFReference / Frequency;
732 LFScale = props->Send[i].LFReference / Frequency;
733 WetGainHF[i] = maxf(WetGainHF[i], 0.0625f);
734 WetGainLF[i] = maxf(WetGainLF[i], 0.0625f);
735 for(c = 0;c < num_channels;c++)
737 voice->Send[i].Params[c].FilterType = AF_None;
738 if(WetGainHF[i] != 1.0f) voice->Send[i].Params[c].FilterType |= AF_LowPass;
739 if(WetGainLF[i] != 1.0f) voice->Send[i].Params[c].FilterType |= AF_HighPass;
740 ALfilterState_setParams(
741 &voice->Send[i].Params[c].LowPass, ALfilterType_HighShelf,
742 WetGainHF[i], HFScale, calc_rcpQ_from_slope(WetGainHF[i], 1.0f)
744 ALfilterState_setParams(
745 &voice->Send[i].Params[c].HighPass, ALfilterType_LowShelf,
746 WetGainLF[i], LFScale, calc_rcpQ_from_slope(WetGainLF[i], 1.0f)
752 static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
754 const ALCdevice *Device = ALContext->Device;
755 const ALlistener *Listener = ALContext->Listener;
756 aluVector Position, Velocity, Direction, SourceToListener;
757 ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
758 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
759 ALfloat SourceVolume,ListenerGain;
760 ALfloat DopplerFactor, SpeedOfSound;
761 ALfloat AirAbsorptionFactor;
762 ALfloat RoomAirAbsorption[MAX_SENDS];
763 ALeffectslot *SendSlots[MAX_SENDS];
764 ALfloat Attenuation;
765 ALfloat RoomAttenuation[MAX_SENDS];
766 ALfloat MetersPerUnit;
767 ALfloat RoomRolloffBase;
768 ALfloat RoomRolloff[MAX_SENDS];
769 ALfloat DecayDistance[MAX_SENDS];
770 ALfloat DryGain;
771 ALfloat DryGainHF;
772 ALfloat DryGainLF;
773 ALboolean DryGainHFAuto;
774 ALfloat WetGain[MAX_SENDS];
775 ALfloat WetGainHF[MAX_SENDS];
776 ALfloat WetGainLF[MAX_SENDS];
777 ALfloat HFScale, LFScale;
778 ALboolean WetGainAuto;
779 ALboolean WetGainHFAuto;
780 ALfloat Pitch;
781 ALuint Frequency;
782 ALint NumSends;
783 ALint i, j;
785 /* Get context/device properties */
786 DopplerFactor = Listener->Params.DopplerFactor;
787 SpeedOfSound = Listener->Params.SpeedOfSound;
788 NumSends = Device->NumAuxSends;
789 Frequency = Device->Frequency;
791 /* Get listener properties */
792 ListenerGain = Listener->Params.Gain;
793 MetersPerUnit = Listener->Params.MetersPerUnit;
795 /* Get source properties */
796 SourceVolume = props->Gain;
797 MinVolume = props->MinGain;
798 MaxVolume = props->MaxGain;
799 Pitch = props->Pitch;
800 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
801 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
802 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
803 MinDist = props->RefDistance;
804 MaxDist = props->MaxDistance;
805 Rolloff = props->RollOffFactor;
806 DopplerFactor *= props->DopplerFactor;
807 InnerAngle = props->InnerAngle;
808 OuterAngle = props->OuterAngle;
809 AirAbsorptionFactor = props->AirAbsorptionFactor;
810 DryGainHFAuto = props->DryGainHFAuto;
811 WetGainAuto = props->WetGainAuto;
812 WetGainHFAuto = props->WetGainHFAuto;
813 RoomRolloffBase = props->RoomRolloffFactor;
815 voice->Direct.Buffer = Device->Dry.Buffer;
816 voice->Direct.Channels = Device->Dry.NumChannels;
817 for(i = 0;i < NumSends;i++)
819 SendSlots[i] = props->Send[i].Slot;
820 if(!SendSlots[i] && i == 0)
821 SendSlots[i] = Device->DefaultSlot;
822 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
824 SendSlots[i] = NULL;
825 RoomRolloff[i] = 0.0f;
826 DecayDistance[i] = 0.0f;
827 RoomAirAbsorption[i] = 1.0f;
829 else if(SendSlots[i]->Params.AuxSendAuto)
831 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
832 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
833 SPEEDOFSOUNDMETRESPERSEC;
834 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
836 else
838 /* If the slot's auxiliary send auto is off, the data sent to the
839 * effect slot is the same as the dry path, sans filter effects */
840 RoomRolloff[i] = Rolloff;
841 DecayDistance[i] = 0.0f;
842 RoomAirAbsorption[i] = AIRABSORBGAINHF;
845 if(!SendSlots[i])
847 voice->Send[i].Buffer = NULL;
848 voice->Send[i].Channels = 0;
850 else
852 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
853 voice->Send[i].Channels = SendSlots[i]->NumChannels;
857 /* Transform source to listener space (convert to head relative) */
858 if(props->HeadRelative == AL_FALSE)
860 const aluMatrixf *Matrix = &Listener->Params.Matrix;
861 /* Transform source vectors */
862 Position = aluMatrixfVector(Matrix, &Position);
863 Velocity = aluMatrixfVector(Matrix, &Velocity);
864 Direction = aluMatrixfVector(Matrix, &Direction);
866 else
868 const aluVector *lvelocity = &Listener->Params.Velocity;
869 /* Offset the source velocity to be relative of the listener velocity */
870 Velocity.v[0] += lvelocity->v[0];
871 Velocity.v[1] += lvelocity->v[1];
872 Velocity.v[2] += lvelocity->v[2];
875 aluNormalize(Direction.v);
876 SourceToListener.v[0] = -Position.v[0];
877 SourceToListener.v[1] = -Position.v[1];
878 SourceToListener.v[2] = -Position.v[2];
879 SourceToListener.v[3] = 0.0f;
880 Distance = aluNormalize(SourceToListener.v);
882 /* Calculate distance attenuation */
883 ClampedDist = Distance;
885 Attenuation = 1.0f;
886 for(i = 0;i < NumSends;i++)
887 RoomAttenuation[i] = 1.0f;
888 switch(Listener->Params.SourceDistanceModel ?
889 props->DistanceModel : Listener->Params.DistanceModel)
891 case InverseDistanceClamped:
892 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
893 if(MaxDist < MinDist)
894 break;
895 /*fall-through*/
896 case InverseDistance:
897 if(MinDist > 0.0f)
899 ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
900 if(dist > 0.0f) Attenuation = MinDist / dist;
901 for(i = 0;i < NumSends;i++)
903 dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
904 if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
907 break;
909 case LinearDistanceClamped:
910 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
911 if(MaxDist < MinDist)
912 break;
913 /*fall-through*/
914 case LinearDistance:
915 if(MaxDist != MinDist)
917 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
918 Attenuation = maxf(Attenuation, 0.0f);
919 for(i = 0;i < NumSends;i++)
921 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
922 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
925 break;
927 case ExponentDistanceClamped:
928 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
929 if(MaxDist < MinDist)
930 break;
931 /*fall-through*/
932 case ExponentDistance:
933 if(ClampedDist > 0.0f && MinDist > 0.0f)
935 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
936 for(i = 0;i < NumSends;i++)
937 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
939 break;
941 case DisableDistance:
942 ClampedDist = MinDist;
943 break;
946 /* Source Gain + Attenuation */
947 DryGain = SourceVolume * Attenuation;
948 DryGainHF = 1.0f;
949 DryGainLF = 1.0f;
950 for(i = 0;i < NumSends;i++)
952 WetGain[i] = SourceVolume * RoomAttenuation[i];
953 WetGainHF[i] = 1.0f;
954 WetGainLF[i] = 1.0f;
957 /* Distance-based air absorption */
958 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
960 ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
961 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
962 for(i = 0;i < NumSends;i++)
963 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
966 if(WetGainAuto)
968 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
970 /* Apply a decay-time transformation to the wet path, based on the
971 * attenuation of the dry path.
973 * Using the apparent distance, based on the distance attenuation, the
974 * initial decay of the reverb effect is calculated and applied to the
975 * wet path.
977 for(i = 0;i < NumSends;i++)
979 if(DecayDistance[i] > 0.0f)
980 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
984 /* Calculate directional soundcones */
985 if(InnerAngle < 360.0f)
987 ALfloat ConeVolume;
988 ALfloat ConeHF;
989 ALfloat Angle;
990 ALfloat scale;
992 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
993 if(Angle > InnerAngle)
995 if(Angle < OuterAngle)
997 scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
998 ConeVolume = lerp(1.0f, props->OuterGain, scale);
999 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1001 else
1003 ConeVolume = props->OuterGain;
1004 ConeHF = props->OuterGainHF;
1006 DryGain *= ConeVolume;
1007 if(DryGainHFAuto)
1008 DryGainHF *= ConeHF;
1011 /* Wet path uses the total area of the cone emitter (the room will
1012 * receive the same amount of sound regardless of its direction).
1014 scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
1015 (InnerAngle/360.0f);
1016 if(WetGainAuto)
1018 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1019 for(i = 0;i < NumSends;i++)
1020 WetGain[i] *= ConeVolume;
1022 if(WetGainHFAuto)
1024 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1025 for(i = 0;i < NumSends;i++)
1026 WetGainHF[i] *= ConeHF;
1030 /* Apply gain and frequency filters */
1031 DryGain = clampf(DryGain, MinVolume, MaxVolume);
1032 DryGain *= props->Direct.Gain * ListenerGain;
1033 DryGain = minf(DryGain, GAIN_MIX_MAX);
1034 DryGainHF *= props->Direct.GainHF;
1035 DryGainLF *= props->Direct.GainLF;
1036 for(i = 0;i < NumSends;i++)
1038 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
1039 WetGain[i] *= props->Send[i].Gain * ListenerGain;
1040 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1041 WetGainHF[i] *= props->Send[i].GainHF;
1042 WetGainLF[i] *= props->Send[i].GainLF;
1045 /* Calculate velocity-based doppler effect */
1046 if(DopplerFactor > 0.0f)
1048 const aluVector *lvelocity = &Listener->Params.Velocity;
1049 ALfloat VSS, VLS;
1051 if(SpeedOfSound < 1.0f)
1053 DopplerFactor *= 1.0f/SpeedOfSound;
1054 SpeedOfSound = 1.0f;
1057 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1058 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1060 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1061 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1064 /* Calculate fixed-point stepping value, based on the pitch, buffer
1065 * frequency, and output frequency.
1067 Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
1068 if(Pitch > (ALfloat)MAX_PITCH)
1069 voice->Step = MAX_PITCH<<FRACTIONBITS;
1070 else
1071 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1072 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1074 voice->Flags &= ~(VOICE_IS_HRTF | VOICE_HAS_NFC);
1075 if(Device->Render_Mode == HrtfRender)
1077 /* Full HRTF rendering. Skip the virtual channels and render to the
1078 * real outputs.
1080 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1081 ALfloat coeffs[MAX_AMBI_COEFFS];
1082 ALfloat radius = props->Radius;
1083 ALfloat ev = 0.0f, az = 0.0f;
1084 ALfloat spread = 0.0f;
1086 voice->Direct.Buffer = Device->RealOut.Buffer;
1087 voice->Direct.Channels = Device->RealOut.NumChannels;
1089 if(Distance > FLT_EPSILON)
1091 dir[0] = -SourceToListener.v[0];
1092 dir[1] = -SourceToListener.v[1];
1093 dir[2] = -SourceToListener.v[2] * ZScale;
1095 /* Calculate elevation and azimuth only when the source is not at
1096 * the listener. This prevents +0 and -0 Z from producing
1097 * inconsistent panning. Also, clamp Y in case FP precision errors
1098 * cause it to land outside of -1..+1. */
1099 ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1100 az = atan2f(dir[0], -dir[2]);
1102 if(radius > Distance)
1103 spread = F_TAU - Distance/radius*F_PI;
1104 else if(Distance > FLT_EPSILON)
1105 spread = asinf(radius / Distance) * 2.0f;
1107 /* Get the HRIR coefficients and delays. */
1108 GetHrtfCoeffs(Device->HrtfHandle, ev, az, spread,
1109 voice->Direct.Params[0].Hrtf.Target.Coeffs,
1110 voice->Direct.Params[0].Hrtf.Target.Delay);
1111 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain;
1113 CalcDirectionCoeffs(dir, spread, coeffs);
1115 for(i = 0;i < NumSends;i++)
1117 const ALeffectslot *Slot = SendSlots[i];
1118 if(Slot)
1119 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1120 coeffs, WetGain[i], voice->Send[i].Params[0].Gains.Target
1122 else
1123 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1124 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
1127 voice->Flags |= VOICE_IS_HRTF;
1129 else
1131 /* Non-HRTF rendering. */
1132 ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1133 ALfloat coeffs[MAX_AMBI_COEFFS];
1134 ALfloat radius = props->Radius;
1135 ALfloat spread = 0.0f;
1137 /* Get the localized direction, and compute panned gains. */
1138 if(Distance > FLT_EPSILON)
1140 if(Device->AvgSpeakerDist > 0.0f && MetersPerUnit > 0.0f)
1142 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
1143 (Distance*MetersPerUnit * (ALfloat)Device->Frequency);
1144 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
1145 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
1146 /* Clamp w0 for really close distances, to prevent excessive
1147 * bass.
1149 w0 = minf(w0, w1*4.0f);
1151 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
1152 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
1153 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
1155 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
1156 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
1157 voice->Flags |= VOICE_HAS_NFC;
1160 dir[0] = -SourceToListener.v[0];
1161 dir[1] = -SourceToListener.v[1];
1162 dir[2] = -SourceToListener.v[2] * ZScale;
1164 else if(Device->AvgSpeakerDist > 0.0f)
1166 /* If the source distance is 0, set w0 to w1 to act as a pass-
1167 * through. We still want to pass the signal through the filters so
1168 * they keep an appropriate history, in case the source moves away
1169 * from the listener.
1171 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
1172 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
1174 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
1175 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
1176 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
1178 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
1179 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
1180 voice->Flags |= VOICE_HAS_NFC;
1183 if(radius > Distance)
1184 spread = F_TAU - Distance/radius*F_PI;
1185 else if(Distance > FLT_EPSILON)
1186 spread = asinf(radius / Distance) * 2.0f;
1188 if(Device->Render_Mode == StereoPair)
1190 ALfloat ev = asinf(clampf(dir[1], -1.0f, 1.0f));
1191 ALfloat az = atan2f(dir[0], -dir[2]);
1192 CalcAnglePairwiseCoeffs(az, ev, radius, coeffs);
1194 else
1195 CalcDirectionCoeffs(dir, spread, coeffs);
1196 ComputePanningGains(Device->Dry,
1197 coeffs, DryGain, voice->Direct.Params[0].Gains.Target
1200 for(i = 0;i < NumSends;i++)
1202 const ALeffectslot *Slot = SendSlots[i];
1203 if(Slot)
1204 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1205 coeffs, WetGain[i], voice->Send[i].Params[0].Gains.Target
1207 else
1208 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1209 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
1214 HFScale = props->Direct.HFReference / Frequency;
1215 LFScale = props->Direct.LFReference / Frequency;
1216 DryGainHF = maxf(DryGainHF, 0.0625f); /* Limit -24dB */
1217 DryGainLF = maxf(DryGainLF, 0.0625f);
1218 voice->Direct.Params[0].FilterType = AF_None;
1219 if(DryGainHF != 1.0f) voice->Direct.Params[0].FilterType |= AF_LowPass;
1220 if(DryGainLF != 1.0f) voice->Direct.Params[0].FilterType |= AF_HighPass;
1221 ALfilterState_setParams(
1222 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
1223 DryGainHF, HFScale, calc_rcpQ_from_slope(DryGainHF, 1.0f)
1225 ALfilterState_setParams(
1226 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
1227 DryGainLF, LFScale, calc_rcpQ_from_slope(DryGainLF, 1.0f)
1230 for(i = 0;i < NumSends;i++)
1232 HFScale = props->Send[i].HFReference / Frequency;
1233 LFScale = props->Send[i].LFReference / Frequency;
1234 WetGainHF[i] = maxf(WetGainHF[i], 0.0625f);
1235 WetGainLF[i] = maxf(WetGainLF[i], 0.0625f);
1236 voice->Send[i].Params[0].FilterType = AF_None;
1237 if(WetGainHF[i] != 1.0f) voice->Send[i].Params[0].FilterType |= AF_LowPass;
1238 if(WetGainLF[i] != 1.0f) voice->Send[i].Params[0].FilterType |= AF_HighPass;
1239 ALfilterState_setParams(
1240 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
1241 WetGainHF[i], HFScale, calc_rcpQ_from_slope(WetGainHF[i], 1.0f)
1243 ALfilterState_setParams(
1244 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
1245 WetGainLF[i], LFScale, calc_rcpQ_from_slope(WetGainLF[i], 1.0f)
1250 static void CalcSourceParams(ALvoice *voice, ALsource *source, ALCcontext *context, ALboolean force)
1252 const ALbufferlistitem *BufferListItem;
1253 struct ALsourceProps *props;
1255 props = ATOMIC_EXCHANGE_PTR(&source->Update, NULL, almemory_order_acq_rel);
1256 if(!props && !force) return;
1258 if(props)
1260 memcpy(voice->Props, props,
1261 offsetof(struct ALsourceProps, Send[context->Device->NumAuxSends])
1264 ATOMIC_REPLACE_HEAD(struct ALsourceProps*, &source->FreeList, props);
1267 BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
1268 while(BufferListItem != NULL)
1270 const ALbuffer *buffer;
1271 if((buffer=BufferListItem->buffer) != NULL)
1273 if(buffer->FmtChannels == FmtMono)
1274 CalcAttnSourceParams(voice, voice->Props, buffer, context);
1275 else
1276 CalcNonAttnSourceParams(voice, voice->Props, buffer, context);
1277 break;
1279 BufferListItem = BufferListItem->next;
1284 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1286 ALvoice **voice, **voice_end;
1287 ALsource *source;
1288 ALsizei i;
1290 IncrementRef(&ctx->UpdateCount);
1291 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1293 ALboolean force = CalcListenerParams(ctx);
1294 for(i = 0;i < slots->count;i++)
1295 force |= CalcEffectSlotParams(slots->slot[i], ctx->Device);
1297 voice = ctx->Voices;
1298 voice_end = voice + ctx->VoiceCount;
1299 for(;voice != voice_end;++voice)
1301 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1302 if(source) CalcSourceParams(*voice, source, ctx, force);
1305 IncrementRef(&ctx->UpdateCount);
1309 static inline ALfloat aluF2F(ALfloat val)
1310 { return val; }
1312 #define S25_MAX_NORM (16777215.0f/16777216.0f)
1313 static inline ALint aluF2I(ALfloat val)
1315 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1316 * integer range normalized floats can be safely converted to (a bit of the
1317 * exponent helps out, effectively giving 25 bits).
1319 return fastf2i(clampf(val, -1.0f, S25_MAX_NORM)*16777216.0f)<<7;
1321 static inline ALuint aluF2UI(ALfloat val)
1322 { return aluF2I(val)+2147483648u; }
1324 #define S16_MAX_NORM (32767.0f/32768.0f)
1325 static inline ALshort aluF2S(ALfloat val)
1326 { return fastf2i(clampf(val, -1.0f, S16_MAX_NORM)*32768.0f); }
1327 static inline ALushort aluF2US(ALfloat val)
1328 { return aluF2S(val)+32768; }
1330 #define S8_MAX_NORM (127.0f/128.0f)
1331 static inline ALbyte aluF2B(ALfloat val)
1332 { return fastf2i(clampf(val, -1.0f, S8_MAX_NORM)*128.0f); }
1333 static inline ALubyte aluF2UB(ALfloat val)
1334 { return aluF2B(val)+128; }
1336 #define DECL_TEMPLATE(T, func) \
1337 static void Write_##T(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1338 DistanceComp *distcomp, ALsizei SamplesToDo, \
1339 ALsizei numchans) \
1341 ALsizei i, j; \
1342 for(j = 0;j < numchans;j++) \
1344 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1345 T *restrict out = (T*)OutBuffer + j; \
1346 const ALfloat gain = distcomp[j].Gain; \
1347 const ALsizei base = distcomp[j].Length; \
1348 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[j].Buffer, 16); \
1349 if(base > 0 || gain != 1.0f) \
1351 if(SamplesToDo >= base) \
1353 for(i = 0;i < base;i++) \
1354 out[i*numchans] = func(distbuf[i]*gain); \
1355 for(;i < SamplesToDo;i++) \
1356 out[i*numchans] = func(in[i-base]*gain); \
1357 memcpy(distbuf, &in[SamplesToDo-base], base*sizeof(ALfloat)); \
1359 else \
1361 for(i = 0;i < SamplesToDo;i++) \
1362 out[i*numchans] = func(distbuf[i]*gain); \
1363 memmove(distbuf, distbuf+SamplesToDo, \
1364 (base-SamplesToDo)*sizeof(ALfloat)); \
1365 memcpy(distbuf+base-SamplesToDo, in, \
1366 SamplesToDo*sizeof(ALfloat)); \
1369 else for(i = 0;i < SamplesToDo;i++) \
1370 out[i*numchans] = func(in[i]); \
1374 DECL_TEMPLATE(ALfloat, aluF2F)
1375 DECL_TEMPLATE(ALuint, aluF2UI)
1376 DECL_TEMPLATE(ALint, aluF2I)
1377 DECL_TEMPLATE(ALushort, aluF2US)
1378 DECL_TEMPLATE(ALshort, aluF2S)
1379 DECL_TEMPLATE(ALubyte, aluF2UB)
1380 DECL_TEMPLATE(ALbyte, aluF2B)
1382 #undef DECL_TEMPLATE
1385 void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1387 ALsizei SamplesToDo;
1388 ALvoice **voice, **voice_end;
1389 ALeffectslot *slot;
1390 ALsource *source;
1391 ALCcontext *ctx;
1392 FPUCtl oldMode;
1393 ALsizei i, c;
1395 SetMixerFPUMode(&oldMode);
1397 while(size > 0)
1399 SamplesToDo = mini(size, BUFFERSIZE);
1400 for(c = 0;c < device->Dry.NumChannels;c++)
1401 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1402 if(device->Dry.Buffer != device->FOAOut.Buffer)
1403 for(c = 0;c < device->FOAOut.NumChannels;c++)
1404 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1405 if(device->Dry.Buffer != device->RealOut.Buffer)
1406 for(c = 0;c < device->RealOut.NumChannels;c++)
1407 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1409 IncrementRef(&device->MixCount);
1411 if((slot=device->DefaultSlot) != NULL)
1413 CalcEffectSlotParams(device->DefaultSlot, device);
1414 for(c = 0;c < slot->NumChannels;c++)
1415 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1418 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1419 while(ctx)
1421 const struct ALeffectslotArray *auxslots;
1423 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1424 UpdateContextSources(ctx, auxslots);
1426 for(i = 0;i < auxslots->count;i++)
1428 ALeffectslot *slot = auxslots->slot[i];
1429 for(c = 0;c < slot->NumChannels;c++)
1430 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1433 /* source processing */
1434 voice = ctx->Voices;
1435 voice_end = voice + ctx->VoiceCount;
1436 for(;voice != voice_end;++voice)
1438 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1439 if(source && ATOMIC_LOAD(&(*voice)->Playing, almemory_order_relaxed) &&
1440 (*voice)->Step > 0)
1442 if(!MixSource(*voice, source, device, SamplesToDo))
1444 ATOMIC_STORE(&(*voice)->Source, NULL, almemory_order_relaxed);
1445 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1450 /* effect slot processing */
1451 for(i = 0;i < auxslots->count;i++)
1453 const ALeffectslot *slot = auxslots->slot[i];
1454 ALeffectState *state = slot->Params.EffectState;
1455 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1456 state->OutChannels);
1459 ctx = ctx->next;
1462 if(device->DefaultSlot != NULL)
1464 const ALeffectslot *slot = device->DefaultSlot;
1465 ALeffectState *state = slot->Params.EffectState;
1466 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1467 state->OutChannels);
1470 /* Increment the clock time. Every second's worth of samples is
1471 * converted and added to clock base so that large sample counts don't
1472 * overflow during conversion. This also guarantees an exact, stable
1473 * conversion. */
1474 device->SamplesDone += SamplesToDo;
1475 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1476 device->SamplesDone %= device->Frequency;
1477 IncrementRef(&device->MixCount);
1479 if(device->HrtfHandle)
1481 HrtfDirectMixerFunc HrtfMix;
1482 DirectHrtfState *state;
1483 int lidx, ridx;
1485 if(device->AmbiUp)
1486 ambiup_process(device->AmbiUp,
1487 device->Dry.Buffer, device->Dry.NumChannels,
1488 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1491 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1492 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1493 assert(lidx != -1 && ridx != -1);
1495 HrtfMix = SelectHrtfMixer();
1496 state = device->Hrtf;
1497 for(c = 0;c < device->Dry.NumChannels;c++)
1499 HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1500 device->Dry.Buffer[c], state->Offset, state->IrSize,
1501 SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
1502 state->Chan[c].Values, SamplesToDo
1505 state->Offset += SamplesToDo;
1507 else if(device->AmbiDecoder)
1509 if(device->Dry.Buffer != device->FOAOut.Buffer)
1510 bformatdec_upSample(device->AmbiDecoder,
1511 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1512 device->FOAOut.NumChannels, SamplesToDo
1514 bformatdec_process(device->AmbiDecoder,
1515 device->RealOut.Buffer, device->RealOut.NumChannels,
1516 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1519 else if(device->AmbiUp)
1521 ambiup_process(device->AmbiUp,
1522 device->RealOut.Buffer, device->RealOut.NumChannels,
1523 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1526 else if(device->Uhj_Encoder)
1528 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1529 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1530 if(lidx != -1 && ridx != -1)
1532 /* Encode to stereo-compatible 2-channel UHJ output. */
1533 EncodeUhj2(device->Uhj_Encoder,
1534 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1535 device->Dry.Buffer, SamplesToDo
1539 else if(device->Bs2b)
1541 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1542 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1543 if(lidx != -1 && ridx != -1)
1545 /* Apply binaural/crossfeed filter */
1546 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1547 device->RealOut.Buffer[ridx], SamplesToDo);
1551 if(buffer)
1553 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1554 ALsizei OutChannels = device->RealOut.NumChannels;
1555 DistanceComp *DistComp = device->ChannelDelay;
1557 #define WRITE(T, a, b, c, d, e) do { \
1558 Write_##T(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \
1559 buffer = (T*)buffer + (d)*(e); \
1560 } while(0)
1561 switch(device->FmtType)
1563 case DevFmtByte:
1564 WRITE(ALbyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1565 break;
1566 case DevFmtUByte:
1567 WRITE(ALubyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1568 break;
1569 case DevFmtShort:
1570 WRITE(ALshort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1571 break;
1572 case DevFmtUShort:
1573 WRITE(ALushort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1574 break;
1575 case DevFmtInt:
1576 WRITE(ALint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1577 break;
1578 case DevFmtUInt:
1579 WRITE(ALuint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1580 break;
1581 case DevFmtFloat:
1582 WRITE(ALfloat, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1583 break;
1585 #undef WRITE
1588 size -= SamplesToDo;
1591 RestoreFPUMode(&oldMode);
1595 void aluHandleDisconnect(ALCdevice *device)
1597 ALCcontext *Context;
1599 device->Connected = ALC_FALSE;
1601 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1602 while(Context)
1604 ALvoice **voice, **voice_end;
1606 voice = Context->Voices;
1607 voice_end = voice + Context->VoiceCount;
1608 while(voice != voice_end)
1610 ALsource *source = ATOMIC_EXCHANGE_PTR(&(*voice)->Source, NULL,
1611 almemory_order_acq_rel);
1612 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1614 if(source)
1616 ALenum playing = AL_PLAYING;
1617 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1620 voice++;
1622 Context->VoiceCount = 0;
1624 Context = Context->next;