Use normal air absorption for the sends
[openal-soft.git] / Alc / ALu.c
blob7bf6f2ffc6d1f078d116aa00960375b7a679ffd5
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 /* Prior to VS2013, MSVC lacks the round() family of functions. */
144 #if defined(_MSC_VER) && _MSC_VER < 1800
145 static long lroundf(float val)
147 if(val < 0.0)
148 return fastf2i(ceilf(val-0.5f));
149 return fastf2i(floorf(val+0.5f));
151 #endif
153 /* This RNG method was created based on the math found in opusdec. It's quick,
154 * and starting with a seed value of 22222, is suitable for generating
155 * whitenoise.
157 static inline ALuint dither_rng(ALuint *seed)
159 *seed = (*seed * 96314165) + 907633515;
160 return *seed;
164 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
166 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
167 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
168 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
171 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
173 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
176 static ALfloat aluNormalize(ALfloat *vec)
178 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
179 if(length > 0.0f)
181 ALfloat inv_length = 1.0f/length;
182 vec[0] *= inv_length;
183 vec[1] *= inv_length;
184 vec[2] *= inv_length;
186 return length;
189 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
191 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
193 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];
194 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];
195 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];
198 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
200 aluVector v;
201 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];
202 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];
203 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];
204 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];
205 return v;
209 /* Prepares the interpolator for a given rate (determined by increment). A
210 * result of AL_FALSE indicates that the filter output will completely cut
211 * the input signal.
213 * With a bit of work, and a trade of memory for CPU cost, this could be
214 * modified for use with an interpolated increment for buttery-smooth pitch
215 * changes.
217 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
219 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
220 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
221 static const ALuint to[4][BSINC_SCALE_COUNT] =
223 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
224 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
225 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
226 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
228 static const ALuint tm[2][BSINC_SCALE_COUNT] =
230 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
231 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
233 ALfloat sf;
234 ALuint si, pi;
235 ALboolean uncut = AL_TRUE;
237 if(increment > FRACTIONONE)
239 sf = (ALfloat)FRACTIONONE / increment;
240 if(sf < scaleBase)
242 /* Signal has been completely cut. The return result can be used
243 * to skip the filter (and output zeros) as an optimization.
245 sf = 0.0f;
246 si = 0;
247 uncut = AL_FALSE;
249 else
251 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
252 si = fastf2u(sf);
253 /* The interpolation factor is fit to this diagonally-symmetric
254 * curve to reduce the transition ripple caused by interpolating
255 * different scales of the sinc function.
257 sf = 1.0f - cosf(asinf(sf - si));
260 else
262 sf = 0.0f;
263 si = BSINC_SCALE_COUNT - 1;
266 state->sf = sf;
267 state->m = m[si];
268 state->l = -(ALint)((m[si] / 2) - 1);
269 /* The CPU cost of this table re-mapping could be traded for the memory
270 * cost of a complete table map (1024 elements large).
272 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
274 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
275 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
276 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
277 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
279 return uncut;
283 static ALboolean CalcListenerParams(ALCcontext *Context)
285 ALlistener *Listener = Context->Listener;
286 ALfloat N[3], V[3], U[3], P[3];
287 struct ALlistenerProps *props;
288 aluVector vel;
290 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
291 if(!props) return AL_FALSE;
293 /* AT then UP */
294 N[0] = props->Forward[0];
295 N[1] = props->Forward[1];
296 N[2] = props->Forward[2];
297 aluNormalize(N);
298 V[0] = props->Up[0];
299 V[1] = props->Up[1];
300 V[2] = props->Up[2];
301 aluNormalize(V);
302 /* Build and normalize right-vector */
303 aluCrossproduct(N, V, U);
304 aluNormalize(U);
306 aluMatrixfSet(&Listener->Params.Matrix,
307 U[0], V[0], -N[0], 0.0,
308 U[1], V[1], -N[1], 0.0,
309 U[2], V[2], -N[2], 0.0,
310 0.0, 0.0, 0.0, 1.0
313 P[0] = props->Position[0];
314 P[1] = props->Position[1];
315 P[2] = props->Position[2];
316 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
317 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
319 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
320 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
322 Listener->Params.Gain = props->Gain * Context->GainBoost;
323 Listener->Params.MetersPerUnit = props->MetersPerUnit;
325 Listener->Params.DopplerFactor = props->DopplerFactor;
326 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
328 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
329 Listener->Params.DistanceModel = props->DistanceModel;
331 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
332 return AL_TRUE;
335 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
337 struct ALeffectslotProps *props;
338 ALeffectState *state;
340 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
341 if(!props) return AL_FALSE;
343 slot->Params.Gain = props->Gain;
344 slot->Params.AuxSendAuto = props->AuxSendAuto;
345 slot->Params.EffectType = props->Type;
346 if(IsReverbEffect(slot->Params.EffectType))
348 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
349 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
350 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
351 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
352 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
354 else
356 slot->Params.RoomRolloff = 0.0f;
357 slot->Params.DecayTime = 0.0f;
358 slot->Params.DecayHFRatio = 0.0f;
359 slot->Params.AirAbsorptionGainHF = 1.0f;
360 slot->Params.DecayHFLimit = AL_FALSE;
363 /* Swap effect states. No need to play with the ref counts since they keep
364 * the same number of refs.
366 state = props->State;
367 props->State = slot->Params.EffectState;
368 slot->Params.EffectState = state;
370 V(state,update)(device, slot, &props->Props);
372 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
373 return AL_TRUE;
377 static const struct ChanMap MonoMap[1] = {
378 { FrontCenter, 0.0f, 0.0f }
379 }, RearMap[2] = {
380 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
381 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
382 }, QuadMap[4] = {
383 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
384 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
385 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
386 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
387 }, X51Map[6] = {
388 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
389 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
390 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
391 { LFE, 0.0f, 0.0f },
392 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
393 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
394 }, X61Map[7] = {
395 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
396 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
397 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
398 { LFE, 0.0f, 0.0f },
399 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
400 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
401 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
402 }, X71Map[8] = {
403 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
404 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
405 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
406 { LFE, 0.0f, 0.0f },
407 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
408 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
409 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
410 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
413 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
414 const ALfloat Spread, const ALfloat DryGain,
415 const ALfloat DryGainHF, const ALfloat DryGainLF,
416 const ALfloat *WetGain, const ALfloat *WetGainLF,
417 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
418 const ALbuffer *Buffer, const struct ALvoiceProps *props,
419 const ALlistener *Listener, const ALCdevice *Device)
421 struct ChanMap StereoMap[2] = {
422 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
423 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
425 bool DirectChannels = props->DirectChannels;
426 const ALsizei NumSends = Device->NumAuxSends;
427 const ALuint Frequency = Device->Frequency;
428 const struct ChanMap *chans = NULL;
429 ALsizei num_channels = 0;
430 bool isbformat = false;
431 ALfloat downmix_gain = 1.0f;
432 ALsizei c, i, j;
434 switch(Buffer->FmtChannels)
436 case FmtMono:
437 chans = MonoMap;
438 num_channels = 1;
439 /* Mono buffers are never played direct. */
440 DirectChannels = false;
441 break;
443 case FmtStereo:
444 /* Convert counter-clockwise to clockwise. */
445 StereoMap[0].angle = -props->StereoPan[0];
446 StereoMap[1].angle = -props->StereoPan[1];
448 chans = StereoMap;
449 num_channels = 2;
450 downmix_gain = 1.0f / 2.0f;
451 break;
453 case FmtRear:
454 chans = RearMap;
455 num_channels = 2;
456 downmix_gain = 1.0f / 2.0f;
457 break;
459 case FmtQuad:
460 chans = QuadMap;
461 num_channels = 4;
462 downmix_gain = 1.0f / 4.0f;
463 break;
465 case FmtX51:
466 chans = X51Map;
467 num_channels = 6;
468 /* NOTE: Excludes LFE. */
469 downmix_gain = 1.0f / 5.0f;
470 break;
472 case FmtX61:
473 chans = X61Map;
474 num_channels = 7;
475 /* NOTE: Excludes LFE. */
476 downmix_gain = 1.0f / 6.0f;
477 break;
479 case FmtX71:
480 chans = X71Map;
481 num_channels = 8;
482 /* NOTE: Excludes LFE. */
483 downmix_gain = 1.0f / 7.0f;
484 break;
486 case FmtBFormat2D:
487 num_channels = 3;
488 isbformat = true;
489 DirectChannels = false;
490 break;
492 case FmtBFormat3D:
493 num_channels = 4;
494 isbformat = true;
495 DirectChannels = false;
496 break;
499 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
500 if(isbformat)
502 /* Special handling for B-Format sources. */
504 if(Distance > FLT_EPSILON)
506 /* Panning a B-Format sound toward some direction is easy. Just pan
507 * the first (W) channel as a normal mono sound and silence the
508 * others.
510 ALfloat coeffs[MAX_AMBI_COEFFS];
512 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
514 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
515 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
516 (mdist * (ALfloat)Device->Frequency);
517 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
518 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
519 /* Clamp w0 for really close distances, to prevent excessive
520 * bass.
522 w0 = minf(w0, w1*4.0f);
524 /* Only need to adjust the first channel of a B-Format source. */
525 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
526 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
527 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
529 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
530 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
531 voice->Flags |= VOICE_HAS_NFC;
534 if(Device->Render_Mode == StereoPair)
536 ALfloat ev = asinf(Dir[1]);
537 ALfloat az = atan2f(Dir[0], -Dir[2]);
538 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
540 else
541 CalcDirectionCoeffs(Dir, Spread, coeffs);
543 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
544 ComputePanningGains(Device->Dry, coeffs, DryGain*1.414213562f,
545 voice->Direct.Params[0].Gains.Target);
546 for(c = 1;c < num_channels;c++)
548 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
549 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
552 for(i = 0;i < NumSends;i++)
554 const ALeffectslot *Slot = SendSlots[i];
555 if(Slot)
556 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
557 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
559 else
560 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
561 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
562 for(c = 1;c < num_channels;c++)
564 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
565 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
569 else
571 /* Local B-Format sources have their XYZ channels rotated according
572 * to the orientation.
574 ALfloat N[3], V[3], U[3];
575 aluMatrixf matrix;
576 ALfloat scale;
578 if(Device->AvgSpeakerDist > 0.0f)
580 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
581 * is what we want for FOA input. The first channel may have
582 * been previously re-adjusted if panned, so reset it.
584 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f);
585 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f);
586 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 0.0f);
588 voice->Direct.ChannelsPerOrder[0] = 1;
589 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
590 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
591 voice->Direct.ChannelsPerOrder[2] = 0;
592 voice->Flags |= VOICE_HAS_NFC;
595 /* AT then UP */
596 N[0] = props->Orientation[0][0];
597 N[1] = props->Orientation[0][1];
598 N[2] = props->Orientation[0][2];
599 aluNormalize(N);
600 V[0] = props->Orientation[1][0];
601 V[1] = props->Orientation[1][1];
602 V[2] = props->Orientation[1][2];
603 aluNormalize(V);
604 if(!props->HeadRelative)
606 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
607 aluMatrixfFloat3(N, 0.0f, lmatrix);
608 aluMatrixfFloat3(V, 0.0f, lmatrix);
610 /* Build and normalize right-vector */
611 aluCrossproduct(N, V, U);
612 aluNormalize(U);
614 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
615 scale = 1.732050808f;
616 aluMatrixfSet(&matrix,
617 1.414213562f, 0.0f, 0.0f, 0.0f,
618 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
619 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
620 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
623 voice->Direct.Buffer = Device->FOAOut.Buffer;
624 voice->Direct.Channels = Device->FOAOut.NumChannels;
625 for(c = 0;c < num_channels;c++)
626 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
627 voice->Direct.Params[c].Gains.Target);
628 for(i = 0;i < NumSends;i++)
630 const ALeffectslot *Slot = SendSlots[i];
631 if(Slot)
633 for(c = 0;c < num_channels;c++)
634 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
635 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
638 else
640 for(c = 0;c < num_channels;c++)
641 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
642 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
647 else if(DirectChannels)
649 /* Direct source channels always play local. Skip the virtual channels
650 * and write inputs to the matching real outputs.
652 voice->Direct.Buffer = Device->RealOut.Buffer;
653 voice->Direct.Channels = Device->RealOut.NumChannels;
655 for(c = 0;c < num_channels;c++)
657 int idx;
658 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
659 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
660 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
661 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
664 /* Auxiliary sends still use normal channel panning since they mix to
665 * B-Format, which can't channel-match.
667 for(c = 0;c < num_channels;c++)
669 ALfloat coeffs[MAX_AMBI_COEFFS];
670 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
672 for(i = 0;i < NumSends;i++)
674 const ALeffectslot *Slot = SendSlots[i];
675 if(Slot)
676 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
677 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
679 else
680 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
681 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
685 else if(Device->Render_Mode == HrtfRender)
687 /* Full HRTF rendering. Skip the virtual channels and render to the
688 * real outputs.
690 voice->Direct.Buffer = Device->RealOut.Buffer;
691 voice->Direct.Channels = Device->RealOut.NumChannels;
693 if(Distance > FLT_EPSILON)
695 ALfloat coeffs[MAX_AMBI_COEFFS];
696 ALfloat ev, az;
698 ev = asinf(Dir[1]);
699 az = atan2f(Dir[0], -Dir[2]);
701 /* Get the HRIR coefficients and delays just once, for the given
702 * source direction.
704 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
705 voice->Direct.Params[0].Hrtf.Target.Coeffs,
706 voice->Direct.Params[0].Hrtf.Target.Delay);
707 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
709 /* Remaining channels use the same results as the first. */
710 for(c = 1;c < num_channels;c++)
712 /* Skip LFE */
713 if(chans[c].channel == LFE)
714 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
715 sizeof(voice->Direct.Params[c].Hrtf.Target));
716 else
717 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
720 /* Calculate the directional coefficients once, which apply to all
721 * input channels of the source sends.
723 CalcDirectionCoeffs(Dir, Spread, coeffs);
725 for(i = 0;i < NumSends;i++)
727 const ALeffectslot *Slot = SendSlots[i];
728 if(Slot)
729 for(c = 0;c < num_channels;c++)
731 /* Skip LFE */
732 if(chans[c].channel == LFE)
733 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
734 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
735 else
736 ComputePanningGainsBF(Slot->ChanMap,
737 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
738 voice->Send[i].Params[c].Gains.Target
741 else
742 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
743 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
746 else
748 /* Local sources on HRTF play with each channel panned to its
749 * relative location around the listener, providing "virtual
750 * speaker" responses.
752 for(c = 0;c < num_channels;c++)
754 ALfloat coeffs[MAX_AMBI_COEFFS];
756 if(chans[c].channel == LFE)
758 /* Skip LFE */
759 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
760 sizeof(voice->Direct.Params[c].Hrtf.Target));
761 for(i = 0;i < NumSends;i++)
763 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
764 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
766 continue;
769 /* Get the HRIR coefficients and delays for this channel
770 * position.
772 GetHrtfCoeffs(Device->HrtfHandle,
773 chans[c].elevation, chans[c].angle, Spread,
774 voice->Direct.Params[c].Hrtf.Target.Coeffs,
775 voice->Direct.Params[c].Hrtf.Target.Delay
777 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
779 /* Normal panning for auxiliary sends. */
780 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
782 for(i = 0;i < NumSends;i++)
784 const ALeffectslot *Slot = SendSlots[i];
785 if(Slot)
786 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
787 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
789 else
790 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
791 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
796 voice->Flags |= VOICE_HAS_HRTF;
798 else
800 /* Non-HRTF rendering. Use normal panning to the output. */
802 if(Distance > FLT_EPSILON)
804 ALfloat coeffs[MAX_AMBI_COEFFS];
805 ALfloat w0 = 0.0f;
807 /* Calculate NFC filter coefficient if needed. */
808 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
810 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
811 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
812 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
813 w0 = SPEEDOFSOUNDMETRESPERSEC /
814 (mdist * (ALfloat)Device->Frequency);
815 /* Clamp w0 for really close distances, to prevent excessive
816 * bass.
818 w0 = minf(w0, w1*4.0f);
820 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
821 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
822 voice->Flags |= VOICE_HAS_NFC;
825 /* Calculate the directional coefficients once, which apply to all
826 * input channels.
828 if(Device->Render_Mode == StereoPair)
830 ALfloat ev = asinf(Dir[1]);
831 ALfloat az = atan2f(Dir[0], -Dir[2]);
832 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
834 else
835 CalcDirectionCoeffs(Dir, Spread, coeffs);
837 for(c = 0;c < num_channels;c++)
839 /* Adjust NFC filters if needed. */
840 if((voice->Flags&VOICE_HAS_NFC))
842 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
843 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
844 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
847 /* Special-case LFE */
848 if(chans[c].channel == LFE)
850 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
851 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
852 if(Device->Dry.Buffer == Device->RealOut.Buffer)
854 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
855 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
857 continue;
860 ComputePanningGains(Device->Dry,
861 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
865 for(i = 0;i < NumSends;i++)
867 const ALeffectslot *Slot = SendSlots[i];
868 if(Slot)
869 for(c = 0;c < num_channels;c++)
871 /* Skip LFE */
872 if(chans[c].channel == LFE)
873 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
874 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
875 else
876 ComputePanningGainsBF(Slot->ChanMap,
877 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
878 voice->Send[i].Params[c].Gains.Target
881 else
882 for(c = 0;c < num_channels;c++)
884 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
885 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
889 else
891 ALfloat w0 = 0.0f;
893 if(Device->AvgSpeakerDist > 0.0f)
895 /* If the source distance is 0, set w0 to w1 to act as a pass-
896 * through. We still want to pass the signal through the
897 * filters so they keep an appropriate history, in case the
898 * source moves away from the listener.
900 w0 = SPEEDOFSOUNDMETRESPERSEC /
901 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
903 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
904 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
905 voice->Flags |= VOICE_HAS_NFC;
908 for(c = 0;c < num_channels;c++)
910 ALfloat coeffs[MAX_AMBI_COEFFS];
912 if((voice->Flags&VOICE_HAS_NFC))
914 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
915 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
916 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
919 /* Special-case LFE */
920 if(chans[c].channel == LFE)
922 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
923 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
924 if(Device->Dry.Buffer == Device->RealOut.Buffer)
926 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
927 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
930 for(i = 0;i < NumSends;i++)
932 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
933 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
935 continue;
938 if(Device->Render_Mode == StereoPair)
939 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
940 else
941 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
942 ComputePanningGains(Device->Dry,
943 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
946 for(i = 0;i < NumSends;i++)
948 const ALeffectslot *Slot = SendSlots[i];
949 if(Slot)
950 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
951 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
953 else
954 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
955 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
962 ALfloat hfScale = props->Direct.HFReference / Frequency;
963 ALfloat lfScale = props->Direct.LFReference / Frequency;
964 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
965 ALfloat gainLF = maxf(DryGainLF, 0.001f);
967 voice->Direct.FilterType = AF_None;
968 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
969 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
970 ALfilterState_setParams(
971 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
972 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
974 ALfilterState_setParams(
975 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
976 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
978 for(c = 1;c < num_channels;c++)
980 ALfilterState_copyParams(&voice->Direct.Params[c].LowPass,
981 &voice->Direct.Params[0].LowPass);
982 ALfilterState_copyParams(&voice->Direct.Params[c].HighPass,
983 &voice->Direct.Params[0].HighPass);
986 for(i = 0;i < NumSends;i++)
988 ALfloat hfScale = props->Send[i].HFReference / Frequency;
989 ALfloat lfScale = props->Send[i].LFReference / Frequency;
990 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
991 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
993 voice->Send[i].FilterType = AF_None;
994 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
995 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
996 ALfilterState_setParams(
997 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
998 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1000 ALfilterState_setParams(
1001 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
1002 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1004 for(c = 1;c < num_channels;c++)
1006 ALfilterState_copyParams(&voice->Send[i].Params[c].LowPass,
1007 &voice->Send[i].Params[0].LowPass);
1008 ALfilterState_copyParams(&voice->Send[i].Params[c].HighPass,
1009 &voice->Send[i].Params[0].HighPass);
1014 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1016 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1017 const ALCdevice *Device = ALContext->Device;
1018 const ALlistener *Listener = ALContext->Listener;
1019 ALfloat DryGain, DryGainHF, DryGainLF;
1020 ALfloat WetGain[MAX_SENDS];
1021 ALfloat WetGainHF[MAX_SENDS];
1022 ALfloat WetGainLF[MAX_SENDS];
1023 ALeffectslot *SendSlots[MAX_SENDS];
1024 ALfloat Pitch;
1025 ALsizei i;
1027 voice->Direct.Buffer = Device->Dry.Buffer;
1028 voice->Direct.Channels = Device->Dry.NumChannels;
1029 for(i = 0;i < Device->NumAuxSends;i++)
1031 SendSlots[i] = props->Send[i].Slot;
1032 if(!SendSlots[i] && i == 0)
1033 SendSlots[i] = Device->DefaultSlot;
1034 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1036 SendSlots[i] = NULL;
1037 voice->Send[i].Buffer = NULL;
1038 voice->Send[i].Channels = 0;
1040 else
1042 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1043 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1047 /* Calculate the stepping value */
1048 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1049 if(Pitch > (ALfloat)MAX_PITCH)
1050 voice->Step = MAX_PITCH<<FRACTIONBITS;
1051 else
1052 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1053 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1054 voice->Resampler = SelectResampler(props->Resampler);
1056 /* Calculate gains */
1057 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1058 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1059 DryGain = minf(DryGain, GAIN_MIX_MAX);
1060 DryGainHF = props->Direct.GainHF;
1061 DryGainLF = props->Direct.GainLF;
1062 for(i = 0;i < Device->NumAuxSends;i++)
1064 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1065 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1066 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1067 WetGainHF[i] = props->Send[i].GainHF;
1068 WetGainLF[i] = props->Send[i].GainLF;
1071 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1072 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1075 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1077 const ALCdevice *Device = ALContext->Device;
1078 const ALlistener *Listener = ALContext->Listener;
1079 const ALsizei NumSends = Device->NumAuxSends;
1080 aluVector Position, Velocity, Direction, SourceToListener;
1081 ALfloat Distance, ClampedDist, DopplerFactor;
1082 ALfloat RoomAirAbsorption[MAX_SENDS];
1083 ALeffectslot *SendSlots[MAX_SENDS];
1084 ALfloat RoomRolloff[MAX_SENDS];
1085 ALfloat DecayDistance[MAX_SENDS];
1086 ALfloat DecayHFDistance[MAX_SENDS];
1087 ALfloat DryGain, DryGainHF, DryGainLF;
1088 ALfloat WetGain[MAX_SENDS];
1089 ALfloat WetGainHF[MAX_SENDS];
1090 ALfloat WetGainLF[MAX_SENDS];
1091 bool directional;
1092 ALfloat dir[3];
1093 ALfloat spread;
1094 ALfloat Pitch;
1095 ALint i;
1097 /* Set mixing buffers and get send parameters. */
1098 voice->Direct.Buffer = Device->Dry.Buffer;
1099 voice->Direct.Channels = Device->Dry.NumChannels;
1100 for(i = 0;i < NumSends;i++)
1102 SendSlots[i] = props->Send[i].Slot;
1103 if(!SendSlots[i] && i == 0)
1104 SendSlots[i] = Device->DefaultSlot;
1105 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1107 SendSlots[i] = NULL;
1108 RoomRolloff[i] = 0.0f;
1109 DecayDistance[i] = 0.0f;
1110 DecayHFDistance[i] = 0.0f;
1111 RoomAirAbsorption[i] = 1.0f;
1113 else if(SendSlots[i]->Params.AuxSendAuto)
1115 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1116 DecayDistance[i] = SendSlots[i]->Params.DecayTime * SPEEDOFSOUNDMETRESPERSEC;
1117 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1118 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
1119 if(SendSlots[i]->Params.DecayHFLimit && RoomAirAbsorption[i] < 1.0f)
1121 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) /
1122 (log10f(RoomAirAbsorption[i]) * DecayDistance[i]);
1123 limitRatio = minf(limitRatio, SendSlots[i]->Params.DecayHFRatio);
1124 DecayHFDistance[i] = minf(DecayHFDistance[i], limitRatio*DecayDistance[i]);
1127 else
1129 /* If the slot's auxiliary send auto is off, the data sent to the
1130 * effect slot is the same as the dry path, sans filter effects */
1131 RoomRolloff[i] = props->RolloffFactor;
1132 DecayDistance[i] = 0.0f;
1133 DecayHFDistance[i] = 0.0f;
1134 RoomAirAbsorption[i] = AIRABSORBGAINHF;
1137 if(!SendSlots[i])
1139 voice->Send[i].Buffer = NULL;
1140 voice->Send[i].Channels = 0;
1142 else
1144 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1145 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1149 /* Transform source to listener space (convert to head relative) */
1150 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1151 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1152 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1153 if(props->HeadRelative == AL_FALSE)
1155 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1156 /* Transform source vectors */
1157 Position = aluMatrixfVector(Matrix, &Position);
1158 Velocity = aluMatrixfVector(Matrix, &Velocity);
1159 Direction = aluMatrixfVector(Matrix, &Direction);
1161 else
1163 const aluVector *lvelocity = &Listener->Params.Velocity;
1164 /* Offset the source velocity to be relative of the listener velocity */
1165 Velocity.v[0] += lvelocity->v[0];
1166 Velocity.v[1] += lvelocity->v[1];
1167 Velocity.v[2] += lvelocity->v[2];
1170 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1171 SourceToListener.v[0] = -Position.v[0];
1172 SourceToListener.v[1] = -Position.v[1];
1173 SourceToListener.v[2] = -Position.v[2];
1174 SourceToListener.v[3] = 0.0f;
1175 Distance = aluNormalize(SourceToListener.v);
1177 /* Initial source gain */
1178 DryGain = props->Gain;
1179 DryGainHF = 1.0f;
1180 DryGainLF = 1.0f;
1181 for(i = 0;i < NumSends;i++)
1183 WetGain[i] = props->Gain;
1184 WetGainHF[i] = 1.0f;
1185 WetGainLF[i] = 1.0f;
1188 /* Calculate distance attenuation */
1189 ClampedDist = Distance;
1191 switch(Listener->Params.SourceDistanceModel ?
1192 props->DistanceModel : Listener->Params.DistanceModel)
1194 case InverseDistanceClamped:
1195 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1196 if(props->MaxDistance < props->RefDistance)
1197 break;
1198 /*fall-through*/
1199 case InverseDistance:
1200 if(props->RefDistance > 0.0f)
1202 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1203 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1204 for(i = 0;i < NumSends;i++)
1206 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1207 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1210 break;
1212 case LinearDistanceClamped:
1213 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1214 if(props->MaxDistance < props->RefDistance)
1215 break;
1216 /*fall-through*/
1217 case LinearDistance:
1218 if(props->MaxDistance != props->RefDistance)
1220 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1221 (props->MaxDistance-props->RefDistance);
1222 DryGain *= maxf(1.0f - attn, 0.0f);
1223 for(i = 0;i < NumSends;i++)
1225 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1226 (props->MaxDistance-props->RefDistance);
1227 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1230 break;
1232 case ExponentDistanceClamped:
1233 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1234 if(props->MaxDistance < props->RefDistance)
1235 break;
1236 /*fall-through*/
1237 case ExponentDistance:
1238 if(ClampedDist > 0.0f && props->RefDistance > 0.0f)
1240 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1241 for(i = 0;i < NumSends;i++)
1242 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1244 break;
1246 case DisableDistance:
1247 ClampedDist = props->RefDistance;
1248 break;
1251 /* Distance-based air absorption */
1252 if(ClampedDist > props->RefDistance)
1254 ALfloat meters_base = (ClampedDist-props->RefDistance) * Listener->Params.MetersPerUnit;
1255 if(props->AirAbsorptionFactor > 0.0f)
1257 ALfloat absorb = props->AirAbsorptionFactor * meters_base;
1258 DryGainHF *= powf(AIRABSORBGAINHF, absorb*props->RolloffFactor);
1259 for(i = 0;i < NumSends;i++)
1261 if(RoomRolloff[i] > 0.0f)
1262 WetGainHF[i] *= powf(AIRABSORBGAINHF, absorb*RoomRolloff[i]);
1266 if(props->WetGainAuto)
1268 meters_base *= props->RolloffFactor;
1270 /* Apply a decay-time transformation to the wet path, based on the
1271 * source distance in meters. The initial decay of the reverb
1272 * effect is calculated and applied to the wet path.
1274 for(i = 0;i < NumSends;i++)
1276 ALfloat gain;
1278 if(!(DecayDistance[i] > 0.0f))
1279 continue;
1281 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1282 WetGain[i] *= gain;
1283 /* Yes, the wet path's air absorption is applied with
1284 * WetGainAuto on, rather than WetGainHFAuto.
1286 if(gain > 0.0f)
1288 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1289 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1295 /* Calculate directional soundcones */
1296 if(directional && props->InnerAngle < 360.0f)
1298 ALfloat ConeVolume;
1299 ALfloat ConeHF;
1300 ALfloat Angle;
1302 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1303 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1304 if(!(Angle > props->InnerAngle))
1306 ConeVolume = 1.0f;
1307 ConeHF = 1.0f;
1309 else if(Angle < props->OuterAngle)
1311 ALfloat scale = ( Angle-props->InnerAngle) /
1312 (props->OuterAngle-props->InnerAngle);
1313 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1314 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1316 else
1318 ConeVolume = props->OuterGain;
1319 ConeHF = props->OuterGainHF;
1322 DryGain *= ConeVolume;
1323 if(props->DryGainHFAuto)
1324 DryGainHF *= ConeHF;
1325 if(props->WetGainAuto)
1327 for(i = 0;i < NumSends;i++)
1328 WetGain[i] *= ConeVolume;
1330 if(props->WetGainHFAuto)
1332 for(i = 0;i < NumSends;i++)
1333 WetGainHF[i] *= ConeHF;
1337 /* Apply gain and frequency filters */
1338 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1339 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1340 DryGainHF *= props->Direct.GainHF;
1341 DryGainLF *= props->Direct.GainLF;
1342 for(i = 0;i < NumSends;i++)
1344 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1345 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1346 WetGainHF[i] *= props->Send[i].GainHF;
1347 WetGainLF[i] *= props->Send[i].GainLF;
1351 /* Initial source pitch */
1352 Pitch = props->Pitch;
1354 /* Calculate velocity-based doppler effect */
1355 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1356 if(DopplerFactor > 0.0f)
1358 const aluVector *lvelocity = &Listener->Params.Velocity;
1359 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1360 ALfloat vss, vls;
1362 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1363 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1365 if(!(vls < SpeedOfSound))
1367 /* Listener moving away from the source at the speed of sound.
1368 * Sound waves can't catch it.
1370 Pitch = 0.0f;
1372 else if(!(vss < SpeedOfSound))
1374 /* Source moving toward the listener at the speed of sound. Sound
1375 * waves bunch up to extreme frequencies.
1377 Pitch = HUGE_VALF;
1379 else
1381 /* Source and listener movement is nominal. Calculate the proper
1382 * doppler shift.
1384 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1388 /* Adjust pitch based on the buffer and output frequencies, and calculate
1389 * fixed-point stepping value.
1391 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1392 if(Pitch > (ALfloat)MAX_PITCH)
1393 voice->Step = MAX_PITCH<<FRACTIONBITS;
1394 else
1395 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1396 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1397 voice->Resampler = SelectResampler(props->Resampler);
1399 if(Distance > FLT_EPSILON)
1401 dir[0] = -SourceToListener.v[0];
1402 /* Clamp Y, in case rounding errors caused it to end up outside of
1403 * -1...+1.
1405 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1406 dir[2] = -SourceToListener.v[2] * ZScale;
1408 else
1410 dir[0] = 0.0f;
1411 dir[1] = 0.0f;
1412 dir[2] = -1.0f;
1414 if(props->Radius > Distance)
1415 spread = F_TAU - Distance/props->Radius*F_PI;
1416 else if(Distance > FLT_EPSILON)
1417 spread = asinf(props->Radius / Distance) * 2.0f;
1418 else
1419 spread = 0.0f;
1421 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1422 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1425 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1427 ALbufferlistitem *BufferListItem;
1428 struct ALvoiceProps *props;
1430 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1431 if(!props && !force) return;
1433 if(props)
1435 memcpy(voice->Props, props,
1436 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1439 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &voice->FreeList, props);
1441 props = voice->Props;
1443 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1444 while(BufferListItem != NULL)
1446 const ALbuffer *buffer;
1447 if((buffer=BufferListItem->buffer) != NULL)
1449 if(props->SpatializeMode == SpatializeOn ||
1450 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1451 CalcAttnSourceParams(voice, props, buffer, context);
1452 else
1453 CalcNonAttnSourceParams(voice, props, buffer, context);
1454 break;
1456 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1461 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1463 ALvoice **voice, **voice_end;
1464 ALsource *source;
1465 ALsizei i;
1467 IncrementRef(&ctx->UpdateCount);
1468 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1470 ALboolean force = CalcListenerParams(ctx);
1471 for(i = 0;i < slots->count;i++)
1472 force |= CalcEffectSlotParams(slots->slot[i], ctx->Device);
1474 voice = ctx->Voices;
1475 voice_end = voice + ctx->VoiceCount;
1476 for(;voice != voice_end;++voice)
1478 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1479 if(source) CalcSourceParams(*voice, ctx, force);
1482 IncrementRef(&ctx->UpdateCount);
1486 static void ApplyDistanceComp(ALfloatBUFFERSIZE *restrict Samples, DistanceComp *distcomp,
1487 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1489 ALsizei i, c;
1491 Values = ASSUME_ALIGNED(Values, 16);
1492 for(c = 0;c < numchans;c++)
1494 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1495 const ALfloat gain = distcomp[c].Gain;
1496 const ALsizei base = distcomp[c].Length;
1497 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1499 if(base == 0)
1501 if(gain < 1.0f)
1503 for(i = 0;i < SamplesToDo;i++)
1504 inout[i] *= gain;
1506 continue;
1509 if(SamplesToDo >= base)
1511 for(i = 0;i < base;i++)
1512 Values[i] = distbuf[i];
1513 for(;i < SamplesToDo;i++)
1514 Values[i] = inout[i-base];
1515 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1517 else
1519 for(i = 0;i < SamplesToDo;i++)
1520 Values[i] = distbuf[i];
1521 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1522 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1524 for(i = 0;i < SamplesToDo;i++)
1525 inout[i] = Values[i]*gain;
1530 /* NOTE: Non-dithered conversions have unused extra parameters. */
1531 static inline ALfloat aluF2F(ALfloat val, ...)
1532 { return val; }
1533 static inline ALint aluF2I(ALfloat val, ...)
1535 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1536 * integer range normalized floats can be safely converted to (a bit of the
1537 * exponent helps out, effectively giving 25 bits).
1539 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1541 static inline ALshort aluF2S(ALfloat val, ...)
1542 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1543 static inline ALbyte aluF2B(ALfloat val, ...)
1544 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1546 /* Dithered conversion functions. Only applies to 8- and 16-bit output for now,
1547 * as 32-bit int and float are at the limits of the rendered sample depth. This
1548 * can change if the dithering bit depth becomes configurable (effectively
1549 * quantizing to a lower bit depth than the output is capable of).
1551 static inline ALshort aluF2SDithered(ALfloat val, const ALfloat dither_val)
1553 val = val*32768.0f + dither_val;
1554 return lroundf(clampf(val, -32768.0f, 32767.0f));
1556 static inline ALbyte aluF2BDithered(ALfloat val, const ALfloat dither_val)
1558 val = val*128.0f + dither_val;
1559 return lroundf(clampf(val, -128.0f, 127.0f));
1562 /* Define unsigned output variations. */
1563 #define DECL_TEMPLATE(T, Name, func, O) \
1564 static inline T Name(ALfloat val, const ALfloat dither_val) \
1565 { return func(val, dither_val)+O; }
1567 DECL_TEMPLATE(ALubyte, aluF2UB, aluF2B, 128)
1568 DECL_TEMPLATE(ALushort, aluF2US, aluF2S, 32768)
1569 DECL_TEMPLATE(ALuint, aluF2UI, aluF2I, 2147483648u)
1570 DECL_TEMPLATE(ALubyte, aluF2UBDithered, aluF2BDithered, 128)
1571 DECL_TEMPLATE(ALushort, aluF2USDithered, aluF2SDithered, 32768)
1573 #undef DECL_TEMPLATE
1575 #define DECL_TEMPLATE(T, D, func) \
1576 static void Write##T##D(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1577 const ALfloat *restrict DitherValues, \
1578 ALsizei SamplesToDo, ALsizei numchans) \
1580 ALsizei i, j; \
1581 for(j = 0;j < numchans;j++) \
1583 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1584 T *restrict out = (T*)OutBuffer + j; \
1586 for(i = 0;i < SamplesToDo;i++) \
1587 out[i*numchans] = func(in[i], DitherValues[i]); \
1591 DECL_TEMPLATE(ALfloat, /*no dither*/, aluF2F)
1592 DECL_TEMPLATE(ALuint, /*no dither*/, aluF2UI)
1593 DECL_TEMPLATE(ALint, /*no dither*/, aluF2I)
1594 DECL_TEMPLATE(ALushort, /*no dither*/, aluF2US)
1595 DECL_TEMPLATE(ALshort, /*no dither*/, aluF2S)
1596 DECL_TEMPLATE(ALubyte, /*no dither*/, aluF2UB)
1597 DECL_TEMPLATE(ALbyte, /*no dither*/, aluF2B)
1599 DECL_TEMPLATE(ALushort, _Dithered, aluF2USDithered)
1600 DECL_TEMPLATE(ALshort, _Dithered, aluF2SDithered)
1601 DECL_TEMPLATE(ALubyte, _Dithered, aluF2UBDithered)
1602 DECL_TEMPLATE(ALbyte, _Dithered, aluF2BDithered)
1604 #undef DECL_TEMPLATE
1607 void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1609 ALsizei SamplesToDo;
1610 ALvoice **voice, **voice_end;
1611 ALeffectslot *slot;
1612 ALsource *source;
1613 ALCcontext *ctx;
1614 FPUCtl oldMode;
1615 ALsizei i, c;
1617 SetMixerFPUMode(&oldMode);
1619 while(size > 0)
1621 SamplesToDo = mini(size, BUFFERSIZE);
1622 for(c = 0;c < device->Dry.NumChannels;c++)
1623 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1624 if(device->Dry.Buffer != device->FOAOut.Buffer)
1625 for(c = 0;c < device->FOAOut.NumChannels;c++)
1626 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1627 if(device->Dry.Buffer != device->RealOut.Buffer)
1628 for(c = 0;c < device->RealOut.NumChannels;c++)
1629 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1631 IncrementRef(&device->MixCount);
1633 if((slot=device->DefaultSlot) != NULL)
1635 CalcEffectSlotParams(device->DefaultSlot, device);
1636 for(c = 0;c < slot->NumChannels;c++)
1637 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1640 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1641 while(ctx)
1643 const struct ALeffectslotArray *auxslots;
1645 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1646 UpdateContextSources(ctx, auxslots);
1648 for(i = 0;i < auxslots->count;i++)
1650 ALeffectslot *slot = auxslots->slot[i];
1651 for(c = 0;c < slot->NumChannels;c++)
1652 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1655 /* source processing */
1656 voice = ctx->Voices;
1657 voice_end = voice + ctx->VoiceCount;
1658 for(;voice != voice_end;++voice)
1660 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1661 if(source && ATOMIC_LOAD(&(*voice)->Playing, almemory_order_relaxed) &&
1662 (*voice)->Step > 0)
1664 if(!MixSource(*voice, source, device, SamplesToDo))
1666 ATOMIC_STORE(&(*voice)->Source, NULL, almemory_order_relaxed);
1667 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1672 /* effect slot processing */
1673 for(i = 0;i < auxslots->count;i++)
1675 const ALeffectslot *slot = auxslots->slot[i];
1676 ALeffectState *state = slot->Params.EffectState;
1677 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1678 state->OutChannels);
1681 ctx = ctx->next;
1684 if(device->DefaultSlot != NULL)
1686 const ALeffectslot *slot = device->DefaultSlot;
1687 ALeffectState *state = slot->Params.EffectState;
1688 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1689 state->OutChannels);
1692 /* Increment the clock time. Every second's worth of samples is
1693 * converted and added to clock base so that large sample counts don't
1694 * overflow during conversion. This also guarantees an exact, stable
1695 * conversion. */
1696 device->SamplesDone += SamplesToDo;
1697 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1698 device->SamplesDone %= device->Frequency;
1699 IncrementRef(&device->MixCount);
1701 if(device->HrtfHandle)
1703 HrtfDirectMixerFunc HrtfMix;
1704 DirectHrtfState *state;
1705 int lidx, ridx;
1707 if(device->AmbiUp)
1708 ambiup_process(device->AmbiUp,
1709 device->Dry.Buffer, device->Dry.NumChannels,
1710 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1713 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1714 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1715 assert(lidx != -1 && ridx != -1);
1717 HrtfMix = SelectHrtfMixer();
1718 state = device->Hrtf;
1719 for(c = 0;c < device->Dry.NumChannels;c++)
1721 HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1722 device->Dry.Buffer[c], state->Offset, state->IrSize,
1723 SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
1724 state->Chan[c].Values, SamplesToDo
1727 state->Offset += SamplesToDo;
1729 else if(device->AmbiDecoder)
1731 if(device->Dry.Buffer != device->FOAOut.Buffer)
1732 bformatdec_upSample(device->AmbiDecoder,
1733 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1734 device->FOAOut.NumChannels, SamplesToDo
1736 bformatdec_process(device->AmbiDecoder,
1737 device->RealOut.Buffer, device->RealOut.NumChannels,
1738 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1741 else if(device->AmbiUp)
1743 ambiup_process(device->AmbiUp,
1744 device->RealOut.Buffer, device->RealOut.NumChannels,
1745 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1748 else if(device->Uhj_Encoder)
1750 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1751 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1752 if(lidx != -1 && ridx != -1)
1754 /* Encode to stereo-compatible 2-channel UHJ output. */
1755 EncodeUhj2(device->Uhj_Encoder,
1756 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1757 device->Dry.Buffer, SamplesToDo
1761 else if(device->Bs2b)
1763 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1764 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1765 if(lidx != -1 && ridx != -1)
1767 /* Apply binaural/crossfeed filter */
1768 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1769 device->RealOut.Buffer[ridx], SamplesToDo);
1773 if(buffer)
1775 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1776 ALsizei OutChannels = device->RealOut.NumChannels;
1777 struct Compressor *Limiter = device->Limiter;
1778 ALfloat *DitherValues;
1780 /* Use NFCtrlData for temp value storage. */
1781 ApplyDistanceComp(OutBuffer, device->ChannelDelay, device->NFCtrlData,
1782 SamplesToDo, OutChannels);
1784 if(Limiter)
1785 ApplyCompression(Limiter, OutChannels, SamplesToDo, OutBuffer);
1787 /* Dithering. Step 1, generate whitenoise (uniform distribution of
1788 * random values between -1 and +1). Use NFCtrlData for random
1789 * value storage. Step 2 is to add the noise to the samples, before
1790 * rounding and after scaling up to the desired quantization depth,
1791 * which occurs in the sample conversion stage.
1793 if(!device->DitherEnabled)
1794 memset(device->NFCtrlData, 0, SamplesToDo*sizeof(ALfloat));
1795 else
1797 ALuint dither_seed = device->DitherSeed;
1798 ALsizei i;
1800 for(i = 0;i < SamplesToDo;i++)
1802 ALuint rng0 = dither_rng(&dither_seed);
1803 ALuint rng1 = dither_rng(&dither_seed);
1804 device->NFCtrlData[i] = (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1806 device->DitherSeed = dither_seed;
1808 DitherValues = device->NFCtrlData;
1810 #define WRITE(T, D, a, b, c, d, e) do { \
1811 Write##T##D(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \
1812 buffer = (T*)buffer + (d)*(e); \
1813 } while(0)
1814 switch(device->FmtType)
1816 case DevFmtByte:
1817 if(device->DitherEnabled)
1818 WRITE(ALbyte, _Dithered, OutBuffer, buffer, DitherValues,
1819 SamplesToDo, OutChannels);
1820 else
1821 WRITE(ALbyte, /*no dither*/, OutBuffer, buffer, DitherValues,
1822 SamplesToDo, OutChannels);
1823 break;
1824 case DevFmtUByte:
1825 if(device->DitherEnabled)
1826 WRITE(ALubyte, _Dithered, OutBuffer, buffer, DitherValues,
1827 SamplesToDo, OutChannels);
1828 else
1829 WRITE(ALubyte, /*no dither*/, OutBuffer, buffer, DitherValues,
1830 SamplesToDo, OutChannels);
1831 break;
1832 case DevFmtShort:
1833 if(device->DitherEnabled)
1834 WRITE(ALshort, _Dithered, OutBuffer, buffer, DitherValues,
1835 SamplesToDo, OutChannels);
1836 else
1837 WRITE(ALshort, /*no dither*/, OutBuffer, buffer, DitherValues,
1838 SamplesToDo, OutChannels);
1839 break;
1840 case DevFmtUShort:
1841 if(device->DitherEnabled)
1842 WRITE(ALushort, _Dithered, OutBuffer, buffer, DitherValues,
1843 SamplesToDo, OutChannels);
1844 else
1845 WRITE(ALushort, /*no dither*/, OutBuffer, buffer, DitherValues,
1846 SamplesToDo, OutChannels);
1847 break;
1848 case DevFmtInt:
1849 WRITE(ALint, /*no dither*/, OutBuffer, buffer, DitherValues,
1850 SamplesToDo, OutChannels);
1851 break;
1852 case DevFmtUInt:
1853 WRITE(ALuint, /*no dither*/, OutBuffer, buffer, DitherValues,
1854 SamplesToDo, OutChannels);
1855 break;
1856 case DevFmtFloat:
1857 WRITE(ALfloat, /*no dither*/, OutBuffer, buffer, DitherValues,
1858 SamplesToDo, OutChannels);
1859 break;
1861 #undef WRITE
1864 size -= SamplesToDo;
1867 RestoreFPUMode(&oldMode);
1871 void aluHandleDisconnect(ALCdevice *device)
1873 ALCcontext *Context;
1875 device->Connected = ALC_FALSE;
1877 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1878 while(Context)
1880 ALvoice **voice, **voice_end;
1882 voice = Context->Voices;
1883 voice_end = voice + Context->VoiceCount;
1884 while(voice != voice_end)
1886 ALsource *source = ATOMIC_EXCHANGE_PTR(&(*voice)->Source, NULL,
1887 almemory_order_acq_rel);
1888 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1890 if(source)
1892 ALenum playing = AL_PLAYING;
1893 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1896 voice++;
1898 Context->VoiceCount = 0;
1900 Context = Context->next;