Condense an if check
[openal-soft.git] / Alc / ALu.c
blobfef50feb15bb2fc45310b44b1d67aa933503f286
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 "mixer/defs.h"
44 #include "fpu_modes.h"
45 #include "cpu_caps.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 static void ClearArray(ALfloat f[MAX_OUTPUT_CHANNELS])
112 size_t i;
113 for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
114 f[i] = 0.0f;
117 struct ChanMap {
118 enum Channel channel;
119 ALfloat angle;
120 ALfloat elevation;
123 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
126 void DeinitVoice(ALvoice *voice)
128 al_free(ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL));
132 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
134 #ifdef HAVE_NEON
135 if((CPUCapFlags&CPU_CAP_NEON))
136 return MixDirectHrtf_Neon;
137 #endif
138 #ifdef HAVE_SSE
139 if((CPUCapFlags&CPU_CAP_SSE))
140 return MixDirectHrtf_SSE;
141 #endif
143 return MixDirectHrtf_C;
147 /* Prior to VS2013, MSVC lacks the round() family of functions. */
148 #if defined(_MSC_VER) && _MSC_VER < 1800
149 static float roundf(float val)
151 if(val < 0.0f)
152 return ceilf(val-0.5f);
153 return floorf(val+0.5f);
155 #endif
157 /* This RNG method was created based on the math found in opusdec. It's quick,
158 * and starting with a seed value of 22222, is suitable for generating
159 * whitenoise.
161 static inline ALuint dither_rng(ALuint *seed)
163 *seed = (*seed * 96314165) + 907633515;
164 return *seed;
168 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
170 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
171 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
172 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
175 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
177 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
180 static ALfloat aluNormalize(ALfloat *vec)
182 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
183 if(length > 0.0f)
185 ALfloat inv_length = 1.0f/length;
186 vec[0] *= inv_length;
187 vec[1] *= inv_length;
188 vec[2] *= inv_length;
190 return length;
193 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
195 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
197 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];
198 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];
199 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];
202 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
204 aluVector v;
205 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];
206 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];
207 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];
208 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];
209 return v;
213 void aluInit(void)
215 MixDirectHrtf = SelectHrtfMixer();
219 static void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
221 ALbitfieldSOFT enabledevt;
222 AsyncEvent evt;
223 size_t strpos;
224 ALuint scale;
226 enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
227 if(!(enabledevt&EventType_SourceStateChange)) return;
229 evt.EnumType = EventType_SourceStateChange;
230 evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
231 evt.ObjectId = id;
232 evt.Param = AL_STOPPED;
234 /* Normally snprintf would be used, but this is called from the mixer and
235 * that function's not real-time safe, so we have to construct it manually.
237 strcpy(evt.Message, "Source ID "); strpos = 10;
238 scale = 1000000000;
239 while(scale > 0 && scale > id)
240 scale /= 10;
241 while(scale > 0)
243 evt.Message[strpos++] = '0' + ((id/scale)%10);
244 scale /= 10;
246 strcpy(evt.Message+strpos, " state changed to AL_STOPPED");
248 if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
249 alsem_post(&context->EventSem);
253 static void ProcessHrtf(ALCdevice *device, ALsizei SamplesToDo)
255 DirectHrtfState *state;
256 int lidx, ridx;
257 ALsizei c;
259 if(device->AmbiUp)
260 ambiup_process(device->AmbiUp,
261 device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer,
262 SamplesToDo
265 lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
266 ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
267 assert(lidx != -1 && ridx != -1);
269 state = device->Hrtf;
270 for(c = 0;c < device->Dry.NumChannels;c++)
272 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
273 device->Dry.Buffer[c], state->Offset, state->IrSize,
274 state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo
277 state->Offset += SamplesToDo;
280 static void ProcessAmbiDec(ALCdevice *device, ALsizei SamplesToDo)
282 if(device->Dry.Buffer != device->FOAOut.Buffer)
283 bformatdec_upSample(device->AmbiDecoder,
284 device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
285 SamplesToDo
287 bformatdec_process(device->AmbiDecoder,
288 device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
289 SamplesToDo
293 static void ProcessAmbiUp(ALCdevice *device, ALsizei SamplesToDo)
295 ambiup_process(device->AmbiUp,
296 device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer,
297 SamplesToDo
301 static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
303 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
304 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
305 if(LIKELY(lidx != -1 && ridx != -1))
307 /* Encode to stereo-compatible 2-channel UHJ output. */
308 EncodeUhj2(device->Uhj_Encoder,
309 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
310 device->Dry.Buffer, SamplesToDo
315 static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
317 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
318 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
319 if(LIKELY(lidx != -1 && ridx != -1))
321 /* Apply binaural/crossfeed filter */
322 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
323 device->RealOut.Buffer[ridx], SamplesToDo);
327 void aluSelectPostProcess(ALCdevice *device)
329 if(device->HrtfHandle)
330 device->PostProcess = ProcessHrtf;
331 else if(device->AmbiDecoder)
332 device->PostProcess = ProcessAmbiDec;
333 else if(device->AmbiUp)
334 device->PostProcess = ProcessAmbiUp;
335 else if(device->Uhj_Encoder)
336 device->PostProcess = ProcessUhj;
337 else if(device->Bs2b)
338 device->PostProcess = ProcessBs2b;
339 else
340 device->PostProcess = NULL;
344 /* Prepares the interpolator for a given rate (determined by increment). A
345 * result of AL_FALSE indicates that the filter output will completely cut
346 * the input signal.
348 * With a bit of work, and a trade of memory for CPU cost, this could be
349 * modified for use with an interpolated increment for buttery-smooth pitch
350 * changes.
352 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
354 ALfloat sf;
355 ALsizei si;
357 if(increment > FRACTIONONE)
359 sf = (ALfloat)FRACTIONONE / increment;
360 sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
361 si = fastf2i(sf);
362 /* The interpolation factor is fit to this diagonally-symmetric curve
363 * to reduce the transition ripple caused by interpolating different
364 * scales of the sinc function.
366 sf = 1.0f - cosf(asinf(sf - si));
368 else
370 sf = 0.0f;
371 si = BSINC_SCALE_COUNT - 1;
374 state->sf = sf;
375 state->m = table->m[si];
376 state->l = -((state->m/2) - 1);
377 state->filter = table->Tab + table->filterOffset[si];
381 static bool CalcContextParams(ALCcontext *Context)
383 ALlistener *Listener = Context->Listener;
384 struct ALcontextProps *props;
386 props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel);
387 if(!props) return false;
389 Listener->Params.MetersPerUnit = props->MetersPerUnit;
391 Listener->Params.DopplerFactor = props->DopplerFactor;
392 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
393 if(!OverrideReverbSpeedOfSound)
394 Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
395 Listener->Params.MetersPerUnit;
397 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
398 Listener->Params.DistanceModel = props->DistanceModel;
400 ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props);
401 return true;
404 static bool CalcListenerParams(ALCcontext *Context)
406 ALlistener *Listener = Context->Listener;
407 ALfloat N[3], V[3], U[3], P[3];
408 struct ALlistenerProps *props;
409 aluVector vel;
411 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
412 if(!props) return false;
414 /* AT then UP */
415 N[0] = props->Forward[0];
416 N[1] = props->Forward[1];
417 N[2] = props->Forward[2];
418 aluNormalize(N);
419 V[0] = props->Up[0];
420 V[1] = props->Up[1];
421 V[2] = props->Up[2];
422 aluNormalize(V);
423 /* Build and normalize right-vector */
424 aluCrossproduct(N, V, U);
425 aluNormalize(U);
427 aluMatrixfSet(&Listener->Params.Matrix,
428 U[0], V[0], -N[0], 0.0,
429 U[1], V[1], -N[1], 0.0,
430 U[2], V[2], -N[2], 0.0,
431 0.0, 0.0, 0.0, 1.0
434 P[0] = props->Position[0];
435 P[1] = props->Position[1];
436 P[2] = props->Position[2];
437 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
438 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
440 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
441 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
443 Listener->Params.Gain = props->Gain * Context->GainBoost;
445 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props);
446 return true;
449 static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
451 struct ALeffectslotProps *props;
452 ALeffectState *state;
454 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
455 if(!props && !force) return false;
457 if(props)
459 slot->Params.Gain = props->Gain;
460 slot->Params.AuxSendAuto = props->AuxSendAuto;
461 slot->Params.EffectType = props->Type;
462 slot->Params.EffectProps = props->Props;
463 if(IsReverbEffect(props->Type))
465 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
466 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
467 slot->Params.DecayLFRatio = props->Props.Reverb.DecayLFRatio;
468 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
469 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
470 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
472 else
474 slot->Params.RoomRolloff = 0.0f;
475 slot->Params.DecayTime = 0.0f;
476 slot->Params.DecayLFRatio = 0.0f;
477 slot->Params.DecayHFRatio = 0.0f;
478 slot->Params.DecayHFLimit = AL_FALSE;
479 slot->Params.AirAbsorptionGainHF = 1.0f;
482 /* Swap effect states. No need to play with the ref counts since they
483 * keep the same number of refs.
485 state = props->State;
486 props->State = slot->Params.EffectState;
487 slot->Params.EffectState = state;
489 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
491 else
492 state = slot->Params.EffectState;
494 V(state,update)(context, slot, &slot->Params.EffectProps);
495 return true;
499 static const struct ChanMap MonoMap[1] = {
500 { FrontCenter, 0.0f, 0.0f }
501 }, RearMap[2] = {
502 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
503 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
504 }, QuadMap[4] = {
505 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
506 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
507 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
508 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
509 }, X51Map[6] = {
510 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
511 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
512 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
513 { LFE, 0.0f, 0.0f },
514 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
515 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
516 }, X61Map[7] = {
517 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
518 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
519 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
520 { LFE, 0.0f, 0.0f },
521 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
522 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
523 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
524 }, X71Map[8] = {
525 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
526 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
527 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
528 { LFE, 0.0f, 0.0f },
529 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
530 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
531 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
532 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
535 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
536 const ALfloat Spread, const ALfloat DryGain,
537 const ALfloat DryGainHF, const ALfloat DryGainLF,
538 const ALfloat *WetGain, const ALfloat *WetGainLF,
539 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
540 const ALbuffer *Buffer, const struct ALvoiceProps *props,
541 const ALlistener *Listener, const ALCdevice *Device)
543 struct ChanMap StereoMap[2] = {
544 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
545 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
547 bool DirectChannels = props->DirectChannels;
548 const ALsizei NumSends = Device->NumAuxSends;
549 const ALuint Frequency = Device->Frequency;
550 const struct ChanMap *chans = NULL;
551 ALsizei num_channels = 0;
552 bool isbformat = false;
553 ALfloat downmix_gain = 1.0f;
554 ALsizei c, i;
556 switch(Buffer->FmtChannels)
558 case FmtMono:
559 chans = MonoMap;
560 num_channels = 1;
561 /* Mono buffers are never played direct. */
562 DirectChannels = false;
563 break;
565 case FmtStereo:
566 /* Convert counter-clockwise to clockwise. */
567 StereoMap[0].angle = -props->StereoPan[0];
568 StereoMap[1].angle = -props->StereoPan[1];
570 chans = StereoMap;
571 num_channels = 2;
572 downmix_gain = 1.0f / 2.0f;
573 break;
575 case FmtRear:
576 chans = RearMap;
577 num_channels = 2;
578 downmix_gain = 1.0f / 2.0f;
579 break;
581 case FmtQuad:
582 chans = QuadMap;
583 num_channels = 4;
584 downmix_gain = 1.0f / 4.0f;
585 break;
587 case FmtX51:
588 chans = X51Map;
589 num_channels = 6;
590 /* NOTE: Excludes LFE. */
591 downmix_gain = 1.0f / 5.0f;
592 break;
594 case FmtX61:
595 chans = X61Map;
596 num_channels = 7;
597 /* NOTE: Excludes LFE. */
598 downmix_gain = 1.0f / 6.0f;
599 break;
601 case FmtX71:
602 chans = X71Map;
603 num_channels = 8;
604 /* NOTE: Excludes LFE. */
605 downmix_gain = 1.0f / 7.0f;
606 break;
608 case FmtBFormat2D:
609 num_channels = 3;
610 isbformat = true;
611 DirectChannels = false;
612 break;
614 case FmtBFormat3D:
615 num_channels = 4;
616 isbformat = true;
617 DirectChannels = false;
618 break;
621 for(c = 0;c < num_channels;c++)
623 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
624 sizeof(voice->Direct.Params[c].Hrtf.Target));
625 ClearArray(voice->Direct.Params[c].Gains.Target);
627 for(i = 0;i < NumSends;i++)
629 for(c = 0;c < num_channels;c++)
630 ClearArray(voice->Send[i].Params[c].Gains.Target);
633 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
634 if(isbformat)
636 /* Special handling for B-Format sources. */
638 if(Distance > FLT_EPSILON)
640 /* Panning a B-Format sound toward some direction is easy. Just pan
641 * the first (W) channel as a normal mono sound and silence the
642 * others.
644 ALfloat coeffs[MAX_AMBI_COEFFS];
646 if(Device->AvgSpeakerDist > 0.0f)
648 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
649 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
650 (mdist * (ALfloat)Device->Frequency);
651 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
652 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
653 /* Clamp w0 for really close distances, to prevent excessive
654 * bass.
656 w0 = minf(w0, w1*4.0f);
658 /* Only need to adjust the first channel of a B-Format source. */
659 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
661 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
662 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
663 voice->Flags |= VOICE_HAS_NFC;
666 if(Device->Render_Mode == StereoPair)
668 ALfloat ev = asinf(Dir[1]);
669 ALfloat az = atan2f(Dir[0], -Dir[2]);
670 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
672 else
673 CalcDirectionCoeffs(Dir, Spread, coeffs);
675 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
676 ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
677 voice->Direct.Params[0].Gains.Target);
678 for(i = 0;i < NumSends;i++)
680 const ALeffectslot *Slot = SendSlots[i];
681 if(Slot)
682 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
683 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
687 else
689 /* Local B-Format sources have their XYZ channels rotated according
690 * to the orientation.
692 const ALfloat sqrt_2 = sqrtf(2.0f);
693 const ALfloat sqrt_3 = sqrtf(3.0f);
694 ALfloat N[3], V[3], U[3];
695 aluMatrixf matrix;
697 if(Device->AvgSpeakerDist > 0.0f)
699 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
700 * is what we want for FOA input. The first channel may have
701 * been previously re-adjusted if panned, so reset it.
703 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, 0.0f);
705 voice->Direct.ChannelsPerOrder[0] = 1;
706 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
707 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
708 voice->Direct.ChannelsPerOrder[i] = 0;
709 voice->Flags |= VOICE_HAS_NFC;
712 /* AT then UP */
713 N[0] = props->Orientation[0][0];
714 N[1] = props->Orientation[0][1];
715 N[2] = props->Orientation[0][2];
716 aluNormalize(N);
717 V[0] = props->Orientation[1][0];
718 V[1] = props->Orientation[1][1];
719 V[2] = props->Orientation[1][2];
720 aluNormalize(V);
721 if(!props->HeadRelative)
723 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
724 aluMatrixfFloat3(N, 0.0f, lmatrix);
725 aluMatrixfFloat3(V, 0.0f, lmatrix);
727 /* Build and normalize right-vector */
728 aluCrossproduct(N, V, U);
729 aluNormalize(U);
731 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). NOTE: This
732 * matrix is transposed, for the inputs to align on the rows and
733 * outputs on the columns.
735 aluMatrixfSet(&matrix,
736 // ACN0 ACN1 ACN2 ACN3
737 sqrt_2, 0.0f, 0.0f, 0.0f, // Ambi W
738 0.0f, -N[0]*sqrt_3, N[1]*sqrt_3, -N[2]*sqrt_3, // Ambi X
739 0.0f, U[0]*sqrt_3, -U[1]*sqrt_3, U[2]*sqrt_3, // Ambi Y
740 0.0f, -V[0]*sqrt_3, V[1]*sqrt_3, -V[2]*sqrt_3 // Ambi Z
743 voice->Direct.Buffer = Device->FOAOut.Buffer;
744 voice->Direct.Channels = Device->FOAOut.NumChannels;
745 for(c = 0;c < num_channels;c++)
746 ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
747 voice->Direct.Params[c].Gains.Target);
748 for(i = 0;i < NumSends;i++)
750 const ALeffectslot *Slot = SendSlots[i];
751 if(Slot)
753 for(c = 0;c < num_channels;c++)
754 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
755 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
761 else if(DirectChannels)
763 /* Direct source channels always play local. Skip the virtual channels
764 * and write inputs to the matching real outputs.
766 voice->Direct.Buffer = Device->RealOut.Buffer;
767 voice->Direct.Channels = Device->RealOut.NumChannels;
769 for(c = 0;c < num_channels;c++)
771 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
772 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
775 /* Auxiliary sends still use normal channel panning since they mix to
776 * B-Format, which can't channel-match.
778 for(c = 0;c < num_channels;c++)
780 ALfloat coeffs[MAX_AMBI_COEFFS];
781 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
783 for(i = 0;i < NumSends;i++)
785 const ALeffectslot *Slot = SendSlots[i];
786 if(Slot)
787 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
788 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
793 else if(Device->Render_Mode == HrtfRender)
795 /* Full HRTF rendering. Skip the virtual channels and render to the
796 * real outputs.
798 voice->Direct.Buffer = Device->RealOut.Buffer;
799 voice->Direct.Channels = Device->RealOut.NumChannels;
801 if(Distance > FLT_EPSILON)
803 ALfloat coeffs[MAX_AMBI_COEFFS];
804 ALfloat ev, az;
806 ev = asinf(Dir[1]);
807 az = atan2f(Dir[0], -Dir[2]);
809 /* Get the HRIR coefficients and delays just once, for the given
810 * source direction.
812 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
813 voice->Direct.Params[0].Hrtf.Target.Coeffs,
814 voice->Direct.Params[0].Hrtf.Target.Delay);
815 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
817 /* Remaining channels use the same results as the first. */
818 for(c = 1;c < num_channels;c++)
820 /* Skip LFE */
821 if(chans[c].channel != LFE)
822 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
825 /* Calculate the directional coefficients once, which apply to all
826 * input channels of the source sends.
828 CalcDirectionCoeffs(Dir, Spread, coeffs);
830 for(i = 0;i < NumSends;i++)
832 const ALeffectslot *Slot = SendSlots[i];
833 if(Slot)
834 for(c = 0;c < num_channels;c++)
836 /* Skip LFE */
837 if(chans[c].channel != LFE)
838 ComputePanningGainsBF(Slot->ChanMap,
839 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
840 voice->Send[i].Params[c].Gains.Target
845 else
847 /* Local sources on HRTF play with each channel panned to its
848 * relative location around the listener, providing "virtual
849 * speaker" responses.
851 for(c = 0;c < num_channels;c++)
853 ALfloat coeffs[MAX_AMBI_COEFFS];
855 if(chans[c].channel == LFE)
857 /* Skip LFE */
858 continue;
861 /* Get the HRIR coefficients and delays for this channel
862 * position.
864 GetHrtfCoeffs(Device->HrtfHandle,
865 chans[c].elevation, chans[c].angle, Spread,
866 voice->Direct.Params[c].Hrtf.Target.Coeffs,
867 voice->Direct.Params[c].Hrtf.Target.Delay
869 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
871 /* Normal panning for auxiliary sends. */
872 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
874 for(i = 0;i < NumSends;i++)
876 const ALeffectslot *Slot = SendSlots[i];
877 if(Slot)
878 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
879 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
885 voice->Flags |= VOICE_HAS_HRTF;
887 else
889 /* Non-HRTF rendering. Use normal panning to the output. */
891 if(Distance > FLT_EPSILON)
893 ALfloat coeffs[MAX_AMBI_COEFFS];
894 ALfloat w0 = 0.0f;
896 /* Calculate NFC filter coefficient if needed. */
897 if(Device->AvgSpeakerDist > 0.0f)
899 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
900 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
901 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
902 w0 = SPEEDOFSOUNDMETRESPERSEC /
903 (mdist * (ALfloat)Device->Frequency);
904 /* Clamp w0 for really close distances, to prevent excessive
905 * bass.
907 w0 = minf(w0, w1*4.0f);
909 /* Adjust NFC filters. */
910 for(c = 0;c < num_channels;c++)
911 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
913 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
914 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
915 voice->Flags |= VOICE_HAS_NFC;
918 /* Calculate the directional coefficients once, which apply to all
919 * input channels.
921 if(Device->Render_Mode == StereoPair)
923 ALfloat ev = asinf(Dir[1]);
924 ALfloat az = atan2f(Dir[0], -Dir[2]);
925 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
927 else
928 CalcDirectionCoeffs(Dir, Spread, coeffs);
930 for(c = 0;c < num_channels;c++)
932 /* Special-case LFE */
933 if(chans[c].channel == LFE)
935 if(Device->Dry.Buffer == Device->RealOut.Buffer)
937 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
938 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
940 continue;
943 ComputeDryPanGains(&Device->Dry,
944 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
948 for(i = 0;i < NumSends;i++)
950 const ALeffectslot *Slot = SendSlots[i];
951 if(Slot)
952 for(c = 0;c < num_channels;c++)
954 /* Skip LFE */
955 if(chans[c].channel != LFE)
956 ComputePanningGainsBF(Slot->ChanMap,
957 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
958 voice->Send[i].Params[c].Gains.Target
963 else
965 ALfloat w0 = 0.0f;
967 if(Device->AvgSpeakerDist > 0.0f)
969 /* If the source distance is 0, set w0 to w1 to act as a pass-
970 * through. We still want to pass the signal through the
971 * filters so they keep an appropriate history, in case the
972 * source moves away from the listener.
974 w0 = SPEEDOFSOUNDMETRESPERSEC /
975 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
977 for(c = 0;c < num_channels;c++)
978 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
980 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
981 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
982 voice->Flags |= VOICE_HAS_NFC;
985 for(c = 0;c < num_channels;c++)
987 ALfloat coeffs[MAX_AMBI_COEFFS];
989 /* Special-case LFE */
990 if(chans[c].channel == LFE)
992 if(Device->Dry.Buffer == Device->RealOut.Buffer)
994 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
995 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
997 continue;
1000 if(Device->Render_Mode == StereoPair)
1001 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
1002 else
1003 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
1004 ComputeDryPanGains(&Device->Dry,
1005 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
1008 for(i = 0;i < NumSends;i++)
1010 const ALeffectslot *Slot = SendSlots[i];
1011 if(Slot)
1012 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1013 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
1021 ALfloat hfScale = props->Direct.HFReference / Frequency;
1022 ALfloat lfScale = props->Direct.LFReference / Frequency;
1023 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
1024 ALfloat gainLF = maxf(DryGainLF, 0.001f);
1026 voice->Direct.FilterType = AF_None;
1027 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
1028 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
1029 BiquadState_setParams(
1030 &voice->Direct.Params[0].LowPass, BiquadType_HighShelf,
1031 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1033 BiquadState_setParams(
1034 &voice->Direct.Params[0].HighPass, BiquadType_LowShelf,
1035 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1037 for(c = 1;c < num_channels;c++)
1039 BiquadState_copyParams(&voice->Direct.Params[c].LowPass,
1040 &voice->Direct.Params[0].LowPass);
1041 BiquadState_copyParams(&voice->Direct.Params[c].HighPass,
1042 &voice->Direct.Params[0].HighPass);
1045 for(i = 0;i < NumSends;i++)
1047 ALfloat hfScale = props->Send[i].HFReference / Frequency;
1048 ALfloat lfScale = props->Send[i].LFReference / Frequency;
1049 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
1050 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
1052 voice->Send[i].FilterType = AF_None;
1053 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1054 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1055 BiquadState_setParams(
1056 &voice->Send[i].Params[0].LowPass, BiquadType_HighShelf,
1057 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1059 BiquadState_setParams(
1060 &voice->Send[i].Params[0].HighPass, BiquadType_LowShelf,
1061 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1063 for(c = 1;c < num_channels;c++)
1065 BiquadState_copyParams(&voice->Send[i].Params[c].LowPass,
1066 &voice->Send[i].Params[0].LowPass);
1067 BiquadState_copyParams(&voice->Send[i].Params[c].HighPass,
1068 &voice->Send[i].Params[0].HighPass);
1073 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1075 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1076 const ALCdevice *Device = ALContext->Device;
1077 const ALlistener *Listener = ALContext->Listener;
1078 ALfloat DryGain, DryGainHF, DryGainLF;
1079 ALfloat WetGain[MAX_SENDS];
1080 ALfloat WetGainHF[MAX_SENDS];
1081 ALfloat WetGainLF[MAX_SENDS];
1082 ALeffectslot *SendSlots[MAX_SENDS];
1083 ALfloat Pitch;
1084 ALsizei i;
1086 voice->Direct.Buffer = Device->Dry.Buffer;
1087 voice->Direct.Channels = Device->Dry.NumChannels;
1088 for(i = 0;i < Device->NumAuxSends;i++)
1090 SendSlots[i] = props->Send[i].Slot;
1091 if(!SendSlots[i] && i == 0)
1092 SendSlots[i] = ALContext->DefaultSlot;
1093 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1095 SendSlots[i] = NULL;
1096 voice->Send[i].Buffer = NULL;
1097 voice->Send[i].Channels = 0;
1099 else
1101 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1102 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1106 /* Calculate the stepping value */
1107 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1108 if(Pitch > (ALfloat)MAX_PITCH)
1109 voice->Step = MAX_PITCH<<FRACTIONBITS;
1110 else
1111 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1112 if(props->Resampler == BSinc24Resampler)
1113 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1114 else if(props->Resampler == BSinc12Resampler)
1115 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1116 voice->Resampler = SelectResampler(props->Resampler);
1118 /* Calculate gains */
1119 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1120 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1121 DryGain = minf(DryGain, GAIN_MIX_MAX);
1122 DryGainHF = props->Direct.GainHF;
1123 DryGainLF = props->Direct.GainLF;
1124 for(i = 0;i < Device->NumAuxSends;i++)
1126 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1127 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1128 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1129 WetGainHF[i] = props->Send[i].GainHF;
1130 WetGainLF[i] = props->Send[i].GainLF;
1133 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1134 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1137 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1139 const ALCdevice *Device = ALContext->Device;
1140 const ALlistener *Listener = ALContext->Listener;
1141 const ALsizei NumSends = Device->NumAuxSends;
1142 aluVector Position, Velocity, Direction, SourceToListener;
1143 ALfloat Distance, ClampedDist, DopplerFactor;
1144 ALeffectslot *SendSlots[MAX_SENDS];
1145 ALfloat RoomRolloff[MAX_SENDS];
1146 ALfloat DecayDistance[MAX_SENDS];
1147 ALfloat DecayLFDistance[MAX_SENDS];
1148 ALfloat DecayHFDistance[MAX_SENDS];
1149 ALfloat DryGain, DryGainHF, DryGainLF;
1150 ALfloat WetGain[MAX_SENDS];
1151 ALfloat WetGainHF[MAX_SENDS];
1152 ALfloat WetGainLF[MAX_SENDS];
1153 bool directional;
1154 ALfloat dir[3];
1155 ALfloat spread;
1156 ALfloat Pitch;
1157 ALint i;
1159 /* Set mixing buffers and get send parameters. */
1160 voice->Direct.Buffer = Device->Dry.Buffer;
1161 voice->Direct.Channels = Device->Dry.NumChannels;
1162 for(i = 0;i < NumSends;i++)
1164 SendSlots[i] = props->Send[i].Slot;
1165 if(!SendSlots[i] && i == 0)
1166 SendSlots[i] = ALContext->DefaultSlot;
1167 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1169 SendSlots[i] = NULL;
1170 RoomRolloff[i] = 0.0f;
1171 DecayDistance[i] = 0.0f;
1172 DecayLFDistance[i] = 0.0f;
1173 DecayHFDistance[i] = 0.0f;
1175 else if(SendSlots[i]->Params.AuxSendAuto)
1177 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1178 /* Calculate the distances to where this effect's decay reaches
1179 * -60dB.
1181 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1182 Listener->Params.ReverbSpeedOfSound;
1183 DecayLFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayLFRatio;
1184 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1185 if(SendSlots[i]->Params.DecayHFLimit)
1187 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1188 if(airAbsorption < 1.0f)
1190 /* Calculate the distance to where this effect's air
1191 * absorption reaches -60dB, and limit the effect's HF
1192 * decay distance (so it doesn't take any longer to decay
1193 * than the air would allow).
1195 ALfloat absorb_dist = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1196 DecayHFDistance[i] = minf(absorb_dist, DecayHFDistance[i]);
1200 else
1202 /* If the slot's auxiliary send auto is off, the data sent to the
1203 * effect slot is the same as the dry path, sans filter effects */
1204 RoomRolloff[i] = props->RolloffFactor;
1205 DecayDistance[i] = 0.0f;
1206 DecayLFDistance[i] = 0.0f;
1207 DecayHFDistance[i] = 0.0f;
1210 if(!SendSlots[i])
1212 voice->Send[i].Buffer = NULL;
1213 voice->Send[i].Channels = 0;
1215 else
1217 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1218 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1222 /* Transform source to listener space (convert to head relative) */
1223 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1224 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1225 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1226 if(props->HeadRelative == AL_FALSE)
1228 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1229 /* Transform source vectors */
1230 Position = aluMatrixfVector(Matrix, &Position);
1231 Velocity = aluMatrixfVector(Matrix, &Velocity);
1232 Direction = aluMatrixfVector(Matrix, &Direction);
1234 else
1236 const aluVector *lvelocity = &Listener->Params.Velocity;
1237 /* Offset the source velocity to be relative of the listener velocity */
1238 Velocity.v[0] += lvelocity->v[0];
1239 Velocity.v[1] += lvelocity->v[1];
1240 Velocity.v[2] += lvelocity->v[2];
1243 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1244 SourceToListener.v[0] = -Position.v[0];
1245 SourceToListener.v[1] = -Position.v[1];
1246 SourceToListener.v[2] = -Position.v[2];
1247 SourceToListener.v[3] = 0.0f;
1248 Distance = aluNormalize(SourceToListener.v);
1250 /* Initial source gain */
1251 DryGain = props->Gain;
1252 DryGainHF = 1.0f;
1253 DryGainLF = 1.0f;
1254 for(i = 0;i < NumSends;i++)
1256 WetGain[i] = props->Gain;
1257 WetGainHF[i] = 1.0f;
1258 WetGainLF[i] = 1.0f;
1261 /* Calculate distance attenuation */
1262 ClampedDist = Distance;
1264 switch(Listener->Params.SourceDistanceModel ?
1265 props->DistanceModel : Listener->Params.DistanceModel)
1267 case InverseDistanceClamped:
1268 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1269 if(props->MaxDistance < props->RefDistance)
1270 break;
1271 /*fall-through*/
1272 case InverseDistance:
1273 if(!(props->RefDistance > 0.0f))
1274 ClampedDist = props->RefDistance;
1275 else
1277 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1278 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1279 for(i = 0;i < NumSends;i++)
1281 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1282 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1285 break;
1287 case LinearDistanceClamped:
1288 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1289 if(props->MaxDistance < props->RefDistance)
1290 break;
1291 /*fall-through*/
1292 case LinearDistance:
1293 if(!(props->MaxDistance != props->RefDistance))
1294 ClampedDist = props->RefDistance;
1295 else
1297 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1298 (props->MaxDistance-props->RefDistance);
1299 DryGain *= maxf(1.0f - attn, 0.0f);
1300 for(i = 0;i < NumSends;i++)
1302 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1303 (props->MaxDistance-props->RefDistance);
1304 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1307 break;
1309 case ExponentDistanceClamped:
1310 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1311 if(props->MaxDistance < props->RefDistance)
1312 break;
1313 /*fall-through*/
1314 case ExponentDistance:
1315 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1316 ClampedDist = props->RefDistance;
1317 else
1319 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1320 for(i = 0;i < NumSends;i++)
1321 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1323 break;
1325 case DisableDistance:
1326 ClampedDist = props->RefDistance;
1327 break;
1330 /* Distance-based air absorption */
1331 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1333 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1334 Listener->Params.MetersPerUnit;
1335 if(props->AirAbsorptionFactor > 0.0f)
1337 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1338 DryGainHF *= hfattn;
1339 for(i = 0;i < NumSends;i++)
1340 WetGainHF[i] *= hfattn;
1343 if(props->WetGainAuto)
1345 /* Apply a decay-time transformation to the wet path, based on the
1346 * source distance in meters. The initial decay of the reverb
1347 * effect is calculated and applied to the wet path.
1349 for(i = 0;i < NumSends;i++)
1351 ALfloat gain, gainhf, gainlf;
1353 if(!(DecayDistance[i] > 0.0f))
1354 continue;
1356 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1357 WetGain[i] *= gain;
1358 /* Yes, the wet path's air absorption is applied with
1359 * WetGainAuto on, rather than WetGainHFAuto.
1361 if(gain > 0.0f)
1363 gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1364 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1365 gainlf = powf(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i]);
1366 WetGainLF[i] *= minf(gainlf / gain, 1.0f);
1372 /* Calculate directional soundcones */
1373 if(directional && props->InnerAngle < 360.0f)
1375 ALfloat ConeVolume;
1376 ALfloat ConeHF;
1377 ALfloat Angle;
1379 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1380 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1381 if(!(Angle > props->InnerAngle))
1383 ConeVolume = 1.0f;
1384 ConeHF = 1.0f;
1386 else if(Angle < props->OuterAngle)
1388 ALfloat scale = ( Angle-props->InnerAngle) /
1389 (props->OuterAngle-props->InnerAngle);
1390 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1391 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1393 else
1395 ConeVolume = props->OuterGain;
1396 ConeHF = props->OuterGainHF;
1399 DryGain *= ConeVolume;
1400 if(props->DryGainHFAuto)
1401 DryGainHF *= ConeHF;
1402 if(props->WetGainAuto)
1404 for(i = 0;i < NumSends;i++)
1405 WetGain[i] *= ConeVolume;
1407 if(props->WetGainHFAuto)
1409 for(i = 0;i < NumSends;i++)
1410 WetGainHF[i] *= ConeHF;
1414 /* Apply gain and frequency filters */
1415 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1416 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1417 DryGainHF *= props->Direct.GainHF;
1418 DryGainLF *= props->Direct.GainLF;
1419 for(i = 0;i < NumSends;i++)
1421 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1422 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1423 WetGainHF[i] *= props->Send[i].GainHF;
1424 WetGainLF[i] *= props->Send[i].GainLF;
1428 /* Initial source pitch */
1429 Pitch = props->Pitch;
1431 /* Calculate velocity-based doppler effect */
1432 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1433 if(DopplerFactor > 0.0f)
1435 const aluVector *lvelocity = &Listener->Params.Velocity;
1436 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1437 ALfloat vss, vls;
1439 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1440 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1442 if(!(vls < SpeedOfSound))
1444 /* Listener moving away from the source at the speed of sound.
1445 * Sound waves can't catch it.
1447 Pitch = 0.0f;
1449 else if(!(vss < SpeedOfSound))
1451 /* Source moving toward the listener at the speed of sound. Sound
1452 * waves bunch up to extreme frequencies.
1454 Pitch = HUGE_VALF;
1456 else
1458 /* Source and listener movement is nominal. Calculate the proper
1459 * doppler shift.
1461 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1465 /* Adjust pitch based on the buffer and output frequencies, and calculate
1466 * fixed-point stepping value.
1468 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1469 if(Pitch > (ALfloat)MAX_PITCH)
1470 voice->Step = MAX_PITCH<<FRACTIONBITS;
1471 else
1472 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1473 if(props->Resampler == BSinc24Resampler)
1474 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1475 else if(props->Resampler == BSinc12Resampler)
1476 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1477 voice->Resampler = SelectResampler(props->Resampler);
1479 if(Distance > FLT_EPSILON)
1481 dir[0] = -SourceToListener.v[0];
1482 /* Clamp Y, in case rounding errors caused it to end up outside of
1483 * -1...+1.
1485 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1486 dir[2] = -SourceToListener.v[2] * ZScale;
1488 else
1490 dir[0] = 0.0f;
1491 dir[1] = 0.0f;
1492 dir[2] = -1.0f;
1494 if(props->Radius > Distance)
1495 spread = F_TAU - Distance/props->Radius*F_PI;
1496 else if(Distance > FLT_EPSILON)
1497 spread = asinf(props->Radius / Distance) * 2.0f;
1498 else
1499 spread = 0.0f;
1501 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1502 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1505 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1507 ALbufferlistitem *BufferListItem;
1508 struct ALvoiceProps *props;
1510 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1511 if(!props && !force) return;
1513 if(props)
1515 memcpy(voice->Props, props,
1516 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1519 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1521 props = voice->Props;
1523 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1524 while(BufferListItem != NULL)
1526 const ALbuffer *buffer;
1527 if(BufferListItem->num_buffers >= 1 && (buffer=BufferListItem->buffers[0]) != NULL)
1529 if(props->SpatializeMode == SpatializeOn ||
1530 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1531 CalcAttnSourceParams(voice, props, buffer, context);
1532 else
1533 CalcNonAttnSourceParams(voice, props, buffer, context);
1534 break;
1536 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1541 static void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1543 ALvoice **voice, **voice_end;
1544 ALsource *source;
1545 ALsizei i;
1547 IncrementRef(&ctx->UpdateCount);
1548 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1550 bool cforce = CalcContextParams(ctx);
1551 bool force = CalcListenerParams(ctx) | cforce;
1552 for(i = 0;i < slots->count;i++)
1553 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1555 voice = ctx->Voices;
1556 voice_end = voice + ctx->VoiceCount;
1557 for(;voice != voice_end;++voice)
1559 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1560 if(source) CalcSourceParams(*voice, ctx, force);
1563 IncrementRef(&ctx->UpdateCount);
1567 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1568 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1569 ALsizei NumChannels)
1571 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1572 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1573 ALsizei i;
1575 /* Apply an all-pass to all channels, except the front-left and front-
1576 * right, so they maintain the same relative phase.
1578 for(i = 0;i < NumChannels;i++)
1580 if(i == lidx || i == ridx)
1581 continue;
1582 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1585 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1586 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1588 for(i = 0;i < SamplesToDo;i++)
1590 ALfloat lfsum, hfsum;
1591 ALfloat m, s, c;
1593 lfsum = lsplit[0][i] + rsplit[0][i];
1594 hfsum = lsplit[1][i] + rsplit[1][i];
1595 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1597 /* This pans the separate low- and high-frequency sums between being on
1598 * the center channel and the left/right channels. The low-frequency
1599 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1600 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1601 * values can be tweaked.
1603 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1604 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1606 /* The generated center channel signal adds to the existing signal,
1607 * while the modified left and right channels replace.
1609 Buffer[lidx][i] = (m + s) * 0.5f;
1610 Buffer[ridx][i] = (m - s) * 0.5f;
1611 Buffer[cidx][i] += c * 0.5f;
1615 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1616 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1618 ALsizei i, c;
1620 Values = ASSUME_ALIGNED(Values, 16);
1621 for(c = 0;c < numchans;c++)
1623 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1624 const ALfloat gain = distcomp[c].Gain;
1625 const ALsizei base = distcomp[c].Length;
1626 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1628 if(base == 0)
1630 if(gain < 1.0f)
1632 for(i = 0;i < SamplesToDo;i++)
1633 inout[i] *= gain;
1635 continue;
1638 if(SamplesToDo >= base)
1640 for(i = 0;i < base;i++)
1641 Values[i] = distbuf[i];
1642 for(;i < SamplesToDo;i++)
1643 Values[i] = inout[i-base];
1644 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1646 else
1648 for(i = 0;i < SamplesToDo;i++)
1649 Values[i] = distbuf[i];
1650 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1651 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1653 for(i = 0;i < SamplesToDo;i++)
1654 inout[i] = Values[i]*gain;
1658 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1659 const ALfloat quant_scale, const ALsizei SamplesToDo,
1660 const ALsizei numchans)
1662 const ALfloat invscale = 1.0f / quant_scale;
1663 ALuint seed = *dither_seed;
1664 ALsizei c, i;
1666 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1667 * values between -1 and +1). Step 2 is to add the noise to the samples,
1668 * before rounding and after scaling up to the desired quantization depth.
1670 for(c = 0;c < numchans;c++)
1672 ALfloat *restrict samples = Samples[c];
1673 for(i = 0;i < SamplesToDo;i++)
1675 ALfloat val = samples[i] * quant_scale;
1676 ALuint rng0 = dither_rng(&seed);
1677 ALuint rng1 = dither_rng(&seed);
1678 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1679 samples[i] = roundf(val) * invscale;
1682 *dither_seed = seed;
1686 static inline ALfloat Conv_ALfloat(ALfloat val)
1687 { return val; }
1688 static inline ALint Conv_ALint(ALfloat val)
1690 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1691 * integer range normalized floats can be safely converted to (a bit of the
1692 * exponent helps out, effectively giving 25 bits).
1694 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1696 static inline ALshort Conv_ALshort(ALfloat val)
1697 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1698 static inline ALbyte Conv_ALbyte(ALfloat val)
1699 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1701 /* Define unsigned output variations. */
1702 #define DECL_TEMPLATE(T, func, O) \
1703 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1705 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1706 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1707 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1709 #undef DECL_TEMPLATE
1711 #define DECL_TEMPLATE(T, A) \
1712 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1713 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1714 ALsizei numchans) \
1716 ALsizei i, j; \
1717 for(j = 0;j < numchans;j++) \
1719 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1720 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1722 for(i = 0;i < SamplesToDo;i++) \
1723 out[i*numchans] = Conv_##T(in[i]); \
1727 DECL_TEMPLATE(ALfloat, F32)
1728 DECL_TEMPLATE(ALuint, UI32)
1729 DECL_TEMPLATE(ALint, I32)
1730 DECL_TEMPLATE(ALushort, UI16)
1731 DECL_TEMPLATE(ALshort, I16)
1732 DECL_TEMPLATE(ALubyte, UI8)
1733 DECL_TEMPLATE(ALbyte, I8)
1735 #undef DECL_TEMPLATE
1738 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1740 ALsizei SamplesToDo;
1741 ALsizei SamplesDone;
1742 ALCcontext *ctx;
1743 ALsizei i, c;
1745 START_MIXER_MODE();
1746 for(SamplesDone = 0;SamplesDone < NumSamples;)
1748 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1749 for(c = 0;c < device->Dry.NumChannels;c++)
1750 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1751 if(device->Dry.Buffer != device->FOAOut.Buffer)
1752 for(c = 0;c < device->FOAOut.NumChannels;c++)
1753 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1754 if(device->Dry.Buffer != device->RealOut.Buffer)
1755 for(c = 0;c < device->RealOut.NumChannels;c++)
1756 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1758 IncrementRef(&device->MixCount);
1760 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1761 while(ctx)
1763 const struct ALeffectslotArray *auxslots;
1765 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1766 ProcessParamUpdates(ctx, auxslots);
1768 for(i = 0;i < auxslots->count;i++)
1770 ALeffectslot *slot = auxslots->slot[i];
1771 for(c = 0;c < slot->NumChannels;c++)
1772 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1775 /* source processing */
1776 for(i = 0;i < ctx->VoiceCount;i++)
1778 ALvoice *voice = ctx->Voices[i];
1779 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1780 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1781 voice->Step > 0)
1783 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1785 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1786 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1787 SendSourceStoppedEvent(ctx, source->id);
1792 /* effect slot processing */
1793 for(i = 0;i < auxslots->count;i++)
1795 const ALeffectslot *slot = auxslots->slot[i];
1796 ALeffectState *state = slot->Params.EffectState;
1797 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1798 state->OutChannels);
1801 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
1804 /* Increment the clock time. Every second's worth of samples is
1805 * converted and added to clock base so that large sample counts don't
1806 * overflow during conversion. This also guarantees an exact, stable
1807 * conversion. */
1808 device->SamplesDone += SamplesToDo;
1809 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1810 device->SamplesDone %= device->Frequency;
1811 IncrementRef(&device->MixCount);
1813 /* Apply post-process for finalizing the Dry mix to the RealOut
1814 * (Ambisonic decode, UHJ encode, etc).
1816 if(LIKELY(device->PostProcess))
1817 device->PostProcess(device, SamplesToDo);
1819 if(device->Stablizer)
1821 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1822 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1823 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1824 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1826 ApplyStablizer(device->Stablizer, device->RealOut.Buffer, lidx, ridx, cidx,
1827 SamplesToDo, device->RealOut.NumChannels);
1830 ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
1831 SamplesToDo, device->RealOut.NumChannels);
1833 if(device->Limiter)
1834 ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
1835 device->RealOut.Buffer);
1837 if(device->DitherDepth > 0.0f)
1838 ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
1839 SamplesToDo, device->RealOut.NumChannels);
1841 if(OutBuffer)
1843 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1844 ALsizei Channels = device->RealOut.NumChannels;
1846 switch(device->FmtType)
1848 case DevFmtByte:
1849 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1850 break;
1851 case DevFmtUByte:
1852 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1853 break;
1854 case DevFmtShort:
1855 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1856 break;
1857 case DevFmtUShort:
1858 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1859 break;
1860 case DevFmtInt:
1861 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1862 break;
1863 case DevFmtUInt:
1864 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1865 break;
1866 case DevFmtFloat:
1867 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1868 break;
1872 SamplesDone += SamplesToDo;
1874 END_MIXER_MODE();
1878 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1880 ALCcontext *ctx;
1881 AsyncEvent evt;
1882 va_list args;
1883 int msglen;
1885 if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
1886 return;
1888 evt.EnumType = EventType_Disconnected;
1889 evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1890 evt.ObjectId = 0;
1891 evt.Param = 0;
1893 va_start(args, msg);
1894 msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
1895 va_end(args);
1897 if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
1899 evt.Message[sizeof(evt.Message)-1] = 0;
1900 msglen = (int)strlen(evt.Message);
1902 if(msglen > 0)
1903 msg = evt.Message;
1904 else
1906 msg = "<internal error constructing message>";
1907 msglen = (int)strlen(msg);
1910 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1911 while(ctx)
1913 ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire);
1914 ALsizei i;
1916 if((enabledevt&EventType_Disconnected) &&
1917 ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1) == 1)
1918 alsem_post(&ctx->EventSem);
1920 for(i = 0;i < ctx->VoiceCount;i++)
1922 ALvoice *voice = ctx->Voices[i];
1923 ALsource *source;
1925 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_relaxed);
1926 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed))
1928 /* If the source's voice was playing, it's now effectively
1929 * stopped (the source state will be updated the next time it's
1930 * checked).
1932 SendSourceStoppedEvent(ctx, source->id);
1934 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1937 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);