Provide more descriptive messages to disconnection events
[openal-soft.git] / Alc / ALu.c
blob9f4f7a30cb01b2564daa3b2a6f23c4a6b30741f1
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <math.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
29 #include "alMain.h"
30 #include "alSource.h"
31 #include "alBuffer.h"
32 #include "alListener.h"
33 #include "alAuxEffectSlot.h"
34 #include "alu.h"
35 #include "bs2b.h"
36 #include "hrtf.h"
37 #include "mastering.h"
38 #include "uhjfilter.h"
39 #include "bformatdec.h"
40 #include "static_assert.h"
41 #include "ringbuffer.h"
43 #include "fpu_modes.h"
44 #include "cpu_caps.h"
45 #include "mixer_defs.h"
46 #include "bsinc_inc.h"
48 #include "backends/base.h"
51 extern inline ALfloat minf(ALfloat a, ALfloat b);
52 extern inline ALfloat maxf(ALfloat a, ALfloat b);
53 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
55 extern inline ALdouble mind(ALdouble a, ALdouble b);
56 extern inline ALdouble maxd(ALdouble a, ALdouble b);
57 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
59 extern inline ALuint minu(ALuint a, ALuint b);
60 extern inline ALuint maxu(ALuint a, ALuint b);
61 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
63 extern inline ALint mini(ALint a, ALint b);
64 extern inline ALint maxi(ALint a, ALint b);
65 extern inline ALint clampi(ALint val, ALint min, ALint max);
67 extern inline ALint64 mini64(ALint64 a, ALint64 b);
68 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
69 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
71 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
72 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
73 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
75 extern inline size_t minz(size_t a, size_t b);
76 extern inline size_t maxz(size_t a, size_t b);
77 extern inline size_t clampz(size_t val, size_t min, size_t max);
79 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
80 extern inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu);
82 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
84 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
85 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
86 extern inline void aluMatrixfSet(aluMatrixf *matrix,
87 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
88 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
89 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
90 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
93 /* Cone scalar */
94 ALfloat ConeScale = 1.0f;
96 /* Localized Z scalar for mono sources */
97 ALfloat ZScale = 1.0f;
99 /* Force default speed of sound for distance-related reverb decay. */
100 ALboolean OverrideReverbSpeedOfSound = AL_FALSE;
102 const aluMatrixf IdentityMatrixf = {{
103 { 1.0f, 0.0f, 0.0f, 0.0f },
104 { 0.0f, 1.0f, 0.0f, 0.0f },
105 { 0.0f, 0.0f, 1.0f, 0.0f },
106 { 0.0f, 0.0f, 0.0f, 1.0f },
110 struct ChanMap {
111 enum Channel channel;
112 ALfloat angle;
113 ALfloat elevation;
116 static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
119 void DeinitVoice(ALvoice *voice)
121 al_free(ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL));
125 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
127 #ifdef HAVE_NEON
128 if((CPUCapFlags&CPU_CAP_NEON))
129 return MixDirectHrtf_Neon;
130 #endif
131 #ifdef HAVE_SSE
132 if((CPUCapFlags&CPU_CAP_SSE))
133 return MixDirectHrtf_SSE;
134 #endif
136 return MixDirectHrtf_C;
140 /* Prior to VS2013, MSVC lacks the round() family of functions. */
141 #if defined(_MSC_VER) && _MSC_VER < 1800
142 static float roundf(float val)
144 if(val < 0.0f)
145 return ceilf(val-0.5f);
146 return floorf(val+0.5f);
148 #endif
150 /* This RNG method was created based on the math found in opusdec. It's quick,
151 * and starting with a seed value of 22222, is suitable for generating
152 * whitenoise.
154 static inline ALuint dither_rng(ALuint *seed)
156 *seed = (*seed * 96314165) + 907633515;
157 return *seed;
161 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
163 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
164 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
165 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
168 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
170 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
173 static ALfloat aluNormalize(ALfloat *vec)
175 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
176 if(length > 0.0f)
178 ALfloat inv_length = 1.0f/length;
179 vec[0] *= inv_length;
180 vec[1] *= inv_length;
181 vec[2] *= inv_length;
183 return length;
186 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
188 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
190 vec[0] = v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0];
191 vec[1] = v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1];
192 vec[2] = v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2];
195 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
197 aluVector v;
198 v.v[0] = vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0];
199 v.v[1] = vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1];
200 v.v[2] = vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2];
201 v.v[3] = vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3];
202 return v;
206 void aluInit(void)
208 MixDirectHrtf = SelectHrtfMixer();
211 /* Prepares the interpolator for a given rate (determined by increment). A
212 * result of AL_FALSE indicates that the filter output will completely cut
213 * the input signal.
215 * With a bit of work, and a trade of memory for CPU cost, this could be
216 * modified for use with an interpolated increment for buttery-smooth pitch
217 * changes.
219 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
221 ALfloat sf;
222 ALsizei si;
224 if(increment > FRACTIONONE)
226 sf = (ALfloat)FRACTIONONE / increment;
227 sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
228 si = fastf2i(sf);
229 /* The interpolation factor is fit to this diagonally-symmetric curve
230 * to reduce the transition ripple caused by interpolating different
231 * scales of the sinc function.
233 sf = 1.0f - cosf(asinf(sf - si));
235 else
237 sf = 0.0f;
238 si = BSINC_SCALE_COUNT - 1;
241 state->sf = sf;
242 state->m = table->m[si];
243 state->l = -((state->m/2) - 1);
244 state->filter = table->Tab + table->filterOffset[si];
248 static bool CalcContextParams(ALCcontext *Context)
250 ALlistener *Listener = Context->Listener;
251 struct ALcontextProps *props;
253 props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel);
254 if(!props) return false;
256 Listener->Params.MetersPerUnit = props->MetersPerUnit;
258 Listener->Params.DopplerFactor = props->DopplerFactor;
259 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
260 if(!OverrideReverbSpeedOfSound)
261 Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
262 Listener->Params.MetersPerUnit;
264 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
265 Listener->Params.DistanceModel = props->DistanceModel;
267 ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props);
268 return true;
271 static bool CalcListenerParams(ALCcontext *Context)
273 ALlistener *Listener = Context->Listener;
274 ALfloat N[3], V[3], U[3], P[3];
275 struct ALlistenerProps *props;
276 aluVector vel;
278 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
279 if(!props) return false;
281 /* AT then UP */
282 N[0] = props->Forward[0];
283 N[1] = props->Forward[1];
284 N[2] = props->Forward[2];
285 aluNormalize(N);
286 V[0] = props->Up[0];
287 V[1] = props->Up[1];
288 V[2] = props->Up[2];
289 aluNormalize(V);
290 /* Build and normalize right-vector */
291 aluCrossproduct(N, V, U);
292 aluNormalize(U);
294 aluMatrixfSet(&Listener->Params.Matrix,
295 U[0], V[0], -N[0], 0.0,
296 U[1], V[1], -N[1], 0.0,
297 U[2], V[2], -N[2], 0.0,
298 0.0, 0.0, 0.0, 1.0
301 P[0] = props->Position[0];
302 P[1] = props->Position[1];
303 P[2] = props->Position[2];
304 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
305 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
307 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
308 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
310 Listener->Params.Gain = props->Gain * Context->GainBoost;
312 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props);
313 return true;
316 static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
318 struct ALeffectslotProps *props;
319 ALeffectState *state;
321 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
322 if(!props && !force) return false;
324 if(props)
326 slot->Params.Gain = props->Gain;
327 slot->Params.AuxSendAuto = props->AuxSendAuto;
328 slot->Params.EffectType = props->Type;
329 slot->Params.EffectProps = props->Props;
330 if(IsReverbEffect(props->Type))
332 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
333 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
334 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
335 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
336 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
338 else
340 slot->Params.RoomRolloff = 0.0f;
341 slot->Params.DecayTime = 0.0f;
342 slot->Params.DecayHFRatio = 0.0f;
343 slot->Params.DecayHFLimit = AL_FALSE;
344 slot->Params.AirAbsorptionGainHF = 1.0f;
347 /* Swap effect states. No need to play with the ref counts since they
348 * keep the same number of refs.
350 state = props->State;
351 props->State = slot->Params.EffectState;
352 slot->Params.EffectState = state;
354 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
356 else
357 state = slot->Params.EffectState;
359 V(state,update)(context, slot, &slot->Params.EffectProps);
360 return true;
364 static const struct ChanMap MonoMap[1] = {
365 { FrontCenter, 0.0f, 0.0f }
366 }, RearMap[2] = {
367 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
368 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
369 }, QuadMap[4] = {
370 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
371 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
372 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
373 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
374 }, X51Map[6] = {
375 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
376 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
377 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
378 { LFE, 0.0f, 0.0f },
379 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
380 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
381 }, X61Map[7] = {
382 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
383 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
384 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
385 { LFE, 0.0f, 0.0f },
386 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
387 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
388 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
389 }, X71Map[8] = {
390 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
391 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
392 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
393 { LFE, 0.0f, 0.0f },
394 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
395 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
396 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
397 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
400 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
401 const ALfloat Spread, const ALfloat DryGain,
402 const ALfloat DryGainHF, const ALfloat DryGainLF,
403 const ALfloat *WetGain, const ALfloat *WetGainLF,
404 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
405 const ALbuffer *Buffer, const struct ALvoiceProps *props,
406 const ALlistener *Listener, const ALCdevice *Device)
408 struct ChanMap StereoMap[2] = {
409 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
410 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
412 bool DirectChannels = props->DirectChannels;
413 const ALsizei NumSends = Device->NumAuxSends;
414 const ALuint Frequency = Device->Frequency;
415 const struct ChanMap *chans = NULL;
416 ALsizei num_channels = 0;
417 bool isbformat = false;
418 ALfloat downmix_gain = 1.0f;
419 ALsizei c, i, j;
421 switch(Buffer->FmtChannels)
423 case FmtMono:
424 chans = MonoMap;
425 num_channels = 1;
426 /* Mono buffers are never played direct. */
427 DirectChannels = false;
428 break;
430 case FmtStereo:
431 /* Convert counter-clockwise to clockwise. */
432 StereoMap[0].angle = -props->StereoPan[0];
433 StereoMap[1].angle = -props->StereoPan[1];
435 chans = StereoMap;
436 num_channels = 2;
437 downmix_gain = 1.0f / 2.0f;
438 break;
440 case FmtRear:
441 chans = RearMap;
442 num_channels = 2;
443 downmix_gain = 1.0f / 2.0f;
444 break;
446 case FmtQuad:
447 chans = QuadMap;
448 num_channels = 4;
449 downmix_gain = 1.0f / 4.0f;
450 break;
452 case FmtX51:
453 chans = X51Map;
454 num_channels = 6;
455 /* NOTE: Excludes LFE. */
456 downmix_gain = 1.0f / 5.0f;
457 break;
459 case FmtX61:
460 chans = X61Map;
461 num_channels = 7;
462 /* NOTE: Excludes LFE. */
463 downmix_gain = 1.0f / 6.0f;
464 break;
466 case FmtX71:
467 chans = X71Map;
468 num_channels = 8;
469 /* NOTE: Excludes LFE. */
470 downmix_gain = 1.0f / 7.0f;
471 break;
473 case FmtBFormat2D:
474 num_channels = 3;
475 isbformat = true;
476 DirectChannels = false;
477 break;
479 case FmtBFormat3D:
480 num_channels = 4;
481 isbformat = true;
482 DirectChannels = false;
483 break;
486 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
487 if(isbformat)
489 /* Special handling for B-Format sources. */
491 if(Distance > FLT_EPSILON)
493 /* Panning a B-Format sound toward some direction is easy. Just pan
494 * the first (W) channel as a normal mono sound and silence the
495 * others.
497 ALfloat coeffs[MAX_AMBI_COEFFS];
499 if(Device->AvgSpeakerDist > 0.0f)
501 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
502 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
503 (mdist * (ALfloat)Device->Frequency);
504 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
505 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
506 /* Clamp w0 for really close distances, to prevent excessive
507 * bass.
509 w0 = minf(w0, w1*4.0f);
511 /* Only need to adjust the first channel of a B-Format source. */
512 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
513 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
514 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
516 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
517 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
518 voice->Flags |= VOICE_HAS_NFC;
521 if(Device->Render_Mode == StereoPair)
523 ALfloat ev = asinf(Dir[1]);
524 ALfloat az = atan2f(Dir[0], -Dir[2]);
525 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
527 else
528 CalcDirectionCoeffs(Dir, Spread, coeffs);
530 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
531 ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
532 voice->Direct.Params[0].Gains.Target);
533 for(c = 1;c < num_channels;c++)
535 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
536 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
539 for(i = 0;i < NumSends;i++)
541 const ALeffectslot *Slot = SendSlots[i];
542 if(Slot)
543 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
544 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
546 else
547 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
548 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
549 for(c = 1;c < num_channels;c++)
551 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
552 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
556 else
558 /* Local B-Format sources have their XYZ channels rotated according
559 * to the orientation.
561 ALfloat N[3], V[3], U[3];
562 aluMatrixf matrix;
563 ALfloat scale;
565 if(Device->AvgSpeakerDist > 0.0f)
567 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
568 * is what we want for FOA input. The first channel may have
569 * been previously re-adjusted if panned, so reset it.
571 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f);
572 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f);
573 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 0.0f);
575 voice->Direct.ChannelsPerOrder[0] = 1;
576 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
577 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
578 voice->Direct.ChannelsPerOrder[i] = 0;
579 voice->Flags |= VOICE_HAS_NFC;
582 /* AT then UP */
583 N[0] = props->Orientation[0][0];
584 N[1] = props->Orientation[0][1];
585 N[2] = props->Orientation[0][2];
586 aluNormalize(N);
587 V[0] = props->Orientation[1][0];
588 V[1] = props->Orientation[1][1];
589 V[2] = props->Orientation[1][2];
590 aluNormalize(V);
591 if(!props->HeadRelative)
593 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
594 aluMatrixfFloat3(N, 0.0f, lmatrix);
595 aluMatrixfFloat3(V, 0.0f, lmatrix);
597 /* Build and normalize right-vector */
598 aluCrossproduct(N, V, U);
599 aluNormalize(U);
601 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
602 scale = 1.732050808f;
603 aluMatrixfSet(&matrix,
604 1.414213562f, 0.0f, 0.0f, 0.0f,
605 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
606 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
607 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
610 voice->Direct.Buffer = Device->FOAOut.Buffer;
611 voice->Direct.Channels = Device->FOAOut.NumChannels;
612 for(c = 0;c < num_channels;c++)
613 ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
614 voice->Direct.Params[c].Gains.Target);
615 for(i = 0;i < NumSends;i++)
617 const ALeffectslot *Slot = SendSlots[i];
618 if(Slot)
620 for(c = 0;c < num_channels;c++)
621 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
622 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
625 else
627 for(c = 0;c < num_channels;c++)
628 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
629 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
634 else if(DirectChannels)
636 /* Direct source channels always play local. Skip the virtual channels
637 * and write inputs to the matching real outputs.
639 voice->Direct.Buffer = Device->RealOut.Buffer;
640 voice->Direct.Channels = Device->RealOut.NumChannels;
642 for(c = 0;c < num_channels;c++)
644 int idx;
645 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
646 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
647 if((idx=GetChannelIdxByName(&Device->RealOut, chans[c].channel)) != -1)
648 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
651 /* Auxiliary sends still use normal channel panning since they mix to
652 * B-Format, which can't channel-match.
654 for(c = 0;c < num_channels;c++)
656 ALfloat coeffs[MAX_AMBI_COEFFS];
657 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
659 for(i = 0;i < NumSends;i++)
661 const ALeffectslot *Slot = SendSlots[i];
662 if(Slot)
663 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
664 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
666 else
667 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
668 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
672 else if(Device->Render_Mode == HrtfRender)
674 /* Full HRTF rendering. Skip the virtual channels and render to the
675 * real outputs.
677 voice->Direct.Buffer = Device->RealOut.Buffer;
678 voice->Direct.Channels = Device->RealOut.NumChannels;
680 if(Distance > FLT_EPSILON)
682 ALfloat coeffs[MAX_AMBI_COEFFS];
683 ALfloat ev, az;
685 ev = asinf(Dir[1]);
686 az = atan2f(Dir[0], -Dir[2]);
688 /* Get the HRIR coefficients and delays just once, for the given
689 * source direction.
691 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
692 voice->Direct.Params[0].Hrtf.Target.Coeffs,
693 voice->Direct.Params[0].Hrtf.Target.Delay);
694 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
696 /* Remaining channels use the same results as the first. */
697 for(c = 1;c < num_channels;c++)
699 /* Skip LFE */
700 if(chans[c].channel == LFE)
701 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
702 sizeof(voice->Direct.Params[c].Hrtf.Target));
703 else
704 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
707 /* Calculate the directional coefficients once, which apply to all
708 * input channels of the source sends.
710 CalcDirectionCoeffs(Dir, Spread, coeffs);
712 for(i = 0;i < NumSends;i++)
714 const ALeffectslot *Slot = SendSlots[i];
715 if(Slot)
716 for(c = 0;c < num_channels;c++)
718 /* Skip LFE */
719 if(chans[c].channel == LFE)
720 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
721 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
722 else
723 ComputePanningGainsBF(Slot->ChanMap,
724 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
725 voice->Send[i].Params[c].Gains.Target
728 else
729 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
730 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
733 else
735 /* Local sources on HRTF play with each channel panned to its
736 * relative location around the listener, providing "virtual
737 * speaker" responses.
739 for(c = 0;c < num_channels;c++)
741 ALfloat coeffs[MAX_AMBI_COEFFS];
743 if(chans[c].channel == LFE)
745 /* Skip LFE */
746 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
747 sizeof(voice->Direct.Params[c].Hrtf.Target));
748 for(i = 0;i < NumSends;i++)
750 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
751 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
753 continue;
756 /* Get the HRIR coefficients and delays for this channel
757 * position.
759 GetHrtfCoeffs(Device->HrtfHandle,
760 chans[c].elevation, chans[c].angle, Spread,
761 voice->Direct.Params[c].Hrtf.Target.Coeffs,
762 voice->Direct.Params[c].Hrtf.Target.Delay
764 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
766 /* Normal panning for auxiliary sends. */
767 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
769 for(i = 0;i < NumSends;i++)
771 const ALeffectslot *Slot = SendSlots[i];
772 if(Slot)
773 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
774 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
776 else
777 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
778 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
783 voice->Flags |= VOICE_HAS_HRTF;
785 else
787 /* Non-HRTF rendering. Use normal panning to the output. */
789 if(Distance > FLT_EPSILON)
791 ALfloat coeffs[MAX_AMBI_COEFFS];
792 ALfloat w0 = 0.0f;
794 /* Calculate NFC filter coefficient if needed. */
795 if(Device->AvgSpeakerDist > 0.0f)
797 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
798 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
799 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
800 w0 = SPEEDOFSOUNDMETRESPERSEC /
801 (mdist * (ALfloat)Device->Frequency);
802 /* Clamp w0 for really close distances, to prevent excessive
803 * bass.
805 w0 = minf(w0, w1*4.0f);
807 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
808 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
809 voice->Flags |= VOICE_HAS_NFC;
812 /* Calculate the directional coefficients once, which apply to all
813 * input channels.
815 if(Device->Render_Mode == StereoPair)
817 ALfloat ev = asinf(Dir[1]);
818 ALfloat az = atan2f(Dir[0], -Dir[2]);
819 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
821 else
822 CalcDirectionCoeffs(Dir, Spread, coeffs);
824 for(c = 0;c < num_channels;c++)
826 /* Adjust NFC filters if needed. */
827 if((voice->Flags&VOICE_HAS_NFC))
829 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
830 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
831 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
834 /* Special-case LFE */
835 if(chans[c].channel == LFE)
837 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
838 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
839 if(Device->Dry.Buffer == Device->RealOut.Buffer)
841 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
842 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
844 continue;
847 ComputeDryPanGains(&Device->Dry,
848 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
852 for(i = 0;i < NumSends;i++)
854 const ALeffectslot *Slot = SendSlots[i];
855 if(Slot)
856 for(c = 0;c < num_channels;c++)
858 /* Skip LFE */
859 if(chans[c].channel == LFE)
860 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
861 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
862 else
863 ComputePanningGainsBF(Slot->ChanMap,
864 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
865 voice->Send[i].Params[c].Gains.Target
868 else
869 for(c = 0;c < num_channels;c++)
871 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
872 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
876 else
878 ALfloat w0 = 0.0f;
880 if(Device->AvgSpeakerDist > 0.0f)
882 /* If the source distance is 0, set w0 to w1 to act as a pass-
883 * through. We still want to pass the signal through the
884 * filters so they keep an appropriate history, in case the
885 * source moves away from the listener.
887 w0 = SPEEDOFSOUNDMETRESPERSEC /
888 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
890 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
891 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
892 voice->Flags |= VOICE_HAS_NFC;
895 for(c = 0;c < num_channels;c++)
897 ALfloat coeffs[MAX_AMBI_COEFFS];
899 if((voice->Flags&VOICE_HAS_NFC))
901 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
902 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
903 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
906 /* Special-case LFE */
907 if(chans[c].channel == LFE)
909 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
910 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
911 if(Device->Dry.Buffer == Device->RealOut.Buffer)
913 int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
914 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
917 for(i = 0;i < NumSends;i++)
919 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
920 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
922 continue;
925 if(Device->Render_Mode == StereoPair)
926 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
927 else
928 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
929 ComputeDryPanGains(&Device->Dry,
930 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
933 for(i = 0;i < NumSends;i++)
935 const ALeffectslot *Slot = SendSlots[i];
936 if(Slot)
937 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
938 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
940 else
941 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
942 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
949 ALfloat hfScale = props->Direct.HFReference / Frequency;
950 ALfloat lfScale = props->Direct.LFReference / Frequency;
951 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
952 ALfloat gainLF = maxf(DryGainLF, 0.001f);
954 voice->Direct.FilterType = AF_None;
955 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
956 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
957 ALfilterState_setParams(
958 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
959 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
961 ALfilterState_setParams(
962 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
963 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
965 for(c = 1;c < num_channels;c++)
967 ALfilterState_copyParams(&voice->Direct.Params[c].LowPass,
968 &voice->Direct.Params[0].LowPass);
969 ALfilterState_copyParams(&voice->Direct.Params[c].HighPass,
970 &voice->Direct.Params[0].HighPass);
973 for(i = 0;i < NumSends;i++)
975 ALfloat hfScale = props->Send[i].HFReference / Frequency;
976 ALfloat lfScale = props->Send[i].LFReference / Frequency;
977 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
978 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
980 voice->Send[i].FilterType = AF_None;
981 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
982 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
983 ALfilterState_setParams(
984 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
985 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
987 ALfilterState_setParams(
988 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
989 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
991 for(c = 1;c < num_channels;c++)
993 ALfilterState_copyParams(&voice->Send[i].Params[c].LowPass,
994 &voice->Send[i].Params[0].LowPass);
995 ALfilterState_copyParams(&voice->Send[i].Params[c].HighPass,
996 &voice->Send[i].Params[0].HighPass);
1001 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1003 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1004 const ALCdevice *Device = ALContext->Device;
1005 const ALlistener *Listener = ALContext->Listener;
1006 ALfloat DryGain, DryGainHF, DryGainLF;
1007 ALfloat WetGain[MAX_SENDS];
1008 ALfloat WetGainHF[MAX_SENDS];
1009 ALfloat WetGainLF[MAX_SENDS];
1010 ALeffectslot *SendSlots[MAX_SENDS];
1011 ALfloat Pitch;
1012 ALsizei i;
1014 voice->Direct.Buffer = Device->Dry.Buffer;
1015 voice->Direct.Channels = Device->Dry.NumChannels;
1016 for(i = 0;i < Device->NumAuxSends;i++)
1018 SendSlots[i] = props->Send[i].Slot;
1019 if(!SendSlots[i] && i == 0)
1020 SendSlots[i] = ALContext->DefaultSlot;
1021 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1023 SendSlots[i] = NULL;
1024 voice->Send[i].Buffer = NULL;
1025 voice->Send[i].Channels = 0;
1027 else
1029 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1030 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1034 /* Calculate the stepping value */
1035 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1036 if(Pitch > (ALfloat)MAX_PITCH)
1037 voice->Step = MAX_PITCH<<FRACTIONBITS;
1038 else
1039 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1040 if(props->Resampler == BSinc24Resampler)
1041 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1042 else if(props->Resampler == BSinc12Resampler)
1043 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1044 voice->Resampler = SelectResampler(props->Resampler);
1046 /* Calculate gains */
1047 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1048 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1049 DryGain = minf(DryGain, GAIN_MIX_MAX);
1050 DryGainHF = props->Direct.GainHF;
1051 DryGainLF = props->Direct.GainLF;
1052 for(i = 0;i < Device->NumAuxSends;i++)
1054 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1055 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1056 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1057 WetGainHF[i] = props->Send[i].GainHF;
1058 WetGainLF[i] = props->Send[i].GainLF;
1061 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1062 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1065 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1067 const ALCdevice *Device = ALContext->Device;
1068 const ALlistener *Listener = ALContext->Listener;
1069 const ALsizei NumSends = Device->NumAuxSends;
1070 aluVector Position, Velocity, Direction, SourceToListener;
1071 ALfloat Distance, ClampedDist, DopplerFactor;
1072 ALeffectslot *SendSlots[MAX_SENDS];
1073 ALfloat RoomRolloff[MAX_SENDS];
1074 ALfloat DecayDistance[MAX_SENDS];
1075 ALfloat DecayHFDistance[MAX_SENDS];
1076 ALfloat DryGain, DryGainHF, DryGainLF;
1077 ALfloat WetGain[MAX_SENDS];
1078 ALfloat WetGainHF[MAX_SENDS];
1079 ALfloat WetGainLF[MAX_SENDS];
1080 bool directional;
1081 ALfloat dir[3];
1082 ALfloat spread;
1083 ALfloat Pitch;
1084 ALint i;
1086 /* Set mixing buffers and get send parameters. */
1087 voice->Direct.Buffer = Device->Dry.Buffer;
1088 voice->Direct.Channels = Device->Dry.NumChannels;
1089 for(i = 0;i < NumSends;i++)
1091 SendSlots[i] = props->Send[i].Slot;
1092 if(!SendSlots[i] && i == 0)
1093 SendSlots[i] = ALContext->DefaultSlot;
1094 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1096 SendSlots[i] = NULL;
1097 RoomRolloff[i] = 0.0f;
1098 DecayDistance[i] = 0.0f;
1099 DecayHFDistance[i] = 0.0f;
1101 else if(SendSlots[i]->Params.AuxSendAuto)
1103 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1104 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1105 Listener->Params.ReverbSpeedOfSound;
1106 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1107 if(SendSlots[i]->Params.DecayHFLimit)
1109 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1110 if(airAbsorption < 1.0f)
1112 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1113 DecayHFDistance[i] = minf(limitRatio, DecayHFDistance[i]);
1117 else
1119 /* If the slot's auxiliary send auto is off, the data sent to the
1120 * effect slot is the same as the dry path, sans filter effects */
1121 RoomRolloff[i] = props->RolloffFactor;
1122 DecayDistance[i] = 0.0f;
1123 DecayHFDistance[i] = 0.0f;
1126 if(!SendSlots[i])
1128 voice->Send[i].Buffer = NULL;
1129 voice->Send[i].Channels = 0;
1131 else
1133 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1134 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1138 /* Transform source to listener space (convert to head relative) */
1139 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1140 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1141 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1142 if(props->HeadRelative == AL_FALSE)
1144 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1145 /* Transform source vectors */
1146 Position = aluMatrixfVector(Matrix, &Position);
1147 Velocity = aluMatrixfVector(Matrix, &Velocity);
1148 Direction = aluMatrixfVector(Matrix, &Direction);
1150 else
1152 const aluVector *lvelocity = &Listener->Params.Velocity;
1153 /* Offset the source velocity to be relative of the listener velocity */
1154 Velocity.v[0] += lvelocity->v[0];
1155 Velocity.v[1] += lvelocity->v[1];
1156 Velocity.v[2] += lvelocity->v[2];
1159 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1160 SourceToListener.v[0] = -Position.v[0];
1161 SourceToListener.v[1] = -Position.v[1];
1162 SourceToListener.v[2] = -Position.v[2];
1163 SourceToListener.v[3] = 0.0f;
1164 Distance = aluNormalize(SourceToListener.v);
1166 /* Initial source gain */
1167 DryGain = props->Gain;
1168 DryGainHF = 1.0f;
1169 DryGainLF = 1.0f;
1170 for(i = 0;i < NumSends;i++)
1172 WetGain[i] = props->Gain;
1173 WetGainHF[i] = 1.0f;
1174 WetGainLF[i] = 1.0f;
1177 /* Calculate distance attenuation */
1178 ClampedDist = Distance;
1180 switch(Listener->Params.SourceDistanceModel ?
1181 props->DistanceModel : Listener->Params.DistanceModel)
1183 case InverseDistanceClamped:
1184 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1185 if(props->MaxDistance < props->RefDistance)
1186 break;
1187 /*fall-through*/
1188 case InverseDistance:
1189 if(!(props->RefDistance > 0.0f))
1190 ClampedDist = props->RefDistance;
1191 else
1193 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1194 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1195 for(i = 0;i < NumSends;i++)
1197 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1198 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1201 break;
1203 case LinearDistanceClamped:
1204 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1205 if(props->MaxDistance < props->RefDistance)
1206 break;
1207 /*fall-through*/
1208 case LinearDistance:
1209 if(!(props->MaxDistance != props->RefDistance))
1210 ClampedDist = props->RefDistance;
1211 else
1213 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1214 (props->MaxDistance-props->RefDistance);
1215 DryGain *= maxf(1.0f - attn, 0.0f);
1216 for(i = 0;i < NumSends;i++)
1218 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1219 (props->MaxDistance-props->RefDistance);
1220 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1223 break;
1225 case ExponentDistanceClamped:
1226 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1227 if(props->MaxDistance < props->RefDistance)
1228 break;
1229 /*fall-through*/
1230 case ExponentDistance:
1231 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1232 ClampedDist = props->RefDistance;
1233 else
1235 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1236 for(i = 0;i < NumSends;i++)
1237 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1239 break;
1241 case DisableDistance:
1242 ClampedDist = props->RefDistance;
1243 break;
1246 /* Distance-based air absorption */
1247 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1249 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1250 Listener->Params.MetersPerUnit;
1251 if(props->AirAbsorptionFactor > 0.0f)
1253 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1254 DryGainHF *= hfattn;
1255 for(i = 0;i < NumSends;i++)
1256 WetGainHF[i] *= hfattn;
1259 if(props->WetGainAuto)
1261 /* Apply a decay-time transformation to the wet path, based on the
1262 * source distance in meters. The initial decay of the reverb
1263 * effect is calculated and applied to the wet path.
1265 for(i = 0;i < NumSends;i++)
1267 ALfloat gain;
1269 if(!(DecayDistance[i] > 0.0f))
1270 continue;
1272 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1273 WetGain[i] *= gain;
1274 /* Yes, the wet path's air absorption is applied with
1275 * WetGainAuto on, rather than WetGainHFAuto.
1277 if(gain > 0.0f)
1279 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1280 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1286 /* Calculate directional soundcones */
1287 if(directional && props->InnerAngle < 360.0f)
1289 ALfloat ConeVolume;
1290 ALfloat ConeHF;
1291 ALfloat Angle;
1293 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1294 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1295 if(!(Angle > props->InnerAngle))
1297 ConeVolume = 1.0f;
1298 ConeHF = 1.0f;
1300 else if(Angle < props->OuterAngle)
1302 ALfloat scale = ( Angle-props->InnerAngle) /
1303 (props->OuterAngle-props->InnerAngle);
1304 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1305 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1307 else
1309 ConeVolume = props->OuterGain;
1310 ConeHF = props->OuterGainHF;
1313 DryGain *= ConeVolume;
1314 if(props->DryGainHFAuto)
1315 DryGainHF *= ConeHF;
1316 if(props->WetGainAuto)
1318 for(i = 0;i < NumSends;i++)
1319 WetGain[i] *= ConeVolume;
1321 if(props->WetGainHFAuto)
1323 for(i = 0;i < NumSends;i++)
1324 WetGainHF[i] *= ConeHF;
1328 /* Apply gain and frequency filters */
1329 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1330 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1331 DryGainHF *= props->Direct.GainHF;
1332 DryGainLF *= props->Direct.GainLF;
1333 for(i = 0;i < NumSends;i++)
1335 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1336 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1337 WetGainHF[i] *= props->Send[i].GainHF;
1338 WetGainLF[i] *= props->Send[i].GainLF;
1342 /* Initial source pitch */
1343 Pitch = props->Pitch;
1345 /* Calculate velocity-based doppler effect */
1346 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1347 if(DopplerFactor > 0.0f)
1349 const aluVector *lvelocity = &Listener->Params.Velocity;
1350 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1351 ALfloat vss, vls;
1353 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1354 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1356 if(!(vls < SpeedOfSound))
1358 /* Listener moving away from the source at the speed of sound.
1359 * Sound waves can't catch it.
1361 Pitch = 0.0f;
1363 else if(!(vss < SpeedOfSound))
1365 /* Source moving toward the listener at the speed of sound. Sound
1366 * waves bunch up to extreme frequencies.
1368 Pitch = HUGE_VALF;
1370 else
1372 /* Source and listener movement is nominal. Calculate the proper
1373 * doppler shift.
1375 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1379 /* Adjust pitch based on the buffer and output frequencies, and calculate
1380 * fixed-point stepping value.
1382 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1383 if(Pitch > (ALfloat)MAX_PITCH)
1384 voice->Step = MAX_PITCH<<FRACTIONBITS;
1385 else
1386 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1387 if(props->Resampler == BSinc24Resampler)
1388 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
1389 else if(props->Resampler == BSinc12Resampler)
1390 BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
1391 voice->Resampler = SelectResampler(props->Resampler);
1393 if(Distance > FLT_EPSILON)
1395 dir[0] = -SourceToListener.v[0];
1396 /* Clamp Y, in case rounding errors caused it to end up outside of
1397 * -1...+1.
1399 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1400 dir[2] = -SourceToListener.v[2] * ZScale;
1402 else
1404 dir[0] = 0.0f;
1405 dir[1] = 0.0f;
1406 dir[2] = -1.0f;
1408 if(props->Radius > Distance)
1409 spread = F_TAU - Distance/props->Radius*F_PI;
1410 else if(Distance > FLT_EPSILON)
1411 spread = asinf(props->Radius / Distance) * 2.0f;
1412 else
1413 spread = 0.0f;
1415 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1416 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1419 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
1421 ALbufferlistitem *BufferListItem;
1422 struct ALvoiceProps *props;
1424 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1425 if(!props && !force) return;
1427 if(props)
1429 memcpy(voice->Props, props,
1430 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1433 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
1435 props = voice->Props;
1437 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1438 while(BufferListItem != NULL)
1440 const ALbuffer *buffer;
1441 if(BufferListItem->num_buffers >= 1 && (buffer=BufferListItem->buffers[0]) != NULL)
1443 if(props->SpatializeMode == SpatializeOn ||
1444 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1445 CalcAttnSourceParams(voice, props, buffer, context);
1446 else
1447 CalcNonAttnSourceParams(voice, props, buffer, context);
1448 break;
1450 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1455 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1457 ALvoice **voice, **voice_end;
1458 ALsource *source;
1459 ALsizei i;
1461 IncrementRef(&ctx->UpdateCount);
1462 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1464 bool cforce = CalcContextParams(ctx);
1465 bool force = CalcListenerParams(ctx) | cforce;
1466 for(i = 0;i < slots->count;i++)
1467 force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
1469 voice = ctx->Voices;
1470 voice_end = voice + ctx->VoiceCount;
1471 for(;voice != voice_end;++voice)
1473 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1474 if(source) CalcSourceParams(*voice, ctx, force);
1477 IncrementRef(&ctx->UpdateCount);
1481 static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
1482 int lidx, int ridx, int cidx, ALsizei SamplesToDo,
1483 ALsizei NumChannels)
1485 ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
1486 ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
1487 ALsizei i;
1489 /* Apply an all-pass to all channels, except the front-left and front-
1490 * right, so they maintain the same relative phase.
1492 for(i = 0;i < NumChannels;i++)
1494 if(i == lidx || i == ridx)
1495 continue;
1496 splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
1499 bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
1500 bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
1502 for(i = 0;i < SamplesToDo;i++)
1504 ALfloat lfsum, hfsum;
1505 ALfloat m, s, c;
1507 lfsum = lsplit[0][i] + rsplit[0][i];
1508 hfsum = lsplit[1][i] + rsplit[1][i];
1509 s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
1511 /* This pans the separate low- and high-frequency sums between being on
1512 * the center channel and the left/right channels. The low-frequency
1513 * sum is 1/3rd toward center (2/3rds on left/right) and the high-
1514 * frequency sum is 1/4th toward center (3/4ths on left/right). These
1515 * values can be tweaked.
1517 m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
1518 c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
1520 /* The generated center channel signal adds to the existing signal,
1521 * while the modified left and right channels replace.
1523 Buffer[lidx][i] = (m + s) * 0.5f;
1524 Buffer[ridx][i] = (m - s) * 0.5f;
1525 Buffer[cidx][i] += c * 0.5f;
1529 static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
1530 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1532 ALsizei i, c;
1534 Values = ASSUME_ALIGNED(Values, 16);
1535 for(c = 0;c < numchans;c++)
1537 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1538 const ALfloat gain = distcomp[c].Gain;
1539 const ALsizei base = distcomp[c].Length;
1540 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1542 if(base == 0)
1544 if(gain < 1.0f)
1546 for(i = 0;i < SamplesToDo;i++)
1547 inout[i] *= gain;
1549 continue;
1552 if(SamplesToDo >= base)
1554 for(i = 0;i < base;i++)
1555 Values[i] = distbuf[i];
1556 for(;i < SamplesToDo;i++)
1557 Values[i] = inout[i-base];
1558 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1560 else
1562 for(i = 0;i < SamplesToDo;i++)
1563 Values[i] = distbuf[i];
1564 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1565 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1567 for(i = 0;i < SamplesToDo;i++)
1568 inout[i] = Values[i]*gain;
1572 static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
1573 const ALfloat quant_scale, const ALsizei SamplesToDo,
1574 const ALsizei numchans)
1576 const ALfloat invscale = 1.0f / quant_scale;
1577 ALuint seed = *dither_seed;
1578 ALsizei c, i;
1580 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1581 * values between -1 and +1). Step 2 is to add the noise to the samples,
1582 * before rounding and after scaling up to the desired quantization depth.
1584 for(c = 0;c < numchans;c++)
1586 ALfloat *restrict samples = Samples[c];
1587 for(i = 0;i < SamplesToDo;i++)
1589 ALfloat val = samples[i] * quant_scale;
1590 ALuint rng0 = dither_rng(&seed);
1591 ALuint rng1 = dither_rng(&seed);
1592 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1593 samples[i] = roundf(val) * invscale;
1596 *dither_seed = seed;
1600 static inline ALfloat Conv_ALfloat(ALfloat val)
1601 { return val; }
1602 static inline ALint Conv_ALint(ALfloat val)
1604 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1605 * integer range normalized floats can be safely converted to (a bit of the
1606 * exponent helps out, effectively giving 25 bits).
1608 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1610 static inline ALshort Conv_ALshort(ALfloat val)
1611 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1612 static inline ALbyte Conv_ALbyte(ALfloat val)
1613 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1615 /* Define unsigned output variations. */
1616 #define DECL_TEMPLATE(T, func, O) \
1617 static inline T Conv_##T(ALfloat val) { return func(val)+O; }
1619 DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
1620 DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
1621 DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
1623 #undef DECL_TEMPLATE
1625 #define DECL_TEMPLATE(T, A) \
1626 static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
1627 ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
1628 ALsizei numchans) \
1630 ALsizei i, j; \
1631 for(j = 0;j < numchans;j++) \
1633 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1634 T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
1636 for(i = 0;i < SamplesToDo;i++) \
1637 out[i*numchans] = Conv_##T(in[i]); \
1641 DECL_TEMPLATE(ALfloat, F32)
1642 DECL_TEMPLATE(ALuint, UI32)
1643 DECL_TEMPLATE(ALint, I32)
1644 DECL_TEMPLATE(ALushort, UI16)
1645 DECL_TEMPLATE(ALshort, I16)
1646 DECL_TEMPLATE(ALubyte, UI8)
1647 DECL_TEMPLATE(ALbyte, I8)
1649 #undef DECL_TEMPLATE
1652 void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
1654 ALsizei SamplesToDo;
1655 ALsizei SamplesDone;
1656 ALCcontext *ctx;
1657 ALsizei i, c;
1659 START_MIXER_MODE();
1660 for(SamplesDone = 0;SamplesDone < NumSamples;)
1662 SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
1663 for(c = 0;c < device->Dry.NumChannels;c++)
1664 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1665 if(device->Dry.Buffer != device->FOAOut.Buffer)
1666 for(c = 0;c < device->FOAOut.NumChannels;c++)
1667 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1668 if(device->Dry.Buffer != device->RealOut.Buffer)
1669 for(c = 0;c < device->RealOut.NumChannels;c++)
1670 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1672 IncrementRef(&device->MixCount);
1674 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1675 while(ctx)
1677 const struct ALeffectslotArray *auxslots;
1679 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1680 UpdateContextSources(ctx, auxslots);
1682 for(i = 0;i < auxslots->count;i++)
1684 ALeffectslot *slot = auxslots->slot[i];
1685 for(c = 0;c < slot->NumChannels;c++)
1686 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1689 /* source processing */
1690 for(i = 0;i < ctx->VoiceCount;i++)
1692 ALvoice *voice = ctx->Voices[i];
1693 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1694 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1695 voice->Step > 0)
1697 if(!MixSource(voice, source->id, ctx, SamplesToDo))
1699 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1700 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1705 /* effect slot processing */
1706 for(i = 0;i < auxslots->count;i++)
1708 const ALeffectslot *slot = auxslots->slot[i];
1709 ALeffectState *state = slot->Params.EffectState;
1710 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1711 state->OutChannels);
1714 ctx = ctx->next;
1717 /* Increment the clock time. Every second's worth of samples is
1718 * converted and added to clock base so that large sample counts don't
1719 * overflow during conversion. This also guarantees an exact, stable
1720 * conversion. */
1721 device->SamplesDone += SamplesToDo;
1722 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1723 device->SamplesDone %= device->Frequency;
1724 IncrementRef(&device->MixCount);
1726 if(device->HrtfHandle)
1728 DirectHrtfState *state;
1729 int lidx, ridx;
1731 if(device->AmbiUp)
1732 ambiup_process(device->AmbiUp,
1733 device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer,
1734 SamplesToDo
1737 lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1738 ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1739 assert(lidx != -1 && ridx != -1);
1741 state = device->Hrtf;
1742 for(c = 0;c < device->Dry.NumChannels;c++)
1744 MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1745 device->Dry.Buffer[c], state->Offset, state->IrSize,
1746 state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo
1749 state->Offset += SamplesToDo;
1751 else if(device->AmbiDecoder)
1753 if(device->Dry.Buffer != device->FOAOut.Buffer)
1754 bformatdec_upSample(device->AmbiDecoder,
1755 device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
1756 SamplesToDo
1758 bformatdec_process(device->AmbiDecoder,
1759 device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
1760 SamplesToDo
1763 else if(device->AmbiUp)
1765 ambiup_process(device->AmbiUp,
1766 device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer,
1767 SamplesToDo
1770 else if(device->Uhj_Encoder)
1772 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1773 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1774 if(lidx != -1 && ridx != -1)
1776 /* Encode to stereo-compatible 2-channel UHJ output. */
1777 EncodeUhj2(device->Uhj_Encoder,
1778 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1779 device->Dry.Buffer, SamplesToDo
1783 else if(device->Bs2b)
1785 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1786 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1787 if(lidx != -1 && ridx != -1)
1789 /* Apply binaural/crossfeed filter */
1790 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1791 device->RealOut.Buffer[ridx], SamplesToDo);
1795 if(OutBuffer)
1797 ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
1798 ALsizei Channels = device->RealOut.NumChannels;
1800 if(device->Stablizer)
1802 int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
1803 int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
1804 int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
1805 assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
1807 ApplyStablizer(device->Stablizer, Buffer, lidx, ridx, cidx,
1808 SamplesToDo, Channels);
1811 ApplyDistanceComp(Buffer, device->ChannelDelay, device->TempBuffer[0],
1812 SamplesToDo, Channels);
1814 if(device->Limiter)
1815 ApplyCompression(device->Limiter, Channels, SamplesToDo, Buffer);
1817 if(device->DitherDepth > 0.0f)
1818 ApplyDither(Buffer, &device->DitherSeed, device->DitherDepth, SamplesToDo,
1819 Channels);
1821 switch(device->FmtType)
1823 case DevFmtByte:
1824 WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1825 break;
1826 case DevFmtUByte:
1827 WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1828 break;
1829 case DevFmtShort:
1830 WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1831 break;
1832 case DevFmtUShort:
1833 WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1834 break;
1835 case DevFmtInt:
1836 WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1837 break;
1838 case DevFmtUInt:
1839 WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1840 break;
1841 case DevFmtFloat:
1842 WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
1843 break;
1847 SamplesDone += SamplesToDo;
1849 END_MIXER_MODE();
1853 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
1855 ALCcontext *ctx;
1856 AsyncEvent evt;
1857 va_list args;
1858 int msglen;
1860 device->Connected = ALC_FALSE;
1862 evt.EnumType = EventType_Disconnected;
1863 evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
1864 evt.ObjectId = 0;
1865 evt.Param = 0;
1867 va_start(args, msg);
1868 msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
1869 va_end(args);
1871 if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
1873 evt.Message[sizeof(evt.Message)-1] = 0;
1874 msglen = (int)strlen(evt.Message);
1876 if(msglen > 0)
1877 msg = evt.Message;
1878 else
1880 msg = "<internal error constructing message>";
1881 msglen = (int)strlen(msg);
1884 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1885 while(ctx)
1887 ALsizei i;
1889 if((ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire)&EventType_Disconnected) &&
1890 ll_ringbuffer_write_space(ctx->AsyncEvents) > 0)
1892 ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1);
1893 alsem_post(&ctx->EventSem);
1896 for(i = 0;i < ctx->VoiceCount;i++)
1898 ALvoice *voice = ctx->Voices[i];
1899 ALsource *source;
1901 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_acq_rel);
1902 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1904 if(source)
1906 ALenum playing = AL_PLAYING;
1907 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1910 ctx->VoiceCount = 0;
1912 ctx = ctx->next;