Fix NULL pointer dereference
[openal-soft.git] / Alc / ALu.c
blobf73b19853d86cc16d6bb212dfae9253066be30f1
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 "mastering.h"
38 #include "uhjfilter.h"
39 #include "bformatdec.h"
40 #include "static_assert.h"
41 #include "ringbuffer.h"
43 #include "fpu_modes.h"
44 #include "cpu_caps.h"
45 #include "mixer_defs.h"
46 #include "bsinc_inc.h"
48 #include "backends/base.h"
51 extern inline ALfloat minf(ALfloat a, ALfloat b);
52 extern inline ALfloat maxf(ALfloat a, ALfloat b);
53 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
55 extern inline ALdouble mind(ALdouble a, ALdouble b);
56 extern inline ALdouble maxd(ALdouble a, ALdouble b);
57 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
59 extern inline ALuint minu(ALuint a, ALuint b);
60 extern inline ALuint maxu(ALuint a, ALuint b);
61 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
63 extern inline ALint mini(ALint a, ALint b);
64 extern inline ALint maxi(ALint a, ALint b);
65 extern inline ALint clampi(ALint val, ALint min, ALint max);
67 extern inline ALint64 mini64(ALint64 a, ALint64 b);
68 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
69 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
71 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
72 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
73 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
75 extern inline size_t minz(size_t a, size_t b);
76 extern inline size_t maxz(size_t a, size_t b);
77 extern inline size_t clampz(size_t val, size_t min, size_t max);
79 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
80 extern inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu);
82 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
84 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
85 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
86 extern inline void aluMatrixfSet(aluMatrixf *matrix,
87 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
88 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
89 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
90 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
93 /* Cone scalar */
94 ALfloat ConeScale = 1.0f;
96 /* Localized Z scalar for mono sources */
97 ALfloat ZScale = 1.0f;
99 /* Force default speed of sound for distance-related reverb decay. */
100 ALboolean OverrideReverbSpeedOfSound = AL_FALSE;
102 const aluMatrixf IdentityMatrixf = {{
103 { 1.0f, 0.0f, 0.0f, 0.0f },
104 { 0.0f, 1.0f, 0.0f, 0.0f },
105 { 0.0f, 0.0f, 1.0f, 0.0f },
106 { 0.0f, 0.0f, 0.0f, 1.0f },
110 struct ChanMap {
111 enum Channel channel;
112 ALfloat angle;
113 ALfloat elevation;
116 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
119 void DeinitVoice(ALvoice *voice)
121 al_free(ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL));
125 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
127 #ifdef HAVE_NEON
128 if((CPUCapFlags&CPU_CAP_NEON))
129 return MixDirectHrtf_Neon;
130 #endif
131 #ifdef HAVE_SSE
132 if((CPUCapFlags&CPU_CAP_SSE))
133 return MixDirectHrtf_SSE;
134 #endif
136 return MixDirectHrtf_C;
140 /* Prior to VS2013, MSVC lacks the round() family of functions. */
141 #if defined(_MSC_VER) && _MSC_VER < 1800
142 static float roundf(float val)
144 if(val < 0.0f)
145 return ceilf(val-0.5f);
146 return floorf(val+0.5f);
148 #endif
150 /* This RNG method was created based on the math found in opusdec. It's quick,
151 * and starting with a seed value of 22222, is suitable for generating
152 * whitenoise.
154 static inline ALuint dither_rng(ALuint *seed)
156 *seed = (*seed * 96314165) + 907633515;
157 return *seed;
161 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
163 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
164 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
165 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
168 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
170 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
173 static ALfloat aluNormalize(ALfloat *vec)
175 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
176 if(length > 0.0f)
178 ALfloat inv_length = 1.0f/length;
179 vec[0] *= inv_length;
180 vec[1] *= inv_length;
181 vec[2] *= inv_length;
183 return length;
186 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
188 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
190 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];
191 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];
192 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];
195 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
197 aluVector v;
198 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];
199 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];
200 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];
201 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];
202 return v;
206 void aluInit(void)
208 MixDirectHrtf = SelectHrtfMixer();
212 static void ProcessHrtf(ALCdevice *device, ALsizei SamplesToDo)
214 DirectHrtfState *state;
215 int lidx, ridx;
216 ALsizei c;
218 if(device->AmbiUp)
219 ambiup_process(device->AmbiUp,
220 device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer,
221 SamplesToDo
224 lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
225 ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
226 assert(lidx != -1 && ridx != -1);
228 state = device->Hrtf;
229 for(c = 0;c < device->Dry.NumChannels;c++)
231 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
232 device->Dry.Buffer[c], state->Offset, state->IrSize,
233 state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo
236 state->Offset += SamplesToDo;
239 static void ProcessAmbiDec(ALCdevice *device, ALsizei SamplesToDo)
241 if(device->Dry.Buffer != device->FOAOut.Buffer)
242 bformatdec_upSample(device->AmbiDecoder,
243 device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
244 SamplesToDo
246 bformatdec_process(device->AmbiDecoder,
247 device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
248 SamplesToDo
252 static void ProcessAmbiUp(ALCdevice *device, ALsizei SamplesToDo)
254 ambiup_process(device->AmbiUp,
255 device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer,
256 SamplesToDo
260 static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
262 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
263 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
264 if(LIKELY(lidx != -1 && ridx != -1))
266 /* Encode to stereo-compatible 2-channel UHJ output. */
267 EncodeUhj2(device->Uhj_Encoder,
268 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
269 device->Dry.Buffer, SamplesToDo
274 static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
276 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
277 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
278 if(LIKELY(lidx != -1 && ridx != -1))
280 /* Apply binaural/crossfeed filter */
281 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
282 device->RealOut.Buffer[ridx], SamplesToDo);
286 void aluSelectPostProcess(ALCdevice *device)
288 if(device->HrtfHandle)
289 device->PostProcess = ProcessHrtf;
290 else if(device->AmbiDecoder)
291 device->PostProcess = ProcessAmbiDec;
292 else if(device->AmbiUp)
293 device->PostProcess = ProcessAmbiUp;
294 else if(device->Uhj_Encoder)
295 device->PostProcess = ProcessUhj;
296 else if(device->Bs2b)
297 device->PostProcess = ProcessBs2b;
298 else
299 device->PostProcess = NULL;
303 /* Prepares the interpolator for a given rate (determined by increment). A
304 * result of AL_FALSE indicates that the filter output will completely cut
305 * the input signal.
307 * With a bit of work, and a trade of memory for CPU cost, this could be
308 * modified for use with an interpolated increment for buttery-smooth pitch
309 * changes.
311 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
313 ALfloat sf;
314 ALsizei si;
316 if(increment > FRACTIONONE)
318 sf = (ALfloat)FRACTIONONE / increment;
319 sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
320 si = fastf2i(sf);
321 /* The interpolation factor is fit to this diagonally-symmetric curve
322 * to reduce the transition ripple caused by interpolating different
323 * scales of the sinc function.
325 sf = 1.0f - cosf(asinf(sf - si));
327 else
329 sf = 0.0f;
330 si = BSINC_SCALE_COUNT - 1;
333 state->sf = sf;
334 state->m = table->m[si];
335 state->l = -((state->m/2) - 1);
336 state->filter = table->Tab + table->filterOffset[si];
340 static bool CalcContextParams(ALCcontext *Context)
342 ALlistener *Listener = Context->Listener;
343 struct ALcontextProps *props;
345 props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel);
346 if(!props) return false;
348 Listener->Params.MetersPerUnit = props->MetersPerUnit;
350 Listener->Params.DopplerFactor = props->DopplerFactor;
351 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
352 if(!OverrideReverbSpeedOfSound)
353 Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
354 Listener->Params.MetersPerUnit;
356 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
357 Listener->Params.DistanceModel = props->DistanceModel;
359 ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props);
360 return true;
363 static bool CalcListenerParams(ALCcontext *Context)
365 ALlistener *Listener = Context->Listener;
366 ALfloat N[3], V[3], U[3], P[3];
367 struct ALlistenerProps *props;
368 aluVector vel;
370 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
371 if(!props) return false;
373 /* AT then UP */
374 N[0] = props->Forward[0];
375 N[1] = props->Forward[1];
376 N[2] = props->Forward[2];
377 aluNormalize(N);
378 V[0] = props->Up[0];
379 V[1] = props->Up[1];
380 V[2] = props->Up[2];
381 aluNormalize(V);
382 /* Build and normalize right-vector */
383 aluCrossproduct(N, V, U);
384 aluNormalize(U);
386 aluMatrixfSet(&Listener->Params.Matrix,
387 U[0], V[0], -N[0], 0.0,
388 U[1], V[1], -N[1], 0.0,
389 U[2], V[2], -N[2], 0.0,
390 0.0, 0.0, 0.0, 1.0
393 P[0] = props->Position[0];
394 P[1] = props->Position[1];
395 P[2] = props->Position[2];
396 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
397 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
399 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
400 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
402 Listener->Params.Gain = props->Gain * Context->GainBoost;
404 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props);
405 return true;
408 static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
410 struct ALeffectslotProps *props;
411 ALeffectState *state;
413 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
414 if(!props && !force) return false;
416 if(props)
418 slot->Params.Gain = props->Gain;
419 slot->Params.AuxSendAuto = props->AuxSendAuto;
420 slot->Params.EffectType = props->Type;
421 slot->Params.EffectProps = props->Props;
422 if(IsReverbEffect(props->Type))
424 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
425 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
426 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
427 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
428 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
430 else
432 slot->Params.RoomRolloff = 0.0f;
433 slot->Params.DecayTime = 0.0f;
434 slot->Params.DecayHFRatio = 0.0f;
435 slot->Params.DecayHFLimit = AL_FALSE;
436 slot->Params.AirAbsorptionGainHF = 1.0f;
439 /* Swap effect states. No need to play with the ref counts since they
440 * keep the same number of refs.
442 state = props->State;
443 props->State = slot->Params.EffectState;
444 slot->Params.EffectState = state;
446 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
448 else
449 state = slot->Params.EffectState;
451 V(state,update)(context, slot, &slot->Params.EffectProps);
452 return true;
456 static const struct ChanMap MonoMap[1] = {
457 { FrontCenter, 0.0f, 0.0f }
458 }, RearMap[2] = {
459 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
460 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
461 }, QuadMap[4] = {
462 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
463 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
464 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
465 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
466 }, X51Map[6] = {
467 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
468 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
469 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
470 { LFE, 0.0f, 0.0f },
471 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
472 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
473 }, X61Map[7] = {
474 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
475 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
476 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
477 { LFE, 0.0f, 0.0f },
478 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
479 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
480 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
481 }, X71Map[8] = {
482 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
483 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
484 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
485 { LFE, 0.0f, 0.0f },
486 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
487 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
488 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
489 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
492 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
493 const ALfloat Spread, const ALfloat DryGain,
494 const ALfloat DryGainHF, const ALfloat DryGainLF,
495 const ALfloat *WetGain, const ALfloat *WetGainLF,
496 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
497 const ALbuffer *Buffer, const struct ALvoiceProps *props,
498 const ALlistener *Listener, const ALCdevice *Device)
500 struct ChanMap StereoMap[2] = {
501 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
502 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
504 bool DirectChannels = props->DirectChannels;
505 const ALsizei NumSends = Device->NumAuxSends;
506 const ALuint Frequency = Device->Frequency;
507 const struct ChanMap *chans = NULL;
508 ALsizei num_channels = 0;
509 bool isbformat = false;
510 ALfloat downmix_gain = 1.0f;
511 ALsizei c, i, j;
513 switch(Buffer->FmtChannels)
515 case FmtMono:
516 chans = MonoMap;
517 num_channels = 1;
518 /* Mono buffers are never played direct. */
519 DirectChannels = false;
520 break;
522 case FmtStereo:
523 /* Convert counter-clockwise to clockwise. */
524 StereoMap[0].angle = -props->StereoPan[0];
525 StereoMap[1].angle = -props->StereoPan[1];
527 chans = StereoMap;
528 num_channels = 2;
529 downmix_gain = 1.0f / 2.0f;
530 break;
532 case FmtRear:
533 chans = RearMap;
534 num_channels = 2;
535 downmix_gain = 1.0f / 2.0f;
536 break;
538 case FmtQuad:
539 chans = QuadMap;
540 num_channels = 4;
541 downmix_gain = 1.0f / 4.0f;
542 break;
544 case FmtX51:
545 chans = X51Map;
546 num_channels = 6;
547 /* NOTE: Excludes LFE. */
548 downmix_gain = 1.0f / 5.0f;
549 break;
551 case FmtX61:
552 chans = X61Map;
553 num_channels = 7;
554 /* NOTE: Excludes LFE. */
555 downmix_gain = 1.0f / 6.0f;
556 break;
558 case FmtX71:
559 chans = X71Map;
560 num_channels = 8;
561 /* NOTE: Excludes LFE. */
562 downmix_gain = 1.0f / 7.0f;
563 break;
565 case FmtBFormat2D:
566 num_channels = 3;
567 isbformat = true;
568 DirectChannels = false;
569 break;
571 case FmtBFormat3D:
572 num_channels = 4;
573 isbformat = true;
574 DirectChannels = false;
575 break;
578 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
579 if(isbformat)
581 /* Special handling for B-Format sources. */
583 if(Distance > FLT_EPSILON)
585 /* Panning a B-Format sound toward some direction is easy. Just pan
586 * the first (W) channel as a normal mono sound and silence the
587 * others.
589 ALfloat coeffs[MAX_AMBI_COEFFS];
591 if(Device->AvgSpeakerDist > 0.0f)
593 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
594 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
595 (mdist * (ALfloat)Device->Frequency);
596 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
597 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
598 /* Clamp w0 for really close distances, to prevent excessive
599 * bass.
601 w0 = minf(w0, w1*4.0f);
603 /* Only need to adjust the first channel of a B-Format source. */
604 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
606 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
607 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
608 voice->Flags |= VOICE_HAS_NFC;
611 if(Device->Render_Mode == StereoPair)
613 ALfloat ev = asinf(Dir[1]);
614 ALfloat az = atan2f(Dir[0], -Dir[2]);
615 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
617 else
618 CalcDirectionCoeffs(Dir, Spread, coeffs);
620 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
621 ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
622 voice->Direct.Params[0].Gains.Target);
623 for(c = 1;c < num_channels;c++)
625 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
626 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
629 for(i = 0;i < NumSends;i++)
631 const ALeffectslot *Slot = SendSlots[i];
632 if(Slot)
633 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
634 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
636 else
637 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
638 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
639 for(c = 1;c < num_channels;c++)
641 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
642 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
646 else
648 /* Local B-Format sources have their XYZ channels rotated according
649 * to the orientation.
651 ALfloat N[3], V[3], U[3];
652 aluMatrixf matrix;
653 ALfloat scale;
655 if(Device->AvgSpeakerDist > 0.0f)
657 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
658 * is what we want for FOA input. The first channel may have
659 * been previously re-adjusted if panned, so reset it.
661 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, 0.0f);
663 voice->Direct.ChannelsPerOrder[0] = 1;
664 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
665 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
666 voice->Direct.ChannelsPerOrder[i] = 0;
667 voice->Flags |= VOICE_HAS_NFC;
670 /* AT then UP */
671 N[0] = props->Orientation[0][0];
672 N[1] = props->Orientation[0][1];
673 N[2] = props->Orientation[0][2];
674 aluNormalize(N);
675 V[0] = props->Orientation[1][0];
676 V[1] = props->Orientation[1][1];
677 V[2] = props->Orientation[1][2];
678 aluNormalize(V);
679 if(!props->HeadRelative)
681 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
682 aluMatrixfFloat3(N, 0.0f, lmatrix);
683 aluMatrixfFloat3(V, 0.0f, lmatrix);
685 /* Build and normalize right-vector */
686 aluCrossproduct(N, V, U);
687 aluNormalize(U);
689 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
690 scale = 1.732050808f;
691 aluMatrixfSet(&matrix,
692 1.414213562f, 0.0f, 0.0f, 0.0f,
693 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
694 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
695 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
698 voice->Direct.Buffer = Device->FOAOut.Buffer;
699 voice->Direct.Channels = Device->FOAOut.NumChannels;
700 for(c = 0;c < num_channels;c++)
701 ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
702 voice->Direct.Params[c].Gains.Target);
703 for(i = 0;i < NumSends;i++)
705 const ALeffectslot *Slot = SendSlots[i];
706 if(Slot)
708 for(c = 0;c < num_channels;c++)
709 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
710 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
713 else
715 for(c = 0;c < num_channels;c++)
716 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
717 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
722 else if(DirectChannels)
724 /* Direct source channels always play local. Skip the virtual channels
725 * and write inputs to the matching real outputs.
727 voice->Direct.Buffer = Device->RealOut.Buffer;
728 voice->Direct.Channels = Device->RealOut.NumChannels;
730 for(c = 0;c < num_channels;c++)
732 int idx;
733 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
734 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
735 if((idx=GetChannelIdxByName(&Device->RealOut, chans[c].channel)) != -1)
736 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
739 /* Auxiliary sends still use normal channel panning since they mix to
740 * B-Format, which can't channel-match.
742 for(c = 0;c < num_channels;c++)
744 ALfloat coeffs[MAX_AMBI_COEFFS];
745 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
747 for(i = 0;i < NumSends;i++)
749 const ALeffectslot *Slot = SendSlots[i];
750 if(Slot)
751 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
752 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
754 else
755 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
756 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
760 else if(Device->Render_Mode == HrtfRender)
762 /* Full HRTF rendering. Skip the virtual channels and render to the
763 * real outputs.
765 voice->Direct.Buffer = Device->RealOut.Buffer;
766 voice->Direct.Channels = Device->RealOut.NumChannels;
768 if(Distance > FLT_EPSILON)
770 ALfloat coeffs[MAX_AMBI_COEFFS];
771 ALfloat ev, az;
773 ev = asinf(Dir[1]);
774 az = atan2f(Dir[0], -Dir[2]);
776 /* Get the HRIR coefficients and delays just once, for the given
777 * source direction.
779 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
780 voice->Direct.Params[0].Hrtf.Target.Coeffs,
781 voice->Direct.Params[0].Hrtf.Target.Delay);
782 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
784 /* Remaining channels use the same results as the first. */
785 for(c = 1;c < num_channels;c++)
787 /* Skip LFE */
788 if(chans[c].channel == LFE)
789 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
790 sizeof(voice->Direct.Params[c].Hrtf.Target));
791 else
792 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
795 /* Calculate the directional coefficients once, which apply to all
796 * input channels of the source sends.
798 CalcDirectionCoeffs(Dir, Spread, coeffs);
800 for(i = 0;i < NumSends;i++)
802 const ALeffectslot *Slot = SendSlots[i];
803 if(Slot)
804 for(c = 0;c < num_channels;c++)
806 /* Skip LFE */
807 if(chans[c].channel == LFE)
808 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
809 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
810 else
811 ComputePanningGainsBF(Slot->ChanMap,
812 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
813 voice->Send[i].Params[c].Gains.Target
816 else
817 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
818 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
821 else
823 /* Local sources on HRTF play with each channel panned to its
824 * relative location around the listener, providing "virtual
825 * speaker" responses.
827 for(c = 0;c < num_channels;c++)
829 ALfloat coeffs[MAX_AMBI_COEFFS];
831 if(chans[c].channel == LFE)
833 /* Skip LFE */
834 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
835 sizeof(voice->Direct.Params[c].Hrtf.Target));
836 for(i = 0;i < NumSends;i++)
838 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
839 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
841 continue;
844 /* Get the HRIR coefficients and delays for this channel
845 * position.
847 GetHrtfCoeffs(Device->HrtfHandle,
848 chans[c].elevation, chans[c].angle, Spread,
849 voice->Direct.Params[c].Hrtf.Target.Coeffs,
850 voice->Direct.Params[c].Hrtf.Target.Delay
852 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
854 /* Normal panning for auxiliary sends. */
855 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
857 for(i = 0;i < NumSends;i++)
859 const ALeffectslot *Slot = SendSlots[i];
860 if(Slot)
861 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
862 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
864 else
865 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
866 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
871 voice->Flags |= VOICE_HAS_HRTF;
873 else
875 /* Non-HRTF rendering. Use normal panning to the output. */
877 if(Distance > FLT_EPSILON)
879 ALfloat coeffs[MAX_AMBI_COEFFS];
880 ALfloat w0 = 0.0f;
882 /* Calculate NFC filter coefficient if needed. */
883 if(Device->AvgSpeakerDist > 0.0f)
885 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
886 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
887 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
888 w0 = SPEEDOFSOUNDMETRESPERSEC /
889 (mdist * (ALfloat)Device->Frequency);
890 /* Clamp w0 for really close distances, to prevent excessive
891 * bass.
893 w0 = minf(w0, w1*4.0f);
895 /* Adjust NFC filters. */
896 for(c = 0;c < num_channels;c++)
897 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
899 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
900 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
901 voice->Flags |= VOICE_HAS_NFC;
904 /* Calculate the directional coefficients once, which apply to all
905 * input channels.
907 if(Device->Render_Mode == StereoPair)
909 ALfloat ev = asinf(Dir[1]);
910 ALfloat az = atan2f(Dir[0], -Dir[2]);
911 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
913 else
914 CalcDirectionCoeffs(Dir, Spread, coeffs);
916 for(c = 0;c < num_channels;c++)
918 /* Special-case LFE */
919 if(chans[c].channel == LFE)
921 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
922 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
923 if(Device->Dry.Buffer == Device->RealOut.Buffer)
925 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
926 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
928 continue;
931 ComputeDryPanGains(&Device->Dry,
932 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
936 for(i = 0;i < NumSends;i++)
938 const ALeffectslot *Slot = SendSlots[i];
939 if(Slot)
940 for(c = 0;c < num_channels;c++)
942 /* Skip LFE */
943 if(chans[c].channel == LFE)
944 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
945 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
946 else
947 ComputePanningGainsBF(Slot->ChanMap,
948 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
949 voice->Send[i].Params[c].Gains.Target
952 else
953 for(c = 0;c < num_channels;c++)
955 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
956 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
960 else
962 ALfloat w0 = 0.0f;
964 if(Device->AvgSpeakerDist > 0.0f)
966 /* If the source distance is 0, set w0 to w1 to act as a pass-
967 * through. We still want to pass the signal through the
968 * filters so they keep an appropriate history, in case the
969 * source moves away from the listener.
971 w0 = SPEEDOFSOUNDMETRESPERSEC /
972 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
974 for(c = 0;c < num_channels;c++)
975 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
977 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
978 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
979 voice->Flags |= VOICE_HAS_NFC;
982 for(c = 0;c < num_channels;c++)
984 ALfloat coeffs[MAX_AMBI_COEFFS];
986 /* Special-case LFE */
987 if(chans[c].channel == LFE)
989 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
990 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
991 if(Device->Dry.Buffer == Device->RealOut.Buffer)
993 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
994 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
997 for(i = 0;i < NumSends;i++)
999 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1000 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
1002 continue;
1005 if(Device->Render_Mode == StereoPair)
1006 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
1007 else
1008 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
1009 ComputeDryPanGains(&Device->Dry,
1010 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
1013 for(i = 0;i < NumSends;i++)
1015 const ALeffectslot *Slot = SendSlots[i];
1016 if(Slot)
1017 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1018 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
1020 else
1021 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1022 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
1029 ALfloat hfScale = props->Direct.HFReference / Frequency;
1030 ALfloat lfScale = props->Direct.LFReference / Frequency;
1031 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
1032 ALfloat gainLF = maxf(DryGainLF, 0.001f);
1034 voice->Direct.FilterType = AF_None;
1035 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
1036 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
1037 ALfilterState_setParams(
1038 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
1039 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1041 ALfilterState_setParams(
1042 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
1043 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1045 for(c = 1;c < num_channels;c++)
1047 ALfilterState_copyParams(&voice->Direct.Params[c].LowPass,
1048 &voice->Direct.Params[0].LowPass);
1049 ALfilterState_copyParams(&voice->Direct.Params[c].HighPass,
1050 &voice->Direct.Params[0].HighPass);
1053 for(i = 0;i < NumSends;i++)
1055 ALfloat hfScale = props->Send[i].HFReference / Frequency;
1056 ALfloat lfScale = props->Send[i].LFReference / Frequency;
1057 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
1058 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
1060 voice->Send[i].FilterType = AF_None;
1061 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1062 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1063 ALfilterState_setParams(
1064 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
1065 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1067 ALfilterState_setParams(
1068 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
1069 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1071 for(c = 1;c < num_channels;c++)
1073 ALfilterState_copyParams(&voice->Send[i].Params[c].LowPass,
1074 &voice->Send[i].Params[0].LowPass);
1075 ALfilterState_copyParams(&voice->Send[i].Params[c].HighPass,
1076 &voice->Send[i].Params[0].HighPass);
1081 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1083 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1084 const ALCdevice *Device = ALContext->Device;
1085 const ALlistener *Listener = ALContext->Listener;
1086 ALfloat DryGain, DryGainHF, DryGainLF;
1087 ALfloat WetGain[MAX_SENDS];
1088 ALfloat WetGainHF[MAX_SENDS];
1089 ALfloat WetGainLF[MAX_SENDS];
1090 ALeffectslot *SendSlots[MAX_SENDS];
1091 ALfloat Pitch;
1092 ALsizei i;
1094 voice->Direct.Buffer = Device->Dry.Buffer;
1095 voice->Direct.Channels = Device->Dry.NumChannels;
1096 for(i = 0;i < Device->NumAuxSends;i++)
1098 SendSlots[i] = props->Send[i].Slot;
1099 if(!SendSlots[i] && i == 0)
1100 SendSlots[i] = ALContext->DefaultSlot;
1101 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1103 SendSlots[i] = NULL;
1104 voice->Send[i].Buffer = NULL;
1105 voice->Send[i].Channels = 0;
1107 else
1109 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1110 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1114 /* Calculate the stepping value */
1115 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1116 if(Pitch > (ALfloat)MAX_PITCH)
1117 voice->Step = MAX_PITCH<<FRACTIONBITS;
1118 else
1119 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1120 if(props->Resampler == BSinc24Resampler)
1121 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1122 else if(props->Resampler == BSinc12Resampler)
1123 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1124 voice->Resampler = SelectResampler(props->Resampler);
1126 /* Calculate gains */
1127 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1128 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1129 DryGain = minf(DryGain, GAIN_MIX_MAX);
1130 DryGainHF = props->Direct.GainHF;
1131 DryGainLF = props->Direct.GainLF;
1132 for(i = 0;i < Device->NumAuxSends;i++)
1134 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1135 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1136 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1137 WetGainHF[i] = props->Send[i].GainHF;
1138 WetGainLF[i] = props->Send[i].GainLF;
1141 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1142 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1145 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1147 const ALCdevice *Device = ALContext->Device;
1148 const ALlistener *Listener = ALContext->Listener;
1149 const ALsizei NumSends = Device->NumAuxSends;
1150 aluVector Position, Velocity, Direction, SourceToListener;
1151 ALfloat Distance, ClampedDist, DopplerFactor;
1152 ALeffectslot *SendSlots[MAX_SENDS];
1153 ALfloat RoomRolloff[MAX_SENDS];
1154 ALfloat DecayDistance[MAX_SENDS];
1155 ALfloat DecayHFDistance[MAX_SENDS];
1156 ALfloat DryGain, DryGainHF, DryGainLF;
1157 ALfloat WetGain[MAX_SENDS];
1158 ALfloat WetGainHF[MAX_SENDS];
1159 ALfloat WetGainLF[MAX_SENDS];
1160 bool directional;
1161 ALfloat dir[3];
1162 ALfloat spread;
1163 ALfloat Pitch;
1164 ALint i;
1166 /* Set mixing buffers and get send parameters. */
1167 voice->Direct.Buffer = Device->Dry.Buffer;
1168 voice->Direct.Channels = Device->Dry.NumChannels;
1169 for(i = 0;i < NumSends;i++)
1171 SendSlots[i] = props->Send[i].Slot;
1172 if(!SendSlots[i] && i == 0)
1173 SendSlots[i] = ALContext->DefaultSlot;
1174 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1176 SendSlots[i] = NULL;
1177 RoomRolloff[i] = 0.0f;
1178 DecayDistance[i] = 0.0f;
1179 DecayHFDistance[i] = 0.0f;
1181 else if(SendSlots[i]->Params.AuxSendAuto)
1183 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1184 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1185 Listener->Params.ReverbSpeedOfSound;
1186 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1187 if(SendSlots[i]->Params.DecayHFLimit)
1189 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1190 if(airAbsorption < 1.0f)
1192 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1193 DecayHFDistance[i] = minf(limitRatio, DecayHFDistance[i]);
1197 else
1199 /* If the slot's auxiliary send auto is off, the data sent to the
1200 * effect slot is the same as the dry path, sans filter effects */
1201 RoomRolloff[i] = props->RolloffFactor;
1202 DecayDistance[i] = 0.0f;
1203 DecayHFDistance[i] = 0.0f;
1206 if(!SendSlots[i])
1208 voice->Send[i].Buffer = NULL;
1209 voice->Send[i].Channels = 0;
1211 else
1213 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1214 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1218 /* Transform source to listener space (convert to head relative) */
1219 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1220 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1221 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1222 if(props->HeadRelative == AL_FALSE)
1224 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1225 /* Transform source vectors */
1226 Position = aluMatrixfVector(Matrix, &Position);
1227 Velocity = aluMatrixfVector(Matrix, &Velocity);
1228 Direction = aluMatrixfVector(Matrix, &Direction);
1230 else
1232 const aluVector *lvelocity = &Listener->Params.Velocity;
1233 /* Offset the source velocity to be relative of the listener velocity */
1234 Velocity.v[0] += lvelocity->v[0];
1235 Velocity.v[1] += lvelocity->v[1];
1236 Velocity.v[2] += lvelocity->v[2];
1239 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1240 SourceToListener.v[0] = -Position.v[0];
1241 SourceToListener.v[1] = -Position.v[1];
1242 SourceToListener.v[2] = -Position.v[2];
1243 SourceToListener.v[3] = 0.0f;
1244 Distance = aluNormalize(SourceToListener.v);
1246 /* Initial source gain */
1247 DryGain = props->Gain;
1248 DryGainHF = 1.0f;
1249 DryGainLF = 1.0f;
1250 for(i = 0;i < NumSends;i++)
1252 WetGain[i] = props->Gain;
1253 WetGainHF[i] = 1.0f;
1254 WetGainLF[i] = 1.0f;
1257 /* Calculate distance attenuation */
1258 ClampedDist = Distance;
1260 switch(Listener->Params.SourceDistanceModel ?
1261 props->DistanceModel : Listener->Params.DistanceModel)
1263 case InverseDistanceClamped:
1264 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1265 if(props->MaxDistance < props->RefDistance)
1266 break;
1267 /*fall-through*/
1268 case InverseDistance:
1269 if(!(props->RefDistance > 0.0f))
1270 ClampedDist = props->RefDistance;
1271 else
1273 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1274 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1275 for(i = 0;i < NumSends;i++)
1277 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1278 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1281 break;
1283 case LinearDistanceClamped:
1284 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1285 if(props->MaxDistance < props->RefDistance)
1286 break;
1287 /*fall-through*/
1288 case LinearDistance:
1289 if(!(props->MaxDistance != props->RefDistance))
1290 ClampedDist = props->RefDistance;
1291 else
1293 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1294 (props->MaxDistance-props->RefDistance);
1295 DryGain *= maxf(1.0f - attn, 0.0f);
1296 for(i = 0;i < NumSends;i++)
1298 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1299 (props->MaxDistance-props->RefDistance);
1300 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1303 break;
1305 case ExponentDistanceClamped:
1306 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1307 if(props->MaxDistance < props->RefDistance)
1308 break;
1309 /*fall-through*/
1310 case ExponentDistance:
1311 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1312 ClampedDist = props->RefDistance;
1313 else
1315 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1316 for(i = 0;i < NumSends;i++)
1317 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1319 break;
1321 case DisableDistance:
1322 ClampedDist = props->RefDistance;
1323 break;
1326 /* Distance-based air absorption */
1327 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1329 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1330 Listener->Params.MetersPerUnit;
1331 if(props->AirAbsorptionFactor > 0.0f)
1333 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1334 DryGainHF *= hfattn;
1335 for(i = 0;i < NumSends;i++)
1336 WetGainHF[i] *= hfattn;
1339 if(props->WetGainAuto)
1341 /* Apply a decay-time transformation to the wet path, based on the
1342 * source distance in meters. The initial decay of the reverb
1343 * effect is calculated and applied to the wet path.
1345 for(i = 0;i < NumSends;i++)
1347 ALfloat gain;
1349 if(!(DecayDistance[i] > 0.0f))
1350 continue;
1352 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1353 WetGain[i] *= gain;
1354 /* Yes, the wet path's air absorption is applied with
1355 * WetGainAuto on, rather than WetGainHFAuto.
1357 if(gain > 0.0f)
1359 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1360 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1366 /* Calculate directional soundcones */
1367 if(directional && props->InnerAngle < 360.0f)
1369 ALfloat ConeVolume;
1370 ALfloat ConeHF;
1371 ALfloat Angle;
1373 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1374 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1375 if(!(Angle > props->InnerAngle))
1377 ConeVolume = 1.0f;
1378 ConeHF = 1.0f;
1380 else if(Angle < props->OuterAngle)
1382 ALfloat scale = ( Angle-props->InnerAngle) /
1383 (props->OuterAngle-props->InnerAngle);
1384 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1385 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1387 else
1389 ConeVolume = props->OuterGain;
1390 ConeHF = props->OuterGainHF;
1393 DryGain *= ConeVolume;
1394 if(props->DryGainHFAuto)
1395 DryGainHF *= ConeHF;
1396 if(props->WetGainAuto)
1398 for(i = 0;i < NumSends;i++)
1399 WetGain[i] *= ConeVolume;
1401 if(props->WetGainHFAuto)
1403 for(i = 0;i < NumSends;i++)
1404 WetGainHF[i] *= ConeHF;
1408 /* Apply gain and frequency filters */
1409 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1410 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1411 DryGainHF *= props->Direct.GainHF;
1412 DryGainLF *= props->Direct.GainLF;
1413 for(i = 0;i < NumSends;i++)
1415 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1416 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1417 WetGainHF[i] *= props->Send[i].GainHF;
1418 WetGainLF[i] *= props->Send[i].GainLF;
1422 /* Initial source pitch */
1423 Pitch = props->Pitch;
1425 /* Calculate velocity-based doppler effect */
1426 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1427 if(DopplerFactor > 0.0f)
1429 const aluVector *lvelocity = &Listener->Params.Velocity;
1430 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1431 ALfloat vss, vls;
1433 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1434 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1436 if(!(vls < SpeedOfSound))
1438 /* Listener moving away from the source at the speed of sound.
1439 * Sound waves can't catch it.
1441 Pitch = 0.0f;
1443 else if(!(vss < SpeedOfSound))
1445 /* Source moving toward the listener at the speed of sound. Sound
1446 * waves bunch up to extreme frequencies.
1448 Pitch = HUGE_VALF;
1450 else
1452 /* Source and listener movement is nominal. Calculate the proper
1453 * doppler shift.
1455 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1459 /* Adjust pitch based on the buffer and output frequencies, and calculate
1460 * fixed-point stepping value.
1462 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1463 if(Pitch > (ALfloat)MAX_PITCH)
1464 voice->Step = MAX_PITCH<<FRACTIONBITS;
1465 else
1466 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1467 if(props->Resampler == BSinc24Resampler)
1468 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1469 else if(props->Resampler == BSinc12Resampler)
1470 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1471 voice->Resampler = SelectResampler(props->Resampler);
1473 if(Distance > FLT_EPSILON)
1475 dir[0] = -SourceToListener.v[0];
1476 /* Clamp Y, in case rounding errors caused it to end up outside of
1477 * -1...+1.
1479 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1480 dir[2] = -SourceToListener.v[2] * ZScale;
1482 else
1484 dir[0] = 0.0f;
1485 dir[1] = 0.0f;
1486 dir[2] = -1.0f;
1488 if(props->Radius > Distance)
1489 spread = F_TAU - Distance/props->Radius*F_PI;
1490 else if(Distance > FLT_EPSILON)
1491 spread = asinf(props->Radius / Distance) * 2.0f;
1492 else
1493 spread = 0.0f;
1495 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1496 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1499 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1501 ALbufferlistitem *BufferListItem;
1502 struct ALvoiceProps *props;
1504 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1505 if(!props && !force) return;
1507 if(props)
1509 memcpy(voice->Props, props,
1510 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1513 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1515 props = voice->Props;
1517 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1518 while(BufferListItem != NULL)
1520 const ALbuffer *buffer;
1521 if(BufferListItem->num_buffers >= 1 && (buffer=BufferListItem->buffers[0]) != NULL)
1523 if(props->SpatializeMode == SpatializeOn ||
1524 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1525 CalcAttnSourceParams(voice, props, buffer, context);
1526 else
1527 CalcNonAttnSourceParams(voice, props, buffer, context);
1528 break;
1530 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1535 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1537 ALvoice **voice, **voice_end;
1538 ALsource *source;
1539 ALsizei i;
1541 IncrementRef(&ctx->UpdateCount);
1542 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1544 bool cforce = CalcContextParams(ctx);
1545 bool force = CalcListenerParams(ctx) | cforce;
1546 for(i = 0;i < slots->count;i++)
1547 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1549 voice = ctx->Voices;
1550 voice_end = voice + ctx->VoiceCount;
1551 for(;voice != voice_end;++voice)
1553 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1554 if(source) CalcSourceParams(*voice, ctx, force);
1557 IncrementRef(&ctx->UpdateCount);
1561 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1562 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1563 ALsizei NumChannels)
1565 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1566 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1567 ALsizei i;
1569 /* Apply an all-pass to all channels, except the front-left and front-
1570 * right, so they maintain the same relative phase.
1572 for(i = 0;i < NumChannels;i++)
1574 if(i == lidx || i == ridx)
1575 continue;
1576 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1579 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1580 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1582 for(i = 0;i < SamplesToDo;i++)
1584 ALfloat lfsum, hfsum;
1585 ALfloat m, s, c;
1587 lfsum = lsplit[0][i] + rsplit[0][i];
1588 hfsum = lsplit[1][i] + rsplit[1][i];
1589 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1591 /* This pans the separate low- and high-frequency sums between being on
1592 * the center channel and the left/right channels. The low-frequency
1593 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1594 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1595 * values can be tweaked.
1597 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1598 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1600 /* The generated center channel signal adds to the existing signal,
1601 * while the modified left and right channels replace.
1603 Buffer[lidx][i] = (m + s) * 0.5f;
1604 Buffer[ridx][i] = (m - s) * 0.5f;
1605 Buffer[cidx][i] += c * 0.5f;
1609 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1610 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1612 ALsizei i, c;
1614 Values = ASSUME_ALIGNED(Values, 16);
1615 for(c = 0;c < numchans;c++)
1617 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1618 const ALfloat gain = distcomp[c].Gain;
1619 const ALsizei base = distcomp[c].Length;
1620 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1622 if(base == 0)
1624 if(gain < 1.0f)
1626 for(i = 0;i < SamplesToDo;i++)
1627 inout[i] *= gain;
1629 continue;
1632 if(SamplesToDo >= base)
1634 for(i = 0;i < base;i++)
1635 Values[i] = distbuf[i];
1636 for(;i < SamplesToDo;i++)
1637 Values[i] = inout[i-base];
1638 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1640 else
1642 for(i = 0;i < SamplesToDo;i++)
1643 Values[i] = distbuf[i];
1644 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1645 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1647 for(i = 0;i < SamplesToDo;i++)
1648 inout[i] = Values[i]*gain;
1652 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1653 const ALfloat quant_scale, const ALsizei SamplesToDo,
1654 const ALsizei numchans)
1656 const ALfloat invscale = 1.0f / quant_scale;
1657 ALuint seed = *dither_seed;
1658 ALsizei c, i;
1660 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1661 * values between -1 and +1). Step 2 is to add the noise to the samples,
1662 * before rounding and after scaling up to the desired quantization depth.
1664 for(c = 0;c < numchans;c++)
1666 ALfloat *restrict samples = Samples[c];
1667 for(i = 0;i < SamplesToDo;i++)
1669 ALfloat val = samples[i] * quant_scale;
1670 ALuint rng0 = dither_rng(&seed);
1671 ALuint rng1 = dither_rng(&seed);
1672 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1673 samples[i] = roundf(val) * invscale;
1676 *dither_seed = seed;
1680 static inline ALfloat Conv_ALfloat(ALfloat val)
1681 { return val; }
1682 static inline ALint Conv_ALint(ALfloat val)
1684 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1685 * integer range normalized floats can be safely converted to (a bit of the
1686 * exponent helps out, effectively giving 25 bits).
1688 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1690 static inline ALshort Conv_ALshort(ALfloat val)
1691 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1692 static inline ALbyte Conv_ALbyte(ALfloat val)
1693 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1695 /* Define unsigned output variations. */
1696 #define DECL_TEMPLATE(T, func, O) \
1697 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1699 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1700 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1701 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1703 #undef DECL_TEMPLATE
1705 #define DECL_TEMPLATE(T, A) \
1706 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1707 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1708 ALsizei numchans) \
1710 ALsizei i, j; \
1711 for(j = 0;j < numchans;j++) \
1713 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1714 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1716 for(i = 0;i < SamplesToDo;i++) \
1717 out[i*numchans] = Conv_##T(in[i]); \
1721 DECL_TEMPLATE(ALfloat, F32)
1722 DECL_TEMPLATE(ALuint, UI32)
1723 DECL_TEMPLATE(ALint, I32)
1724 DECL_TEMPLATE(ALushort, UI16)
1725 DECL_TEMPLATE(ALshort, I16)
1726 DECL_TEMPLATE(ALubyte, UI8)
1727 DECL_TEMPLATE(ALbyte, I8)
1729 #undef DECL_TEMPLATE
1732 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1734 ALsizei SamplesToDo;
1735 ALsizei SamplesDone;
1736 ALCcontext *ctx;
1737 ALsizei i, c;
1739 START_MIXER_MODE();
1740 for(SamplesDone = 0;SamplesDone < NumSamples;)
1742 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1743 for(c = 0;c < device->Dry.NumChannels;c++)
1744 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1745 if(device->Dry.Buffer != device->FOAOut.Buffer)
1746 for(c = 0;c < device->FOAOut.NumChannels;c++)
1747 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1748 if(device->Dry.Buffer != device->RealOut.Buffer)
1749 for(c = 0;c < device->RealOut.NumChannels;c++)
1750 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1752 IncrementRef(&device->MixCount);
1754 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1755 while(ctx)
1757 const struct ALeffectslotArray *auxslots;
1759 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1760 UpdateContextSources(ctx, auxslots);
1762 for(i = 0;i < auxslots->count;i++)
1764 ALeffectslot *slot = auxslots->slot[i];
1765 for(c = 0;c < slot->NumChannels;c++)
1766 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1769 /* source processing */
1770 for(i = 0;i < ctx->VoiceCount;i++)
1772 ALvoice *voice = ctx->Voices[i];
1773 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1774 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1775 voice->Step > 0)
1777 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1779 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1780 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1785 /* effect slot processing */
1786 for(i = 0;i < auxslots->count;i++)
1788 const ALeffectslot *slot = auxslots->slot[i];
1789 ALeffectState *state = slot->Params.EffectState;
1790 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1791 state->OutChannels);
1794 ctx = ctx->next;
1797 /* Increment the clock time. Every second's worth of samples is
1798 * converted and added to clock base so that large sample counts don't
1799 * overflow during conversion. This also guarantees an exact, stable
1800 * conversion. */
1801 device->SamplesDone += SamplesToDo;
1802 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1803 device->SamplesDone %= device->Frequency;
1804 IncrementRef(&device->MixCount);
1806 /* Apply post-process for finalizing the Dry mix to the RealOut
1807 * (Ambisonic decode, UHJ encode, etc).
1809 if(LIKELY(device->PostProcess))
1810 device->PostProcess(device, SamplesToDo);
1812 if(OutBuffer)
1814 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1815 ALsizei Channels = device->RealOut.NumChannels;
1817 if(device->Stablizer)
1819 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1820 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1821 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1822 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1824 ApplyStablizer(device->Stablizer, Buffer, lidx, ridx, cidx,
1825 SamplesToDo, Channels);
1828 ApplyDistanceComp(Buffer, device->ChannelDelay, device->TempBuffer[0],
1829 SamplesToDo, Channels);
1831 if(device->Limiter)
1832 ApplyCompression(device->Limiter, Channels, SamplesToDo, Buffer);
1834 if(device->DitherDepth > 0.0f)
1835 ApplyDither(Buffer, &device->DitherSeed, device->DitherDepth, SamplesToDo,
1836 Channels);
1838 switch(device->FmtType)
1840 case DevFmtByte:
1841 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1842 break;
1843 case DevFmtUByte:
1844 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1845 break;
1846 case DevFmtShort:
1847 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1848 break;
1849 case DevFmtUShort:
1850 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1851 break;
1852 case DevFmtInt:
1853 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1854 break;
1855 case DevFmtUInt:
1856 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1857 break;
1858 case DevFmtFloat:
1859 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1860 break;
1864 SamplesDone += SamplesToDo;
1866 END_MIXER_MODE();
1870 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1872 ALCcontext *ctx;
1873 AsyncEvent evt;
1874 va_list args;
1875 int msglen;
1877 if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
1878 return;
1880 evt.EnumType = EventType_Disconnected;
1881 evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1882 evt.ObjectId = 0;
1883 evt.Param = 0;
1885 va_start(args, msg);
1886 msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
1887 va_end(args);
1889 if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
1891 evt.Message[sizeof(evt.Message)-1] = 0;
1892 msglen = (int)strlen(evt.Message);
1894 if(msglen > 0)
1895 msg = evt.Message;
1896 else
1898 msg = "<internal error constructing message>";
1899 msglen = (int)strlen(msg);
1902 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1903 while(ctx)
1905 ALsizei i;
1907 if((ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire)&EventType_Disconnected) &&
1908 ll_ringbuffer_write_space(ctx->AsyncEvents) > 0)
1910 ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1);
1911 alsem_post(&ctx->EventSem);
1914 for(i = 0;i < ctx->VoiceCount;i++)
1916 ALvoice *voice = ctx->Voices[i];
1918 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1919 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1922 ctx = ctx->next;