Increase the filter slope to -12dB/octave
[openal-soft.git] / Alc / ALu.c
blob7936c706d794ed526331c70d7e80b9eb4c7af406
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <math.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
29 #include "alMain.h"
30 #include "alSource.h"
31 #include "alBuffer.h"
32 #include "alListener.h"
33 #include "alAuxEffectSlot.h"
34 #include "alu.h"
35 #include "bs2b.h"
36 #include "hrtf.h"
37 #include "uhjfilter.h"
38 #include "bformatdec.h"
39 #include "static_assert.h"
41 #include "mixer_defs.h"
43 #include "backends/base.h"
46 struct ChanMap {
47 enum Channel channel;
48 ALfloat angle;
49 ALfloat elevation;
52 /* Cone scalar */
53 ALfloat ConeScale = 1.0f;
55 /* Localized Z scalar for mono sources */
56 ALfloat ZScale = 1.0f;
58 extern inline ALfloat minf(ALfloat a, ALfloat b);
59 extern inline ALfloat maxf(ALfloat a, ALfloat b);
60 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
62 extern inline ALdouble mind(ALdouble a, ALdouble b);
63 extern inline ALdouble maxd(ALdouble a, ALdouble b);
64 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
66 extern inline ALuint minu(ALuint a, ALuint b);
67 extern inline ALuint maxu(ALuint a, ALuint b);
68 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
70 extern inline ALint mini(ALint a, ALint b);
71 extern inline ALint maxi(ALint a, ALint b);
72 extern inline ALint clampi(ALint val, ALint min, ALint max);
74 extern inline ALint64 mini64(ALint64 a, ALint64 b);
75 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
76 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
78 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
79 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
80 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
82 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
83 extern inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac);
85 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
87 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
88 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
89 extern inline void aluMatrixfSet(aluMatrixf *matrix,
90 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
91 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
92 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
93 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
95 const aluMatrixf IdentityMatrixf = {{
96 { 1.0f, 0.0f, 0.0f, 0.0f },
97 { 0.0f, 1.0f, 0.0f, 0.0f },
98 { 0.0f, 0.0f, 1.0f, 0.0f },
99 { 0.0f, 0.0f, 0.0f, 1.0f },
103 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
105 #ifdef HAVE_SSE
106 if((CPUCapFlags&CPU_CAP_SSE))
107 return MixDirectHrtf_SSE;
108 #endif
109 #ifdef HAVE_NEON
110 if((CPUCapFlags&CPU_CAP_NEON))
111 return MixDirectHrtf_Neon;
112 #endif
114 return MixDirectHrtf_C;
118 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
120 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
121 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
122 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
125 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
127 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
130 static ALfloat aluNormalize(ALfloat *vec)
132 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
133 if(length > 0.0f)
135 ALfloat inv_length = 1.0f/length;
136 vec[0] *= inv_length;
137 vec[1] *= inv_length;
138 vec[2] *= inv_length;
140 return length;
143 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
145 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
147 vec[0] = v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0];
148 vec[1] = v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1];
149 vec[2] = v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2];
152 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
154 aluVector v;
155 v.v[0] = vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0];
156 v.v[1] = vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1];
157 v.v[2] = vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2];
158 v.v[3] = vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3];
159 return v;
163 /* Prepares the interpolator for a given rate (determined by increment). A
164 * result of AL_FALSE indicates that the filter output will completely cut
165 * the input signal.
167 * With a bit of work, and a trade of memory for CPU cost, this could be
168 * modified for use with an interpolated increment for buttery-smooth pitch
169 * changes.
171 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
173 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
174 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
175 static const ALuint to[4][BSINC_SCALE_COUNT] =
177 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
178 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
179 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
180 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
182 static const ALuint tm[2][BSINC_SCALE_COUNT] =
184 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
185 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
187 ALfloat sf;
188 ALuint si, pi;
189 ALboolean uncut = AL_TRUE;
191 if(increment > FRACTIONONE)
193 sf = (ALfloat)FRACTIONONE / increment;
194 if(sf < scaleBase)
196 /* Signal has been completely cut. The return result can be used
197 * to skip the filter (and output zeros) as an optimization.
199 sf = 0.0f;
200 si = 0;
201 uncut = AL_FALSE;
203 else
205 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
206 si = fastf2u(sf);
207 /* The interpolation factor is fit to this diagonally-symmetric
208 * curve to reduce the transition ripple caused by interpolating
209 * different scales of the sinc function.
211 sf = 1.0f - cosf(asinf(sf - si));
214 else
216 sf = 0.0f;
217 si = BSINC_SCALE_COUNT - 1;
220 state->sf = sf;
221 state->m = m[si];
222 state->l = -(ALint)((m[si] / 2) - 1);
223 /* The CPU cost of this table re-mapping could be traded for the memory
224 * cost of a complete table map (1024 elements large).
226 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
228 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
229 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
230 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
231 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
233 return uncut;
237 static ALboolean CalcListenerParams(ALCcontext *Context)
239 ALlistener *Listener = Context->Listener;
240 ALfloat N[3], V[3], U[3], P[3];
241 struct ALlistenerProps *props;
242 aluVector vel;
244 props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &Listener->Update, NULL, almemory_order_acq_rel);
245 if(!props) return AL_FALSE;
247 /* AT then UP */
248 N[0] = 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(struct ALeffectslotProps*, &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(struct ALsourceProps*, &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, ALeffectslot *slot)
1286 ALvoice **voice, **voice_end;
1287 ALsource *source;
1289 IncrementRef(&ctx->UpdateCount);
1290 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1292 ALboolean force = CalcListenerParams(ctx);
1293 while(slot)
1295 force |= CalcEffectSlotParams(slot, ctx->Device);
1296 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1299 voice = ctx->Voices;
1300 voice_end = voice + ctx->VoiceCount;
1301 for(;voice != voice_end;++voice)
1303 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1304 if(source) CalcSourceParams(*voice, source, ctx, force);
1307 IncrementRef(&ctx->UpdateCount);
1311 /* Specialized function to clamp to [-1, +1] with only one branch. This also
1312 * converts NaN to 0. */
1313 static inline ALfloat aluClampf(ALfloat val)
1315 if(fabsf(val) <= 1.0f) return val;
1316 return (ALfloat)((0.0f < val) - (val < 0.0f));
1319 static inline ALfloat aluF2F(ALfloat val)
1320 { return val; }
1322 static inline ALint aluF2I(ALfloat val)
1324 /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
1325 * integer range normalized floats can be safely converted to.
1327 return fastf2i(aluClampf(val)*16777215.0f)<<7;
1329 static inline ALuint aluF2UI(ALfloat val)
1330 { return aluF2I(val)+2147483648u; }
1332 static inline ALshort aluF2S(ALfloat val)
1333 { return fastf2i(aluClampf(val)*32767.0f); }
1334 static inline ALushort aluF2US(ALfloat val)
1335 { return aluF2S(val)+32768; }
1337 static inline ALbyte aluF2B(ALfloat val)
1338 { return fastf2i(aluClampf(val)*127.0f); }
1339 static inline ALubyte aluF2UB(ALfloat val)
1340 { return aluF2B(val)+128; }
1342 #define DECL_TEMPLATE(T, func) \
1343 static void Write_##T(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1344 DistanceComp *distcomp, ALsizei SamplesToDo, \
1345 ALsizei numchans) \
1347 ALsizei i, j; \
1348 for(j = 0;j < numchans;j++) \
1350 const ALfloat *in = InBuffer[j]; \
1351 T *restrict out = (T*)OutBuffer + j; \
1352 const ALfloat gain = distcomp[j].Gain; \
1353 const ALsizei base = distcomp[j].Length; \
1354 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[j].Buffer, 16); \
1355 if(base > 0 || gain != 1.0f) \
1357 if(SamplesToDo >= base) \
1359 for(i = 0;i < base;i++) \
1360 out[i*numchans] = func(distbuf[i]*gain); \
1361 for(;i < SamplesToDo;i++) \
1362 out[i*numchans] = func(in[i-base]*gain); \
1363 memcpy(distbuf, &in[SamplesToDo-base], base*sizeof(ALfloat)); \
1365 else \
1367 for(i = 0;i < SamplesToDo;i++) \
1368 out[i*numchans] = func(distbuf[i]*gain); \
1369 memmove(distbuf, distbuf+SamplesToDo, \
1370 (base-SamplesToDo)*sizeof(ALfloat)); \
1371 memcpy(distbuf+base-SamplesToDo, in, \
1372 SamplesToDo*sizeof(ALfloat)); \
1375 else for(i = 0;i < SamplesToDo;i++) \
1376 out[i*numchans] = func(in[i]); \
1380 DECL_TEMPLATE(ALfloat, aluF2F)
1381 DECL_TEMPLATE(ALuint, aluF2UI)
1382 DECL_TEMPLATE(ALint, aluF2I)
1383 DECL_TEMPLATE(ALushort, aluF2US)
1384 DECL_TEMPLATE(ALshort, aluF2S)
1385 DECL_TEMPLATE(ALubyte, aluF2UB)
1386 DECL_TEMPLATE(ALbyte, aluF2B)
1388 #undef DECL_TEMPLATE
1391 void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1393 ALsizei SamplesToDo;
1394 ALvoice **voice, **voice_end;
1395 ALeffectslot *slot;
1396 ALsource *source;
1397 ALCcontext *ctx;
1398 FPUCtl oldMode;
1399 ALsizei i, c;
1401 SetMixerFPUMode(&oldMode);
1403 while(size > 0)
1405 SamplesToDo = mini(size, BUFFERSIZE);
1406 for(c = 0;c < device->Dry.NumChannels;c++)
1407 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1408 if(device->Dry.Buffer != device->FOAOut.Buffer)
1409 for(c = 0;c < device->FOAOut.NumChannels;c++)
1410 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1411 if(device->Dry.Buffer != device->RealOut.Buffer)
1412 for(c = 0;c < device->RealOut.NumChannels;c++)
1413 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1415 IncrementRef(&device->MixCount);
1417 if((slot=device->DefaultSlot) != NULL)
1419 CalcEffectSlotParams(device->DefaultSlot, device);
1420 for(i = 0;i < slot->NumChannels;i++)
1421 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1424 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1425 while(ctx)
1427 ALeffectslot *slotroot;
1429 slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList, almemory_order_acquire);
1430 UpdateContextSources(ctx, slotroot);
1432 slot = slotroot;
1433 while(slot)
1435 for(i = 0;i < slot->NumChannels;i++)
1436 memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
1437 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1440 /* source processing */
1441 voice = ctx->Voices;
1442 voice_end = voice + ctx->VoiceCount;
1443 for(;voice != voice_end;++voice)
1445 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1446 if(source && ATOMIC_LOAD(&(*voice)->Playing, almemory_order_relaxed) &&
1447 (*voice)->Step > 0)
1449 if(!MixSource(*voice, source, device, SamplesToDo))
1451 ATOMIC_STORE(&(*voice)->Source, NULL, almemory_order_relaxed);
1452 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1457 /* effect slot processing */
1458 slot = slotroot;
1459 while(slot)
1461 ALeffectState *state = slot->Params.EffectState;
1462 V(state,process)(SamplesToDo, SAFE_CONST(ALfloatBUFFERSIZE*,slot->WetBuffer),
1463 state->OutBuffer, state->OutChannels);
1464 slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
1467 ctx = ctx->next;
1470 if(device->DefaultSlot != NULL)
1472 const ALeffectslot *slot = device->DefaultSlot;
1473 ALeffectState *state = slot->Params.EffectState;
1474 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1475 state->OutChannels);
1478 /* Increment the clock time. Every second's worth of samples is
1479 * converted and added to clock base so that large sample counts don't
1480 * overflow during conversion. This also guarantees an exact, stable
1481 * conversion. */
1482 device->SamplesDone += SamplesToDo;
1483 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1484 device->SamplesDone %= device->Frequency;
1485 IncrementRef(&device->MixCount);
1487 if(device->HrtfHandle)
1489 HrtfDirectMixerFunc HrtfMix;
1490 DirectHrtfState *state;
1491 int lidx, ridx;
1493 if(device->AmbiUp)
1494 ambiup_process(device->AmbiUp,
1495 device->Dry.Buffer, device->Dry.NumChannels,
1496 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1499 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1500 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1501 assert(lidx != -1 && ridx != -1);
1503 HrtfMix = SelectHrtfMixer();
1504 state = device->Hrtf;
1505 for(c = 0;c < device->Dry.NumChannels;c++)
1507 HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1508 device->Dry.Buffer[c], state->Offset, state->IrSize,
1509 SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
1510 state->Chan[c].Values, SamplesToDo
1513 state->Offset += SamplesToDo;
1515 else if(device->AmbiDecoder)
1517 if(device->Dry.Buffer != device->FOAOut.Buffer)
1518 bformatdec_upSample(device->AmbiDecoder,
1519 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1520 device->FOAOut.NumChannels, SamplesToDo
1522 bformatdec_process(device->AmbiDecoder,
1523 device->RealOut.Buffer, device->RealOut.NumChannels,
1524 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1527 else if(device->AmbiUp)
1529 ambiup_process(device->AmbiUp,
1530 device->RealOut.Buffer, device->RealOut.NumChannels,
1531 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1534 else if(device->Uhj_Encoder)
1536 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1537 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1538 if(lidx != -1 && ridx != -1)
1540 /* Encode to stereo-compatible 2-channel UHJ output. */
1541 EncodeUhj2(device->Uhj_Encoder,
1542 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1543 device->Dry.Buffer, SamplesToDo
1547 else if(device->Bs2b)
1549 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1550 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1551 if(lidx != -1 && ridx != -1)
1553 /* Apply binaural/crossfeed filter */
1554 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1555 device->RealOut.Buffer[ridx], SamplesToDo);
1559 if(buffer)
1561 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1562 ALsizei OutChannels = device->RealOut.NumChannels;
1563 DistanceComp *DistComp = device->ChannelDelay;
1565 #define WRITE(T, a, b, c, d, e) do { \
1566 Write_##T(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \
1567 buffer = (T*)buffer + (d)*(e); \
1568 } while(0)
1569 switch(device->FmtType)
1571 case DevFmtByte:
1572 WRITE(ALbyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1573 break;
1574 case DevFmtUByte:
1575 WRITE(ALubyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1576 break;
1577 case DevFmtShort:
1578 WRITE(ALshort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1579 break;
1580 case DevFmtUShort:
1581 WRITE(ALushort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1582 break;
1583 case DevFmtInt:
1584 WRITE(ALint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1585 break;
1586 case DevFmtUInt:
1587 WRITE(ALuint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1588 break;
1589 case DevFmtFloat:
1590 WRITE(ALfloat, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1591 break;
1593 #undef WRITE
1596 size -= SamplesToDo;
1599 RestoreFPUMode(&oldMode);
1603 void aluHandleDisconnect(ALCdevice *device)
1605 ALCcontext *Context;
1607 device->Connected = ALC_FALSE;
1609 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1610 while(Context)
1612 ALvoice **voice, **voice_end;
1614 voice = Context->Voices;
1615 voice_end = voice + Context->VoiceCount;
1616 while(voice != voice_end)
1618 ALsource *source = ATOMIC_EXCHANGE(ALsource*, &(*voice)->Source, NULL,
1619 almemory_order_acq_rel);
1620 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1622 if(source)
1624 ALenum playing = AL_PLAYING;
1625 ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(ALenum, &source->state, &playing, AL_STOPPED);
1628 voice++;
1630 Context->VoiceCount = 0;
1632 Context = Context->next;