Handle attenuated sources in CalcPanningAndFilters
[openal-soft.git] / Alc / ALu.c
blobe3160dabd82b41ff3b2e3024e69de222aa45c35b
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 void DeinitVoice(ALvoice *voice)
105 struct ALvoiceProps *props;
106 size_t count = 0;
108 props = ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL);
109 if(props) al_free(props);
111 props = ATOMIC_EXCHANGE_PTR(&voice->FreeList, NULL, almemory_order_relaxed);
112 while(props)
114 struct ALvoiceProps *next;
115 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
116 al_free(props);
117 props = next;
118 ++count;
120 /* This is excessively spammy if it traces every voice destruction, so just
121 * warn if it was unexpectedly large.
123 if(count > 3)
124 WARN("Freed "SZFMT" voice property objects\n", count);
128 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
130 #ifdef HAVE_NEON
131 if((CPUCapFlags&CPU_CAP_NEON))
132 return MixDirectHrtf_Neon;
133 #endif
134 #ifdef HAVE_SSE
135 if((CPUCapFlags&CPU_CAP_SSE))
136 return MixDirectHrtf_SSE;
137 #endif
139 return MixDirectHrtf_C;
143 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
145 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
146 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
147 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
150 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
152 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
155 static ALfloat aluNormalize(ALfloat *vec)
157 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
158 if(length > 0.0f)
160 ALfloat inv_length = 1.0f/length;
161 vec[0] *= inv_length;
162 vec[1] *= inv_length;
163 vec[2] *= inv_length;
165 return length;
168 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
170 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
172 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];
173 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];
174 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];
177 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
179 aluVector v;
180 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];
181 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];
182 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];
183 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];
184 return v;
188 /* Prepares the interpolator for a given rate (determined by increment). A
189 * result of AL_FALSE indicates that the filter output will completely cut
190 * the input signal.
192 * With a bit of work, and a trade of memory for CPU cost, this could be
193 * modified for use with an interpolated increment for buttery-smooth pitch
194 * changes.
196 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
198 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
199 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
200 static const ALuint to[4][BSINC_SCALE_COUNT] =
202 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
203 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
204 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
205 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
207 static const ALuint tm[2][BSINC_SCALE_COUNT] =
209 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
210 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
212 ALfloat sf;
213 ALuint si, pi;
214 ALboolean uncut = AL_TRUE;
216 if(increment > FRACTIONONE)
218 sf = (ALfloat)FRACTIONONE / increment;
219 if(sf < scaleBase)
221 /* Signal has been completely cut. The return result can be used
222 * to skip the filter (and output zeros) as an optimization.
224 sf = 0.0f;
225 si = 0;
226 uncut = AL_FALSE;
228 else
230 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
231 si = fastf2u(sf);
232 /* The interpolation factor is fit to this diagonally-symmetric
233 * curve to reduce the transition ripple caused by interpolating
234 * different scales of the sinc function.
236 sf = 1.0f - cosf(asinf(sf - si));
239 else
241 sf = 0.0f;
242 si = BSINC_SCALE_COUNT - 1;
245 state->sf = sf;
246 state->m = m[si];
247 state->l = -(ALint)((m[si] / 2) - 1);
248 /* The CPU cost of this table re-mapping could be traded for the memory
249 * cost of a complete table map (1024 elements large).
251 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
253 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
254 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
255 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
256 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
258 return uncut;
262 static ALboolean CalcListenerParams(ALCcontext *Context)
264 ALlistener *Listener = Context->Listener;
265 ALfloat N[3], V[3], U[3], P[3];
266 struct ALlistenerProps *props;
267 aluVector vel;
269 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
270 if(!props) return AL_FALSE;
272 /* AT then UP */
273 N[0] = props->Forward[0];
274 N[1] = props->Forward[1];
275 N[2] = props->Forward[2];
276 aluNormalize(N);
277 V[0] = props->Up[0];
278 V[1] = props->Up[1];
279 V[2] = props->Up[2];
280 aluNormalize(V);
281 /* Build and normalize right-vector */
282 aluCrossproduct(N, V, U);
283 aluNormalize(U);
285 aluMatrixfSet(&Listener->Params.Matrix,
286 U[0], V[0], -N[0], 0.0,
287 U[1], V[1], -N[1], 0.0,
288 U[2], V[2], -N[2], 0.0,
289 0.0, 0.0, 0.0, 1.0
292 P[0] = props->Position[0];
293 P[1] = props->Position[1];
294 P[2] = props->Position[2];
295 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
296 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
298 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
299 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
301 Listener->Params.Gain = props->Gain * Context->GainBoost;
302 Listener->Params.MetersPerUnit = props->MetersPerUnit;
304 Listener->Params.DopplerFactor = props->DopplerFactor;
305 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
307 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
308 Listener->Params.DistanceModel = props->DistanceModel;
310 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
311 return AL_TRUE;
314 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
316 struct ALeffectslotProps *props;
317 ALeffectState *state;
319 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
320 if(!props) return AL_FALSE;
322 slot->Params.Gain = props->Gain;
323 slot->Params.AuxSendAuto = props->AuxSendAuto;
324 slot->Params.EffectType = props->Type;
325 if(IsReverbEffect(slot->Params.EffectType))
327 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
328 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
329 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
331 else
333 slot->Params.RoomRolloff = 0.0f;
334 slot->Params.DecayTime = 0.0f;
335 slot->Params.AirAbsorptionGainHF = 1.0f;
338 /* Swap effect states. No need to play with the ref counts since they keep
339 * the same number of refs.
341 state = props->State;
342 props->State = slot->Params.EffectState;
343 slot->Params.EffectState = state;
345 V(state,update)(device, slot, &props->Props);
347 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
348 return AL_TRUE;
352 static const struct ChanMap MonoMap[1] = {
353 { FrontCenter, 0.0f, 0.0f }
354 }, RearMap[2] = {
355 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
356 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
357 }, QuadMap[4] = {
358 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
359 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
360 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
361 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
362 }, X51Map[6] = {
363 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
364 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
365 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
366 { LFE, 0.0f, 0.0f },
367 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
368 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
369 }, X61Map[7] = {
370 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
371 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
372 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
373 { LFE, 0.0f, 0.0f },
374 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
375 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
376 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
377 }, X71Map[8] = {
378 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
379 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
380 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
381 { LFE, 0.0f, 0.0f },
382 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
383 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
384 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
385 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
388 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
389 const ALfloat Spread, const ALfloat DryGain,
390 const ALfloat DryGainHF, const ALfloat DryGainLF,
391 const ALfloat *WetGain, const ALfloat *WetGainLF,
392 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
393 const ALbuffer *Buffer, const struct ALvoiceProps *props,
394 const ALlistener *Listener, const ALCdevice *Device)
396 struct ChanMap StereoMap[2] = {
397 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
398 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
400 bool DirectChannels = props->DirectChannels;
401 const ALsizei NumSends = Device->NumAuxSends;
402 const ALuint Frequency = Device->Frequency;
403 const struct ChanMap *chans = NULL;
404 ALsizei num_channels = 0;
405 bool isbformat = false;
406 ALsizei c, i, j;
408 switch(Buffer->FmtChannels)
410 case FmtMono:
411 chans = MonoMap;
412 num_channels = 1;
413 /* Mono buffers are never played direct. */
414 DirectChannels = false;
415 break;
417 case FmtStereo:
418 /* Convert counter-clockwise to clockwise. */
419 StereoMap[0].angle = -props->StereoPan[0];
420 StereoMap[1].angle = -props->StereoPan[1];
422 chans = StereoMap;
423 num_channels = 2;
424 break;
426 case FmtRear:
427 chans = RearMap;
428 num_channels = 2;
429 break;
431 case FmtQuad:
432 chans = QuadMap;
433 num_channels = 4;
434 break;
436 case FmtX51:
437 chans = X51Map;
438 num_channels = 6;
439 break;
441 case FmtX61:
442 chans = X61Map;
443 num_channels = 7;
444 break;
446 case FmtX71:
447 chans = X71Map;
448 num_channels = 8;
449 break;
451 case FmtBFormat2D:
452 num_channels = 3;
453 isbformat = true;
454 DirectChannels = false;
455 break;
457 case FmtBFormat3D:
458 num_channels = 4;
459 isbformat = true;
460 DirectChannels = false;
461 break;
464 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
465 if(isbformat)
467 /* Special handling for B-Format sources. */
469 if(Distance > FLT_EPSILON)
471 /* Panning a B-Format sound toward some direction is easy. Just pan
472 * the first (W) channel as a normal mono sound and silence the
473 * others.
475 ALfloat coeffs[MAX_AMBI_COEFFS];
477 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
479 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
480 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
481 (mdist * (ALfloat)Device->Frequency);
482 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
483 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
484 /* Clamp w0 for really close distances, to prevent excessive
485 * bass.
487 w0 = minf(w0, w1*4.0f);
489 /* Only need to adjust the first channel of a B-Format source. */
490 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
491 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
492 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
494 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
495 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
496 voice->Flags |= VOICE_HAS_NFC;
499 if(Device->Render_Mode == StereoPair)
501 ALfloat ev = asinf(Dir[1]);
502 ALfloat az = atan2f(Dir[0], -Dir[2]);
503 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
505 else
506 CalcDirectionCoeffs(Dir, Spread, coeffs);
508 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
509 ComputePanningGains(Device->Dry, coeffs, DryGain*1.414213562f,
510 voice->Direct.Params[0].Gains.Target);
511 for(c = 1;c < num_channels;c++)
513 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
514 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
517 for(i = 0;i < NumSends;i++)
519 const ALeffectslot *Slot = SendSlots[i];
520 if(Slot)
521 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
522 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
524 else
525 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
526 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
527 for(c = 1;c < num_channels;c++)
529 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
530 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
534 else
536 /* Non-panned B-Format has its XYZ channels rotated according to
537 * the orientation.
539 ALfloat N[3], V[3], U[3];
540 aluMatrixf matrix;
541 ALfloat scale;
543 if(Device->AvgSpeakerDist > 0.0f)
545 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
546 * is what we want for FOA input. The first channel may have
547 * been previously re-adjusted if panned, so reset it.
549 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f);
550 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f);
551 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 0.0f);
553 voice->Direct.ChannelsPerOrder[0] = 1;
554 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
555 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
556 voice->Direct.ChannelsPerOrder[2] = 0;
557 voice->Flags |= VOICE_HAS_NFC;
560 /* AT then UP */
561 N[0] = props->Orientation[0][0];
562 N[1] = props->Orientation[0][1];
563 N[2] = props->Orientation[0][2];
564 aluNormalize(N);
565 V[0] = props->Orientation[1][0];
566 V[1] = props->Orientation[1][1];
567 V[2] = props->Orientation[1][2];
568 aluNormalize(V);
569 if(!props->HeadRelative)
571 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
572 aluMatrixfFloat3(N, 0.0f, lmatrix);
573 aluMatrixfFloat3(V, 0.0f, lmatrix);
575 /* Build and normalize right-vector */
576 aluCrossproduct(N, V, U);
577 aluNormalize(U);
579 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
580 scale = 1.732050808f;
581 aluMatrixfSet(&matrix,
582 1.414213562f, 0.0f, 0.0f, 0.0f,
583 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
584 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
585 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
588 voice->Direct.Buffer = Device->FOAOut.Buffer;
589 voice->Direct.Channels = Device->FOAOut.NumChannels;
590 for(c = 0;c < num_channels;c++)
591 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
592 voice->Direct.Params[c].Gains.Target);
593 for(i = 0;i < NumSends;i++)
595 const ALeffectslot *Slot = SendSlots[i];
596 if(Slot)
598 for(c = 0;c < num_channels;c++)
599 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
600 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
603 else
605 for(c = 0;c < num_channels;c++)
606 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
607 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
612 else if(DirectChannels)
614 /* Skip the virtual channels and write inputs to the real output with
615 * no explicit panning.
617 voice->Direct.Buffer = Device->RealOut.Buffer;
618 voice->Direct.Channels = Device->RealOut.NumChannels;
620 for(c = 0;c < num_channels;c++)
622 int idx;
623 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
624 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
625 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
626 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
629 /* Auxiliary sends still use normal channel panning since they mix to
630 * B-Format, which can't channel-match.
632 for(c = 0;c < num_channels;c++)
634 ALfloat coeffs[MAX_AMBI_COEFFS];
635 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
637 for(i = 0;i < NumSends;i++)
639 const ALeffectslot *Slot = SendSlots[i];
640 if(Slot)
641 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
642 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
644 else
645 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
646 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
650 else if(Device->Render_Mode == HrtfRender)
652 /* Full HRTF rendering. Skip the virtual channels and render to the
653 * real outputs.
655 voice->Direct.Buffer = Device->RealOut.Buffer;
656 voice->Direct.Channels = Device->RealOut.NumChannels;
658 if(Distance > FLT_EPSILON)
660 ALfloat coeffs[MAX_AMBI_COEFFS];
661 ALfloat ev, az;
663 ev = asinf(Dir[1]);
664 az = atan2f(Dir[0], -Dir[2]);
666 /* Get the HRIR coefficients and delays just once, for the given
667 * source direction.
669 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
670 voice->Direct.Params[0].Hrtf.Target.Coeffs,
671 voice->Direct.Params[0].Hrtf.Target.Delay);
672 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain;
674 /* Remaining channels use the same results as the first. */
675 for(c = 1;c < num_channels;c++)
677 /* Skip LFE */
678 if(chans[c].channel == LFE)
679 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
680 sizeof(voice->Direct.Params[c].Hrtf.Target));
681 else
682 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
685 /* Calculate the directional coefficients once, which apply to all
686 * input channels of the source sends.
688 CalcDirectionCoeffs(Dir, Spread, coeffs);
690 for(i = 0;i < NumSends;i++)
692 const ALeffectslot *Slot = SendSlots[i];
693 if(Slot)
694 for(c = 0;c < num_channels;c++)
696 /* Skip LFE */
697 if(chans[c].channel == LFE)
698 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
699 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
700 else
701 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
702 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
705 else
706 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
707 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
710 else
712 for(c = 0;c < num_channels;c++)
714 ALfloat coeffs[MAX_AMBI_COEFFS];
716 if(chans[c].channel == LFE)
718 /* Skip LFE */
719 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
720 sizeof(voice->Direct.Params[c].Hrtf.Target));
721 for(i = 0;i < NumSends;i++)
723 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
724 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
726 continue;
729 /* Get the HRIR coefficients and delays for this channel
730 * position.
732 GetHrtfCoeffs(Device->HrtfHandle,
733 chans[c].elevation, chans[c].angle, Spread,
734 voice->Direct.Params[c].Hrtf.Target.Coeffs,
735 voice->Direct.Params[c].Hrtf.Target.Delay
737 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
739 /* Normal panning for auxiliary sends. */
740 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
742 for(i = 0;i < NumSends;i++)
744 const ALeffectslot *Slot = SendSlots[i];
745 if(Slot)
746 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
747 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
749 else
750 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
751 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
756 voice->Flags |= VOICE_HAS_HRTF;
758 else
760 /* Non-HRTF rendering. Use normal panning to the output. */
762 if(Distance > FLT_EPSILON)
764 ALfloat coeffs[MAX_AMBI_COEFFS];
765 ALfloat w0 = 0.0f;
767 /* Calculate NFC filter coefficient if needed. */
768 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
770 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
771 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
772 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
773 w0 = SPEEDOFSOUNDMETRESPERSEC /
774 (mdist * (ALfloat)Device->Frequency);
775 /* Clamp w0 for really close distances, to prevent excessive
776 * bass.
778 w0 = minf(w0, w1*4.0f);
780 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
781 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
782 voice->Flags |= VOICE_HAS_NFC;
785 /* Calculate the directional coefficients once, which apply to all
786 * input channels.
788 if(Device->Render_Mode == StereoPair)
790 ALfloat ev = asinf(Dir[1]);
791 ALfloat az = atan2f(Dir[0], -Dir[2]);
792 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
794 else
795 CalcDirectionCoeffs(Dir, Spread, coeffs);
797 for(c = 0;c < num_channels;c++)
799 /* Adjust NFC filters if needed. */
800 if((voice->Flags&VOICE_HAS_NFC))
802 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
803 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
804 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
807 /* Special-case LFE */
808 if(chans[c].channel == LFE)
810 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
811 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
812 if(Device->Dry.Buffer == Device->RealOut.Buffer)
814 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
815 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
817 continue;
820 ComputePanningGains(Device->Dry,
821 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
825 for(i = 0;i < NumSends;i++)
827 const ALeffectslot *Slot = SendSlots[i];
828 if(Slot)
829 for(c = 0;c < num_channels;c++)
831 /* Skip LFE */
832 if(chans[c].channel == LFE)
833 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
834 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
835 else
836 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
837 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
840 else
841 for(c = 0;c < num_channels;c++)
843 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
844 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
848 else
850 ALfloat w0 = 0.0f;
852 if(Device->AvgSpeakerDist > 0.0f)
854 /* If the source distance is 0, set w0 to w1 to act as a pass-
855 * through. We still want to pass the signal through the
856 * filters so they keep an appropriate history, in case the
857 * source moves away from the listener.
859 w0 = SPEEDOFSOUNDMETRESPERSEC /
860 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
862 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
863 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
864 voice->Flags |= VOICE_HAS_NFC;
867 for(c = 0;c < num_channels;c++)
869 ALfloat coeffs[MAX_AMBI_COEFFS];
871 if((voice->Flags&VOICE_HAS_NFC))
873 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
874 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
875 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
878 /* Special-case LFE */
879 if(chans[c].channel == LFE)
881 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
882 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
883 if(Device->Dry.Buffer == Device->RealOut.Buffer)
885 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
886 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
889 for(i = 0;i < NumSends;i++)
891 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
892 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
894 continue;
897 if(Device->Render_Mode == StereoPair)
898 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
899 else
900 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
901 ComputePanningGains(Device->Dry,
902 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
905 for(i = 0;i < NumSends;i++)
907 const ALeffectslot *Slot = SendSlots[i];
908 if(Slot)
909 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
910 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
912 else
913 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
914 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
921 ALfloat hfScale = props->Direct.HFReference / Frequency;
922 ALfloat lfScale = props->Direct.LFReference / Frequency;
923 ALfloat gainHF = maxf(DryGainHF, 0.0625f); /* Limit -24dB */
924 ALfloat gainLF = maxf(DryGainLF, 0.0625f);
925 for(c = 0;c < num_channels;c++)
927 voice->Direct.Params[c].FilterType = AF_None;
928 if(gainHF != 1.0f) voice->Direct.Params[c].FilterType |= AF_LowPass;
929 if(gainLF != 1.0f) voice->Direct.Params[c].FilterType |= AF_HighPass;
930 ALfilterState_setParams(
931 &voice->Direct.Params[c].LowPass, ALfilterType_HighShelf,
932 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
934 ALfilterState_setParams(
935 &voice->Direct.Params[c].HighPass, ALfilterType_LowShelf,
936 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
940 for(i = 0;i < NumSends;i++)
942 ALfloat hfScale = props->Send[i].HFReference / Frequency;
943 ALfloat lfScale = props->Send[i].LFReference / Frequency;
944 ALfloat gainHF = maxf(WetGainHF[i], 0.0625f);
945 ALfloat gainLF = maxf(WetGainLF[i], 0.0625f);
946 for(c = 0;c < num_channels;c++)
948 voice->Send[i].Params[c].FilterType = AF_None;
949 if(gainHF != 1.0f) voice->Send[i].Params[c].FilterType |= AF_LowPass;
950 if(gainLF != 1.0f) voice->Send[i].Params[c].FilterType |= AF_HighPass;
951 ALfilterState_setParams(
952 &voice->Send[i].Params[c].LowPass, ALfilterType_HighShelf,
953 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
955 ALfilterState_setParams(
956 &voice->Send[i].Params[c].HighPass, ALfilterType_LowShelf,
957 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
963 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
965 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
966 const ALCdevice *Device = ALContext->Device;
967 const ALlistener *Listener = ALContext->Listener;
968 ALfloat DryGain, DryGainHF, DryGainLF;
969 ALfloat WetGain[MAX_SENDS];
970 ALfloat WetGainHF[MAX_SENDS];
971 ALfloat WetGainLF[MAX_SENDS];
972 ALeffectslot *SendSlots[MAX_SENDS];
973 ALfloat Pitch;
974 ALsizei i;
976 voice->Direct.Buffer = Device->Dry.Buffer;
977 voice->Direct.Channels = Device->Dry.NumChannels;
978 for(i = 0;i < Device->NumAuxSends;i++)
980 SendSlots[i] = props->Send[i].Slot;
981 if(!SendSlots[i] && i == 0)
982 SendSlots[i] = Device->DefaultSlot;
983 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
985 SendSlots[i] = NULL;
986 voice->Send[i].Buffer = NULL;
987 voice->Send[i].Channels = 0;
989 else
991 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
992 voice->Send[i].Channels = SendSlots[i]->NumChannels;
996 /* Calculate the stepping value */
997 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
998 if(Pitch > (ALfloat)MAX_PITCH)
999 voice->Step = MAX_PITCH<<FRACTIONBITS;
1000 else
1001 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1002 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1003 voice->Resampler = SelectResampler(props->Resampler);
1005 /* Calculate gains */
1006 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1007 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1008 DryGain = minf(DryGain, GAIN_MIX_MAX);
1009 DryGainHF = props->Direct.GainHF;
1010 DryGainLF = props->Direct.GainLF;
1011 for(i = 0;i < Device->NumAuxSends;i++)
1013 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1014 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1015 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1016 WetGainHF[i] = props->Send[i].GainHF;
1017 WetGainLF[i] = props->Send[i].GainLF;
1020 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1021 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1024 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1026 const ALCdevice *Device = ALContext->Device;
1027 const ALlistener *Listener = ALContext->Listener;
1028 const ALsizei NumSends = Device->NumAuxSends;
1029 aluVector Position, Velocity, Direction, SourceToListener;
1030 ALfloat Distance, ClampedDist;
1031 ALfloat DopplerFactor;
1032 ALfloat RoomAirAbsorption[MAX_SENDS];
1033 ALeffectslot *SendSlots[MAX_SENDS];
1034 ALfloat Attenuation;
1035 ALfloat RoomAttenuation[MAX_SENDS];
1036 ALfloat RoomRolloff[MAX_SENDS];
1037 ALfloat DecayDistance[MAX_SENDS];
1038 ALfloat DryGain, DryGainHF, DryGainLF;
1039 ALfloat WetGain[MAX_SENDS];
1040 ALfloat WetGainHF[MAX_SENDS];
1041 ALfloat WetGainLF[MAX_SENDS];
1042 ALfloat dir[3];
1043 ALfloat spread;
1044 ALfloat Pitch;
1045 ALint i;
1047 Pitch = 1.0f;
1048 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1049 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1050 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1052 voice->Direct.Buffer = Device->Dry.Buffer;
1053 voice->Direct.Channels = Device->Dry.NumChannels;
1054 for(i = 0;i < NumSends;i++)
1056 SendSlots[i] = props->Send[i].Slot;
1057 if(!SendSlots[i] && i == 0)
1058 SendSlots[i] = Device->DefaultSlot;
1059 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1061 SendSlots[i] = NULL;
1062 RoomRolloff[i] = 0.0f;
1063 DecayDistance[i] = 0.0f;
1064 RoomAirAbsorption[i] = 1.0f;
1066 else if(SendSlots[i]->Params.AuxSendAuto)
1068 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1069 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1070 SPEEDOFSOUNDMETRESPERSEC;
1071 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
1073 else
1075 /* If the slot's auxiliary send auto is off, the data sent to the
1076 * effect slot is the same as the dry path, sans filter effects */
1077 RoomRolloff[i] = props->RollOffFactor;
1078 DecayDistance[i] = 0.0f;
1079 RoomAirAbsorption[i] = AIRABSORBGAINHF;
1082 if(!SendSlots[i])
1084 voice->Send[i].Buffer = NULL;
1085 voice->Send[i].Channels = 0;
1087 else
1089 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1090 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1094 /* Transform source to listener space (convert to head relative) */
1095 if(props->HeadRelative == AL_FALSE)
1097 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1098 /* Transform source vectors */
1099 Position = aluMatrixfVector(Matrix, &Position);
1100 Velocity = aluMatrixfVector(Matrix, &Velocity);
1101 Direction = aluMatrixfVector(Matrix, &Direction);
1103 else
1105 const aluVector *lvelocity = &Listener->Params.Velocity;
1106 /* Offset the source velocity to be relative of the listener velocity */
1107 Velocity.v[0] += lvelocity->v[0];
1108 Velocity.v[1] += lvelocity->v[1];
1109 Velocity.v[2] += lvelocity->v[2];
1112 aluNormalize(Direction.v);
1113 SourceToListener.v[0] = -Position.v[0];
1114 SourceToListener.v[1] = -Position.v[1];
1115 SourceToListener.v[2] = -Position.v[2];
1116 SourceToListener.v[3] = 0.0f;
1117 Distance = aluNormalize(SourceToListener.v);
1119 /* Calculate distance attenuation */
1120 ClampedDist = Distance;
1122 Attenuation = 1.0f;
1123 for(i = 0;i < NumSends;i++)
1124 RoomAttenuation[i] = 1.0f;
1125 switch(Listener->Params.SourceDistanceModel ?
1126 props->DistanceModel : Listener->Params.DistanceModel)
1128 case InverseDistanceClamped:
1129 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1130 if(props->MaxDistance < props->RefDistance)
1131 break;
1132 /*fall-through*/
1133 case InverseDistance:
1134 if(props->RefDistance > 0.0f)
1136 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RollOffFactor);
1137 if(dist > 0.0f) Attenuation = props->RefDistance / dist;
1138 for(i = 0;i < NumSends;i++)
1140 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1141 if(dist > 0.0f) RoomAttenuation[i] = props->RefDistance / dist;
1144 break;
1146 case LinearDistanceClamped:
1147 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1148 if(props->MaxDistance < props->RefDistance)
1149 break;
1150 /*fall-through*/
1151 case LinearDistance:
1152 if(props->MaxDistance != props->RefDistance)
1154 Attenuation = props->RollOffFactor * (ClampedDist-props->RefDistance) /
1155 (props->MaxDistance-props->RefDistance);
1156 Attenuation = maxf(1.0f - Attenuation, 0.0f);
1157 for(i = 0;i < NumSends;i++)
1159 RoomAttenuation[i] = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1160 (props->MaxDistance-props->RefDistance);
1161 RoomAttenuation[i] = maxf(1.0f - RoomAttenuation[i], 0.0f);
1164 break;
1166 case ExponentDistanceClamped:
1167 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1168 if(props->MaxDistance < props->RefDistance)
1169 break;
1170 /*fall-through*/
1171 case ExponentDistance:
1172 if(ClampedDist > 0.0f && props->RefDistance > 0.0f)
1174 Attenuation = powf(ClampedDist/props->RefDistance, -props->RollOffFactor);
1175 for(i = 0;i < NumSends;i++)
1176 RoomAttenuation[i] = powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1178 break;
1180 case DisableDistance:
1181 ClampedDist = props->RefDistance;
1182 break;
1185 /* Source Gain + Attenuation */
1186 DryGain = props->Gain * Attenuation;
1187 DryGainHF = 1.0f;
1188 DryGainLF = 1.0f;
1189 for(i = 0;i < NumSends;i++)
1191 WetGain[i] = props->Gain * RoomAttenuation[i];
1192 WetGainHF[i] = 1.0f;
1193 WetGainLF[i] = 1.0f;
1196 /* Distance-based air absorption */
1197 if(props->AirAbsorptionFactor > 0.0f && ClampedDist > props->RefDistance)
1199 ALfloat meters = (ClampedDist-props->RefDistance) * Listener->Params.MetersPerUnit;
1200 DryGainHF *= powf(AIRABSORBGAINHF, props->AirAbsorptionFactor*meters);
1201 for(i = 0;i < NumSends;i++)
1202 WetGainHF[i] *= powf(RoomAirAbsorption[i], props->AirAbsorptionFactor*meters);
1205 if(props->WetGainAuto)
1207 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1209 /* Apply a decay-time transformation to the wet path, based on the
1210 * attenuation of the dry path.
1212 * Using the apparent distance, based on the distance attenuation, the
1213 * initial decay of the reverb effect is calculated and applied to the
1214 * wet path.
1216 for(i = 0;i < NumSends;i++)
1218 if(DecayDistance[i] > 0.0f)
1219 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1223 /* Calculate directional soundcones */
1224 if(props->InnerAngle < 360.0f)
1226 ALfloat ConeVolume;
1227 ALfloat ConeHF;
1228 ALfloat Angle;
1229 ALfloat scale;
1231 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1232 if(Angle > props->InnerAngle)
1234 if(Angle < props->OuterAngle)
1236 scale = (Angle-props->InnerAngle) / (props->OuterAngle-props->InnerAngle);
1237 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1238 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1240 else
1242 ConeVolume = props->OuterGain;
1243 ConeHF = props->OuterGainHF;
1245 DryGain *= ConeVolume;
1246 if(props->DryGainHFAuto)
1247 DryGainHF *= ConeHF;
1250 /* Wet path uses the total area of the cone emitter (the room will
1251 * receive the same amount of sound regardless of its direction).
1253 scale = (asinf(maxf((props->OuterAngle-props->InnerAngle)/360.0f, 0.0f)) / F_PI) +
1254 (props->InnerAngle/360.0f);
1255 if(props->WetGainAuto)
1257 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1258 for(i = 0;i < NumSends;i++)
1259 WetGain[i] *= ConeVolume;
1261 if(props->WetGainHFAuto)
1263 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1264 for(i = 0;i < NumSends;i++)
1265 WetGainHF[i] *= ConeHF;
1269 /* Apply gain and frequency filters */
1270 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1271 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1272 DryGain = minf(DryGain, GAIN_MIX_MAX);
1273 DryGainHF *= props->Direct.GainHF;
1274 DryGainLF *= props->Direct.GainLF;
1275 for(i = 0;i < NumSends;i++)
1277 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1278 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1279 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1280 WetGainHF[i] *= props->Send[i].GainHF;
1281 WetGainLF[i] *= props->Send[i].GainLF;
1284 /* Calculate velocity-based doppler effect */
1285 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1286 if(DopplerFactor > 0.0f)
1288 const aluVector *lvelocity = &Listener->Params.Velocity;
1289 ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1290 ALfloat VSS, VLS;
1292 if(SpeedOfSound < 1.0f)
1294 DopplerFactor *= 1.0f/SpeedOfSound;
1295 SpeedOfSound = 1.0f;
1298 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1299 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1301 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1302 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1305 /* Calculate fixed-point stepping value, based on the pitch, buffer
1306 * frequency, and output frequency.
1308 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1309 if(Pitch > (ALfloat)MAX_PITCH)
1310 voice->Step = MAX_PITCH<<FRACTIONBITS;
1311 else
1312 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1313 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1314 voice->Resampler = SelectResampler(props->Resampler);
1316 if(Distance > FLT_EPSILON)
1318 dir[0] = -SourceToListener.v[0];
1319 /* Clamp Y, in case rounding errors caused it to end up outside of
1320 * -1...+1.
1322 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1323 dir[2] = -SourceToListener.v[2] * ZScale;
1325 else
1327 dir[0] = 0.0f;
1328 dir[1] = 0.0f;
1329 dir[2] = -1.0f;
1331 if(props->Radius > Distance)
1332 spread = F_TAU - Distance/props->Radius*F_PI;
1333 else if(Distance > FLT_EPSILON)
1334 spread = asinf(props->Radius / Distance) * 2.0f;
1335 else
1336 spread = 0.0f;
1338 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1339 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1342 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1344 ALbufferlistitem *BufferListItem;
1345 struct ALvoiceProps *props;
1347 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1348 if(!props && !force) return;
1350 if(props)
1352 memcpy(voice->Props, props,
1353 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1356 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &voice->FreeList, props);
1359 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1360 while(BufferListItem != NULL)
1362 const ALbuffer *buffer;
1363 if((buffer=BufferListItem->buffer) != NULL)
1365 if(buffer->FmtChannels == FmtMono)
1366 CalcAttnSourceParams(voice, voice->Props, buffer, context);
1367 else
1368 CalcNonAttnSourceParams(voice, voice->Props, buffer, context);
1369 break;
1371 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1376 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1378 ALvoice **voice, **voice_end;
1379 ALsource *source;
1380 ALsizei i;
1382 IncrementRef(&ctx->UpdateCount);
1383 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1385 ALboolean force = CalcListenerParams(ctx);
1386 for(i = 0;i < slots->count;i++)
1387 force |= CalcEffectSlotParams(slots->slot[i], ctx->Device);
1389 voice = ctx->Voices;
1390 voice_end = voice + ctx->VoiceCount;
1391 for(;voice != voice_end;++voice)
1393 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1394 if(source) CalcSourceParams(*voice, ctx, force);
1397 IncrementRef(&ctx->UpdateCount);
1401 static ALfloat ApplyLimiter(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALsizei NumChans,
1402 const ALfloat AttackRate, const ALfloat ReleaseRate,
1403 const ALfloat InGain, ALfloat (*restrict Gains),
1404 const ALsizei SamplesToDo)
1406 bool do_limit = false;
1407 ALsizei c, i;
1409 OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
1410 Gains = ASSUME_ALIGNED(Gains, 16);
1412 for(i = 0;i < SamplesToDo;i++)
1413 Gains[i] = 1.0f;
1415 for(c = 0;c < NumChans;c++)
1417 ALfloat lastgain = InGain;
1418 for(i = 0;i < SamplesToDo;i++)
1420 /* Clamp limiter range to 0dB...-80dB. */
1421 ALfloat gain = 1.0f / clampf(fabsf(OutBuffer[c][i]), 1.0f, 1000.0f);
1422 if(lastgain >= gain)
1423 lastgain = maxf(lastgain*AttackRate, gain);
1424 else
1425 lastgain = minf(lastgain/ReleaseRate, gain);
1426 do_limit |= (lastgain < 1.0f);
1428 lastgain = minf(lastgain, Gains[i]);
1429 Gains[i] = lastgain;
1432 if(do_limit)
1434 for(c = 0;c < NumChans;c++)
1436 for(i = 0;i < SamplesToDo;i++)
1437 OutBuffer[c][i] *= Gains[i];
1441 return Gains[SamplesToDo-1];
1444 static inline ALfloat aluF2F(ALfloat val)
1445 { return val; }
1447 #define S25_MAX_NORM (16777215.0f/16777216.0f)
1448 static inline ALint aluF2I(ALfloat val)
1450 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1451 * integer range normalized floats can be safely converted to (a bit of the
1452 * exponent helps out, effectively giving 25 bits).
1454 return fastf2i(clampf(val, -1.0f, S25_MAX_NORM)*16777216.0f)<<7;
1456 static inline ALuint aluF2UI(ALfloat val)
1457 { return aluF2I(val)+2147483648u; }
1459 #define S16_MAX_NORM (32767.0f/32768.0f)
1460 static inline ALshort aluF2S(ALfloat val)
1461 { return fastf2i(clampf(val, -1.0f, S16_MAX_NORM)*32768.0f); }
1462 static inline ALushort aluF2US(ALfloat val)
1463 { return aluF2S(val)+32768; }
1465 #define S8_MAX_NORM (127.0f/128.0f)
1466 static inline ALbyte aluF2B(ALfloat val)
1467 { return fastf2i(clampf(val, -1.0f, S8_MAX_NORM)*128.0f); }
1468 static inline ALubyte aluF2UB(ALfloat val)
1469 { return aluF2B(val)+128; }
1471 #define DECL_TEMPLATE(T, func) \
1472 static void Write_##T(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1473 DistanceComp *distcomp, ALsizei SamplesToDo, \
1474 ALsizei numchans) \
1476 ALsizei i, j; \
1477 for(j = 0;j < numchans;j++) \
1479 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1480 T *restrict out = (T*)OutBuffer + j; \
1481 const ALfloat gain = distcomp[j].Gain; \
1482 const ALsizei base = distcomp[j].Length; \
1483 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[j].Buffer, 16); \
1484 if(base > 0 || gain != 1.0f) \
1486 if(SamplesToDo >= base) \
1488 for(i = 0;i < base;i++) \
1489 out[i*numchans] = func(distbuf[i]*gain); \
1490 for(;i < SamplesToDo;i++) \
1491 out[i*numchans] = func(in[i-base]*gain); \
1492 memcpy(distbuf, &in[SamplesToDo-base], base*sizeof(ALfloat)); \
1494 else \
1496 for(i = 0;i < SamplesToDo;i++) \
1497 out[i*numchans] = func(distbuf[i]*gain); \
1498 memmove(distbuf, distbuf+SamplesToDo, \
1499 (base-SamplesToDo)*sizeof(ALfloat)); \
1500 memcpy(distbuf+base-SamplesToDo, in, \
1501 SamplesToDo*sizeof(ALfloat)); \
1504 else for(i = 0;i < SamplesToDo;i++) \
1505 out[i*numchans] = func(in[i]); \
1509 DECL_TEMPLATE(ALfloat, aluF2F)
1510 DECL_TEMPLATE(ALuint, aluF2UI)
1511 DECL_TEMPLATE(ALint, aluF2I)
1512 DECL_TEMPLATE(ALushort, aluF2US)
1513 DECL_TEMPLATE(ALshort, aluF2S)
1514 DECL_TEMPLATE(ALubyte, aluF2UB)
1515 DECL_TEMPLATE(ALbyte, aluF2B)
1517 #undef DECL_TEMPLATE
1520 void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1522 ALsizei SamplesToDo;
1523 ALvoice **voice, **voice_end;
1524 ALeffectslot *slot;
1525 ALsource *source;
1526 ALCcontext *ctx;
1527 FPUCtl oldMode;
1528 ALsizei i, c;
1530 SetMixerFPUMode(&oldMode);
1532 while(size > 0)
1534 SamplesToDo = mini(size, BUFFERSIZE);
1535 for(c = 0;c < device->Dry.NumChannels;c++)
1536 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1537 if(device->Dry.Buffer != device->FOAOut.Buffer)
1538 for(c = 0;c < device->FOAOut.NumChannels;c++)
1539 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1540 if(device->Dry.Buffer != device->RealOut.Buffer)
1541 for(c = 0;c < device->RealOut.NumChannels;c++)
1542 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1544 IncrementRef(&device->MixCount);
1546 if((slot=device->DefaultSlot) != NULL)
1548 CalcEffectSlotParams(device->DefaultSlot, device);
1549 for(c = 0;c < slot->NumChannels;c++)
1550 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1553 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1554 while(ctx)
1556 const struct ALeffectslotArray *auxslots;
1558 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1559 UpdateContextSources(ctx, auxslots);
1561 for(i = 0;i < auxslots->count;i++)
1563 ALeffectslot *slot = auxslots->slot[i];
1564 for(c = 0;c < slot->NumChannels;c++)
1565 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1568 /* source processing */
1569 voice = ctx->Voices;
1570 voice_end = voice + ctx->VoiceCount;
1571 for(;voice != voice_end;++voice)
1573 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1574 if(source && ATOMIC_LOAD(&(*voice)->Playing, almemory_order_relaxed) &&
1575 (*voice)->Step > 0)
1577 if(!MixSource(*voice, source, device, SamplesToDo))
1579 ATOMIC_STORE(&(*voice)->Source, NULL, almemory_order_relaxed);
1580 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1585 /* effect slot processing */
1586 for(i = 0;i < auxslots->count;i++)
1588 const ALeffectslot *slot = auxslots->slot[i];
1589 ALeffectState *state = slot->Params.EffectState;
1590 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1591 state->OutChannels);
1594 ctx = ctx->next;
1597 if(device->DefaultSlot != NULL)
1599 const ALeffectslot *slot = device->DefaultSlot;
1600 ALeffectState *state = slot->Params.EffectState;
1601 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1602 state->OutChannels);
1605 /* Increment the clock time. Every second's worth of samples is
1606 * converted and added to clock base so that large sample counts don't
1607 * overflow during conversion. This also guarantees an exact, stable
1608 * conversion. */
1609 device->SamplesDone += SamplesToDo;
1610 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1611 device->SamplesDone %= device->Frequency;
1612 IncrementRef(&device->MixCount);
1614 if(device->HrtfHandle)
1616 HrtfDirectMixerFunc HrtfMix;
1617 DirectHrtfState *state;
1618 int lidx, ridx;
1620 if(device->AmbiUp)
1621 ambiup_process(device->AmbiUp,
1622 device->Dry.Buffer, device->Dry.NumChannels,
1623 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1626 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1627 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1628 assert(lidx != -1 && ridx != -1);
1630 HrtfMix = SelectHrtfMixer();
1631 state = device->Hrtf;
1632 for(c = 0;c < device->Dry.NumChannels;c++)
1634 HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1635 device->Dry.Buffer[c], state->Offset, state->IrSize,
1636 SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
1637 state->Chan[c].Values, SamplesToDo
1640 state->Offset += SamplesToDo;
1642 else if(device->AmbiDecoder)
1644 if(device->Dry.Buffer != device->FOAOut.Buffer)
1645 bformatdec_upSample(device->AmbiDecoder,
1646 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1647 device->FOAOut.NumChannels, SamplesToDo
1649 bformatdec_process(device->AmbiDecoder,
1650 device->RealOut.Buffer, device->RealOut.NumChannels,
1651 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1654 else if(device->AmbiUp)
1656 ambiup_process(device->AmbiUp,
1657 device->RealOut.Buffer, device->RealOut.NumChannels,
1658 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1661 else if(device->Uhj_Encoder)
1663 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1664 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1665 if(lidx != -1 && ridx != -1)
1667 /* Encode to stereo-compatible 2-channel UHJ output. */
1668 EncodeUhj2(device->Uhj_Encoder,
1669 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1670 device->Dry.Buffer, SamplesToDo
1674 else if(device->Bs2b)
1676 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1677 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1678 if(lidx != -1 && ridx != -1)
1680 /* Apply binaural/crossfeed filter */
1681 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1682 device->RealOut.Buffer[ridx], SamplesToDo);
1686 if(buffer)
1688 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1689 ALsizei OutChannels = device->RealOut.NumChannels;
1690 DistanceComp *DistComp;
1692 if(device->LimiterGain > 0.0f)
1694 /* Limiter attack drops -80dB in 50ms. */
1695 const ALfloat AttackRate = powf(0.0001f, 1.0f/(device->Frequency*0.05f));
1696 /* Limiter release raises +80dB in 1s. */
1697 const ALfloat ReleaseRate = powf(0.0001f, 1.0f/(device->Frequency*1.0f));
1699 /* Use NFCtrlData for temp gain storage. */
1700 device->LimiterGain = ApplyLimiter(OutBuffer, OutChannels,
1701 AttackRate, ReleaseRate, device->LimiterGain, device->NFCtrlData,
1702 SamplesToDo
1706 DistComp = device->ChannelDelay;
1707 #define WRITE(T, a, b, c, d, e) do { \
1708 Write_##T(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \
1709 buffer = (T*)buffer + (d)*(e); \
1710 } while(0)
1711 switch(device->FmtType)
1713 case DevFmtByte:
1714 WRITE(ALbyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1715 break;
1716 case DevFmtUByte:
1717 WRITE(ALubyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1718 break;
1719 case DevFmtShort:
1720 WRITE(ALshort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1721 break;
1722 case DevFmtUShort:
1723 WRITE(ALushort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1724 break;
1725 case DevFmtInt:
1726 WRITE(ALint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1727 break;
1728 case DevFmtUInt:
1729 WRITE(ALuint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1730 break;
1731 case DevFmtFloat:
1732 WRITE(ALfloat, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1733 break;
1735 #undef WRITE
1738 size -= SamplesToDo;
1741 RestoreFPUMode(&oldMode);
1745 void aluHandleDisconnect(ALCdevice *device)
1747 ALCcontext *Context;
1749 device->Connected = ALC_FALSE;
1751 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1752 while(Context)
1754 ALvoice **voice, **voice_end;
1756 voice = Context->Voices;
1757 voice_end = voice + Context->VoiceCount;
1758 while(voice != voice_end)
1760 ALsource *source = ATOMIC_EXCHANGE_PTR(&(*voice)->Source, NULL,
1761 almemory_order_acq_rel);
1762 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1764 if(source)
1766 ALenum playing = AL_PLAYING;
1767 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1770 voice++;
1772 Context->VoiceCount = 0;
1774 Context = Context->next;