Annotate the B-Format rotation/conversion matrix
[openal-soft.git] / Alc / ALu.c
blobc670fa5ebafd86a65d3a7c727963e91643ac037f
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 struct ChanMap {
111 enum Channel channel;
112 ALfloat angle;
113 ALfloat elevation;
116 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
119 void DeinitVoice(ALvoice *voice)
121 al_free(ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL));
125 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
127 #ifdef HAVE_NEON
128 if((CPUCapFlags&CPU_CAP_NEON))
129 return MixDirectHrtf_Neon;
130 #endif
131 #ifdef HAVE_SSE
132 if((CPUCapFlags&CPU_CAP_SSE))
133 return MixDirectHrtf_SSE;
134 #endif
136 return MixDirectHrtf_C;
140 /* Prior to VS2013, MSVC lacks the round() family of functions. */
141 #if defined(_MSC_VER) && _MSC_VER < 1800
142 static float roundf(float val)
144 if(val < 0.0f)
145 return ceilf(val-0.5f);
146 return floorf(val+0.5f);
148 #endif
150 /* This RNG method was created based on the math found in opusdec. It's quick,
151 * and starting with a seed value of 22222, is suitable for generating
152 * whitenoise.
154 static inline ALuint dither_rng(ALuint *seed)
156 *seed = (*seed * 96314165) + 907633515;
157 return *seed;
161 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
163 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
164 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
165 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
168 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
170 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
173 static ALfloat aluNormalize(ALfloat *vec)
175 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
176 if(length > 0.0f)
178 ALfloat inv_length = 1.0f/length;
179 vec[0] *= inv_length;
180 vec[1] *= inv_length;
181 vec[2] *= inv_length;
183 return length;
186 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
188 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
190 vec[0] = v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0];
191 vec[1] = v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1];
192 vec[2] = v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2];
195 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
197 aluVector v;
198 v.v[0] = vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0];
199 v.v[1] = vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1];
200 v.v[2] = vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2];
201 v.v[3] = vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3];
202 return v;
206 void aluInit(void)
208 MixDirectHrtf = SelectHrtfMixer();
212 static void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
214 ALbitfieldSOFT enabledevt;
215 AsyncEvent evt;
216 size_t strpos;
217 ALuint scale;
219 enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
220 if(!(enabledevt&EventType_SourceStateChange)) return;
222 evt.EnumType = EventType_SourceStateChange;
223 evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
224 evt.ObjectId = id;
225 evt.Param = AL_STOPPED;
227 /* Normally snprintf would be used, but this is called from the mixer and
228 * that function's not real-time safe, so we have to construct it manually.
230 strcpy(evt.Message, "Source ID "); strpos = 10;
231 scale = 1000000000;
232 while(scale > 0 && scale > id)
233 scale /= 10;
234 while(scale > 0)
236 evt.Message[strpos++] = '0' + ((id/scale)%10);
237 scale /= 10;
239 strcpy(evt.Message+strpos, " state changed to AL_STOPPED");
241 if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
242 alsem_post(&context->EventSem);
246 static void ProcessHrtf(ALCdevice *device, ALsizei SamplesToDo)
248 DirectHrtfState *state;
249 int lidx, ridx;
250 ALsizei c;
252 if(device->AmbiUp)
253 ambiup_process(device->AmbiUp,
254 device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer,
255 SamplesToDo
258 lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
259 ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
260 assert(lidx != -1 && ridx != -1);
262 state = device->Hrtf;
263 for(c = 0;c < device->Dry.NumChannels;c++)
265 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
266 device->Dry.Buffer[c], state->Offset, state->IrSize,
267 state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo
270 state->Offset += SamplesToDo;
273 static void ProcessAmbiDec(ALCdevice *device, ALsizei SamplesToDo)
275 if(device->Dry.Buffer != device->FOAOut.Buffer)
276 bformatdec_upSample(device->AmbiDecoder,
277 device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
278 SamplesToDo
280 bformatdec_process(device->AmbiDecoder,
281 device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
282 SamplesToDo
286 static void ProcessAmbiUp(ALCdevice *device, ALsizei SamplesToDo)
288 ambiup_process(device->AmbiUp,
289 device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer,
290 SamplesToDo
294 static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
296 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
297 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
298 if(LIKELY(lidx != -1 && ridx != -1))
300 /* Encode to stereo-compatible 2-channel UHJ output. */
301 EncodeUhj2(device->Uhj_Encoder,
302 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
303 device->Dry.Buffer, SamplesToDo
308 static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
310 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
311 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
312 if(LIKELY(lidx != -1 && ridx != -1))
314 /* Apply binaural/crossfeed filter */
315 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
316 device->RealOut.Buffer[ridx], SamplesToDo);
320 void aluSelectPostProcess(ALCdevice *device)
322 if(device->HrtfHandle)
323 device->PostProcess = ProcessHrtf;
324 else if(device->AmbiDecoder)
325 device->PostProcess = ProcessAmbiDec;
326 else if(device->AmbiUp)
327 device->PostProcess = ProcessAmbiUp;
328 else if(device->Uhj_Encoder)
329 device->PostProcess = ProcessUhj;
330 else if(device->Bs2b)
331 device->PostProcess = ProcessBs2b;
332 else
333 device->PostProcess = NULL;
337 /* Prepares the interpolator for a given rate (determined by increment). A
338 * result of AL_FALSE indicates that the filter output will completely cut
339 * the input signal.
341 * With a bit of work, and a trade of memory for CPU cost, this could be
342 * modified for use with an interpolated increment for buttery-smooth pitch
343 * changes.
345 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
347 ALfloat sf;
348 ALsizei si;
350 if(increment > FRACTIONONE)
352 sf = (ALfloat)FRACTIONONE / increment;
353 sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
354 si = fastf2i(sf);
355 /* The interpolation factor is fit to this diagonally-symmetric curve
356 * to reduce the transition ripple caused by interpolating different
357 * scales of the sinc function.
359 sf = 1.0f - cosf(asinf(sf - si));
361 else
363 sf = 0.0f;
364 si = BSINC_SCALE_COUNT - 1;
367 state->sf = sf;
368 state->m = table->m[si];
369 state->l = -((state->m/2) - 1);
370 state->filter = table->Tab + table->filterOffset[si];
374 static bool CalcContextParams(ALCcontext *Context)
376 ALlistener *Listener = Context->Listener;
377 struct ALcontextProps *props;
379 props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel);
380 if(!props) return false;
382 Listener->Params.MetersPerUnit = props->MetersPerUnit;
384 Listener->Params.DopplerFactor = props->DopplerFactor;
385 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
386 if(!OverrideReverbSpeedOfSound)
387 Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
388 Listener->Params.MetersPerUnit;
390 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
391 Listener->Params.DistanceModel = props->DistanceModel;
393 ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props);
394 return true;
397 static bool CalcListenerParams(ALCcontext *Context)
399 ALlistener *Listener = Context->Listener;
400 ALfloat N[3], V[3], U[3], P[3];
401 struct ALlistenerProps *props;
402 aluVector vel;
404 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
405 if(!props) return false;
407 /* AT then UP */
408 N[0] = props->Forward[0];
409 N[1] = props->Forward[1];
410 N[2] = props->Forward[2];
411 aluNormalize(N);
412 V[0] = props->Up[0];
413 V[1] = props->Up[1];
414 V[2] = props->Up[2];
415 aluNormalize(V);
416 /* Build and normalize right-vector */
417 aluCrossproduct(N, V, U);
418 aluNormalize(U);
420 aluMatrixfSet(&Listener->Params.Matrix,
421 U[0], V[0], -N[0], 0.0,
422 U[1], V[1], -N[1], 0.0,
423 U[2], V[2], -N[2], 0.0,
424 0.0, 0.0, 0.0, 1.0
427 P[0] = props->Position[0];
428 P[1] = props->Position[1];
429 P[2] = props->Position[2];
430 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
431 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
433 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
434 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
436 Listener->Params.Gain = props->Gain * Context->GainBoost;
438 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props);
439 return true;
442 static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
444 struct ALeffectslotProps *props;
445 ALeffectState *state;
447 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
448 if(!props && !force) return false;
450 if(props)
452 slot->Params.Gain = props->Gain;
453 slot->Params.AuxSendAuto = props->AuxSendAuto;
454 slot->Params.EffectType = props->Type;
455 slot->Params.EffectProps = props->Props;
456 if(IsReverbEffect(props->Type))
458 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
459 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
460 slot->Params.DecayLFRatio = props->Props.Reverb.DecayLFRatio;
461 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
462 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
463 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
465 else
467 slot->Params.RoomRolloff = 0.0f;
468 slot->Params.DecayTime = 0.0f;
469 slot->Params.DecayLFRatio = 0.0f;
470 slot->Params.DecayHFRatio = 0.0f;
471 slot->Params.DecayHFLimit = AL_FALSE;
472 slot->Params.AirAbsorptionGainHF = 1.0f;
475 /* Swap effect states. No need to play with the ref counts since they
476 * keep the same number of refs.
478 state = props->State;
479 props->State = slot->Params.EffectState;
480 slot->Params.EffectState = state;
482 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
484 else
485 state = slot->Params.EffectState;
487 V(state,update)(context, slot, &slot->Params.EffectProps);
488 return true;
492 static const struct ChanMap MonoMap[1] = {
493 { FrontCenter, 0.0f, 0.0f }
494 }, RearMap[2] = {
495 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
496 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
497 }, QuadMap[4] = {
498 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
499 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
500 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
501 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
502 }, X51Map[6] = {
503 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
504 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
505 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
506 { LFE, 0.0f, 0.0f },
507 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
508 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
509 }, X61Map[7] = {
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 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
515 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
516 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
517 }, X71Map[8] = {
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 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
523 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
524 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
525 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
528 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
529 const ALfloat Spread, const ALfloat DryGain,
530 const ALfloat DryGainHF, const ALfloat DryGainLF,
531 const ALfloat *WetGain, const ALfloat *WetGainLF,
532 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
533 const ALbuffer *Buffer, const struct ALvoiceProps *props,
534 const ALlistener *Listener, const ALCdevice *Device)
536 struct ChanMap StereoMap[2] = {
537 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
538 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
540 bool DirectChannels = props->DirectChannels;
541 const ALsizei NumSends = Device->NumAuxSends;
542 const ALuint Frequency = Device->Frequency;
543 const struct ChanMap *chans = NULL;
544 ALsizei num_channels = 0;
545 bool isbformat = false;
546 ALfloat downmix_gain = 1.0f;
547 ALsizei c, i, j;
549 switch(Buffer->FmtChannels)
551 case FmtMono:
552 chans = MonoMap;
553 num_channels = 1;
554 /* Mono buffers are never played direct. */
555 DirectChannels = false;
556 break;
558 case FmtStereo:
559 /* Convert counter-clockwise to clockwise. */
560 StereoMap[0].angle = -props->StereoPan[0];
561 StereoMap[1].angle = -props->StereoPan[1];
563 chans = StereoMap;
564 num_channels = 2;
565 downmix_gain = 1.0f / 2.0f;
566 break;
568 case FmtRear:
569 chans = RearMap;
570 num_channels = 2;
571 downmix_gain = 1.0f / 2.0f;
572 break;
574 case FmtQuad:
575 chans = QuadMap;
576 num_channels = 4;
577 downmix_gain = 1.0f / 4.0f;
578 break;
580 case FmtX51:
581 chans = X51Map;
582 num_channels = 6;
583 /* NOTE: Excludes LFE. */
584 downmix_gain = 1.0f / 5.0f;
585 break;
587 case FmtX61:
588 chans = X61Map;
589 num_channels = 7;
590 /* NOTE: Excludes LFE. */
591 downmix_gain = 1.0f / 6.0f;
592 break;
594 case FmtX71:
595 chans = X71Map;
596 num_channels = 8;
597 /* NOTE: Excludes LFE. */
598 downmix_gain = 1.0f / 7.0f;
599 break;
601 case FmtBFormat2D:
602 num_channels = 3;
603 isbformat = true;
604 DirectChannels = false;
605 break;
607 case FmtBFormat3D:
608 num_channels = 4;
609 isbformat = true;
610 DirectChannels = false;
611 break;
614 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
615 if(isbformat)
617 /* Special handling for B-Format sources. */
619 if(Distance > FLT_EPSILON)
621 /* Panning a B-Format sound toward some direction is easy. Just pan
622 * the first (W) channel as a normal mono sound and silence the
623 * others.
625 ALfloat coeffs[MAX_AMBI_COEFFS];
627 if(Device->AvgSpeakerDist > 0.0f)
629 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
630 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
631 (mdist * (ALfloat)Device->Frequency);
632 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
633 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
634 /* Clamp w0 for really close distances, to prevent excessive
635 * bass.
637 w0 = minf(w0, w1*4.0f);
639 /* Only need to adjust the first channel of a B-Format source. */
640 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
642 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
643 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
644 voice->Flags |= VOICE_HAS_NFC;
647 if(Device->Render_Mode == StereoPair)
649 ALfloat ev = asinf(Dir[1]);
650 ALfloat az = atan2f(Dir[0], -Dir[2]);
651 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
653 else
654 CalcDirectionCoeffs(Dir, Spread, coeffs);
656 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
657 ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
658 voice->Direct.Params[0].Gains.Target);
659 for(c = 1;c < num_channels;c++)
661 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
662 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
665 for(i = 0;i < NumSends;i++)
667 const ALeffectslot *Slot = SendSlots[i];
668 if(Slot)
669 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
670 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
672 else
673 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
674 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
675 for(c = 1;c < num_channels;c++)
677 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
678 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
682 else
684 /* Local B-Format sources have their XYZ channels rotated according
685 * to the orientation.
687 const ALfloat sqrt_2 = sqrtf(2.0f);
688 const ALfloat sqrt_3 = sqrtf(3.0f);
689 ALfloat N[3], V[3], U[3];
690 aluMatrixf matrix;
692 if(Device->AvgSpeakerDist > 0.0f)
694 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
695 * is what we want for FOA input. The first channel may have
696 * been previously re-adjusted if panned, so reset it.
698 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, 0.0f);
700 voice->Direct.ChannelsPerOrder[0] = 1;
701 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
702 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
703 voice->Direct.ChannelsPerOrder[i] = 0;
704 voice->Flags |= VOICE_HAS_NFC;
707 /* AT then UP */
708 N[0] = props->Orientation[0][0];
709 N[1] = props->Orientation[0][1];
710 N[2] = props->Orientation[0][2];
711 aluNormalize(N);
712 V[0] = props->Orientation[1][0];
713 V[1] = props->Orientation[1][1];
714 V[2] = props->Orientation[1][2];
715 aluNormalize(V);
716 if(!props->HeadRelative)
718 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
719 aluMatrixfFloat3(N, 0.0f, lmatrix);
720 aluMatrixfFloat3(V, 0.0f, lmatrix);
722 /* Build and normalize right-vector */
723 aluCrossproduct(N, V, U);
724 aluNormalize(U);
726 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). NOTE: This
727 * matrix is transposed, for the inputs to align on the rows and
728 * outputs on the columns.
730 aluMatrixfSet(&matrix,
731 // ACN0 ACN1 ACN2 ACN3
732 sqrt_2, 0.0f, 0.0f, 0.0f, // Ambi W
733 0.0f, -N[0]*sqrt_3, N[1]*sqrt_3, -N[2]*sqrt_3, // Ambi X
734 0.0f, U[0]*sqrt_3, -U[1]*sqrt_3, U[2]*sqrt_3, // Ambi Y
735 0.0f, -V[0]*sqrt_3, V[1]*sqrt_3, -V[2]*sqrt_3 // Ambi Z
738 voice->Direct.Buffer = Device->FOAOut.Buffer;
739 voice->Direct.Channels = Device->FOAOut.NumChannels;
740 for(c = 0;c < num_channels;c++)
741 ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
742 voice->Direct.Params[c].Gains.Target);
743 for(i = 0;i < NumSends;i++)
745 const ALeffectslot *Slot = SendSlots[i];
746 if(Slot)
748 for(c = 0;c < num_channels;c++)
749 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
750 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
753 else
755 for(c = 0;c < num_channels;c++)
756 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
757 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
762 else if(DirectChannels)
764 /* Direct source channels always play local. Skip the virtual channels
765 * and write inputs to the matching real outputs.
767 voice->Direct.Buffer = Device->RealOut.Buffer;
768 voice->Direct.Channels = Device->RealOut.NumChannels;
770 for(c = 0;c < num_channels;c++)
772 int idx;
773 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
774 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
775 if((idx=GetChannelIdxByName(&Device->RealOut, chans[c].channel)) != -1)
776 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
779 /* Auxiliary sends still use normal channel panning since they mix to
780 * B-Format, which can't channel-match.
782 for(c = 0;c < num_channels;c++)
784 ALfloat coeffs[MAX_AMBI_COEFFS];
785 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
787 for(i = 0;i < NumSends;i++)
789 const ALeffectslot *Slot = SendSlots[i];
790 if(Slot)
791 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
792 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
794 else
795 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
796 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
800 else if(Device->Render_Mode == HrtfRender)
802 /* Full HRTF rendering. Skip the virtual channels and render to the
803 * real outputs.
805 voice->Direct.Buffer = Device->RealOut.Buffer;
806 voice->Direct.Channels = Device->RealOut.NumChannels;
808 if(Distance > FLT_EPSILON)
810 ALfloat coeffs[MAX_AMBI_COEFFS];
811 ALfloat ev, az;
813 ev = asinf(Dir[1]);
814 az = atan2f(Dir[0], -Dir[2]);
816 /* Get the HRIR coefficients and delays just once, for the given
817 * source direction.
819 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
820 voice->Direct.Params[0].Hrtf.Target.Coeffs,
821 voice->Direct.Params[0].Hrtf.Target.Delay);
822 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
824 /* Remaining channels use the same results as the first. */
825 for(c = 1;c < num_channels;c++)
827 /* Skip LFE */
828 if(chans[c].channel == LFE)
829 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
830 sizeof(voice->Direct.Params[c].Hrtf.Target));
831 else
832 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
835 /* Calculate the directional coefficients once, which apply to all
836 * input channels of the source sends.
838 CalcDirectionCoeffs(Dir, Spread, coeffs);
840 for(i = 0;i < NumSends;i++)
842 const ALeffectslot *Slot = SendSlots[i];
843 if(Slot)
844 for(c = 0;c < num_channels;c++)
846 /* Skip LFE */
847 if(chans[c].channel == LFE)
848 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
849 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
850 else
851 ComputePanningGainsBF(Slot->ChanMap,
852 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
853 voice->Send[i].Params[c].Gains.Target
856 else
857 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
858 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
861 else
863 /* Local sources on HRTF play with each channel panned to its
864 * relative location around the listener, providing "virtual
865 * speaker" responses.
867 for(c = 0;c < num_channels;c++)
869 ALfloat coeffs[MAX_AMBI_COEFFS];
871 if(chans[c].channel == LFE)
873 /* Skip LFE */
874 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
875 sizeof(voice->Direct.Params[c].Hrtf.Target));
876 for(i = 0;i < NumSends;i++)
878 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
879 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
881 continue;
884 /* Get the HRIR coefficients and delays for this channel
885 * position.
887 GetHrtfCoeffs(Device->HrtfHandle,
888 chans[c].elevation, chans[c].angle, Spread,
889 voice->Direct.Params[c].Hrtf.Target.Coeffs,
890 voice->Direct.Params[c].Hrtf.Target.Delay
892 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
894 /* Normal panning for auxiliary sends. */
895 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
897 for(i = 0;i < NumSends;i++)
899 const ALeffectslot *Slot = SendSlots[i];
900 if(Slot)
901 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
902 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
904 else
905 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
906 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
911 voice->Flags |= VOICE_HAS_HRTF;
913 else
915 /* Non-HRTF rendering. Use normal panning to the output. */
917 if(Distance > FLT_EPSILON)
919 ALfloat coeffs[MAX_AMBI_COEFFS];
920 ALfloat w0 = 0.0f;
922 /* Calculate NFC filter coefficient if needed. */
923 if(Device->AvgSpeakerDist > 0.0f)
925 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
926 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
927 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
928 w0 = SPEEDOFSOUNDMETRESPERSEC /
929 (mdist * (ALfloat)Device->Frequency);
930 /* Clamp w0 for really close distances, to prevent excessive
931 * bass.
933 w0 = minf(w0, w1*4.0f);
935 /* Adjust NFC filters. */
936 for(c = 0;c < num_channels;c++)
937 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
939 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
940 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
941 voice->Flags |= VOICE_HAS_NFC;
944 /* Calculate the directional coefficients once, which apply to all
945 * input channels.
947 if(Device->Render_Mode == StereoPair)
949 ALfloat ev = asinf(Dir[1]);
950 ALfloat az = atan2f(Dir[0], -Dir[2]);
951 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
953 else
954 CalcDirectionCoeffs(Dir, Spread, coeffs);
956 for(c = 0;c < num_channels;c++)
958 /* Special-case LFE */
959 if(chans[c].channel == LFE)
961 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
962 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
963 if(Device->Dry.Buffer == Device->RealOut.Buffer)
965 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
966 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
968 continue;
971 ComputeDryPanGains(&Device->Dry,
972 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
976 for(i = 0;i < NumSends;i++)
978 const ALeffectslot *Slot = SendSlots[i];
979 if(Slot)
980 for(c = 0;c < num_channels;c++)
982 /* Skip LFE */
983 if(chans[c].channel == LFE)
984 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
985 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
986 else
987 ComputePanningGainsBF(Slot->ChanMap,
988 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
989 voice->Send[i].Params[c].Gains.Target
992 else
993 for(c = 0;c < num_channels;c++)
995 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
996 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
1000 else
1002 ALfloat w0 = 0.0f;
1004 if(Device->AvgSpeakerDist > 0.0f)
1006 /* If the source distance is 0, set w0 to w1 to act as a pass-
1007 * through. We still want to pass the signal through the
1008 * filters so they keep an appropriate history, in case the
1009 * source moves away from the listener.
1011 w0 = SPEEDOFSOUNDMETRESPERSEC /
1012 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
1014 for(c = 0;c < num_channels;c++)
1015 NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
1017 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
1018 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
1019 voice->Flags |= VOICE_HAS_NFC;
1022 for(c = 0;c < num_channels;c++)
1024 ALfloat coeffs[MAX_AMBI_COEFFS];
1026 /* Special-case LFE */
1027 if(chans[c].channel == LFE)
1029 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
1030 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
1031 if(Device->Dry.Buffer == Device->RealOut.Buffer)
1033 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
1034 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
1037 for(i = 0;i < NumSends;i++)
1039 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1040 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
1042 continue;
1045 if(Device->Render_Mode == StereoPair)
1046 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
1047 else
1048 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
1049 ComputeDryPanGains(&Device->Dry,
1050 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
1053 for(i = 0;i < NumSends;i++)
1055 const ALeffectslot *Slot = SendSlots[i];
1056 if(Slot)
1057 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
1058 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
1060 else
1061 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
1062 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
1069 ALfloat hfScale = props->Direct.HFReference / Frequency;
1070 ALfloat lfScale = props->Direct.LFReference / Frequency;
1071 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
1072 ALfloat gainLF = maxf(DryGainLF, 0.001f);
1074 voice->Direct.FilterType = AF_None;
1075 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
1076 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
1077 BiquadState_setParams(
1078 &voice->Direct.Params[0].LowPass, BiquadType_HighShelf,
1079 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1081 BiquadState_setParams(
1082 &voice->Direct.Params[0].HighPass, BiquadType_LowShelf,
1083 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1085 for(c = 1;c < num_channels;c++)
1087 BiquadState_copyParams(&voice->Direct.Params[c].LowPass,
1088 &voice->Direct.Params[0].LowPass);
1089 BiquadState_copyParams(&voice->Direct.Params[c].HighPass,
1090 &voice->Direct.Params[0].HighPass);
1093 for(i = 0;i < NumSends;i++)
1095 ALfloat hfScale = props->Send[i].HFReference / Frequency;
1096 ALfloat lfScale = props->Send[i].LFReference / Frequency;
1097 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
1098 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
1100 voice->Send[i].FilterType = AF_None;
1101 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
1102 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
1103 BiquadState_setParams(
1104 &voice->Send[i].Params[0].LowPass, BiquadType_HighShelf,
1105 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1107 BiquadState_setParams(
1108 &voice->Send[i].Params[0].HighPass, BiquadType_LowShelf,
1109 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1111 for(c = 1;c < num_channels;c++)
1113 BiquadState_copyParams(&voice->Send[i].Params[c].LowPass,
1114 &voice->Send[i].Params[0].LowPass);
1115 BiquadState_copyParams(&voice->Send[i].Params[c].HighPass,
1116 &voice->Send[i].Params[0].HighPass);
1121 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1123 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1124 const ALCdevice *Device = ALContext->Device;
1125 const ALlistener *Listener = ALContext->Listener;
1126 ALfloat DryGain, DryGainHF, DryGainLF;
1127 ALfloat WetGain[MAX_SENDS];
1128 ALfloat WetGainHF[MAX_SENDS];
1129 ALfloat WetGainLF[MAX_SENDS];
1130 ALeffectslot *SendSlots[MAX_SENDS];
1131 ALfloat Pitch;
1132 ALsizei i;
1134 voice->Direct.Buffer = Device->Dry.Buffer;
1135 voice->Direct.Channels = Device->Dry.NumChannels;
1136 for(i = 0;i < Device->NumAuxSends;i++)
1138 SendSlots[i] = props->Send[i].Slot;
1139 if(!SendSlots[i] && i == 0)
1140 SendSlots[i] = ALContext->DefaultSlot;
1141 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1143 SendSlots[i] = NULL;
1144 voice->Send[i].Buffer = NULL;
1145 voice->Send[i].Channels = 0;
1147 else
1149 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1150 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1154 /* Calculate the stepping value */
1155 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1156 if(Pitch > (ALfloat)MAX_PITCH)
1157 voice->Step = MAX_PITCH<<FRACTIONBITS;
1158 else
1159 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1160 if(props->Resampler == BSinc24Resampler)
1161 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1162 else if(props->Resampler == BSinc12Resampler)
1163 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1164 voice->Resampler = SelectResampler(props->Resampler);
1166 /* Calculate gains */
1167 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1168 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1169 DryGain = minf(DryGain, GAIN_MIX_MAX);
1170 DryGainHF = props->Direct.GainHF;
1171 DryGainLF = props->Direct.GainLF;
1172 for(i = 0;i < Device->NumAuxSends;i++)
1174 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1175 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1176 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1177 WetGainHF[i] = props->Send[i].GainHF;
1178 WetGainLF[i] = props->Send[i].GainLF;
1181 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1182 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1185 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1187 const ALCdevice *Device = ALContext->Device;
1188 const ALlistener *Listener = ALContext->Listener;
1189 const ALsizei NumSends = Device->NumAuxSends;
1190 aluVector Position, Velocity, Direction, SourceToListener;
1191 ALfloat Distance, ClampedDist, DopplerFactor;
1192 ALeffectslot *SendSlots[MAX_SENDS];
1193 ALfloat RoomRolloff[MAX_SENDS];
1194 ALfloat DecayDistance[MAX_SENDS];
1195 ALfloat DecayLFDistance[MAX_SENDS];
1196 ALfloat DecayHFDistance[MAX_SENDS];
1197 ALfloat DryGain, DryGainHF, DryGainLF;
1198 ALfloat WetGain[MAX_SENDS];
1199 ALfloat WetGainHF[MAX_SENDS];
1200 ALfloat WetGainLF[MAX_SENDS];
1201 bool directional;
1202 ALfloat dir[3];
1203 ALfloat spread;
1204 ALfloat Pitch;
1205 ALint i;
1207 /* Set mixing buffers and get send parameters. */
1208 voice->Direct.Buffer = Device->Dry.Buffer;
1209 voice->Direct.Channels = Device->Dry.NumChannels;
1210 for(i = 0;i < NumSends;i++)
1212 SendSlots[i] = props->Send[i].Slot;
1213 if(!SendSlots[i] && i == 0)
1214 SendSlots[i] = ALContext->DefaultSlot;
1215 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1217 SendSlots[i] = NULL;
1218 RoomRolloff[i] = 0.0f;
1219 DecayDistance[i] = 0.0f;
1220 DecayLFDistance[i] = 0.0f;
1221 DecayHFDistance[i] = 0.0f;
1223 else if(SendSlots[i]->Params.AuxSendAuto)
1225 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1226 /* Calculate the distances to where this effect's decay reaches
1227 * -60dB.
1229 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1230 Listener->Params.ReverbSpeedOfSound;
1231 DecayLFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayLFRatio;
1232 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1233 if(SendSlots[i]->Params.DecayHFLimit)
1235 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1236 if(airAbsorption < 1.0f)
1238 /* Calculate the distance to where this effect's air
1239 * absorption reaches -60dB, and limit the effect's HF
1240 * decay distance (so it doesn't take any longer to decay
1241 * than the air would allow).
1243 ALfloat absorb_dist = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1244 DecayHFDistance[i] = minf(absorb_dist, DecayHFDistance[i]);
1248 else
1250 /* If the slot's auxiliary send auto is off, the data sent to the
1251 * effect slot is the same as the dry path, sans filter effects */
1252 RoomRolloff[i] = props->RolloffFactor;
1253 DecayDistance[i] = 0.0f;
1254 DecayLFDistance[i] = 0.0f;
1255 DecayHFDistance[i] = 0.0f;
1258 if(!SendSlots[i])
1260 voice->Send[i].Buffer = NULL;
1261 voice->Send[i].Channels = 0;
1263 else
1265 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1266 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1270 /* Transform source to listener space (convert to head relative) */
1271 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1272 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1273 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1274 if(props->HeadRelative == AL_FALSE)
1276 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1277 /* Transform source vectors */
1278 Position = aluMatrixfVector(Matrix, &Position);
1279 Velocity = aluMatrixfVector(Matrix, &Velocity);
1280 Direction = aluMatrixfVector(Matrix, &Direction);
1282 else
1284 const aluVector *lvelocity = &Listener->Params.Velocity;
1285 /* Offset the source velocity to be relative of the listener velocity */
1286 Velocity.v[0] += lvelocity->v[0];
1287 Velocity.v[1] += lvelocity->v[1];
1288 Velocity.v[2] += lvelocity->v[2];
1291 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1292 SourceToListener.v[0] = -Position.v[0];
1293 SourceToListener.v[1] = -Position.v[1];
1294 SourceToListener.v[2] = -Position.v[2];
1295 SourceToListener.v[3] = 0.0f;
1296 Distance = aluNormalize(SourceToListener.v);
1298 /* Initial source gain */
1299 DryGain = props->Gain;
1300 DryGainHF = 1.0f;
1301 DryGainLF = 1.0f;
1302 for(i = 0;i < NumSends;i++)
1304 WetGain[i] = props->Gain;
1305 WetGainHF[i] = 1.0f;
1306 WetGainLF[i] = 1.0f;
1309 /* Calculate distance attenuation */
1310 ClampedDist = Distance;
1312 switch(Listener->Params.SourceDistanceModel ?
1313 props->DistanceModel : Listener->Params.DistanceModel)
1315 case InverseDistanceClamped:
1316 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1317 if(props->MaxDistance < props->RefDistance)
1318 break;
1319 /*fall-through*/
1320 case InverseDistance:
1321 if(!(props->RefDistance > 0.0f))
1322 ClampedDist = props->RefDistance;
1323 else
1325 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1326 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1327 for(i = 0;i < NumSends;i++)
1329 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1330 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1333 break;
1335 case LinearDistanceClamped:
1336 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1337 if(props->MaxDistance < props->RefDistance)
1338 break;
1339 /*fall-through*/
1340 case LinearDistance:
1341 if(!(props->MaxDistance != props->RefDistance))
1342 ClampedDist = props->RefDistance;
1343 else
1345 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1346 (props->MaxDistance-props->RefDistance);
1347 DryGain *= maxf(1.0f - attn, 0.0f);
1348 for(i = 0;i < NumSends;i++)
1350 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1351 (props->MaxDistance-props->RefDistance);
1352 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1355 break;
1357 case ExponentDistanceClamped:
1358 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1359 if(props->MaxDistance < props->RefDistance)
1360 break;
1361 /*fall-through*/
1362 case ExponentDistance:
1363 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1364 ClampedDist = props->RefDistance;
1365 else
1367 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1368 for(i = 0;i < NumSends;i++)
1369 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1371 break;
1373 case DisableDistance:
1374 ClampedDist = props->RefDistance;
1375 break;
1378 /* Distance-based air absorption */
1379 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1381 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1382 Listener->Params.MetersPerUnit;
1383 if(props->AirAbsorptionFactor > 0.0f)
1385 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1386 DryGainHF *= hfattn;
1387 for(i = 0;i < NumSends;i++)
1388 WetGainHF[i] *= hfattn;
1391 if(props->WetGainAuto)
1393 /* Apply a decay-time transformation to the wet path, based on the
1394 * source distance in meters. The initial decay of the reverb
1395 * effect is calculated and applied to the wet path.
1397 for(i = 0;i < NumSends;i++)
1399 ALfloat gain, gainhf, gainlf;
1401 if(!(DecayDistance[i] > 0.0f))
1402 continue;
1404 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1405 WetGain[i] *= gain;
1406 /* Yes, the wet path's air absorption is applied with
1407 * WetGainAuto on, rather than WetGainHFAuto.
1409 if(gain > 0.0f)
1411 gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1412 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1413 gainlf = powf(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i]);
1414 WetGainLF[i] *= minf(gainlf / gain, 1.0f);
1420 /* Calculate directional soundcones */
1421 if(directional && props->InnerAngle < 360.0f)
1423 ALfloat ConeVolume;
1424 ALfloat ConeHF;
1425 ALfloat Angle;
1427 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1428 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1429 if(!(Angle > props->InnerAngle))
1431 ConeVolume = 1.0f;
1432 ConeHF = 1.0f;
1434 else if(Angle < props->OuterAngle)
1436 ALfloat scale = ( Angle-props->InnerAngle) /
1437 (props->OuterAngle-props->InnerAngle);
1438 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1439 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1441 else
1443 ConeVolume = props->OuterGain;
1444 ConeHF = props->OuterGainHF;
1447 DryGain *= ConeVolume;
1448 if(props->DryGainHFAuto)
1449 DryGainHF *= ConeHF;
1450 if(props->WetGainAuto)
1452 for(i = 0;i < NumSends;i++)
1453 WetGain[i] *= ConeVolume;
1455 if(props->WetGainHFAuto)
1457 for(i = 0;i < NumSends;i++)
1458 WetGainHF[i] *= ConeHF;
1462 /* Apply gain and frequency filters */
1463 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1464 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1465 DryGainHF *= props->Direct.GainHF;
1466 DryGainLF *= props->Direct.GainLF;
1467 for(i = 0;i < NumSends;i++)
1469 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1470 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1471 WetGainHF[i] *= props->Send[i].GainHF;
1472 WetGainLF[i] *= props->Send[i].GainLF;
1476 /* Initial source pitch */
1477 Pitch = props->Pitch;
1479 /* Calculate velocity-based doppler effect */
1480 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1481 if(DopplerFactor > 0.0f)
1483 const aluVector *lvelocity = &Listener->Params.Velocity;
1484 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1485 ALfloat vss, vls;
1487 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1488 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1490 if(!(vls < SpeedOfSound))
1492 /* Listener moving away from the source at the speed of sound.
1493 * Sound waves can't catch it.
1495 Pitch = 0.0f;
1497 else if(!(vss < SpeedOfSound))
1499 /* Source moving toward the listener at the speed of sound. Sound
1500 * waves bunch up to extreme frequencies.
1502 Pitch = HUGE_VALF;
1504 else
1506 /* Source and listener movement is nominal. Calculate the proper
1507 * doppler shift.
1509 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1513 /* Adjust pitch based on the buffer and output frequencies, and calculate
1514 * fixed-point stepping value.
1516 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1517 if(Pitch > (ALfloat)MAX_PITCH)
1518 voice->Step = MAX_PITCH<<FRACTIONBITS;
1519 else
1520 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1521 if(props->Resampler == BSinc24Resampler)
1522 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1523 else if(props->Resampler == BSinc12Resampler)
1524 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1525 voice->Resampler = SelectResampler(props->Resampler);
1527 if(Distance > FLT_EPSILON)
1529 dir[0] = -SourceToListener.v[0];
1530 /* Clamp Y, in case rounding errors caused it to end up outside of
1531 * -1...+1.
1533 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1534 dir[2] = -SourceToListener.v[2] * ZScale;
1536 else
1538 dir[0] = 0.0f;
1539 dir[1] = 0.0f;
1540 dir[2] = -1.0f;
1542 if(props->Radius > Distance)
1543 spread = F_TAU - Distance/props->Radius*F_PI;
1544 else if(Distance > FLT_EPSILON)
1545 spread = asinf(props->Radius / Distance) * 2.0f;
1546 else
1547 spread = 0.0f;
1549 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1550 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1553 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1555 ALbufferlistitem *BufferListItem;
1556 struct ALvoiceProps *props;
1558 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1559 if(!props && !force) return;
1561 if(props)
1563 memcpy(voice->Props, props,
1564 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1567 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1569 props = voice->Props;
1571 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1572 while(BufferListItem != NULL)
1574 const ALbuffer *buffer;
1575 if(BufferListItem->num_buffers >= 1 && (buffer=BufferListItem->buffers[0]) != NULL)
1577 if(props->SpatializeMode == SpatializeOn ||
1578 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1579 CalcAttnSourceParams(voice, props, buffer, context);
1580 else
1581 CalcNonAttnSourceParams(voice, props, buffer, context);
1582 break;
1584 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1589 static void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1591 ALvoice **voice, **voice_end;
1592 ALsource *source;
1593 ALsizei i;
1595 IncrementRef(&ctx->UpdateCount);
1596 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1598 bool cforce = CalcContextParams(ctx);
1599 bool force = CalcListenerParams(ctx) | cforce;
1600 for(i = 0;i < slots->count;i++)
1601 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1603 voice = ctx->Voices;
1604 voice_end = voice + ctx->VoiceCount;
1605 for(;voice != voice_end;++voice)
1607 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1608 if(source) CalcSourceParams(*voice, ctx, force);
1611 IncrementRef(&ctx->UpdateCount);
1615 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1616 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1617 ALsizei NumChannels)
1619 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1620 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1621 ALsizei i;
1623 /* Apply an all-pass to all channels, except the front-left and front-
1624 * right, so they maintain the same relative phase.
1626 for(i = 0;i < NumChannels;i++)
1628 if(i == lidx || i == ridx)
1629 continue;
1630 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1633 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1634 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1636 for(i = 0;i < SamplesToDo;i++)
1638 ALfloat lfsum, hfsum;
1639 ALfloat m, s, c;
1641 lfsum = lsplit[0][i] + rsplit[0][i];
1642 hfsum = lsplit[1][i] + rsplit[1][i];
1643 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1645 /* This pans the separate low- and high-frequency sums between being on
1646 * the center channel and the left/right channels. The low-frequency
1647 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1648 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1649 * values can be tweaked.
1651 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1652 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1654 /* The generated center channel signal adds to the existing signal,
1655 * while the modified left and right channels replace.
1657 Buffer[lidx][i] = (m + s) * 0.5f;
1658 Buffer[ridx][i] = (m - s) * 0.5f;
1659 Buffer[cidx][i] += c * 0.5f;
1663 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1664 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1666 ALsizei i, c;
1668 Values = ASSUME_ALIGNED(Values, 16);
1669 for(c = 0;c < numchans;c++)
1671 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1672 const ALfloat gain = distcomp[c].Gain;
1673 const ALsizei base = distcomp[c].Length;
1674 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1676 if(base == 0)
1678 if(gain < 1.0f)
1680 for(i = 0;i < SamplesToDo;i++)
1681 inout[i] *= gain;
1683 continue;
1686 if(SamplesToDo >= base)
1688 for(i = 0;i < base;i++)
1689 Values[i] = distbuf[i];
1690 for(;i < SamplesToDo;i++)
1691 Values[i] = inout[i-base];
1692 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1694 else
1696 for(i = 0;i < SamplesToDo;i++)
1697 Values[i] = distbuf[i];
1698 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1699 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1701 for(i = 0;i < SamplesToDo;i++)
1702 inout[i] = Values[i]*gain;
1706 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1707 const ALfloat quant_scale, const ALsizei SamplesToDo,
1708 const ALsizei numchans)
1710 const ALfloat invscale = 1.0f / quant_scale;
1711 ALuint seed = *dither_seed;
1712 ALsizei c, i;
1714 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1715 * values between -1 and +1). Step 2 is to add the noise to the samples,
1716 * before rounding and after scaling up to the desired quantization depth.
1718 for(c = 0;c < numchans;c++)
1720 ALfloat *restrict samples = Samples[c];
1721 for(i = 0;i < SamplesToDo;i++)
1723 ALfloat val = samples[i] * quant_scale;
1724 ALuint rng0 = dither_rng(&seed);
1725 ALuint rng1 = dither_rng(&seed);
1726 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1727 samples[i] = roundf(val) * invscale;
1730 *dither_seed = seed;
1734 static inline ALfloat Conv_ALfloat(ALfloat val)
1735 { return val; }
1736 static inline ALint Conv_ALint(ALfloat val)
1738 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1739 * integer range normalized floats can be safely converted to (a bit of the
1740 * exponent helps out, effectively giving 25 bits).
1742 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1744 static inline ALshort Conv_ALshort(ALfloat val)
1745 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1746 static inline ALbyte Conv_ALbyte(ALfloat val)
1747 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1749 /* Define unsigned output variations. */
1750 #define DECL_TEMPLATE(T, func, O) \
1751 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1753 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1754 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1755 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1757 #undef DECL_TEMPLATE
1759 #define DECL_TEMPLATE(T, A) \
1760 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1761 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1762 ALsizei numchans) \
1764 ALsizei i, j; \
1765 for(j = 0;j < numchans;j++) \
1767 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1768 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1770 for(i = 0;i < SamplesToDo;i++) \
1771 out[i*numchans] = Conv_##T(in[i]); \
1775 DECL_TEMPLATE(ALfloat, F32)
1776 DECL_TEMPLATE(ALuint, UI32)
1777 DECL_TEMPLATE(ALint, I32)
1778 DECL_TEMPLATE(ALushort, UI16)
1779 DECL_TEMPLATE(ALshort, I16)
1780 DECL_TEMPLATE(ALubyte, UI8)
1781 DECL_TEMPLATE(ALbyte, I8)
1783 #undef DECL_TEMPLATE
1786 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1788 ALsizei SamplesToDo;
1789 ALsizei SamplesDone;
1790 ALCcontext *ctx;
1791 ALsizei i, c;
1793 START_MIXER_MODE();
1794 for(SamplesDone = 0;SamplesDone < NumSamples;)
1796 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1797 for(c = 0;c < device->Dry.NumChannels;c++)
1798 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1799 if(device->Dry.Buffer != device->FOAOut.Buffer)
1800 for(c = 0;c < device->FOAOut.NumChannels;c++)
1801 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1802 if(device->Dry.Buffer != device->RealOut.Buffer)
1803 for(c = 0;c < device->RealOut.NumChannels;c++)
1804 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1806 IncrementRef(&device->MixCount);
1808 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1809 while(ctx)
1811 const struct ALeffectslotArray *auxslots;
1813 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1814 ProcessParamUpdates(ctx, auxslots);
1816 for(i = 0;i < auxslots->count;i++)
1818 ALeffectslot *slot = auxslots->slot[i];
1819 for(c = 0;c < slot->NumChannels;c++)
1820 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1823 /* source processing */
1824 for(i = 0;i < ctx->VoiceCount;i++)
1826 ALvoice *voice = ctx->Voices[i];
1827 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1828 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1829 voice->Step > 0)
1831 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1833 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1834 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1835 SendSourceStoppedEvent(ctx, source->id);
1840 /* effect slot processing */
1841 for(i = 0;i < auxslots->count;i++)
1843 const ALeffectslot *slot = auxslots->slot[i];
1844 ALeffectState *state = slot->Params.EffectState;
1845 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1846 state->OutChannels);
1849 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
1852 /* Increment the clock time. Every second's worth of samples is
1853 * converted and added to clock base so that large sample counts don't
1854 * overflow during conversion. This also guarantees an exact, stable
1855 * conversion. */
1856 device->SamplesDone += SamplesToDo;
1857 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1858 device->SamplesDone %= device->Frequency;
1859 IncrementRef(&device->MixCount);
1861 /* Apply post-process for finalizing the Dry mix to the RealOut
1862 * (Ambisonic decode, UHJ encode, etc).
1864 if(LIKELY(device->PostProcess))
1865 device->PostProcess(device, SamplesToDo);
1867 if(device->Stablizer)
1869 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1870 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1871 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1872 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1874 ApplyStablizer(device->Stablizer, device->RealOut.Buffer, lidx, ridx, cidx,
1875 SamplesToDo, device->RealOut.NumChannels);
1878 ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
1879 SamplesToDo, device->RealOut.NumChannels);
1881 if(device->Limiter)
1882 ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
1883 device->RealOut.Buffer);
1885 if(device->DitherDepth > 0.0f)
1886 ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
1887 SamplesToDo, device->RealOut.NumChannels);
1889 if(OutBuffer)
1891 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1892 ALsizei Channels = device->RealOut.NumChannels;
1894 switch(device->FmtType)
1896 case DevFmtByte:
1897 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1898 break;
1899 case DevFmtUByte:
1900 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1901 break;
1902 case DevFmtShort:
1903 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1904 break;
1905 case DevFmtUShort:
1906 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1907 break;
1908 case DevFmtInt:
1909 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1910 break;
1911 case DevFmtUInt:
1912 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1913 break;
1914 case DevFmtFloat:
1915 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1916 break;
1920 SamplesDone += SamplesToDo;
1922 END_MIXER_MODE();
1926 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1928 ALCcontext *ctx;
1929 AsyncEvent evt;
1930 va_list args;
1931 int msglen;
1933 if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
1934 return;
1936 evt.EnumType = EventType_Disconnected;
1937 evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1938 evt.ObjectId = 0;
1939 evt.Param = 0;
1941 va_start(args, msg);
1942 msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
1943 va_end(args);
1945 if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
1947 evt.Message[sizeof(evt.Message)-1] = 0;
1948 msglen = (int)strlen(evt.Message);
1950 if(msglen > 0)
1951 msg = evt.Message;
1952 else
1954 msg = "<internal error constructing message>";
1955 msglen = (int)strlen(msg);
1958 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1959 while(ctx)
1961 ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire);
1962 ALsizei i;
1964 if((enabledevt&EventType_Disconnected) &&
1965 ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1) == 1)
1966 alsem_post(&ctx->EventSem);
1968 for(i = 0;i < ctx->VoiceCount;i++)
1970 ALvoice *voice = ctx->Voices[i];
1971 ALsource *source;
1973 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_relaxed);
1974 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed))
1976 /* If the source's voice was playing, it's now effectively
1977 * stopped (the source state will be updated the next time it's
1978 * checked).
1980 SendSourceStoppedEvent(ctx, source->id);
1982 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1985 ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);