An output device buffer is likely
[openal-soft.git] / Alc / ALu.c
blob177509b6bae49ae2f73ec460d87ccbb10737f34f
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"
42 #include "filters/splitter.h"
44 #include "mixer/defs.h"
45 #include "fpu_modes.h"
46 #include "cpu_caps.h"
47 #include "bsinc_inc.h"
49 #include "backends/base.h"
52 extern inline ALfloat minf(ALfloat a, ALfloat b);
53 extern inline ALfloat maxf(ALfloat a, ALfloat b);
54 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
56 extern inline ALdouble mind(ALdouble a, ALdouble b);
57 extern inline ALdouble maxd(ALdouble a, ALdouble b);
58 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
60 extern inline ALuint minu(ALuint a, ALuint b);
61 extern inline ALuint maxu(ALuint a, ALuint b);
62 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
64 extern inline ALint mini(ALint a, ALint b);
65 extern inline ALint maxi(ALint a, ALint b);
66 extern inline ALint clampi(ALint val, ALint min, ALint max);
68 extern inline ALint64 mini64(ALint64 a, ALint64 b);
69 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
70 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
72 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
73 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
74 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
76 extern inline size_t minz(size_t a, size_t b);
77 extern inline size_t maxz(size_t a, size_t b);
78 extern inline size_t clampz(size_t val, size_t min, size_t max);
80 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
81 extern inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu);
83 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
85 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
86 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
87 extern inline void aluMatrixfSet(aluMatrixf *matrix,
88 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
89 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
90 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
91 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
94 /* Cone scalar */
95 ALfloat ConeScale = 1.0f;
97 /* Localized Z scalar for mono sources */
98 ALfloat ZScale = 1.0f;
100 /* Force default speed of sound for distance-related reverb decay. */
101 ALboolean OverrideReverbSpeedOfSound = AL_FALSE;
103 const aluMatrixf IdentityMatrixf = {{
104 { 1.0f, 0.0f, 0.0f, 0.0f },
105 { 0.0f, 1.0f, 0.0f, 0.0f },
106 { 0.0f, 0.0f, 1.0f, 0.0f },
107 { 0.0f, 0.0f, 0.0f, 1.0f },
111 static void ClearArray(ALfloat f[MAX_OUTPUT_CHANNELS])
113 size_t i;
114 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
115 f[i] = 0.0f;
118 struct ChanMap {
119 enum Channel channel;
120 ALfloat angle;
121 ALfloat elevation;
124 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
127 void DeinitVoice(ALvoice *voice)
129 al_free(ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL));
133 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
135 #ifdef HAVE_NEON
136 if((CPUCapFlags&CPU_CAP_NEON))
137 return MixDirectHrtf_Neon;
138 #endif
139 #ifdef HAVE_SSE
140 if((CPUCapFlags&CPU_CAP_SSE))
141 return MixDirectHrtf_SSE;
142 #endif
144 return MixDirectHrtf_C;
148 /* Prior to VS2013, MSVC lacks the round() family of functions. */
149 #if defined(_MSC_VER) && _MSC_VER < 1800
150 static float roundf(float val)
152 if(val < 0.0f)
153 return ceilf(val-0.5f);
154 return floorf(val+0.5f);
156 #endif
158 /* This RNG method was created based on the math found in opusdec. It's quick,
159 * and starting with a seed value of 22222, is suitable for generating
160 * whitenoise.
162 static inline ALuint dither_rng(ALuint *seed)
164 *seed = (*seed * 96314165) + 907633515;
165 return *seed;
169 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
171 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
172 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
173 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
176 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
178 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
181 static ALfloat aluNormalize(ALfloat *vec)
183 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
184 if(length > FLT_EPSILON)
186 ALfloat inv_length = 1.0f/length;
187 vec[0] *= inv_length;
188 vec[1] *= inv_length;
189 vec[2] *= inv_length;
190 return length;
192 vec[0] = vec[1] = vec[2] = 0.0f;
193 return 0.0f;
196 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
198 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
200 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];
201 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];
202 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];
205 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
207 aluVector v;
208 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];
209 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];
210 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];
211 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];
212 return v;
216 void aluInit(void)
218 MixDirectHrtf = SelectHrtfMixer();
222 static void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
224 ALbitfieldSOFT enabledevt;
225 AsyncEvent evt;
226 size_t strpos;
227 ALuint scale;
229 enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
230 if(!(enabledevt&EventType_SourceStateChange)) return;
232 evt.EnumType = EventType_SourceStateChange;
233 evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
234 evt.ObjectId = id;
235 evt.Param = AL_STOPPED;
237 /* Normally snprintf would be used, but this is called from the mixer and
238 * that function's not real-time safe, so we have to construct it manually.
240 strcpy(evt.Message, "Source ID "); strpos = 10;
241 scale = 1000000000;
242 while(scale > 0 && scale > id)
243 scale /= 10;
244 while(scale > 0)
246 evt.Message[strpos++] = '0' + ((id/scale)%10);
247 scale /= 10;
249 strcpy(evt.Message+strpos, " state changed to AL_STOPPED");
251 if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
252 alsem_post(&context->EventSem);
256 static void ProcessHrtf(ALCdevice *device, ALsizei SamplesToDo)
258 DirectHrtfState *state;
259 int lidx, ridx;
260 ALsizei c;
262 if(device->AmbiUp)
263 ambiup_process(device->AmbiUp,
264 device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer,
265 SamplesToDo
268 lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
269 ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
270 assert(lidx != -1 && ridx != -1);
272 state = device->Hrtf;
273 for(c = 0;c < device->Dry.NumChannels;c++)
275 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
276 device->Dry.Buffer[c], state->Offset, state->IrSize,
277 state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo
280 state->Offset += SamplesToDo;
283 static void ProcessAmbiDec(ALCdevice *device, ALsizei SamplesToDo)
285 if(device->Dry.Buffer != device->FOAOut.Buffer)
286 bformatdec_upSample(device->AmbiDecoder,
287 device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
288 SamplesToDo
290 bformatdec_process(device->AmbiDecoder,
291 device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
292 SamplesToDo
296 static void ProcessAmbiUp(ALCdevice *device, ALsizei SamplesToDo)
298 ambiup_process(device->AmbiUp,
299 device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer,
300 SamplesToDo
304 static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
306 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
307 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
308 assert(lidx != -1 && ridx != -1);
310 /* Encode to stereo-compatible 2-channel UHJ output. */
311 EncodeUhj2(device->Uhj_Encoder,
312 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
313 device->Dry.Buffer, SamplesToDo
317 static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
319 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
320 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
321 assert(lidx != -1 && ridx != -1);
323 /* Apply binaural/crossfeed filter */
324 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
325 device->RealOut.Buffer[ridx], SamplesToDo);
328 void aluSelectPostProcess(ALCdevice *device)
330 if(device->HrtfHandle)
331 device->PostProcess = ProcessHrtf;
332 else if(device->AmbiDecoder)
333 device->PostProcess = ProcessAmbiDec;
334 else if(device->AmbiUp)
335 device->PostProcess = ProcessAmbiUp;
336 else if(device->Uhj_Encoder)
337 device->PostProcess = ProcessUhj;
338 else if(device->Bs2b)
339 device->PostProcess = ProcessBs2b;
340 else
341 device->PostProcess = NULL;
345 /* Prepares the interpolator for a given rate (determined by increment). A
346 * result of AL_FALSE indicates that the filter output will completely cut
347 * the input signal.
349 * With a bit of work, and a trade of memory for CPU cost, this could be
350 * modified for use with an interpolated increment for buttery-smooth pitch
351 * changes.
353 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
355 ALfloat sf;
356 ALsizei si;
358 if(increment > FRACTIONONE)
360 sf = (ALfloat)FRACTIONONE / increment;
361 sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
362 si = fastf2i(sf);
363 /* The interpolation factor is fit to this diagonally-symmetric curve
364 * to reduce the transition ripple caused by interpolating different
365 * scales of the sinc function.
367 sf = 1.0f - cosf(asinf(sf - si));
369 else
371 sf = 0.0f;
372 si = BSINC_SCALE_COUNT - 1;
375 state->sf = sf;
376 state->m = table->m[si];
377 state->l = -((state->m/2) - 1);
378 state->filter = table->Tab + table->filterOffset[si];
382 static bool CalcContextParams(ALCcontext *Context)
384 ALlistener *Listener = Context->Listener;
385 struct ALcontextProps *props;
387 props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel);
388 if(!props) return false;
390 Listener->Params.MetersPerUnit = props->MetersPerUnit;
392 Listener->Params.DopplerFactor = props->DopplerFactor;
393 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
394 if(!OverrideReverbSpeedOfSound)
395 Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
396 Listener->Params.MetersPerUnit;
398 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
399 Listener->Params.DistanceModel = props->DistanceModel;
401 ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props);
402 return true;
405 static bool CalcListenerParams(ALCcontext *Context)
407 ALlistener *Listener = Context->Listener;
408 ALfloat N[3], V[3], U[3], P[3];
409 struct ALlistenerProps *props;
410 aluVector vel;
412 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
413 if(!props) return false;
415 /* AT then UP */
416 N[0] = props->Forward[0];
417 N[1] = props->Forward[1];
418 N[2] = props->Forward[2];
419 aluNormalize(N);
420 V[0] = props->Up[0];
421 V[1] = props->Up[1];
422 V[2] = props->Up[2];
423 aluNormalize(V);
424 /* Build and normalize right-vector */
425 aluCrossproduct(N, V, U);
426 aluNormalize(U);
428 aluMatrixfSet(&Listener->Params.Matrix,
429 U[0], V[0], -N[0], 0.0,
430 U[1], V[1], -N[1], 0.0,
431 U[2], V[2], -N[2], 0.0,
432 0.0, 0.0, 0.0, 1.0
435 P[0] = props->Position[0];
436 P[1] = props->Position[1];
437 P[2] = props->Position[2];
438 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
439 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
441 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
442 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
444 Listener->Params.Gain = props->Gain * Context->GainBoost;
446 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props);
447 return true;
450 static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
452 struct ALeffectslotProps *props;
453 ALeffectState *state;
455 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
456 if(!props && !force) return false;
458 if(props)
460 slot->Params.Gain = props->Gain;
461 slot->Params.AuxSendAuto = props->AuxSendAuto;
462 slot->Params.EffectType = props->Type;
463 slot->Params.EffectProps = props->Props;
464 if(IsReverbEffect(props->Type))
466 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
467 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
468 slot->Params.DecayLFRatio = props->Props.Reverb.DecayLFRatio;
469 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
470 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
471 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
473 else
475 slot->Params.RoomRolloff = 0.0f;
476 slot->Params.DecayTime = 0.0f;
477 slot->Params.DecayLFRatio = 0.0f;
478 slot->Params.DecayHFRatio = 0.0f;
479 slot->Params.DecayHFLimit = AL_FALSE;
480 slot->Params.AirAbsorptionGainHF = 1.0f;
483 /* Swap effect states. No need to play with the ref counts since they
484 * keep the same number of refs.
486 state = props->State;
487 props->State = slot->Params.EffectState;
488 slot->Params.EffectState = state;
490 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
492 else
493 state = slot->Params.EffectState;
495 V(state,update)(context, slot, &slot->Params.EffectProps);
496 return true;
500 static const struct ChanMap MonoMap[1] = {
501 { FrontCenter, 0.0f, 0.0f }
502 }, RearMap[2] = {
503 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
504 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
505 }, QuadMap[4] = {
506 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
507 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
508 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
509 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
510 }, X51Map[6] = {
511 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
512 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
513 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
514 { LFE, 0.0f, 0.0f },
515 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
516 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
517 }, X61Map[7] = {
518 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
519 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
520 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
521 { LFE, 0.0f, 0.0f },
522 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
523 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
524 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
525 }, X71Map[8] = {
526 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
527 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
528 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
529 { LFE, 0.0f, 0.0f },
530 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
531 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
532 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
533 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
536 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALfloat Elev,
537 const ALfloat Distance, const ALfloat Spread,
538 const ALfloat DryGain, const ALfloat DryGainHF,
539 const ALfloat DryGainLF, const ALfloat *WetGain,
540 const ALfloat *WetGainLF, const ALfloat *WetGainHF,
541 ALeffectslot **SendSlots, const ALbuffer *Buffer,
542 const struct ALvoiceProps *props, const ALlistener *Listener,
543 const ALCdevice *Device)
545 struct ChanMap StereoMap[2] = {
546 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
547 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
549 bool DirectChannels = props->DirectChannels;
550 const ALsizei NumSends = Device->NumAuxSends;
551 const ALuint Frequency = Device->Frequency;
552 const struct ChanMap *chans = NULL;
553 ALsizei num_channels = 0;
554 bool isbformat = false;
555 ALfloat downmix_gain = 1.0f;
556 ALsizei c, i;
558 switch(Buffer->FmtChannels)
560 case FmtMono:
561 chans = MonoMap;
562 num_channels = 1;
563 /* Mono buffers are never played direct. */
564 DirectChannels = false;
565 break;
567 case FmtStereo:
568 /* Convert counter-clockwise to clockwise. */
569 StereoMap[0].angle = -props->StereoPan[0];
570 StereoMap[1].angle = -props->StereoPan[1];
572 chans = StereoMap;
573 num_channels = 2;
574 downmix_gain = 1.0f / 2.0f;
575 break;
577 case FmtRear:
578 chans = RearMap;
579 num_channels = 2;
580 downmix_gain = 1.0f / 2.0f;
581 break;
583 case FmtQuad:
584 chans = QuadMap;
585 num_channels = 4;
586 downmix_gain = 1.0f / 4.0f;
587 break;
589 case FmtX51:
590 chans = X51Map;
591 num_channels = 6;
592 /* NOTE: Excludes LFE. */
593 downmix_gain = 1.0f / 5.0f;
594 break;
596 case FmtX61:
597 chans = X61Map;
598 num_channels = 7;
599 /* NOTE: Excludes LFE. */
600 downmix_gain = 1.0f / 6.0f;
601 break;
603 case FmtX71:
604 chans = X71Map;
605 num_channels = 8;
606 /* NOTE: Excludes LFE. */
607 downmix_gain = 1.0f / 7.0f;
608 break;
610 case FmtBFormat2D:
611 num_channels = 3;
612 isbformat = true;
613 DirectChannels = false;
614 break;
616 case FmtBFormat3D:
617 num_channels = 4;
618 isbformat = true;
619 DirectChannels = false;
620 break;
623 for(c = 0;c < num_channels;c++)
625 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
626 sizeof(voice->Direct.Params[c].Hrtf.Target));
627 ClearArray(voice->Direct.Params[c].Gains.Target);
629 for(i = 0;i < NumSends;i++)
631 for(c = 0;c < num_channels;c++)
632 ClearArray(voice->Send[i].Params[c].Gains.Target);
635 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
636 if(isbformat)
638 /* Special handling for B-Format sources. */
640 if(Distance > FLT_EPSILON)
642 /* Panning a B-Format sound toward some direction is easy. Just pan
643 * the first (W) channel as a normal mono sound and silence the
644 * others.
646 ALfloat coeffs[MAX_AMBI_COEFFS];
648 if(Device->AvgSpeakerDist > 0.0f)
650 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
651 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
652 (mdist * (ALfloat)Device->Frequency);
653 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
654 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
655 /* Clamp w0 for really close distances, to prevent excessive
656 * bass.
658 w0 = minf(w0, w1*4.0f);
660 /* Only need to adjust the first channel of a B-Format source. */
661 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
663 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
664 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
665 voice->Flags |= VOICE_HAS_NFC;
668 if(Device->Render_Mode == StereoPair)
669 CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
670 else
671 CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
673 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
674 ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
675 voice->Direct.Params[0].Gains.Target);
676 for(i = 0;i < NumSends;i++)
678 const ALeffectslot *Slot = SendSlots[i];
679 if(Slot)
680 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
681 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
685 else
687 /* Local B-Format sources have their XYZ channels rotated according
688 * to the orientation.
690 const ALfloat sqrt_2 = sqrtf(2.0f);
691 const ALfloat sqrt_3 = sqrtf(3.0f);
692 ALfloat N[3], V[3], U[3];
693 aluMatrixf matrix;
695 if(Device->AvgSpeakerDist > 0.0f)
697 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
698 * is what we want for FOA input. The first channel may have
699 * been previously re-adjusted if panned, so reset it.
701 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, 0.0f);
703 voice->Direct.ChannelsPerOrder[0] = 1;
704 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
705 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
706 voice->Direct.ChannelsPerOrder[i] = 0;
707 voice->Flags |= VOICE_HAS_NFC;
710 /* AT then UP */
711 N[0] = props->Orientation[0][0];
712 N[1] = props->Orientation[0][1];
713 N[2] = props->Orientation[0][2];
714 aluNormalize(N);
715 V[0] = props->Orientation[1][0];
716 V[1] = props->Orientation[1][1];
717 V[2] = props->Orientation[1][2];
718 aluNormalize(V);
719 if(!props->HeadRelative)
721 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
722 aluMatrixfFloat3(N, 0.0f, lmatrix);
723 aluMatrixfFloat3(V, 0.0f, lmatrix);
725 /* Build and normalize right-vector */
726 aluCrossproduct(N, V, U);
727 aluNormalize(U);
729 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). NOTE: This
730 * matrix is transposed, for the inputs to align on the rows and
731 * outputs on the columns.
733 aluMatrixfSet(&matrix,
734 // ACN0 ACN1 ACN2 ACN3
735 sqrt_2, 0.0f, 0.0f, 0.0f, // Ambi W
736 0.0f, -N[0]*sqrt_3, N[1]*sqrt_3, -N[2]*sqrt_3, // Ambi X
737 0.0f, U[0]*sqrt_3, -U[1]*sqrt_3, U[2]*sqrt_3, // Ambi Y
738 0.0f, -V[0]*sqrt_3, V[1]*sqrt_3, -V[2]*sqrt_3 // Ambi Z
741 voice->Direct.Buffer = Device->FOAOut.Buffer;
742 voice->Direct.Channels = Device->FOAOut.NumChannels;
743 for(c = 0;c < num_channels;c++)
744 ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
745 voice->Direct.Params[c].Gains.Target);
746 for(i = 0;i < NumSends;i++)
748 const ALeffectslot *Slot = SendSlots[i];
749 if(Slot)
751 for(c = 0;c < num_channels;c++)
752 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
753 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
759 else if(DirectChannels)
761 /* Direct source channels always play local. Skip the virtual channels
762 * and write inputs to the matching real outputs.
764 voice->Direct.Buffer = Device->RealOut.Buffer;
765 voice->Direct.Channels = Device->RealOut.NumChannels;
767 for(c = 0;c < num_channels;c++)
769 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
770 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
773 /* Auxiliary sends still use normal channel panning since they mix to
774 * B-Format, which can't channel-match.
776 for(c = 0;c < num_channels;c++)
778 ALfloat coeffs[MAX_AMBI_COEFFS];
779 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
781 for(i = 0;i < NumSends;i++)
783 const ALeffectslot *Slot = SendSlots[i];
784 if(Slot)
785 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
786 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
791 else if(Device->Render_Mode == HrtfRender)
793 /* Full HRTF rendering. Skip the virtual channels and render to the
794 * real outputs.
796 voice->Direct.Buffer = Device->RealOut.Buffer;
797 voice->Direct.Channels = Device->RealOut.NumChannels;
799 if(Distance > FLT_EPSILON)
801 ALfloat coeffs[MAX_AMBI_COEFFS];
803 /* Get the HRIR coefficients and delays just once, for the given
804 * source direction.
806 GetHrtfCoeffs(Device->HrtfHandle, Elev, Azi, Spread,
807 voice->Direct.Params[0].Hrtf.Target.Coeffs,
808 voice->Direct.Params[0].Hrtf.Target.Delay);
809 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
811 /* Remaining channels use the same results as the first. */
812 for(c = 1;c < num_channels;c++)
814 /* Skip LFE */
815 if(chans[c].channel != LFE)
816 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
819 /* Calculate the directional coefficients once, which apply to all
820 * input channels of the source sends.
822 CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
824 for(i = 0;i < NumSends;i++)
826 const ALeffectslot *Slot = SendSlots[i];
827 if(Slot)
828 for(c = 0;c < num_channels;c++)
830 /* Skip LFE */
831 if(chans[c].channel != LFE)
832 ComputePanningGainsBF(Slot->ChanMap,
833 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
834 voice->Send[i].Params[c].Gains.Target
839 else
841 /* Local sources on HRTF play with each channel panned to its
842 * relative location around the listener, providing "virtual
843 * speaker" responses.
845 for(c = 0;c < num_channels;c++)
847 ALfloat coeffs[MAX_AMBI_COEFFS];
849 if(chans[c].channel == LFE)
851 /* Skip LFE */
852 continue;
855 /* Get the HRIR coefficients and delays for this channel
856 * position.
858 GetHrtfCoeffs(Device->HrtfHandle,
859 chans[c].elevation, chans[c].angle, Spread,
860 voice->Direct.Params[c].Hrtf.Target.Coeffs,
861 voice->Direct.Params[c].Hrtf.Target.Delay
863 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
865 /* Normal panning for auxiliary sends. */
866 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
868 for(i = 0;i < NumSends;i++)
870 const ALeffectslot *Slot = SendSlots[i];
871 if(Slot)
872 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
873 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
879 voice->Flags |= VOICE_HAS_HRTF;
881 else
883 /* Non-HRTF rendering. Use normal panning to the output. */
885 if(Distance > FLT_EPSILON)
887 ALfloat coeffs[MAX_AMBI_COEFFS];
888 ALfloat w0 = 0.0f;
890 /* Calculate NFC filter coefficient if needed. */
891 if(Device->AvgSpeakerDist > 0.0f)
893 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
894 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
895 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
896 w0 = SPEEDOFSOUNDMETRESPERSEC /
897 (mdist * (ALfloat)Device->Frequency);
898 /* Clamp w0 for really close distances, to prevent excessive
899 * bass.
901 w0 = minf(w0, w1*4.0f);
903 /* Adjust NFC filters. */
904 for(c = 0;c < num_channels;c++)
905 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
907 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
908 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
909 voice->Flags |= VOICE_HAS_NFC;
912 /* Calculate the directional coefficients once, which apply to all
913 * input channels.
915 if(Device->Render_Mode == StereoPair)
916 CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
917 else
918 CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
920 for(c = 0;c < num_channels;c++)
922 /* Special-case LFE */
923 if(chans[c].channel == LFE)
925 if(Device->Dry.Buffer == Device->RealOut.Buffer)
927 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
928 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
930 continue;
933 ComputeDryPanGains(&Device->Dry,
934 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
938 for(i = 0;i < NumSends;i++)
940 const ALeffectslot *Slot = SendSlots[i];
941 if(Slot)
942 for(c = 0;c < num_channels;c++)
944 /* Skip LFE */
945 if(chans[c].channel != LFE)
946 ComputePanningGainsBF(Slot->ChanMap,
947 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
948 voice->Send[i].Params[c].Gains.Target
953 else
955 ALfloat w0 = 0.0f;
957 if(Device->AvgSpeakerDist > 0.0f)
959 /* If the source distance is 0, set w0 to w1 to act as a pass-
960 * through. We still want to pass the signal through the
961 * filters so they keep an appropriate history, in case the
962 * source moves away from the listener.
964 w0 = SPEEDOFSOUNDMETRESPERSEC /
965 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
967 for(c = 0;c < num_channels;c++)
968 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
970 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
971 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
972 voice->Flags |= VOICE_HAS_NFC;
975 for(c = 0;c < num_channels;c++)
977 ALfloat coeffs[MAX_AMBI_COEFFS];
979 /* Special-case LFE */
980 if(chans[c].channel == LFE)
982 if(Device->Dry.Buffer == Device->RealOut.Buffer)
984 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
985 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
987 continue;
990 if(Device->Render_Mode == StereoPair)
991 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
992 else
993 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
994 ComputeDryPanGains(&Device->Dry,
995 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
998 for(i = 0;i < NumSends;i++)
1000 const ALeffectslot *Slot = SendSlots[i];
1001 if(Slot)
1002 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1003 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
1011 ALfloat hfScale = props->Direct.HFReference / Frequency;
1012 ALfloat lfScale = props->Direct.LFReference / Frequency;
1013 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
1014 ALfloat gainLF = maxf(DryGainLF, 0.001f);
1016 voice->Direct.FilterType = AF_None;
1017 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
1018 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
1019 BiquadFilter_setParams(
1020 &voice->Direct.Params[0].LowPass, BiquadType_HighShelf,
1021 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1023 BiquadFilter_setParams(
1024 &voice->Direct.Params[0].HighPass, BiquadType_LowShelf,
1025 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1027 for(c = 1;c < num_channels;c++)
1029 BiquadFilter_copyParams(&voice->Direct.Params[c].LowPass,
1030 &voice->Direct.Params[0].LowPass);
1031 BiquadFilter_copyParams(&voice->Direct.Params[c].HighPass,
1032 &voice->Direct.Params[0].HighPass);
1035 for(i = 0;i < NumSends;i++)
1037 ALfloat hfScale = props->Send[i].HFReference / Frequency;
1038 ALfloat lfScale = props->Send[i].LFReference / Frequency;
1039 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
1040 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
1042 voice->Send[i].FilterType = AF_None;
1043 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1044 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1045 BiquadFilter_setParams(
1046 &voice->Send[i].Params[0].LowPass, BiquadType_HighShelf,
1047 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1049 BiquadFilter_setParams(
1050 &voice->Send[i].Params[0].HighPass, BiquadType_LowShelf,
1051 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1053 for(c = 1;c < num_channels;c++)
1055 BiquadFilter_copyParams(&voice->Send[i].Params[c].LowPass,
1056 &voice->Send[i].Params[0].LowPass);
1057 BiquadFilter_copyParams(&voice->Send[i].Params[c].HighPass,
1058 &voice->Send[i].Params[0].HighPass);
1063 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1065 const ALCdevice *Device = ALContext->Device;
1066 const ALlistener *Listener = ALContext->Listener;
1067 ALfloat DryGain, DryGainHF, DryGainLF;
1068 ALfloat WetGain[MAX_SENDS];
1069 ALfloat WetGainHF[MAX_SENDS];
1070 ALfloat WetGainLF[MAX_SENDS];
1071 ALeffectslot *SendSlots[MAX_SENDS];
1072 ALfloat Pitch;
1073 ALsizei i;
1075 voice->Direct.Buffer = Device->Dry.Buffer;
1076 voice->Direct.Channels = Device->Dry.NumChannels;
1077 for(i = 0;i < Device->NumAuxSends;i++)
1079 SendSlots[i] = props->Send[i].Slot;
1080 if(!SendSlots[i] && i == 0)
1081 SendSlots[i] = ALContext->DefaultSlot;
1082 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1084 SendSlots[i] = NULL;
1085 voice->Send[i].Buffer = NULL;
1086 voice->Send[i].Channels = 0;
1088 else
1090 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1091 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1095 /* Calculate the stepping value */
1096 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1097 if(Pitch > (ALfloat)MAX_PITCH)
1098 voice->Step = MAX_PITCH<<FRACTIONBITS;
1099 else
1100 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1101 if(props->Resampler == BSinc24Resampler)
1102 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1103 else if(props->Resampler == BSinc12Resampler)
1104 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1105 voice->Resampler = SelectResampler(props->Resampler);
1107 /* Calculate gains */
1108 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1109 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1110 DryGain = minf(DryGain, GAIN_MIX_MAX);
1111 DryGainHF = props->Direct.GainHF;
1112 DryGainLF = props->Direct.GainLF;
1113 for(i = 0;i < Device->NumAuxSends;i++)
1115 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1116 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1117 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1118 WetGainHF[i] = props->Send[i].GainHF;
1119 WetGainLF[i] = props->Send[i].GainLF;
1122 CalcPanningAndFilters(voice, 0.0f, 0.0f, 0.0f, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1123 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1126 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1128 const ALCdevice *Device = ALContext->Device;
1129 const ALlistener *Listener = ALContext->Listener;
1130 const ALsizei NumSends = Device->NumAuxSends;
1131 aluVector Position, Velocity, Direction, SourceToListener;
1132 ALfloat Distance, ClampedDist, DopplerFactor;
1133 ALeffectslot *SendSlots[MAX_SENDS];
1134 ALfloat RoomRolloff[MAX_SENDS];
1135 ALfloat DecayDistance[MAX_SENDS];
1136 ALfloat DecayLFDistance[MAX_SENDS];
1137 ALfloat DecayHFDistance[MAX_SENDS];
1138 ALfloat DryGain, DryGainHF, DryGainLF;
1139 ALfloat WetGain[MAX_SENDS];
1140 ALfloat WetGainHF[MAX_SENDS];
1141 ALfloat WetGainLF[MAX_SENDS];
1142 bool directional;
1143 ALfloat ev, az;
1144 ALfloat spread;
1145 ALfloat Pitch;
1146 ALint i;
1148 /* Set mixing buffers and get send parameters. */
1149 voice->Direct.Buffer = Device->Dry.Buffer;
1150 voice->Direct.Channels = Device->Dry.NumChannels;
1151 for(i = 0;i < NumSends;i++)
1153 SendSlots[i] = props->Send[i].Slot;
1154 if(!SendSlots[i] && i == 0)
1155 SendSlots[i] = ALContext->DefaultSlot;
1156 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1158 SendSlots[i] = NULL;
1159 RoomRolloff[i] = 0.0f;
1160 DecayDistance[i] = 0.0f;
1161 DecayLFDistance[i] = 0.0f;
1162 DecayHFDistance[i] = 0.0f;
1164 else if(SendSlots[i]->Params.AuxSendAuto)
1166 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1167 /* Calculate the distances to where this effect's decay reaches
1168 * -60dB.
1170 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1171 Listener->Params.ReverbSpeedOfSound;
1172 DecayLFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayLFRatio;
1173 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1174 if(SendSlots[i]->Params.DecayHFLimit)
1176 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1177 if(airAbsorption < 1.0f)
1179 /* Calculate the distance to where this effect's air
1180 * absorption reaches -60dB, and limit the effect's HF
1181 * decay distance (so it doesn't take any longer to decay
1182 * than the air would allow).
1184 ALfloat absorb_dist = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1185 DecayHFDistance[i] = minf(absorb_dist, DecayHFDistance[i]);
1189 else
1191 /* If the slot's auxiliary send auto is off, the data sent to the
1192 * effect slot is the same as the dry path, sans filter effects */
1193 RoomRolloff[i] = props->RolloffFactor;
1194 DecayDistance[i] = 0.0f;
1195 DecayLFDistance[i] = 0.0f;
1196 DecayHFDistance[i] = 0.0f;
1199 if(!SendSlots[i])
1201 voice->Send[i].Buffer = NULL;
1202 voice->Send[i].Channels = 0;
1204 else
1206 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1207 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1211 /* Transform source to listener space (convert to head relative) */
1212 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1213 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1214 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1215 if(props->HeadRelative == AL_FALSE)
1217 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1218 /* Transform source vectors */
1219 Position = aluMatrixfVector(Matrix, &Position);
1220 Velocity = aluMatrixfVector(Matrix, &Velocity);
1221 Direction = aluMatrixfVector(Matrix, &Direction);
1223 else
1225 const aluVector *lvelocity = &Listener->Params.Velocity;
1226 /* Offset the source velocity to be relative of the listener velocity */
1227 Velocity.v[0] += lvelocity->v[0];
1228 Velocity.v[1] += lvelocity->v[1];
1229 Velocity.v[2] += lvelocity->v[2];
1232 directional = aluNormalize(Direction.v) > 0.0f;
1233 SourceToListener.v[0] = -Position.v[0];
1234 SourceToListener.v[1] = -Position.v[1];
1235 SourceToListener.v[2] = -Position.v[2];
1236 SourceToListener.v[3] = 0.0f;
1237 Distance = aluNormalize(SourceToListener.v);
1239 /* Initial source gain */
1240 DryGain = props->Gain;
1241 DryGainHF = 1.0f;
1242 DryGainLF = 1.0f;
1243 for(i = 0;i < NumSends;i++)
1245 WetGain[i] = props->Gain;
1246 WetGainHF[i] = 1.0f;
1247 WetGainLF[i] = 1.0f;
1250 /* Calculate distance attenuation */
1251 ClampedDist = Distance;
1253 switch(Listener->Params.SourceDistanceModel ?
1254 props->DistanceModel : Listener->Params.DistanceModel)
1256 case InverseDistanceClamped:
1257 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1258 if(props->MaxDistance < props->RefDistance)
1259 break;
1260 /*fall-through*/
1261 case InverseDistance:
1262 if(!(props->RefDistance > 0.0f))
1263 ClampedDist = props->RefDistance;
1264 else
1266 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1267 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1268 for(i = 0;i < NumSends;i++)
1270 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1271 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1274 break;
1276 case LinearDistanceClamped:
1277 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1278 if(props->MaxDistance < props->RefDistance)
1279 break;
1280 /*fall-through*/
1281 case LinearDistance:
1282 if(!(props->MaxDistance != props->RefDistance))
1283 ClampedDist = props->RefDistance;
1284 else
1286 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1287 (props->MaxDistance-props->RefDistance);
1288 DryGain *= maxf(1.0f - attn, 0.0f);
1289 for(i = 0;i < NumSends;i++)
1291 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1292 (props->MaxDistance-props->RefDistance);
1293 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1296 break;
1298 case ExponentDistanceClamped:
1299 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1300 if(props->MaxDistance < props->RefDistance)
1301 break;
1302 /*fall-through*/
1303 case ExponentDistance:
1304 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1305 ClampedDist = props->RefDistance;
1306 else
1308 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1309 for(i = 0;i < NumSends;i++)
1310 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1312 break;
1314 case DisableDistance:
1315 ClampedDist = props->RefDistance;
1316 break;
1319 /* Calculate directional soundcones */
1320 if(directional && props->InnerAngle < 360.0f)
1322 ALfloat ConeVolume;
1323 ALfloat ConeHF;
1324 ALfloat Angle;
1326 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1327 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1328 if(!(Angle > props->InnerAngle))
1330 ConeVolume = 1.0f;
1331 ConeHF = 1.0f;
1333 else if(Angle < props->OuterAngle)
1335 ALfloat scale = ( Angle-props->InnerAngle) /
1336 (props->OuterAngle-props->InnerAngle);
1337 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1338 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1340 else
1342 ConeVolume = props->OuterGain;
1343 ConeHF = props->OuterGainHF;
1346 DryGain *= ConeVolume;
1347 if(props->DryGainHFAuto)
1348 DryGainHF *= ConeHF;
1349 if(props->WetGainAuto)
1351 for(i = 0;i < NumSends;i++)
1352 WetGain[i] *= ConeVolume;
1354 if(props->WetGainHFAuto)
1356 for(i = 0;i < NumSends;i++)
1357 WetGainHF[i] *= ConeHF;
1361 /* Apply gain and frequency filters */
1362 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1363 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1364 DryGainHF *= props->Direct.GainHF;
1365 DryGainLF *= props->Direct.GainLF;
1366 for(i = 0;i < NumSends;i++)
1368 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1369 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1370 WetGainHF[i] *= props->Send[i].GainHF;
1371 WetGainLF[i] *= props->Send[i].GainLF;
1374 /* Distance-based air absorption and initial send decay. */
1375 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1377 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1378 Listener->Params.MetersPerUnit;
1379 if(props->AirAbsorptionFactor > 0.0f)
1381 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1382 DryGainHF *= hfattn;
1383 for(i = 0;i < NumSends;i++)
1384 WetGainHF[i] *= hfattn;
1387 if(props->WetGainAuto)
1389 /* Apply a decay-time transformation to the wet path, based on the
1390 * source distance in meters. The initial decay of the reverb
1391 * effect is calculated and applied to the wet path.
1393 for(i = 0;i < NumSends;i++)
1395 ALfloat gain, gainhf, gainlf;
1397 if(!(DecayDistance[i] > 0.0f))
1398 continue;
1400 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1401 WetGain[i] *= gain;
1402 /* Yes, the wet path's air absorption is applied with
1403 * WetGainAuto on, rather than WetGainHFAuto.
1405 if(gain > 0.0f)
1407 gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1408 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1409 gainlf = powf(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i]);
1410 WetGainLF[i] *= minf(gainlf / gain, 1.0f);
1417 /* Initial source pitch */
1418 Pitch = props->Pitch;
1420 /* Calculate velocity-based doppler effect */
1421 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1422 if(DopplerFactor > 0.0f)
1424 const aluVector *lvelocity = &Listener->Params.Velocity;
1425 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1426 ALfloat vss, vls;
1428 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1429 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1431 if(!(vls < SpeedOfSound))
1433 /* Listener moving away from the source at the speed of sound.
1434 * Sound waves can't catch it.
1436 Pitch = 0.0f;
1438 else if(!(vss < SpeedOfSound))
1440 /* Source moving toward the listener at the speed of sound. Sound
1441 * waves bunch up to extreme frequencies.
1443 Pitch = HUGE_VALF;
1445 else
1447 /* Source and listener movement is nominal. Calculate the proper
1448 * doppler shift.
1450 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1454 /* Adjust pitch based on the buffer and output frequencies, and calculate
1455 * fixed-point stepping value.
1457 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1458 if(Pitch > (ALfloat)MAX_PITCH)
1459 voice->Step = MAX_PITCH<<FRACTIONBITS;
1460 else
1461 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1462 if(props->Resampler == BSinc24Resampler)
1463 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1464 else if(props->Resampler == BSinc12Resampler)
1465 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1466 voice->Resampler = SelectResampler(props->Resampler);
1468 if(Distance > 0.0f)
1470 /* Clamp Y, in case rounding errors caused it to end up outside of
1471 * -1...+1.
1473 ev = asinf(clampf(-SourceToListener.v[1], -1.0f, 1.0f));
1474 /* Double negation on Z cancels out; negate once for changing source-
1475 * to-listener to listener-to-source, and again for right-handed coords
1476 * with -Z in front.
1478 az = atan2f(-SourceToListener.v[0], SourceToListener.v[2]*ZScale);
1480 else
1481 ev = az = 0.0f;
1483 if(props->Radius > Distance)
1484 spread = F_TAU - Distance/props->Radius*F_PI;
1485 else if(Distance > 0.0f)
1486 spread = asinf(props->Radius / Distance) * 2.0f;
1487 else
1488 spread = 0.0f;
1490 CalcPanningAndFilters(voice, az, ev, Distance, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1491 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1494 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1496 ALbufferlistitem *BufferListItem;
1497 struct ALvoiceProps *props;
1499 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1500 if(!props && !force) return;
1502 if(props)
1504 memcpy(voice->Props, props,
1505 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1508 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1510 props = voice->Props;
1512 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1513 while(BufferListItem != NULL)
1515 const ALbuffer *buffer = NULL;
1516 ALsizei i = 0;
1517 while(!buffer && i < BufferListItem->num_buffers)
1518 buffer = BufferListItem->buffers[i];
1519 if(LIKELY(buffer))
1521 if(props->SpatializeMode == SpatializeOn ||
1522 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1523 CalcAttnSourceParams(voice, props, buffer, context);
1524 else
1525 CalcNonAttnSourceParams(voice, props, buffer, context);
1526 break;
1528 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1533 static void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1535 ALvoice **voice, **voice_end;
1536 ALsource *source;
1537 ALsizei i;
1539 IncrementRef(&ctx->UpdateCount);
1540 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1542 bool cforce = CalcContextParams(ctx);
1543 bool force = CalcListenerParams(ctx) | cforce;
1544 for(i = 0;i < slots->count;i++)
1545 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1547 voice = ctx->Voices;
1548 voice_end = voice + ctx->VoiceCount;
1549 for(;voice != voice_end;++voice)
1551 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1552 if(source) CalcSourceParams(*voice, ctx, force);
1555 IncrementRef(&ctx->UpdateCount);
1559 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1560 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1561 ALsizei NumChannels)
1563 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1564 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1565 ALsizei i;
1567 /* Apply an all-pass to all channels, except the front-left and front-
1568 * right, so they maintain the same relative phase.
1570 for(i = 0;i < NumChannels;i++)
1572 if(i == lidx || i == ridx)
1573 continue;
1574 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1577 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1578 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1580 for(i = 0;i < SamplesToDo;i++)
1582 ALfloat lfsum, hfsum;
1583 ALfloat m, s, c;
1585 lfsum = lsplit[0][i] + rsplit[0][i];
1586 hfsum = lsplit[1][i] + rsplit[1][i];
1587 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1589 /* This pans the separate low- and high-frequency sums between being on
1590 * the center channel and the left/right channels. The low-frequency
1591 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1592 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1593 * values can be tweaked.
1595 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1596 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1598 /* The generated center channel signal adds to the existing signal,
1599 * while the modified left and right channels replace.
1601 Buffer[lidx][i] = (m + s) * 0.5f;
1602 Buffer[ridx][i] = (m - s) * 0.5f;
1603 Buffer[cidx][i] += c * 0.5f;
1607 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1608 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1610 ALsizei i, c;
1612 Values = ASSUME_ALIGNED(Values, 16);
1613 for(c = 0;c < numchans;c++)
1615 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1616 const ALfloat gain = distcomp[c].Gain;
1617 const ALsizei base = distcomp[c].Length;
1618 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1620 if(base == 0)
1622 if(gain < 1.0f)
1624 for(i = 0;i < SamplesToDo;i++)
1625 inout[i] *= gain;
1627 continue;
1630 if(SamplesToDo >= base)
1632 for(i = 0;i < base;i++)
1633 Values[i] = distbuf[i];
1634 for(;i < SamplesToDo;i++)
1635 Values[i] = inout[i-base];
1636 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1638 else
1640 for(i = 0;i < SamplesToDo;i++)
1641 Values[i] = distbuf[i];
1642 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1643 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1645 for(i = 0;i < SamplesToDo;i++)
1646 inout[i] = Values[i]*gain;
1650 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1651 const ALfloat quant_scale, const ALsizei SamplesToDo,
1652 const ALsizei numchans)
1654 const ALfloat invscale = 1.0f / quant_scale;
1655 ALuint seed = *dither_seed;
1656 ALsizei c, i;
1658 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1659 * values between -1 and +1). Step 2 is to add the noise to the samples,
1660 * before rounding and after scaling up to the desired quantization depth.
1662 for(c = 0;c < numchans;c++)
1664 ALfloat *restrict samples = Samples[c];
1665 for(i = 0;i < SamplesToDo;i++)
1667 ALfloat val = samples[i] * quant_scale;
1668 ALuint rng0 = dither_rng(&seed);
1669 ALuint rng1 = dither_rng(&seed);
1670 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1671 samples[i] = roundf(val) * invscale;
1674 *dither_seed = seed;
1678 static inline ALfloat Conv_ALfloat(ALfloat val)
1679 { return val; }
1680 static inline ALint Conv_ALint(ALfloat val)
1682 /* Floats have a 23-bit mantissa. A bit of the exponent helps out along
1683 * with the sign bit, giving 25 bits. So [-16777216, +16777216] is the max
1684 * integer range normalized floats can be converted to before losing
1685 * precision.
1687 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1689 static inline ALshort Conv_ALshort(ALfloat val)
1690 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1691 static inline ALbyte Conv_ALbyte(ALfloat val)
1692 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1694 /* Define unsigned output variations. */
1695 #define DECL_TEMPLATE(T, func, O) \
1696 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1698 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1699 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1700 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1702 #undef DECL_TEMPLATE
1704 #define DECL_TEMPLATE(T, A) \
1705 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1706 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1707 ALsizei numchans) \
1709 ALsizei i, j; \
1710 for(j = 0;j < numchans;j++) \
1712 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1713 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1715 for(i = 0;i < SamplesToDo;i++) \
1716 out[i*numchans] = Conv_##T(in[i]); \
1720 DECL_TEMPLATE(ALfloat, F32)
1721 DECL_TEMPLATE(ALuint, UI32)
1722 DECL_TEMPLATE(ALint, I32)
1723 DECL_TEMPLATE(ALushort, UI16)
1724 DECL_TEMPLATE(ALshort, I16)
1725 DECL_TEMPLATE(ALubyte, UI8)
1726 DECL_TEMPLATE(ALbyte, I8)
1728 #undef DECL_TEMPLATE
1731 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1733 ALsizei SamplesToDo;
1734 ALsizei SamplesDone;
1735 ALCcontext *ctx;
1736 ALsizei i, c;
1738 START_MIXER_MODE();
1739 for(SamplesDone = 0;SamplesDone < NumSamples;)
1741 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1742 for(c = 0;c < device->Dry.NumChannels;c++)
1743 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1744 if(device->Dry.Buffer != device->FOAOut.Buffer)
1745 for(c = 0;c < device->FOAOut.NumChannels;c++)
1746 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1747 if(device->Dry.Buffer != device->RealOut.Buffer)
1748 for(c = 0;c < device->RealOut.NumChannels;c++)
1749 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1751 IncrementRef(&device->MixCount);
1753 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1754 while(ctx)
1756 const struct ALeffectslotArray *auxslots;
1758 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1759 ProcessParamUpdates(ctx, auxslots);
1761 for(i = 0;i < auxslots->count;i++)
1763 ALeffectslot *slot = auxslots->slot[i];
1764 for(c = 0;c < slot->NumChannels;c++)
1765 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1768 /* source processing */
1769 for(i = 0;i < ctx->VoiceCount;i++)
1771 ALvoice *voice = ctx->Voices[i];
1772 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1773 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1774 voice->Step > 0)
1776 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1778 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1779 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1780 SendSourceStoppedEvent(ctx, source->id);
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 = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
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(device->Stablizer)
1814 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1815 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1816 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1817 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1819 ApplyStablizer(device->Stablizer, device->RealOut.Buffer, lidx, ridx, cidx,
1820 SamplesToDo, device->RealOut.NumChannels);
1823 ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
1824 SamplesToDo, device->RealOut.NumChannels);
1826 if(device->Limiter)
1827 ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
1828 device->RealOut.Buffer);
1830 if(device->DitherDepth > 0.0f)
1831 ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
1832 SamplesToDo, device->RealOut.NumChannels);
1834 if(LIKELY(OutBuffer))
1836 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1837 ALsizei Channels = device->RealOut.NumChannels;
1839 switch(device->FmtType)
1841 case DevFmtByte:
1842 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1843 break;
1844 case DevFmtUByte:
1845 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1846 break;
1847 case DevFmtShort:
1848 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1849 break;
1850 case DevFmtUShort:
1851 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1852 break;
1853 case DevFmtInt:
1854 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1855 break;
1856 case DevFmtUInt:
1857 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1858 break;
1859 case DevFmtFloat:
1860 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1861 break;
1865 SamplesDone += SamplesToDo;
1867 END_MIXER_MODE();
1871 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1873 ALCcontext *ctx;
1874 AsyncEvent evt;
1875 va_list args;
1876 int msglen;
1878 if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
1879 return;
1881 evt.EnumType = EventType_Disconnected;
1882 evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1883 evt.ObjectId = 0;
1884 evt.Param = 0;
1886 va_start(args, msg);
1887 msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
1888 va_end(args);
1890 if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
1892 evt.Message[sizeof(evt.Message)-1] = 0;
1893 msglen = (int)strlen(evt.Message);
1895 if(msglen > 0)
1896 msg = evt.Message;
1897 else
1899 msg = "<internal error constructing message>";
1900 msglen = (int)strlen(msg);
1903 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1904 while(ctx)
1906 ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire);
1907 ALsizei i;
1909 if((enabledevt&EventType_Disconnected) &&
1910 ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1) == 1)
1911 alsem_post(&ctx->EventSem);
1913 for(i = 0;i < ctx->VoiceCount;i++)
1915 ALvoice *voice = ctx->Voices[i];
1916 ALsource *source;
1918 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_relaxed);
1919 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed))
1921 /* If the source's voice was playing, it's now effectively
1922 * stopped (the source state will be updated the next time it's
1923 * checked).
1925 SendSourceStoppedEvent(ctx, source->id);
1927 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1930 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);