Add an option to dither 8- and 16-bit output
[openal-soft.git] / Alc / ALu.c
bloba297292fa4952d90c814963a359358b84b73b608
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 struct OutputLimiter *alloc_limiter(void)
105 struct OutputLimiter *limiter = al_calloc(16, sizeof(*limiter));
106 /* Limiter attack drops -80dB in 50ms. */
107 limiter->AttackRate = 0.05f;
108 /* Limiter release raises +80dB in 1s. */
109 limiter->ReleaseRate = 1.0f;
110 limiter->Gain = 1.0f;
111 return limiter;
114 void DeinitVoice(ALvoice *voice)
116 struct ALvoiceProps *props;
117 size_t count = 0;
119 props = ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL);
120 if(props) al_free(props);
122 props = ATOMIC_EXCHANGE_PTR(&voice->FreeList, NULL, almemory_order_relaxed);
123 while(props)
125 struct ALvoiceProps *next;
126 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
127 al_free(props);
128 props = next;
129 ++count;
131 /* This is excessively spammy if it traces every voice destruction, so just
132 * warn if it was unexpectedly large.
134 if(count > 3)
135 WARN("Freed "SZFMT" voice property objects\n", count);
139 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
141 #ifdef HAVE_NEON
142 if((CPUCapFlags&CPU_CAP_NEON))
143 return MixDirectHrtf_Neon;
144 #endif
145 #ifdef HAVE_SSE
146 if((CPUCapFlags&CPU_CAP_SSE))
147 return MixDirectHrtf_SSE;
148 #endif
150 return MixDirectHrtf_C;
154 /* Prior to VS2013, MSVC lacks the round() family of functions. */
155 #if defined(_MSC_VER) && _MSC_VER < 1800
156 static long lroundf(float val)
158 if(val < 0.0)
159 return fastf2i(ceilf(val-0.5f));
160 return fastf2i(floorf(val+0.5f));
162 #endif
164 /* This RNG method was created based on the math found in opusdec. It's quick,
165 * and starting with a seed value of 22222, is suitable for generating
166 * whitenoise.
168 static inline ALuint dither_rng(ALuint *seed)
170 *seed = (*seed * 96314165) + 907633515;
171 return *seed;
175 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
177 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
178 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
179 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
182 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
184 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
187 static ALfloat aluNormalize(ALfloat *vec)
189 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
190 if(length > 0.0f)
192 ALfloat inv_length = 1.0f/length;
193 vec[0] *= inv_length;
194 vec[1] *= inv_length;
195 vec[2] *= inv_length;
197 return length;
200 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
202 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
204 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];
205 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];
206 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];
209 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
211 aluVector v;
212 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];
213 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];
214 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];
215 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];
216 return v;
220 /* Prepares the interpolator for a given rate (determined by increment). A
221 * result of AL_FALSE indicates that the filter output will completely cut
222 * the input signal.
224 * With a bit of work, and a trade of memory for CPU cost, this could be
225 * modified for use with an interpolated increment for buttery-smooth pitch
226 * changes.
228 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
230 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
231 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
232 static const ALuint to[4][BSINC_SCALE_COUNT] =
234 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
235 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
236 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
237 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
239 static const ALuint tm[2][BSINC_SCALE_COUNT] =
241 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
242 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
244 ALfloat sf;
245 ALuint si, pi;
246 ALboolean uncut = AL_TRUE;
248 if(increment > FRACTIONONE)
250 sf = (ALfloat)FRACTIONONE / increment;
251 if(sf < scaleBase)
253 /* Signal has been completely cut. The return result can be used
254 * to skip the filter (and output zeros) as an optimization.
256 sf = 0.0f;
257 si = 0;
258 uncut = AL_FALSE;
260 else
262 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
263 si = fastf2u(sf);
264 /* The interpolation factor is fit to this diagonally-symmetric
265 * curve to reduce the transition ripple caused by interpolating
266 * different scales of the sinc function.
268 sf = 1.0f - cosf(asinf(sf - si));
271 else
273 sf = 0.0f;
274 si = BSINC_SCALE_COUNT - 1;
277 state->sf = sf;
278 state->m = m[si];
279 state->l = -(ALint)((m[si] / 2) - 1);
280 /* The CPU cost of this table re-mapping could be traded for the memory
281 * cost of a complete table map (1024 elements large).
283 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
285 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
286 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
287 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
288 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
290 return uncut;
294 static ALboolean CalcListenerParams(ALCcontext *Context)
296 ALlistener *Listener = Context->Listener;
297 ALfloat N[3], V[3], U[3], P[3];
298 struct ALlistenerProps *props;
299 aluVector vel;
301 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
302 if(!props) return AL_FALSE;
304 /* AT then UP */
305 N[0] = props->Forward[0];
306 N[1] = props->Forward[1];
307 N[2] = props->Forward[2];
308 aluNormalize(N);
309 V[0] = props->Up[0];
310 V[1] = props->Up[1];
311 V[2] = props->Up[2];
312 aluNormalize(V);
313 /* Build and normalize right-vector */
314 aluCrossproduct(N, V, U);
315 aluNormalize(U);
317 aluMatrixfSet(&Listener->Params.Matrix,
318 U[0], V[0], -N[0], 0.0,
319 U[1], V[1], -N[1], 0.0,
320 U[2], V[2], -N[2], 0.0,
321 0.0, 0.0, 0.0, 1.0
324 P[0] = props->Position[0];
325 P[1] = props->Position[1];
326 P[2] = props->Position[2];
327 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
328 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
330 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
331 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
333 Listener->Params.Gain = props->Gain * Context->GainBoost;
334 Listener->Params.MetersPerUnit = props->MetersPerUnit;
336 Listener->Params.DopplerFactor = props->DopplerFactor;
337 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
339 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
340 Listener->Params.DistanceModel = props->DistanceModel;
342 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
343 return AL_TRUE;
346 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
348 struct ALeffectslotProps *props;
349 ALeffectState *state;
351 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
352 if(!props) return AL_FALSE;
354 slot->Params.Gain = props->Gain;
355 slot->Params.AuxSendAuto = props->AuxSendAuto;
356 slot->Params.EffectType = props->Type;
357 if(IsReverbEffect(slot->Params.EffectType))
359 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
360 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
361 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
362 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
363 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
365 else
367 slot->Params.RoomRolloff = 0.0f;
368 slot->Params.DecayTime = 0.0f;
369 slot->Params.DecayHFRatio = 0.0f;
370 slot->Params.AirAbsorptionGainHF = 1.0f;
371 slot->Params.DecayHFLimit = AL_FALSE;
374 /* Swap effect states. No need to play with the ref counts since they keep
375 * the same number of refs.
377 state = props->State;
378 props->State = slot->Params.EffectState;
379 slot->Params.EffectState = state;
381 V(state,update)(device, slot, &props->Props);
383 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
384 return AL_TRUE;
388 static const struct ChanMap MonoMap[1] = {
389 { FrontCenter, 0.0f, 0.0f }
390 }, RearMap[2] = {
391 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
392 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
393 }, QuadMap[4] = {
394 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
395 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
396 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
397 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
398 }, X51Map[6] = {
399 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
400 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
401 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
402 { LFE, 0.0f, 0.0f },
403 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
404 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
405 }, X61Map[7] = {
406 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
407 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
408 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
409 { LFE, 0.0f, 0.0f },
410 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
411 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
412 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
413 }, X71Map[8] = {
414 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
415 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
416 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
417 { LFE, 0.0f, 0.0f },
418 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
419 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
420 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
421 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
424 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
425 const ALfloat Spread, const ALfloat DryGain,
426 const ALfloat DryGainHF, const ALfloat DryGainLF,
427 const ALfloat *WetGain, const ALfloat *WetGainLF,
428 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
429 const ALbuffer *Buffer, const struct ALvoiceProps *props,
430 const ALlistener *Listener, const ALCdevice *Device)
432 struct ChanMap StereoMap[2] = {
433 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
434 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
436 bool DirectChannels = props->DirectChannels;
437 const ALsizei NumSends = Device->NumAuxSends;
438 const ALuint Frequency = Device->Frequency;
439 const struct ChanMap *chans = NULL;
440 ALsizei num_channels = 0;
441 bool isbformat = false;
442 ALfloat downmix_gain = 1.0f;
443 ALsizei c, i, j;
445 switch(Buffer->FmtChannels)
447 case FmtMono:
448 chans = MonoMap;
449 num_channels = 1;
450 /* Mono buffers are never played direct. */
451 DirectChannels = false;
452 break;
454 case FmtStereo:
455 /* Convert counter-clockwise to clockwise. */
456 StereoMap[0].angle = -props->StereoPan[0];
457 StereoMap[1].angle = -props->StereoPan[1];
459 chans = StereoMap;
460 num_channels = 2;
461 downmix_gain = 1.0f / 2.0f;
462 break;
464 case FmtRear:
465 chans = RearMap;
466 num_channels = 2;
467 downmix_gain = 1.0f / 2.0f;
468 break;
470 case FmtQuad:
471 chans = QuadMap;
472 num_channels = 4;
473 downmix_gain = 1.0f / 4.0f;
474 break;
476 case FmtX51:
477 chans = X51Map;
478 num_channels = 6;
479 /* NOTE: Excludes LFE. */
480 downmix_gain = 1.0f / 5.0f;
481 break;
483 case FmtX61:
484 chans = X61Map;
485 num_channels = 7;
486 /* NOTE: Excludes LFE. */
487 downmix_gain = 1.0f / 6.0f;
488 break;
490 case FmtX71:
491 chans = X71Map;
492 num_channels = 8;
493 /* NOTE: Excludes LFE. */
494 downmix_gain = 1.0f / 7.0f;
495 break;
497 case FmtBFormat2D:
498 num_channels = 3;
499 isbformat = true;
500 DirectChannels = false;
501 break;
503 case FmtBFormat3D:
504 num_channels = 4;
505 isbformat = true;
506 DirectChannels = false;
507 break;
510 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
511 if(isbformat)
513 /* Special handling for B-Format sources. */
515 if(Distance > FLT_EPSILON)
517 /* Panning a B-Format sound toward some direction is easy. Just pan
518 * the first (W) channel as a normal mono sound and silence the
519 * others.
521 ALfloat coeffs[MAX_AMBI_COEFFS];
523 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
525 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
526 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
527 (mdist * (ALfloat)Device->Frequency);
528 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
529 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
530 /* Clamp w0 for really close distances, to prevent excessive
531 * bass.
533 w0 = minf(w0, w1*4.0f);
535 /* Only need to adjust the first channel of a B-Format source. */
536 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
537 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
538 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
540 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
541 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
542 voice->Flags |= VOICE_HAS_NFC;
545 if(Device->Render_Mode == StereoPair)
547 ALfloat ev = asinf(Dir[1]);
548 ALfloat az = atan2f(Dir[0], -Dir[2]);
549 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
551 else
552 CalcDirectionCoeffs(Dir, Spread, coeffs);
554 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
555 ComputePanningGains(Device->Dry, coeffs, DryGain*1.414213562f,
556 voice->Direct.Params[0].Gains.Target);
557 for(c = 1;c < num_channels;c++)
559 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
560 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
563 for(i = 0;i < NumSends;i++)
565 const ALeffectslot *Slot = SendSlots[i];
566 if(Slot)
567 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
568 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
570 else
571 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
572 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
573 for(c = 1;c < num_channels;c++)
575 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
576 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
580 else
582 /* Local B-Format sources have their XYZ channels rotated according
583 * to the orientation.
585 ALfloat N[3], V[3], U[3];
586 aluMatrixf matrix;
587 ALfloat scale;
589 if(Device->AvgSpeakerDist > 0.0f)
591 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
592 * is what we want for FOA input. The first channel may have
593 * been previously re-adjusted if panned, so reset it.
595 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f);
596 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f);
597 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 0.0f);
599 voice->Direct.ChannelsPerOrder[0] = 1;
600 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
601 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
602 voice->Direct.ChannelsPerOrder[2] = 0;
603 voice->Flags |= VOICE_HAS_NFC;
606 /* AT then UP */
607 N[0] = props->Orientation[0][0];
608 N[1] = props->Orientation[0][1];
609 N[2] = props->Orientation[0][2];
610 aluNormalize(N);
611 V[0] = props->Orientation[1][0];
612 V[1] = props->Orientation[1][1];
613 V[2] = props->Orientation[1][2];
614 aluNormalize(V);
615 if(!props->HeadRelative)
617 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
618 aluMatrixfFloat3(N, 0.0f, lmatrix);
619 aluMatrixfFloat3(V, 0.0f, lmatrix);
621 /* Build and normalize right-vector */
622 aluCrossproduct(N, V, U);
623 aluNormalize(U);
625 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
626 scale = 1.732050808f;
627 aluMatrixfSet(&matrix,
628 1.414213562f, 0.0f, 0.0f, 0.0f,
629 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
630 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
631 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
634 voice->Direct.Buffer = Device->FOAOut.Buffer;
635 voice->Direct.Channels = Device->FOAOut.NumChannels;
636 for(c = 0;c < num_channels;c++)
637 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
638 voice->Direct.Params[c].Gains.Target);
639 for(i = 0;i < NumSends;i++)
641 const ALeffectslot *Slot = SendSlots[i];
642 if(Slot)
644 for(c = 0;c < num_channels;c++)
645 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
646 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
649 else
651 for(c = 0;c < num_channels;c++)
652 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
653 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
658 else if(DirectChannels)
660 /* Direct source channels always play local. Skip the virtual channels
661 * and write inputs to the matching real outputs.
663 voice->Direct.Buffer = Device->RealOut.Buffer;
664 voice->Direct.Channels = Device->RealOut.NumChannels;
666 for(c = 0;c < num_channels;c++)
668 int idx;
669 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
670 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
671 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
672 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
675 /* Auxiliary sends still use normal channel panning since they mix to
676 * B-Format, which can't channel-match.
678 for(c = 0;c < num_channels;c++)
680 ALfloat coeffs[MAX_AMBI_COEFFS];
681 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
683 for(i = 0;i < NumSends;i++)
685 const ALeffectslot *Slot = SendSlots[i];
686 if(Slot)
687 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
688 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
690 else
691 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
692 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
696 else if(Device->Render_Mode == HrtfRender)
698 /* Full HRTF rendering. Skip the virtual channels and render to the
699 * real outputs.
701 voice->Direct.Buffer = Device->RealOut.Buffer;
702 voice->Direct.Channels = Device->RealOut.NumChannels;
704 if(Distance > FLT_EPSILON)
706 ALfloat coeffs[MAX_AMBI_COEFFS];
707 ALfloat ev, az;
709 ev = asinf(Dir[1]);
710 az = atan2f(Dir[0], -Dir[2]);
712 /* Get the HRIR coefficients and delays just once, for the given
713 * source direction.
715 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
716 voice->Direct.Params[0].Hrtf.Target.Coeffs,
717 voice->Direct.Params[0].Hrtf.Target.Delay);
718 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
720 /* Remaining channels use the same results as the first. */
721 for(c = 1;c < num_channels;c++)
723 /* Skip LFE */
724 if(chans[c].channel == LFE)
725 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
726 sizeof(voice->Direct.Params[c].Hrtf.Target));
727 else
728 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
731 /* Calculate the directional coefficients once, which apply to all
732 * input channels of the source sends.
734 CalcDirectionCoeffs(Dir, Spread, coeffs);
736 for(i = 0;i < NumSends;i++)
738 const ALeffectslot *Slot = SendSlots[i];
739 if(Slot)
740 for(c = 0;c < num_channels;c++)
742 /* Skip LFE */
743 if(chans[c].channel == LFE)
744 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
745 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
746 else
747 ComputePanningGainsBF(Slot->ChanMap,
748 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
749 voice->Send[i].Params[c].Gains.Target
752 else
753 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
754 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
757 else
759 /* Local sources on HRTF play with each channel panned to its
760 * relative location around the listener, providing "virtual
761 * speaker" responses.
763 for(c = 0;c < num_channels;c++)
765 ALfloat coeffs[MAX_AMBI_COEFFS];
767 if(chans[c].channel == LFE)
769 /* Skip LFE */
770 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
771 sizeof(voice->Direct.Params[c].Hrtf.Target));
772 for(i = 0;i < NumSends;i++)
774 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
775 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
777 continue;
780 /* Get the HRIR coefficients and delays for this channel
781 * position.
783 GetHrtfCoeffs(Device->HrtfHandle,
784 chans[c].elevation, chans[c].angle, Spread,
785 voice->Direct.Params[c].Hrtf.Target.Coeffs,
786 voice->Direct.Params[c].Hrtf.Target.Delay
788 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
790 /* Normal panning for auxiliary sends. */
791 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
793 for(i = 0;i < NumSends;i++)
795 const ALeffectslot *Slot = SendSlots[i];
796 if(Slot)
797 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
798 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
800 else
801 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
802 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
807 voice->Flags |= VOICE_HAS_HRTF;
809 else
811 /* Non-HRTF rendering. Use normal panning to the output. */
813 if(Distance > FLT_EPSILON)
815 ALfloat coeffs[MAX_AMBI_COEFFS];
816 ALfloat w0 = 0.0f;
818 /* Calculate NFC filter coefficient if needed. */
819 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
821 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
822 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
823 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
824 w0 = SPEEDOFSOUNDMETRESPERSEC /
825 (mdist * (ALfloat)Device->Frequency);
826 /* Clamp w0 for really close distances, to prevent excessive
827 * bass.
829 w0 = minf(w0, w1*4.0f);
831 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
832 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
833 voice->Flags |= VOICE_HAS_NFC;
836 /* Calculate the directional coefficients once, which apply to all
837 * input channels.
839 if(Device->Render_Mode == StereoPair)
841 ALfloat ev = asinf(Dir[1]);
842 ALfloat az = atan2f(Dir[0], -Dir[2]);
843 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
845 else
846 CalcDirectionCoeffs(Dir, Spread, coeffs);
848 for(c = 0;c < num_channels;c++)
850 /* Adjust NFC filters if needed. */
851 if((voice->Flags&VOICE_HAS_NFC))
853 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
854 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
855 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
858 /* Special-case LFE */
859 if(chans[c].channel == LFE)
861 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
862 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
863 if(Device->Dry.Buffer == Device->RealOut.Buffer)
865 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
866 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
868 continue;
871 ComputePanningGains(Device->Dry,
872 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
876 for(i = 0;i < NumSends;i++)
878 const ALeffectslot *Slot = SendSlots[i];
879 if(Slot)
880 for(c = 0;c < num_channels;c++)
882 /* Skip LFE */
883 if(chans[c].channel == LFE)
884 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
885 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
886 else
887 ComputePanningGainsBF(Slot->ChanMap,
888 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
889 voice->Send[i].Params[c].Gains.Target
892 else
893 for(c = 0;c < num_channels;c++)
895 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
896 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
900 else
902 ALfloat w0 = 0.0f;
904 if(Device->AvgSpeakerDist > 0.0f)
906 /* If the source distance is 0, set w0 to w1 to act as a pass-
907 * through. We still want to pass the signal through the
908 * filters so they keep an appropriate history, in case the
909 * source moves away from the listener.
911 w0 = SPEEDOFSOUNDMETRESPERSEC /
912 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
914 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
915 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
916 voice->Flags |= VOICE_HAS_NFC;
919 for(c = 0;c < num_channels;c++)
921 ALfloat coeffs[MAX_AMBI_COEFFS];
923 if((voice->Flags&VOICE_HAS_NFC))
925 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
926 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
927 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
930 /* Special-case LFE */
931 if(chans[c].channel == LFE)
933 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
934 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
935 if(Device->Dry.Buffer == Device->RealOut.Buffer)
937 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
938 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
941 for(i = 0;i < NumSends;i++)
943 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
944 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
946 continue;
949 if(Device->Render_Mode == StereoPair)
950 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
951 else
952 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
953 ComputePanningGains(Device->Dry,
954 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
957 for(i = 0;i < NumSends;i++)
959 const ALeffectslot *Slot = SendSlots[i];
960 if(Slot)
961 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
962 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
964 else
965 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
966 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
973 ALfloat hfScale = props->Direct.HFReference / Frequency;
974 ALfloat lfScale = props->Direct.LFReference / Frequency;
975 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
976 ALfloat gainLF = maxf(DryGainLF, 0.001f);
978 voice->Direct.FilterType = AF_None;
979 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
980 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
981 ALfilterState_setParams(
982 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
983 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
985 ALfilterState_setParams(
986 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
987 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
989 for(c = 1;c < num_channels;c++)
991 ALfilterState_copyParams(&voice->Direct.Params[c].LowPass,
992 &voice->Direct.Params[0].LowPass);
993 ALfilterState_copyParams(&voice->Direct.Params[c].HighPass,
994 &voice->Direct.Params[0].HighPass);
997 for(i = 0;i < NumSends;i++)
999 ALfloat hfScale = props->Send[i].HFReference / Frequency;
1000 ALfloat lfScale = props->Send[i].LFReference / Frequency;
1001 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
1002 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
1004 voice->Send[i].FilterType = AF_None;
1005 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1006 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1007 ALfilterState_setParams(
1008 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
1009 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1011 ALfilterState_setParams(
1012 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
1013 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1015 for(c = 1;c < num_channels;c++)
1017 ALfilterState_copyParams(&voice->Send[i].Params[c].LowPass,
1018 &voice->Send[i].Params[0].LowPass);
1019 ALfilterState_copyParams(&voice->Send[i].Params[c].HighPass,
1020 &voice->Send[i].Params[0].HighPass);
1025 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1027 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1028 const ALCdevice *Device = ALContext->Device;
1029 const ALlistener *Listener = ALContext->Listener;
1030 ALfloat DryGain, DryGainHF, DryGainLF;
1031 ALfloat WetGain[MAX_SENDS];
1032 ALfloat WetGainHF[MAX_SENDS];
1033 ALfloat WetGainLF[MAX_SENDS];
1034 ALeffectslot *SendSlots[MAX_SENDS];
1035 ALfloat Pitch;
1036 ALsizei i;
1038 voice->Direct.Buffer = Device->Dry.Buffer;
1039 voice->Direct.Channels = Device->Dry.NumChannels;
1040 for(i = 0;i < Device->NumAuxSends;i++)
1042 SendSlots[i] = props->Send[i].Slot;
1043 if(!SendSlots[i] && i == 0)
1044 SendSlots[i] = Device->DefaultSlot;
1045 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1047 SendSlots[i] = NULL;
1048 voice->Send[i].Buffer = NULL;
1049 voice->Send[i].Channels = 0;
1051 else
1053 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1054 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1058 /* Calculate the stepping value */
1059 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1060 if(Pitch > (ALfloat)MAX_PITCH)
1061 voice->Step = MAX_PITCH<<FRACTIONBITS;
1062 else
1063 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1064 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1065 voice->Resampler = SelectResampler(props->Resampler);
1067 /* Calculate gains */
1068 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1069 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1070 DryGain = minf(DryGain, GAIN_MIX_MAX);
1071 DryGainHF = props->Direct.GainHF;
1072 DryGainLF = props->Direct.GainLF;
1073 for(i = 0;i < Device->NumAuxSends;i++)
1075 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1076 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1077 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1078 WetGainHF[i] = props->Send[i].GainHF;
1079 WetGainLF[i] = props->Send[i].GainLF;
1082 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1083 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1086 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1088 const ALCdevice *Device = ALContext->Device;
1089 const ALlistener *Listener = ALContext->Listener;
1090 const ALsizei NumSends = Device->NumAuxSends;
1091 aluVector Position, Velocity, Direction, SourceToListener;
1092 ALfloat Distance, ClampedDist, DopplerFactor;
1093 ALfloat RoomAirAbsorption[MAX_SENDS];
1094 ALeffectslot *SendSlots[MAX_SENDS];
1095 ALfloat RoomRolloff[MAX_SENDS];
1096 ALfloat DecayDistance[MAX_SENDS];
1097 ALfloat DecayHFDistance[MAX_SENDS];
1098 ALfloat DryGain, DryGainHF, DryGainLF;
1099 ALfloat WetGain[MAX_SENDS];
1100 ALfloat WetGainHF[MAX_SENDS];
1101 ALfloat WetGainLF[MAX_SENDS];
1102 bool directional;
1103 ALfloat dir[3];
1104 ALfloat spread;
1105 ALfloat Pitch;
1106 ALint i;
1108 /* Set mixing buffers and get send parameters. */
1109 voice->Direct.Buffer = Device->Dry.Buffer;
1110 voice->Direct.Channels = Device->Dry.NumChannels;
1111 for(i = 0;i < NumSends;i++)
1113 SendSlots[i] = props->Send[i].Slot;
1114 if(!SendSlots[i] && i == 0)
1115 SendSlots[i] = Device->DefaultSlot;
1116 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1118 SendSlots[i] = NULL;
1119 RoomRolloff[i] = 0.0f;
1120 DecayDistance[i] = 0.0f;
1121 DecayHFDistance[i] = 0.0f;
1122 RoomAirAbsorption[i] = 1.0f;
1124 else if(SendSlots[i]->Params.AuxSendAuto)
1126 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1127 DecayDistance[i] = SendSlots[i]->Params.DecayTime * SPEEDOFSOUNDMETRESPERSEC;
1128 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1129 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
1130 if(SendSlots[i]->Params.DecayHFLimit && RoomAirAbsorption[i] < 1.0f)
1132 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) /
1133 (log10f(RoomAirAbsorption[i]) * DecayDistance[i]);
1134 limitRatio = minf(limitRatio, SendSlots[i]->Params.DecayHFRatio);
1135 DecayHFDistance[i] = minf(DecayHFDistance[i], limitRatio*DecayDistance[i]);
1138 else
1140 /* If the slot's auxiliary send auto is off, the data sent to the
1141 * effect slot is the same as the dry path, sans filter effects */
1142 RoomRolloff[i] = props->RolloffFactor;
1143 DecayDistance[i] = 0.0f;
1144 DecayHFDistance[i] = 0.0f;
1145 RoomAirAbsorption[i] = AIRABSORBGAINHF;
1148 if(!SendSlots[i])
1150 voice->Send[i].Buffer = NULL;
1151 voice->Send[i].Channels = 0;
1153 else
1155 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1156 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1160 /* Transform source to listener space (convert to head relative) */
1161 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1162 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1163 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1164 if(props->HeadRelative == AL_FALSE)
1166 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1167 /* Transform source vectors */
1168 Position = aluMatrixfVector(Matrix, &Position);
1169 Velocity = aluMatrixfVector(Matrix, &Velocity);
1170 Direction = aluMatrixfVector(Matrix, &Direction);
1172 else
1174 const aluVector *lvelocity = &Listener->Params.Velocity;
1175 /* Offset the source velocity to be relative of the listener velocity */
1176 Velocity.v[0] += lvelocity->v[0];
1177 Velocity.v[1] += lvelocity->v[1];
1178 Velocity.v[2] += lvelocity->v[2];
1181 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1182 SourceToListener.v[0] = -Position.v[0];
1183 SourceToListener.v[1] = -Position.v[1];
1184 SourceToListener.v[2] = -Position.v[2];
1185 SourceToListener.v[3] = 0.0f;
1186 Distance = aluNormalize(SourceToListener.v);
1188 /* Initial source gain */
1189 DryGain = props->Gain;
1190 DryGainHF = 1.0f;
1191 DryGainLF = 1.0f;
1192 for(i = 0;i < NumSends;i++)
1194 WetGain[i] = props->Gain;
1195 WetGainHF[i] = 1.0f;
1196 WetGainLF[i] = 1.0f;
1199 /* Calculate distance attenuation */
1200 ClampedDist = Distance;
1202 switch(Listener->Params.SourceDistanceModel ?
1203 props->DistanceModel : Listener->Params.DistanceModel)
1205 case InverseDistanceClamped:
1206 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1207 if(props->MaxDistance < props->RefDistance)
1208 break;
1209 /*fall-through*/
1210 case InverseDistance:
1211 if(props->RefDistance > 0.0f)
1213 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1214 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1215 for(i = 0;i < NumSends;i++)
1217 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1218 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1221 break;
1223 case LinearDistanceClamped:
1224 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1225 if(props->MaxDistance < props->RefDistance)
1226 break;
1227 /*fall-through*/
1228 case LinearDistance:
1229 if(props->MaxDistance != props->RefDistance)
1231 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1232 (props->MaxDistance-props->RefDistance);
1233 DryGain *= maxf(1.0f - attn, 0.0f);
1234 for(i = 0;i < NumSends;i++)
1236 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1237 (props->MaxDistance-props->RefDistance);
1238 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1241 break;
1243 case ExponentDistanceClamped:
1244 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1245 if(props->MaxDistance < props->RefDistance)
1246 break;
1247 /*fall-through*/
1248 case ExponentDistance:
1249 if(ClampedDist > 0.0f && props->RefDistance > 0.0f)
1251 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1252 for(i = 0;i < NumSends;i++)
1253 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1255 break;
1257 case DisableDistance:
1258 ClampedDist = props->RefDistance;
1259 break;
1262 /* Distance-based air absorption */
1263 if(ClampedDist > props->RefDistance)
1265 ALfloat meters_base = (ClampedDist-props->RefDistance) * Listener->Params.MetersPerUnit;
1266 if(props->AirAbsorptionFactor > 0.0f)
1268 ALfloat absorb = props->AirAbsorptionFactor * meters_base;
1269 DryGainHF *= powf(AIRABSORBGAINHF, absorb*props->RolloffFactor);
1270 for(i = 0;i < NumSends;i++)
1272 if(RoomRolloff[i] > 0.0f)
1273 WetGainHF[i] *= powf(RoomAirAbsorption[i], absorb*RoomRolloff[i]);
1277 if(props->WetGainAuto)
1279 meters_base *= props->RolloffFactor;
1281 /* Apply a decay-time transformation to the wet path, based on the
1282 * source distance in meters. The initial decay of the reverb
1283 * effect is calculated and applied to the wet path.
1285 for(i = 0;i < NumSends;i++)
1287 ALfloat gain;
1289 if(!(DecayDistance[i] > 0.0f))
1290 continue;
1292 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1293 WetGain[i] *= gain;
1294 /* Yes, the wet path's air absorption is applied with
1295 * WetGainAuto on, rather than WetGainHFAuto.
1297 if(gain > 0.0f)
1299 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1300 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1306 /* Calculate directional soundcones */
1307 if(directional && props->InnerAngle < 360.0f)
1309 ALfloat ConeVolume;
1310 ALfloat ConeHF;
1311 ALfloat Angle;
1313 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1314 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1315 if(!(Angle > props->InnerAngle))
1317 ConeVolume = 1.0f;
1318 ConeHF = 1.0f;
1320 else if(Angle < props->OuterAngle)
1322 ALfloat scale = ( Angle-props->InnerAngle) /
1323 (props->OuterAngle-props->InnerAngle);
1324 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1325 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1327 else
1329 ConeVolume = props->OuterGain;
1330 ConeHF = props->OuterGainHF;
1333 DryGain *= ConeVolume;
1334 if(props->DryGainHFAuto)
1335 DryGainHF *= ConeHF;
1336 if(props->WetGainAuto)
1338 for(i = 0;i < NumSends;i++)
1339 WetGain[i] *= ConeVolume;
1341 if(props->WetGainHFAuto)
1343 for(i = 0;i < NumSends;i++)
1344 WetGainHF[i] *= ConeHF;
1348 /* Apply gain and frequency filters */
1349 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1350 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1351 DryGainHF *= props->Direct.GainHF;
1352 DryGainLF *= props->Direct.GainLF;
1353 for(i = 0;i < NumSends;i++)
1355 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1356 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1357 WetGainHF[i] *= props->Send[i].GainHF;
1358 WetGainLF[i] *= props->Send[i].GainLF;
1362 /* Initial source pitch */
1363 Pitch = props->Pitch;
1365 /* Calculate velocity-based doppler effect */
1366 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1367 if(DopplerFactor > 0.0f)
1369 const aluVector *lvelocity = &Listener->Params.Velocity;
1370 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1371 ALfloat vss, vls;
1373 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1374 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1376 if(!(vls < SpeedOfSound))
1378 /* Listener moving away from the source at the speed of sound.
1379 * Sound waves can't catch it.
1381 Pitch = 0.0f;
1383 else if(!(vss < SpeedOfSound))
1385 /* Source moving toward the listener at the speed of sound. Sound
1386 * waves bunch up to extreme frequencies.
1388 Pitch = HUGE_VALF;
1390 else
1392 /* Source and listener movement is nominal. Calculate the proper
1393 * doppler shift.
1395 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1399 /* Adjust pitch based on the buffer and output frequencies, and calculate
1400 * fixed-point stepping value.
1402 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1403 if(Pitch > (ALfloat)MAX_PITCH)
1404 voice->Step = MAX_PITCH<<FRACTIONBITS;
1405 else
1406 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1407 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1408 voice->Resampler = SelectResampler(props->Resampler);
1410 if(Distance > FLT_EPSILON)
1412 dir[0] = -SourceToListener.v[0];
1413 /* Clamp Y, in case rounding errors caused it to end up outside of
1414 * -1...+1.
1416 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1417 dir[2] = -SourceToListener.v[2] * ZScale;
1419 else
1421 dir[0] = 0.0f;
1422 dir[1] = 0.0f;
1423 dir[2] = -1.0f;
1425 if(props->Radius > Distance)
1426 spread = F_TAU - Distance/props->Radius*F_PI;
1427 else if(Distance > FLT_EPSILON)
1428 spread = asinf(props->Radius / Distance) * 2.0f;
1429 else
1430 spread = 0.0f;
1432 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1433 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1436 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1438 ALbufferlistitem *BufferListItem;
1439 struct ALvoiceProps *props;
1441 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1442 if(!props && !force) return;
1444 if(props)
1446 memcpy(voice->Props, props,
1447 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1450 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &voice->FreeList, props);
1452 props = voice->Props;
1454 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1455 while(BufferListItem != NULL)
1457 const ALbuffer *buffer;
1458 if((buffer=BufferListItem->buffer) != NULL)
1460 if(props->SpatializeMode == SpatializeOn ||
1461 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1462 CalcAttnSourceParams(voice, props, buffer, context);
1463 else
1464 CalcNonAttnSourceParams(voice, props, buffer, context);
1465 break;
1467 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1472 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1474 ALvoice **voice, **voice_end;
1475 ALsource *source;
1476 ALsizei i;
1478 IncrementRef(&ctx->UpdateCount);
1479 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1481 ALboolean force = CalcListenerParams(ctx);
1482 for(i = 0;i < slots->count;i++)
1483 force |= CalcEffectSlotParams(slots->slot[i], ctx->Device);
1485 voice = ctx->Voices;
1486 voice_end = voice + ctx->VoiceCount;
1487 for(;voice != voice_end;++voice)
1489 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1490 if(source) CalcSourceParams(*voice, ctx, force);
1493 IncrementRef(&ctx->UpdateCount);
1497 static_assert(LIMITER_VALUE_MAX < (UINT_MAX/LIMITER_WINDOW_SIZE), "LIMITER_VALUE_MAX is too big");
1499 static void ApplyLimiter(struct OutputLimiter *Limiter,
1500 ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALsizei NumChans,
1501 const ALfloat AttackRate, const ALfloat ReleaseRate,
1502 ALfloat *restrict Values, const ALsizei SamplesToDo)
1504 bool do_limit = false;
1505 ALsizei c, i;
1507 OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
1508 Values = ASSUME_ALIGNED(Values, 16);
1510 for(i = 0;i < SamplesToDo;i++)
1511 Values[i] = 0.0f;
1513 /* First, find the maximum amplitude (squared) for each sample position in each channel. */
1514 for(c = 0;c < NumChans;c++)
1516 for(i = 0;i < SamplesToDo;i++)
1518 ALfloat amp = OutBuffer[c][i];
1519 Values[i] = maxf(Values[i], amp*amp);
1523 /* Next, calculate the gains needed to limit the output. */
1525 ALfloat lastgain = Limiter->Gain;
1526 ALsizei wpos = Limiter->Pos;
1527 ALuint sum = Limiter->SquaredSum;
1528 ALfloat gain, rms;
1530 for(i = 0;i < SamplesToDo;i++)
1532 sum -= Limiter->Window[wpos];
1533 Limiter->Window[wpos] = fastf2u(minf(Values[i]*65536.0f, LIMITER_VALUE_MAX));
1534 sum += Limiter->Window[wpos];
1536 rms = sqrtf((ALfloat)sum / ((ALfloat)LIMITER_WINDOW_SIZE*65536.0f));
1538 /* Clamp the minimum RMS to 0dB. The uint used for the squared sum
1539 * inherently limits the maximum RMS to about 21dB, thus the gain
1540 * ranges from 0dB to -21dB.
1542 gain = 1.0f / maxf(rms, 1.0f);
1543 if(lastgain >= gain)
1544 lastgain = maxf(lastgain*AttackRate, gain);
1545 else
1546 lastgain = minf(lastgain/ReleaseRate, gain);
1547 do_limit |= (lastgain < 1.0f);
1548 Values[i] = lastgain;
1550 wpos = (wpos+1)&LIMITER_WINDOW_MASK;
1553 Limiter->Gain = lastgain;
1554 Limiter->Pos = wpos;
1555 Limiter->SquaredSum = sum;
1557 if(do_limit)
1559 /* Finally, apply the gains to each channel. */
1560 for(c = 0;c < NumChans;c++)
1562 for(i = 0;i < SamplesToDo;i++)
1563 OutBuffer[c][i] *= Values[i];
1569 /* NOTE: Non-dithered conversions have unused extra parameters. */
1570 static inline ALfloat aluF2F(ALfloat val, ...)
1571 { return val; }
1572 static inline ALint aluF2I(ALfloat val, ...)
1574 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1575 * integer range normalized floats can be safely converted to (a bit of the
1576 * exponent helps out, effectively giving 25 bits).
1578 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1580 static inline ALshort aluF2S(ALfloat val, ...)
1581 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1582 static inline ALbyte aluF2B(ALfloat val, ...)
1583 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1585 /* Dithered conversion functions. Only applies to 8- and 16-bit output for now,
1586 * as 32-bit int and float are at the limits of the rendered sample depth. This
1587 * can change if the dithering bit depth becomes configurable (effectively
1588 * quantizing to a lower bit depth than the output is capable of).
1590 static inline ALshort aluF2SDithered(ALfloat val, const ALfloat dither_val)
1592 val = val*32768.0f + dither_val;
1593 return lroundf(clampf(val, -32768.0f, 32767.0f));
1595 static inline ALbyte aluF2BDithered(ALfloat val, const ALfloat dither_val)
1597 val = val*128.0f + dither_val;
1598 return lroundf(clampf(val, -128.0f, 127.0f));
1601 /* Define unsigned output variations. */
1602 #define DECL_TEMPLATE(T, Name, func, O) \
1603 static inline T Name(ALfloat val, const ALfloat dither_val) \
1604 { return func(val, dither_val)+O; }
1606 DECL_TEMPLATE(ALubyte, aluF2UB, aluF2B, 128)
1607 DECL_TEMPLATE(ALushort, aluF2US, aluF2S, 32768)
1608 DECL_TEMPLATE(ALuint, aluF2UI, aluF2I, 2147483648u)
1609 DECL_TEMPLATE(ALubyte, aluF2UBDithered, aluF2BDithered, 128)
1610 DECL_TEMPLATE(ALushort, aluF2USDithered, aluF2SDithered, 32768)
1612 #undef DECL_TEMPLATE
1614 #define DECL_TEMPLATE(T, D, func) \
1615 static void Write##T##D(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1616 DistanceComp *distcomp, \
1617 const ALfloat *restrict DitherValues, \
1618 ALsizei SamplesToDo, ALsizei numchans) \
1620 ALsizei i, j; \
1621 for(j = 0;j < numchans;j++) \
1623 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1624 T *restrict out = (T*)OutBuffer + j; \
1625 const ALfloat gain = distcomp[j].Gain; \
1626 const ALsizei base = distcomp[j].Length; \
1627 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[j].Buffer, 16); \
1628 if(base > 0 || gain != 1.0f) \
1630 if(SamplesToDo >= base) \
1632 for(i = 0;i < base;i++) \
1633 out[i*numchans] = func(distbuf[i]*gain, DitherValues[i]); \
1634 for(;i < SamplesToDo;i++) \
1635 out[i*numchans] = func(in[i-base]*gain, DitherValues[i]); \
1636 memcpy(distbuf, &in[SamplesToDo-base], base*sizeof(ALfloat)); \
1638 else \
1640 for(i = 0;i < SamplesToDo;i++) \
1641 out[i*numchans] = func(distbuf[i]*gain, DitherValues[i]); \
1642 memmove(distbuf, distbuf+SamplesToDo, \
1643 (base-SamplesToDo)*sizeof(ALfloat)); \
1644 memcpy(distbuf+base-SamplesToDo, in, \
1645 SamplesToDo*sizeof(ALfloat)); \
1648 else for(i = 0;i < SamplesToDo;i++) \
1649 out[i*numchans] = func(in[i], DitherValues[i]); \
1653 DECL_TEMPLATE(ALfloat, /*no dither*/, aluF2F)
1654 DECL_TEMPLATE(ALuint, /*no dither*/, aluF2UI)
1655 DECL_TEMPLATE(ALint, /*no dither*/, aluF2I)
1656 DECL_TEMPLATE(ALushort, /*no dither*/, aluF2US)
1657 DECL_TEMPLATE(ALshort, /*no dither*/, aluF2S)
1658 DECL_TEMPLATE(ALubyte, /*no dither*/, aluF2UB)
1659 DECL_TEMPLATE(ALbyte, /*no dither*/, aluF2B)
1661 DECL_TEMPLATE(ALushort, _Dithered, aluF2USDithered)
1662 DECL_TEMPLATE(ALshort, _Dithered, aluF2SDithered)
1663 DECL_TEMPLATE(ALubyte, _Dithered, aluF2UBDithered)
1664 DECL_TEMPLATE(ALbyte, _Dithered, aluF2BDithered)
1666 #undef DECL_TEMPLATE
1669 void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1671 ALsizei SamplesToDo;
1672 ALvoice **voice, **voice_end;
1673 ALeffectslot *slot;
1674 ALsource *source;
1675 ALCcontext *ctx;
1676 FPUCtl oldMode;
1677 ALsizei i, c;
1679 SetMixerFPUMode(&oldMode);
1681 while(size > 0)
1683 SamplesToDo = mini(size, BUFFERSIZE);
1684 for(c = 0;c < device->Dry.NumChannels;c++)
1685 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1686 if(device->Dry.Buffer != device->FOAOut.Buffer)
1687 for(c = 0;c < device->FOAOut.NumChannels;c++)
1688 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1689 if(device->Dry.Buffer != device->RealOut.Buffer)
1690 for(c = 0;c < device->RealOut.NumChannels;c++)
1691 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1693 IncrementRef(&device->MixCount);
1695 if((slot=device->DefaultSlot) != NULL)
1697 CalcEffectSlotParams(device->DefaultSlot, device);
1698 for(c = 0;c < slot->NumChannels;c++)
1699 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1702 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1703 while(ctx)
1705 const struct ALeffectslotArray *auxslots;
1707 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1708 UpdateContextSources(ctx, auxslots);
1710 for(i = 0;i < auxslots->count;i++)
1712 ALeffectslot *slot = auxslots->slot[i];
1713 for(c = 0;c < slot->NumChannels;c++)
1714 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1717 /* source processing */
1718 voice = ctx->Voices;
1719 voice_end = voice + ctx->VoiceCount;
1720 for(;voice != voice_end;++voice)
1722 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1723 if(source && ATOMIC_LOAD(&(*voice)->Playing, almemory_order_relaxed) &&
1724 (*voice)->Step > 0)
1726 if(!MixSource(*voice, source, device, SamplesToDo))
1728 ATOMIC_STORE(&(*voice)->Source, NULL, almemory_order_relaxed);
1729 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1734 /* effect slot processing */
1735 for(i = 0;i < auxslots->count;i++)
1737 const ALeffectslot *slot = auxslots->slot[i];
1738 ALeffectState *state = slot->Params.EffectState;
1739 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1740 state->OutChannels);
1743 ctx = ctx->next;
1746 if(device->DefaultSlot != NULL)
1748 const ALeffectslot *slot = device->DefaultSlot;
1749 ALeffectState *state = slot->Params.EffectState;
1750 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1751 state->OutChannels);
1754 /* Increment the clock time. Every second's worth of samples is
1755 * converted and added to clock base so that large sample counts don't
1756 * overflow during conversion. This also guarantees an exact, stable
1757 * conversion. */
1758 device->SamplesDone += SamplesToDo;
1759 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1760 device->SamplesDone %= device->Frequency;
1761 IncrementRef(&device->MixCount);
1763 if(device->HrtfHandle)
1765 HrtfDirectMixerFunc HrtfMix;
1766 DirectHrtfState *state;
1767 int lidx, ridx;
1769 if(device->AmbiUp)
1770 ambiup_process(device->AmbiUp,
1771 device->Dry.Buffer, device->Dry.NumChannels,
1772 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1775 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1776 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1777 assert(lidx != -1 && ridx != -1);
1779 HrtfMix = SelectHrtfMixer();
1780 state = device->Hrtf;
1781 for(c = 0;c < device->Dry.NumChannels;c++)
1783 HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1784 device->Dry.Buffer[c], state->Offset, state->IrSize,
1785 SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
1786 state->Chan[c].Values, SamplesToDo
1789 state->Offset += SamplesToDo;
1791 else if(device->AmbiDecoder)
1793 if(device->Dry.Buffer != device->FOAOut.Buffer)
1794 bformatdec_upSample(device->AmbiDecoder,
1795 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1796 device->FOAOut.NumChannels, SamplesToDo
1798 bformatdec_process(device->AmbiDecoder,
1799 device->RealOut.Buffer, device->RealOut.NumChannels,
1800 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1803 else if(device->AmbiUp)
1805 ambiup_process(device->AmbiUp,
1806 device->RealOut.Buffer, device->RealOut.NumChannels,
1807 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1810 else if(device->Uhj_Encoder)
1812 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1813 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1814 if(lidx != -1 && ridx != -1)
1816 /* Encode to stereo-compatible 2-channel UHJ output. */
1817 EncodeUhj2(device->Uhj_Encoder,
1818 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1819 device->Dry.Buffer, SamplesToDo
1823 else if(device->Bs2b)
1825 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1826 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1827 if(lidx != -1 && ridx != -1)
1829 /* Apply binaural/crossfeed filter */
1830 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1831 device->RealOut.Buffer[ridx], SamplesToDo);
1835 if(buffer)
1837 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1838 ALsizei OutChannels = device->RealOut.NumChannels;
1839 struct OutputLimiter *Limiter = device->Limiter;
1840 DistanceComp *DistComp;
1841 ALfloat *DitherValues;
1843 if(Limiter)
1845 const ALfloat AttackRate = powf(0.0001f, 1.0f/(device->Frequency*Limiter->AttackRate));
1846 const ALfloat ReleaseRate = powf(0.0001f, 1.0f/(device->Frequency*Limiter->ReleaseRate));
1848 /* Use NFCtrlData for temp value storage. */
1849 ApplyLimiter(Limiter, OutBuffer, OutChannels,
1850 AttackRate, ReleaseRate, device->NFCtrlData, SamplesToDo
1854 /* Dithering. Step 1, generate whitenoise (uniform distribution of
1855 * random values between -1 and +1). Use NFCtrlData for random
1856 * value storage. Step 2 is to add the noise to the samples, before
1857 * rounding and after scaling up to the desired quantization depth,
1858 * which occurs in the sample conversion stage.
1860 if(!device->DitherEnabled)
1861 memset(device->NFCtrlData, 0, SamplesToDo*sizeof(ALfloat));
1862 else
1864 ALuint dither_seed = device->DitherSeed;
1865 ALsizei i;
1867 for(i = 0;i < SamplesToDo;i++)
1869 ALuint rng0 = dither_rng(&dither_seed);
1870 ALuint rng1 = dither_rng(&dither_seed);
1871 device->NFCtrlData[i] = (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1873 device->DitherSeed = dither_seed;
1875 DitherValues = device->NFCtrlData;
1877 DistComp = device->ChannelDelay;
1878 #define WRITE(T, D, a, b, c, d, e, f) do { \
1879 Write##T##D(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e), (f)); \
1880 buffer = (T*)buffer + (e)*(f); \
1881 } while(0)
1882 switch(device->FmtType)
1884 case DevFmtByte:
1885 if(device->DitherEnabled)
1886 WRITE(ALbyte, _Dithered, OutBuffer, buffer, DistComp, DitherValues,
1887 SamplesToDo, OutChannels);
1888 else
1889 WRITE(ALbyte, /*no dither*/, OutBuffer, buffer, DistComp, DitherValues,
1890 SamplesToDo, OutChannels);
1891 break;
1892 case DevFmtUByte:
1893 if(device->DitherEnabled)
1894 WRITE(ALubyte, _Dithered, OutBuffer, buffer, DistComp, DitherValues,
1895 SamplesToDo, OutChannels);
1896 else
1897 WRITE(ALubyte, /*no dither*/, OutBuffer, buffer, DistComp, DitherValues,
1898 SamplesToDo, OutChannels);
1899 break;
1900 case DevFmtShort:
1901 if(device->DitherEnabled)
1902 WRITE(ALshort, _Dithered, OutBuffer, buffer, DistComp, DitherValues,
1903 SamplesToDo, OutChannels);
1904 else
1905 WRITE(ALshort, /*no dither*/, OutBuffer, buffer, DistComp, DitherValues,
1906 SamplesToDo, OutChannels);
1907 break;
1908 case DevFmtUShort:
1909 if(device->DitherEnabled)
1910 WRITE(ALushort, _Dithered, OutBuffer, buffer, DistComp, DitherValues,
1911 SamplesToDo, OutChannels);
1912 else
1913 WRITE(ALushort, /*no dither*/, OutBuffer, buffer, DistComp, DitherValues,
1914 SamplesToDo, OutChannels);
1915 break;
1916 case DevFmtInt:
1917 WRITE(ALint, /*no dither*/, OutBuffer, buffer, DistComp, DitherValues,
1918 SamplesToDo, OutChannels);
1919 break;
1920 case DevFmtUInt:
1921 WRITE(ALuint, /*no dither*/, OutBuffer, buffer, DistComp, DitherValues,
1922 SamplesToDo, OutChannels);
1923 break;
1924 case DevFmtFloat:
1925 WRITE(ALfloat, /*no dither*/, OutBuffer, buffer, DistComp, DitherValues,
1926 SamplesToDo, OutChannels);
1927 break;
1929 #undef WRITE
1932 size -= SamplesToDo;
1935 RestoreFPUMode(&oldMode);
1939 void aluHandleDisconnect(ALCdevice *device)
1941 ALCcontext *Context;
1943 device->Connected = ALC_FALSE;
1945 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1946 while(Context)
1948 ALvoice **voice, **voice_end;
1950 voice = Context->Voices;
1951 voice_end = voice + Context->VoiceCount;
1952 while(voice != voice_end)
1954 ALsource *source = ATOMIC_EXCHANGE_PTR(&(*voice)->Source, NULL,
1955 almemory_order_acq_rel);
1956 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1958 if(source)
1960 ALenum playing = AL_PLAYING;
1961 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1964 voice++;
1966 Context->VoiceCount = 0;
1968 Context = Context->next;