Use macros to set and restore the mixer FPU mode
[openal-soft.git] / Alc / ALu.c
blob2562632087ccc1bd5d35aab726ed8063ebcac42c
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 "uhjfilter.h"
38 #include "bformatdec.h"
39 #include "static_assert.h"
41 #include "mixer_defs.h"
43 #include "backends/base.h"
46 struct ChanMap {
47 enum Channel channel;
48 ALfloat angle;
49 ALfloat elevation;
52 /* Cone scalar */
53 ALfloat ConeScale = 1.0f;
55 /* Localized Z scalar for mono sources */
56 ALfloat ZScale = 1.0f;
58 extern inline ALfloat minf(ALfloat a, ALfloat b);
59 extern inline ALfloat maxf(ALfloat a, ALfloat b);
60 extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
62 extern inline ALdouble mind(ALdouble a, ALdouble b);
63 extern inline ALdouble maxd(ALdouble a, ALdouble b);
64 extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
66 extern inline ALuint minu(ALuint a, ALuint b);
67 extern inline ALuint maxu(ALuint a, ALuint b);
68 extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
70 extern inline ALint mini(ALint a, ALint b);
71 extern inline ALint maxi(ALint a, ALint b);
72 extern inline ALint clampi(ALint val, ALint min, ALint max);
74 extern inline ALint64 mini64(ALint64 a, ALint64 b);
75 extern inline ALint64 maxi64(ALint64 a, ALint64 b);
76 extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
78 extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
79 extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
80 extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
82 extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
83 extern inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALsizei frac);
85 extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
87 extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
88 ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
89 extern inline void aluMatrixfSet(aluMatrixf *matrix,
90 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
91 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
92 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
93 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
95 const aluMatrixf IdentityMatrixf = {{
96 { 1.0f, 0.0f, 0.0f, 0.0f },
97 { 0.0f, 1.0f, 0.0f, 0.0f },
98 { 0.0f, 0.0f, 1.0f, 0.0f },
99 { 0.0f, 0.0f, 0.0f, 1.0f },
103 void DeinitVoice(ALvoice *voice)
105 struct ALvoiceProps *props;
106 size_t count = 0;
108 props = ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL);
109 if(props) al_free(props);
111 props = ATOMIC_EXCHANGE_PTR(&voice->FreeList, NULL, almemory_order_relaxed);
112 while(props)
114 struct ALvoiceProps *next;
115 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
116 al_free(props);
117 props = next;
118 ++count;
120 /* This is excessively spammy if it traces every voice destruction, so just
121 * warn if it was unexpectedly large.
123 if(count > 3)
124 WARN("Freed "SZFMT" voice property objects\n", count);
128 static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
130 #ifdef HAVE_NEON
131 if((CPUCapFlags&CPU_CAP_NEON))
132 return MixDirectHrtf_Neon;
133 #endif
134 #ifdef HAVE_SSE
135 if((CPUCapFlags&CPU_CAP_SSE))
136 return MixDirectHrtf_SSE;
137 #endif
139 return MixDirectHrtf_C;
143 /* Prior to VS2013, MSVC lacks the round() family of functions. */
144 #if defined(_MSC_VER) && _MSC_VER < 1800
145 static float roundf(float val)
147 if(val < 0.0f)
148 return ceilf(val-0.5f);
149 return floorf(val+0.5f);
151 #endif
153 /* This RNG method was created based on the math found in opusdec. It's quick,
154 * and starting with a seed value of 22222, is suitable for generating
155 * whitenoise.
157 static inline ALuint dither_rng(ALuint *seed)
159 *seed = (*seed * 96314165) + 907633515;
160 return *seed;
164 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
166 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
167 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
168 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
171 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
173 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
176 static ALfloat aluNormalize(ALfloat *vec)
178 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
179 if(length > 0.0f)
181 ALfloat inv_length = 1.0f/length;
182 vec[0] *= inv_length;
183 vec[1] *= inv_length;
184 vec[2] *= inv_length;
186 return length;
189 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
191 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
193 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];
194 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];
195 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];
198 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
200 aluVector v;
201 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];
202 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];
203 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];
204 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];
205 return v;
209 /* Prepares the interpolator for a given rate (determined by increment). A
210 * result of AL_FALSE indicates that the filter output will completely cut
211 * the input signal.
213 * With a bit of work, and a trade of memory for CPU cost, this could be
214 * modified for use with an interpolated increment for buttery-smooth pitch
215 * changes.
217 ALboolean BsincPrepare(const ALuint increment, BsincState *state)
219 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
220 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
221 static const ALuint to[4][BSINC_SCALE_COUNT] =
223 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
224 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
225 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
226 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
228 static const ALuint tm[2][BSINC_SCALE_COUNT] =
230 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
231 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
233 ALfloat sf;
234 ALsizei si, pi;
235 ALboolean uncut = AL_TRUE;
237 if(increment > FRACTIONONE)
239 sf = (ALfloat)FRACTIONONE / increment;
240 if(sf < scaleBase)
242 /* Signal has been completely cut. The return result can be used
243 * to skip the filter (and output zeros) as an optimization.
245 sf = 0.0f;
246 si = 0;
247 uncut = AL_FALSE;
249 else
251 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
252 si = fastf2i(sf);
253 /* The interpolation factor is fit to this diagonally-symmetric
254 * curve to reduce the transition ripple caused by interpolating
255 * different scales of the sinc function.
257 sf = 1.0f - cosf(asinf(sf - si));
260 else
262 sf = 0.0f;
263 si = BSINC_SCALE_COUNT - 1;
266 state->sf = sf;
267 state->m = m[si];
268 state->l = -(ALint)((m[si] / 2) - 1);
269 /* The CPU cost of this table re-mapping could be traded for the memory
270 * cost of a complete table map (1024 elements large).
272 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
274 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
275 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
276 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
277 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
279 return uncut;
283 static ALboolean CalcListenerParams(ALCcontext *Context)
285 ALlistener *Listener = Context->Listener;
286 ALfloat N[3], V[3], U[3], P[3];
287 struct ALlistenerProps *props;
288 aluVector vel;
290 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
291 if(!props) return AL_FALSE;
293 /* AT then UP */
294 N[0] = props->Forward[0];
295 N[1] = props->Forward[1];
296 N[2] = props->Forward[2];
297 aluNormalize(N);
298 V[0] = props->Up[0];
299 V[1] = props->Up[1];
300 V[2] = props->Up[2];
301 aluNormalize(V);
302 /* Build and normalize right-vector */
303 aluCrossproduct(N, V, U);
304 aluNormalize(U);
306 aluMatrixfSet(&Listener->Params.Matrix,
307 U[0], V[0], -N[0], 0.0,
308 U[1], V[1], -N[1], 0.0,
309 U[2], V[2], -N[2], 0.0,
310 0.0, 0.0, 0.0, 1.0
313 P[0] = props->Position[0];
314 P[1] = props->Position[1];
315 P[2] = props->Position[2];
316 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
317 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
319 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
320 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
322 Listener->Params.Gain = props->Gain * Context->GainBoost;
323 Listener->Params.MetersPerUnit = props->MetersPerUnit;
325 Listener->Params.DopplerFactor = props->DopplerFactor;
326 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
328 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
329 Listener->Params.DistanceModel = props->DistanceModel;
331 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
332 return AL_TRUE;
335 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
337 struct ALeffectslotProps *props;
338 ALeffectState *state;
340 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
341 if(!props) return AL_FALSE;
343 slot->Params.Gain = props->Gain;
344 slot->Params.AuxSendAuto = props->AuxSendAuto;
345 slot->Params.EffectType = props->Type;
346 if(IsReverbEffect(slot->Params.EffectType))
348 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
349 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
350 slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
351 slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
352 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
354 else
356 slot->Params.RoomRolloff = 0.0f;
357 slot->Params.DecayTime = 0.0f;
358 slot->Params.DecayHFRatio = 0.0f;
359 slot->Params.DecayHFLimit = AL_FALSE;
360 slot->Params.AirAbsorptionGainHF = 1.0f;
363 /* Swap effect states. No need to play with the ref counts since they keep
364 * the same number of refs.
366 state = props->State;
367 props->State = slot->Params.EffectState;
368 slot->Params.EffectState = state;
370 V(state,update)(device, slot, &props->Props);
372 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
373 return AL_TRUE;
377 static const struct ChanMap MonoMap[1] = {
378 { FrontCenter, 0.0f, 0.0f }
379 }, RearMap[2] = {
380 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
381 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
382 }, QuadMap[4] = {
383 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
384 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
385 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
386 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
387 }, X51Map[6] = {
388 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
389 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
390 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
391 { LFE, 0.0f, 0.0f },
392 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
393 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
394 }, X61Map[7] = {
395 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
396 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
397 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
398 { LFE, 0.0f, 0.0f },
399 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
400 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
401 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
402 }, X71Map[8] = {
403 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
404 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
405 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
406 { LFE, 0.0f, 0.0f },
407 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
408 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
409 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
410 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
413 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
414 const ALfloat Spread, const ALfloat DryGain,
415 const ALfloat DryGainHF, const ALfloat DryGainLF,
416 const ALfloat *WetGain, const ALfloat *WetGainLF,
417 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
418 const ALbuffer *Buffer, const struct ALvoiceProps *props,
419 const ALlistener *Listener, const ALCdevice *Device)
421 struct ChanMap StereoMap[2] = {
422 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
423 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
425 bool DirectChannels = props->DirectChannels;
426 const ALsizei NumSends = Device->NumAuxSends;
427 const ALuint Frequency = Device->Frequency;
428 const struct ChanMap *chans = NULL;
429 ALsizei num_channels = 0;
430 bool isbformat = false;
431 ALfloat downmix_gain = 1.0f;
432 ALsizei c, i, j;
434 switch(Buffer->FmtChannels)
436 case FmtMono:
437 chans = MonoMap;
438 num_channels = 1;
439 /* Mono buffers are never played direct. */
440 DirectChannels = false;
441 break;
443 case FmtStereo:
444 /* Convert counter-clockwise to clockwise. */
445 StereoMap[0].angle = -props->StereoPan[0];
446 StereoMap[1].angle = -props->StereoPan[1];
448 chans = StereoMap;
449 num_channels = 2;
450 downmix_gain = 1.0f / 2.0f;
451 break;
453 case FmtRear:
454 chans = RearMap;
455 num_channels = 2;
456 downmix_gain = 1.0f / 2.0f;
457 break;
459 case FmtQuad:
460 chans = QuadMap;
461 num_channels = 4;
462 downmix_gain = 1.0f / 4.0f;
463 break;
465 case FmtX51:
466 chans = X51Map;
467 num_channels = 6;
468 /* NOTE: Excludes LFE. */
469 downmix_gain = 1.0f / 5.0f;
470 break;
472 case FmtX61:
473 chans = X61Map;
474 num_channels = 7;
475 /* NOTE: Excludes LFE. */
476 downmix_gain = 1.0f / 6.0f;
477 break;
479 case FmtX71:
480 chans = X71Map;
481 num_channels = 8;
482 /* NOTE: Excludes LFE. */
483 downmix_gain = 1.0f / 7.0f;
484 break;
486 case FmtBFormat2D:
487 num_channels = 3;
488 isbformat = true;
489 DirectChannels = false;
490 break;
492 case FmtBFormat3D:
493 num_channels = 4;
494 isbformat = true;
495 DirectChannels = false;
496 break;
499 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
500 if(isbformat)
502 /* Special handling for B-Format sources. */
504 if(Distance > FLT_EPSILON)
506 /* Panning a B-Format sound toward some direction is easy. Just pan
507 * the first (W) channel as a normal mono sound and silence the
508 * others.
510 ALfloat coeffs[MAX_AMBI_COEFFS];
512 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
514 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
515 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
516 (mdist * (ALfloat)Device->Frequency);
517 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
518 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
519 /* Clamp w0 for really close distances, to prevent excessive
520 * bass.
522 w0 = minf(w0, w1*4.0f);
524 /* Only need to adjust the first channel of a B-Format source. */
525 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
526 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
527 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
529 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
530 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
531 voice->Flags |= VOICE_HAS_NFC;
534 if(Device->Render_Mode == StereoPair)
536 ALfloat ev = asinf(Dir[1]);
537 ALfloat az = atan2f(Dir[0], -Dir[2]);
538 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
540 else
541 CalcDirectionCoeffs(Dir, Spread, coeffs);
543 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
544 ComputePanningGains(Device->Dry, coeffs, DryGain*1.414213562f,
545 voice->Direct.Params[0].Gains.Target);
546 for(c = 1;c < num_channels;c++)
548 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
549 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
552 for(i = 0;i < NumSends;i++)
554 const ALeffectslot *Slot = SendSlots[i];
555 if(Slot)
556 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
557 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
559 else
560 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
561 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
562 for(c = 1;c < num_channels;c++)
564 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
565 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
569 else
571 /* Local B-Format sources have their XYZ channels rotated according
572 * to the orientation.
574 ALfloat N[3], V[3], U[3];
575 aluMatrixf matrix;
576 ALfloat scale;
578 if(Device->AvgSpeakerDist > 0.0f)
580 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
581 * is what we want for FOA input. The first channel may have
582 * been previously re-adjusted if panned, so reset it.
584 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f);
585 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f);
586 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 0.0f);
588 voice->Direct.ChannelsPerOrder[0] = 1;
589 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
590 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
591 voice->Direct.ChannelsPerOrder[2] = 0;
592 voice->Flags |= VOICE_HAS_NFC;
595 /* AT then UP */
596 N[0] = props->Orientation[0][0];
597 N[1] = props->Orientation[0][1];
598 N[2] = props->Orientation[0][2];
599 aluNormalize(N);
600 V[0] = props->Orientation[1][0];
601 V[1] = props->Orientation[1][1];
602 V[2] = props->Orientation[1][2];
603 aluNormalize(V);
604 if(!props->HeadRelative)
606 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
607 aluMatrixfFloat3(N, 0.0f, lmatrix);
608 aluMatrixfFloat3(V, 0.0f, lmatrix);
610 /* Build and normalize right-vector */
611 aluCrossproduct(N, V, U);
612 aluNormalize(U);
614 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
615 scale = 1.732050808f;
616 aluMatrixfSet(&matrix,
617 1.414213562f, 0.0f, 0.0f, 0.0f,
618 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
619 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
620 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
623 voice->Direct.Buffer = Device->FOAOut.Buffer;
624 voice->Direct.Channels = Device->FOAOut.NumChannels;
625 for(c = 0;c < num_channels;c++)
626 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
627 voice->Direct.Params[c].Gains.Target);
628 for(i = 0;i < NumSends;i++)
630 const ALeffectslot *Slot = SendSlots[i];
631 if(Slot)
633 for(c = 0;c < num_channels;c++)
634 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
635 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
638 else
640 for(c = 0;c < num_channels;c++)
641 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
642 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
647 else if(DirectChannels)
649 /* Direct source channels always play local. Skip the virtual channels
650 * and write inputs to the matching real outputs.
652 voice->Direct.Buffer = Device->RealOut.Buffer;
653 voice->Direct.Channels = Device->RealOut.NumChannels;
655 for(c = 0;c < num_channels;c++)
657 int idx;
658 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
659 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
660 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
661 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
664 /* Auxiliary sends still use normal channel panning since they mix to
665 * B-Format, which can't channel-match.
667 for(c = 0;c < num_channels;c++)
669 ALfloat coeffs[MAX_AMBI_COEFFS];
670 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
672 for(i = 0;i < NumSends;i++)
674 const ALeffectslot *Slot = SendSlots[i];
675 if(Slot)
676 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
677 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
679 else
680 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
681 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
685 else if(Device->Render_Mode == HrtfRender)
687 /* Full HRTF rendering. Skip the virtual channels and render to the
688 * real outputs.
690 voice->Direct.Buffer = Device->RealOut.Buffer;
691 voice->Direct.Channels = Device->RealOut.NumChannels;
693 if(Distance > FLT_EPSILON)
695 ALfloat coeffs[MAX_AMBI_COEFFS];
696 ALfloat ev, az;
698 ev = asinf(Dir[1]);
699 az = atan2f(Dir[0], -Dir[2]);
701 /* Get the HRIR coefficients and delays just once, for the given
702 * source direction.
704 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
705 voice->Direct.Params[0].Hrtf.Target.Coeffs,
706 voice->Direct.Params[0].Hrtf.Target.Delay);
707 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
709 /* Remaining channels use the same results as the first. */
710 for(c = 1;c < num_channels;c++)
712 /* Skip LFE */
713 if(chans[c].channel == LFE)
714 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
715 sizeof(voice->Direct.Params[c].Hrtf.Target));
716 else
717 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
720 /* Calculate the directional coefficients once, which apply to all
721 * input channels of the source sends.
723 CalcDirectionCoeffs(Dir, Spread, coeffs);
725 for(i = 0;i < NumSends;i++)
727 const ALeffectslot *Slot = SendSlots[i];
728 if(Slot)
729 for(c = 0;c < num_channels;c++)
731 /* Skip LFE */
732 if(chans[c].channel == LFE)
733 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
734 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
735 else
736 ComputePanningGainsBF(Slot->ChanMap,
737 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
738 voice->Send[i].Params[c].Gains.Target
741 else
742 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
743 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
746 else
748 /* Local sources on HRTF play with each channel panned to its
749 * relative location around the listener, providing "virtual
750 * speaker" responses.
752 for(c = 0;c < num_channels;c++)
754 ALfloat coeffs[MAX_AMBI_COEFFS];
756 if(chans[c].channel == LFE)
758 /* Skip LFE */
759 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
760 sizeof(voice->Direct.Params[c].Hrtf.Target));
761 for(i = 0;i < NumSends;i++)
763 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
764 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
766 continue;
769 /* Get the HRIR coefficients and delays for this channel
770 * position.
772 GetHrtfCoeffs(Device->HrtfHandle,
773 chans[c].elevation, chans[c].angle, Spread,
774 voice->Direct.Params[c].Hrtf.Target.Coeffs,
775 voice->Direct.Params[c].Hrtf.Target.Delay
777 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
779 /* Normal panning for auxiliary sends. */
780 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
782 for(i = 0;i < NumSends;i++)
784 const ALeffectslot *Slot = SendSlots[i];
785 if(Slot)
786 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
787 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
789 else
790 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
791 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
796 voice->Flags |= VOICE_HAS_HRTF;
798 else
800 /* Non-HRTF rendering. Use normal panning to the output. */
802 if(Distance > FLT_EPSILON)
804 ALfloat coeffs[MAX_AMBI_COEFFS];
805 ALfloat w0 = 0.0f;
807 /* Calculate NFC filter coefficient if needed. */
808 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
810 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
811 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
812 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
813 w0 = SPEEDOFSOUNDMETRESPERSEC /
814 (mdist * (ALfloat)Device->Frequency);
815 /* Clamp w0 for really close distances, to prevent excessive
816 * bass.
818 w0 = minf(w0, w1*4.0f);
820 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
821 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
822 voice->Flags |= VOICE_HAS_NFC;
825 /* Calculate the directional coefficients once, which apply to all
826 * input channels.
828 if(Device->Render_Mode == StereoPair)
830 ALfloat ev = asinf(Dir[1]);
831 ALfloat az = atan2f(Dir[0], -Dir[2]);
832 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
834 else
835 CalcDirectionCoeffs(Dir, Spread, coeffs);
837 for(c = 0;c < num_channels;c++)
839 /* Adjust NFC filters if needed. */
840 if((voice->Flags&VOICE_HAS_NFC))
842 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
843 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
844 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
847 /* Special-case LFE */
848 if(chans[c].channel == LFE)
850 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
851 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
852 if(Device->Dry.Buffer == Device->RealOut.Buffer)
854 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
855 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
857 continue;
860 ComputePanningGains(Device->Dry,
861 coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
865 for(i = 0;i < NumSends;i++)
867 const ALeffectslot *Slot = SendSlots[i];
868 if(Slot)
869 for(c = 0;c < num_channels;c++)
871 /* Skip LFE */
872 if(chans[c].channel == LFE)
873 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
874 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
875 else
876 ComputePanningGainsBF(Slot->ChanMap,
877 Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
878 voice->Send[i].Params[c].Gains.Target
881 else
882 for(c = 0;c < num_channels;c++)
884 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
885 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
889 else
891 ALfloat w0 = 0.0f;
893 if(Device->AvgSpeakerDist > 0.0f)
895 /* If the source distance is 0, set w0 to w1 to act as a pass-
896 * through. We still want to pass the signal through the
897 * filters so they keep an appropriate history, in case the
898 * source moves away from the listener.
900 w0 = SPEEDOFSOUNDMETRESPERSEC /
901 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
903 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
904 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
905 voice->Flags |= VOICE_HAS_NFC;
908 for(c = 0;c < num_channels;c++)
910 ALfloat coeffs[MAX_AMBI_COEFFS];
912 if((voice->Flags&VOICE_HAS_NFC))
914 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
915 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
916 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
919 /* Special-case LFE */
920 if(chans[c].channel == LFE)
922 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
923 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
924 if(Device->Dry.Buffer == Device->RealOut.Buffer)
926 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
927 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
930 for(i = 0;i < NumSends;i++)
932 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
933 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
935 continue;
938 if(Device->Render_Mode == StereoPair)
939 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
940 else
941 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
942 ComputePanningGains(Device->Dry,
943 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
946 for(i = 0;i < NumSends;i++)
948 const ALeffectslot *Slot = SendSlots[i];
949 if(Slot)
950 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
951 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
953 else
954 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
955 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
962 ALfloat hfScale = props->Direct.HFReference / Frequency;
963 ALfloat lfScale = props->Direct.LFReference / Frequency;
964 ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
965 ALfloat gainLF = maxf(DryGainLF, 0.001f);
967 voice->Direct.FilterType = AF_None;
968 if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
969 if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
970 ALfilterState_setParams(
971 &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf,
972 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
974 ALfilterState_setParams(
975 &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf,
976 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
978 for(c = 1;c < num_channels;c++)
980 ALfilterState_copyParams(&voice->Direct.Params[c].LowPass,
981 &voice->Direct.Params[0].LowPass);
982 ALfilterState_copyParams(&voice->Direct.Params[c].HighPass,
983 &voice->Direct.Params[0].HighPass);
986 for(i = 0;i < NumSends;i++)
988 ALfloat hfScale = props->Send[i].HFReference / Frequency;
989 ALfloat lfScale = props->Send[i].LFReference / Frequency;
990 ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
991 ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
993 voice->Send[i].FilterType = AF_None;
994 if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
995 if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
996 ALfilterState_setParams(
997 &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf,
998 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
1000 ALfilterState_setParams(
1001 &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf,
1002 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
1004 for(c = 1;c < num_channels;c++)
1006 ALfilterState_copyParams(&voice->Send[i].Params[c].LowPass,
1007 &voice->Send[i].Params[0].LowPass);
1008 ALfilterState_copyParams(&voice->Send[i].Params[c].HighPass,
1009 &voice->Send[i].Params[0].HighPass);
1014 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1016 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
1017 const ALCdevice *Device = ALContext->Device;
1018 const ALlistener *Listener = ALContext->Listener;
1019 ALfloat DryGain, DryGainHF, DryGainLF;
1020 ALfloat WetGain[MAX_SENDS];
1021 ALfloat WetGainHF[MAX_SENDS];
1022 ALfloat WetGainLF[MAX_SENDS];
1023 ALeffectslot *SendSlots[MAX_SENDS];
1024 ALfloat Pitch;
1025 ALsizei i;
1027 voice->Direct.Buffer = Device->Dry.Buffer;
1028 voice->Direct.Channels = Device->Dry.NumChannels;
1029 for(i = 0;i < Device->NumAuxSends;i++)
1031 SendSlots[i] = props->Send[i].Slot;
1032 if(!SendSlots[i] && i == 0)
1033 SendSlots[i] = ALContext->DefaultSlot;
1034 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1036 SendSlots[i] = NULL;
1037 voice->Send[i].Buffer = NULL;
1038 voice->Send[i].Channels = 0;
1040 else
1042 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1043 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1047 /* Calculate the stepping value */
1048 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1049 if(Pitch > (ALfloat)MAX_PITCH)
1050 voice->Step = MAX_PITCH<<FRACTIONBITS;
1051 else
1052 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1053 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1054 voice->Resampler = SelectResampler(props->Resampler);
1056 /* Calculate gains */
1057 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1058 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1059 DryGain = minf(DryGain, GAIN_MIX_MAX);
1060 DryGainHF = props->Direct.GainHF;
1061 DryGainLF = props->Direct.GainLF;
1062 for(i = 0;i < Device->NumAuxSends;i++)
1064 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1065 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1066 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1067 WetGainHF[i] = props->Send[i].GainHF;
1068 WetGainLF[i] = props->Send[i].GainLF;
1071 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1072 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1075 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1077 const ALCdevice *Device = ALContext->Device;
1078 const ALlistener *Listener = ALContext->Listener;
1079 const ALsizei NumSends = Device->NumAuxSends;
1080 aluVector Position, Velocity, Direction, SourceToListener;
1081 ALfloat Distance, ClampedDist, DopplerFactor;
1082 ALeffectslot *SendSlots[MAX_SENDS];
1083 ALfloat RoomRolloff[MAX_SENDS];
1084 ALfloat DecayDistance[MAX_SENDS];
1085 ALfloat DecayHFDistance[MAX_SENDS];
1086 ALfloat DryGain, DryGainHF, DryGainLF;
1087 ALfloat WetGain[MAX_SENDS];
1088 ALfloat WetGainHF[MAX_SENDS];
1089 ALfloat WetGainLF[MAX_SENDS];
1090 bool directional;
1091 ALfloat dir[3];
1092 ALfloat spread;
1093 ALfloat Pitch;
1094 ALint i;
1096 /* Set mixing buffers and get send parameters. */
1097 voice->Direct.Buffer = Device->Dry.Buffer;
1098 voice->Direct.Channels = Device->Dry.NumChannels;
1099 for(i = 0;i < NumSends;i++)
1101 SendSlots[i] = props->Send[i].Slot;
1102 if(!SendSlots[i] && i == 0)
1103 SendSlots[i] = ALContext->DefaultSlot;
1104 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1106 SendSlots[i] = NULL;
1107 RoomRolloff[i] = 0.0f;
1108 DecayDistance[i] = 0.0f;
1109 DecayHFDistance[i] = 0.0f;
1111 else if(SendSlots[i]->Params.AuxSendAuto)
1113 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1114 DecayDistance[i] = SendSlots[i]->Params.DecayTime * SPEEDOFSOUNDMETRESPERSEC;
1115 DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
1116 if(SendSlots[i]->Params.DecayHFLimit)
1118 ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
1119 if(airAbsorption < 1.0f)
1121 ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
1122 DecayHFDistance[i] = minf(limitRatio, DecayHFDistance[i]);
1126 else
1128 /* If the slot's auxiliary send auto is off, the data sent to the
1129 * effect slot is the same as the dry path, sans filter effects */
1130 RoomRolloff[i] = props->RolloffFactor;
1131 DecayDistance[i] = 0.0f;
1132 DecayHFDistance[i] = 0.0f;
1135 if(!SendSlots[i])
1137 voice->Send[i].Buffer = NULL;
1138 voice->Send[i].Channels = 0;
1140 else
1142 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1143 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1147 /* Transform source to listener space (convert to head relative) */
1148 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1149 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1150 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1151 if(props->HeadRelative == AL_FALSE)
1153 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1154 /* Transform source vectors */
1155 Position = aluMatrixfVector(Matrix, &Position);
1156 Velocity = aluMatrixfVector(Matrix, &Velocity);
1157 Direction = aluMatrixfVector(Matrix, &Direction);
1159 else
1161 const aluVector *lvelocity = &Listener->Params.Velocity;
1162 /* Offset the source velocity to be relative of the listener velocity */
1163 Velocity.v[0] += lvelocity->v[0];
1164 Velocity.v[1] += lvelocity->v[1];
1165 Velocity.v[2] += lvelocity->v[2];
1168 directional = aluNormalize(Direction.v) > FLT_EPSILON;
1169 SourceToListener.v[0] = -Position.v[0];
1170 SourceToListener.v[1] = -Position.v[1];
1171 SourceToListener.v[2] = -Position.v[2];
1172 SourceToListener.v[3] = 0.0f;
1173 Distance = aluNormalize(SourceToListener.v);
1175 /* Initial source gain */
1176 DryGain = props->Gain;
1177 DryGainHF = 1.0f;
1178 DryGainLF = 1.0f;
1179 for(i = 0;i < NumSends;i++)
1181 WetGain[i] = props->Gain;
1182 WetGainHF[i] = 1.0f;
1183 WetGainLF[i] = 1.0f;
1186 /* Calculate distance attenuation */
1187 ClampedDist = Distance;
1189 switch(Listener->Params.SourceDistanceModel ?
1190 props->DistanceModel : Listener->Params.DistanceModel)
1192 case InverseDistanceClamped:
1193 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1194 if(props->MaxDistance < props->RefDistance)
1195 break;
1196 /*fall-through*/
1197 case InverseDistance:
1198 if(!(props->RefDistance > 0.0f))
1199 ClampedDist = props->RefDistance;
1200 else
1202 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
1203 if(dist > 0.0f) DryGain *= props->RefDistance / dist;
1204 for(i = 0;i < NumSends;i++)
1206 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1207 if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
1210 break;
1212 case LinearDistanceClamped:
1213 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1214 if(props->MaxDistance < props->RefDistance)
1215 break;
1216 /*fall-through*/
1217 case LinearDistance:
1218 if(!(props->MaxDistance != props->RefDistance))
1219 ClampedDist = props->RefDistance;
1220 else
1222 ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
1223 (props->MaxDistance-props->RefDistance);
1224 DryGain *= maxf(1.0f - attn, 0.0f);
1225 for(i = 0;i < NumSends;i++)
1227 attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1228 (props->MaxDistance-props->RefDistance);
1229 WetGain[i] *= maxf(1.0f - attn, 0.0f);
1232 break;
1234 case ExponentDistanceClamped:
1235 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1236 if(props->MaxDistance < props->RefDistance)
1237 break;
1238 /*fall-through*/
1239 case ExponentDistance:
1240 if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
1241 ClampedDist = props->RefDistance;
1242 else
1244 DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
1245 for(i = 0;i < NumSends;i++)
1246 WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1248 break;
1250 case DisableDistance:
1251 ClampedDist = props->RefDistance;
1252 break;
1255 /* Distance-based air absorption */
1256 if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
1258 ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
1259 Listener->Params.MetersPerUnit;
1260 if(props->AirAbsorptionFactor > 0.0f)
1262 ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
1263 DryGainHF *= hfattn;
1264 for(i = 0;i < NumSends;i++)
1265 WetGainHF[i] *= hfattn;
1268 if(props->WetGainAuto)
1270 /* Apply a decay-time transformation to the wet path, based on the
1271 * source distance in meters. The initial decay of the reverb
1272 * effect is calculated and applied to the wet path.
1274 for(i = 0;i < NumSends;i++)
1276 ALfloat gain;
1278 if(!(DecayDistance[i] > 0.0f))
1279 continue;
1281 gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
1282 WetGain[i] *= gain;
1283 /* Yes, the wet path's air absorption is applied with
1284 * WetGainAuto on, rather than WetGainHFAuto.
1286 if(gain > 0.0f)
1288 ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
1289 WetGainHF[i] *= minf(gainhf / gain, 1.0f);
1295 /* Calculate directional soundcones */
1296 if(directional && props->InnerAngle < 360.0f)
1298 ALfloat ConeVolume;
1299 ALfloat ConeHF;
1300 ALfloat Angle;
1302 Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
1303 Angle = RAD2DEG(Angle * ConeScale * 2.0f);
1304 if(!(Angle > props->InnerAngle))
1306 ConeVolume = 1.0f;
1307 ConeHF = 1.0f;
1309 else if(Angle < props->OuterAngle)
1311 ALfloat scale = ( Angle-props->InnerAngle) /
1312 (props->OuterAngle-props->InnerAngle);
1313 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1314 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1316 else
1318 ConeVolume = props->OuterGain;
1319 ConeHF = props->OuterGainHF;
1322 DryGain *= ConeVolume;
1323 if(props->DryGainHFAuto)
1324 DryGainHF *= ConeHF;
1325 if(props->WetGainAuto)
1327 for(i = 0;i < NumSends;i++)
1328 WetGain[i] *= ConeVolume;
1330 if(props->WetGainHFAuto)
1332 for(i = 0;i < NumSends;i++)
1333 WetGainHF[i] *= ConeHF;
1337 /* Apply gain and frequency filters */
1338 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1339 DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1340 DryGainHF *= props->Direct.GainHF;
1341 DryGainLF *= props->Direct.GainLF;
1342 for(i = 0;i < NumSends;i++)
1344 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1345 WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
1346 WetGainHF[i] *= props->Send[i].GainHF;
1347 WetGainLF[i] *= props->Send[i].GainLF;
1351 /* Initial source pitch */
1352 Pitch = props->Pitch;
1354 /* Calculate velocity-based doppler effect */
1355 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1356 if(DopplerFactor > 0.0f)
1358 const aluVector *lvelocity = &Listener->Params.Velocity;
1359 const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1360 ALfloat vss, vls;
1362 vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1363 vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1365 if(!(vls < SpeedOfSound))
1367 /* Listener moving away from the source at the speed of sound.
1368 * Sound waves can't catch it.
1370 Pitch = 0.0f;
1372 else if(!(vss < SpeedOfSound))
1374 /* Source moving toward the listener at the speed of sound. Sound
1375 * waves bunch up to extreme frequencies.
1377 Pitch = HUGE_VALF;
1379 else
1381 /* Source and listener movement is nominal. Calculate the proper
1382 * doppler shift.
1384 Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
1388 /* Adjust pitch based on the buffer and output frequencies, and calculate
1389 * fixed-point stepping value.
1391 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
1392 if(Pitch > (ALfloat)MAX_PITCH)
1393 voice->Step = MAX_PITCH<<FRACTIONBITS;
1394 else
1395 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1396 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1397 voice->Resampler = SelectResampler(props->Resampler);
1399 if(Distance > FLT_EPSILON)
1401 dir[0] = -SourceToListener.v[0];
1402 /* Clamp Y, in case rounding errors caused it to end up outside of
1403 * -1...+1.
1405 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1406 dir[2] = -SourceToListener.v[2] * ZScale;
1408 else
1410 dir[0] = 0.0f;
1411 dir[1] = 0.0f;
1412 dir[2] = -1.0f;
1414 if(props->Radius > Distance)
1415 spread = F_TAU - Distance/props->Radius*F_PI;
1416 else if(Distance > FLT_EPSILON)
1417 spread = asinf(props->Radius / Distance) * 2.0f;
1418 else
1419 spread = 0.0f;
1421 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1422 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1425 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1427 ALbufferlistitem *BufferListItem;
1428 struct ALvoiceProps *props;
1430 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1431 if(!props && !force) return;
1433 if(props)
1435 memcpy(voice->Props, props,
1436 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1439 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &voice->FreeList, props);
1441 props = voice->Props;
1443 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1444 while(BufferListItem != NULL)
1446 const ALbuffer *buffer;
1447 if((buffer=BufferListItem->buffer) != NULL)
1449 if(props->SpatializeMode == SpatializeOn ||
1450 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1451 CalcAttnSourceParams(voice, props, buffer, context);
1452 else
1453 CalcNonAttnSourceParams(voice, props, buffer, context);
1454 break;
1456 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1461 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1463 ALvoice **voice, **voice_end;
1464 ALsource *source;
1465 ALsizei i;
1467 IncrementRef(&ctx->UpdateCount);
1468 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1470 ALboolean force = CalcListenerParams(ctx);
1471 for(i = 0;i < slots->count;i++)
1472 force |= CalcEffectSlotParams(slots->slot[i], ctx->Device);
1474 voice = ctx->Voices;
1475 voice_end = voice + ctx->VoiceCount;
1476 for(;voice != voice_end;++voice)
1478 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1479 if(source) CalcSourceParams(*voice, ctx, force);
1482 IncrementRef(&ctx->UpdateCount);
1486 static void ApplyDistanceComp(ALfloatBUFFERSIZE *restrict Samples, DistanceComp *distcomp,
1487 ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
1489 ALsizei i, c;
1491 Values = ASSUME_ALIGNED(Values, 16);
1492 for(c = 0;c < numchans;c++)
1494 ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
1495 const ALfloat gain = distcomp[c].Gain;
1496 const ALsizei base = distcomp[c].Length;
1497 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
1499 if(base == 0)
1501 if(gain < 1.0f)
1503 for(i = 0;i < SamplesToDo;i++)
1504 inout[i] *= gain;
1506 continue;
1509 if(SamplesToDo >= base)
1511 for(i = 0;i < base;i++)
1512 Values[i] = distbuf[i];
1513 for(;i < SamplesToDo;i++)
1514 Values[i] = inout[i-base];
1515 memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
1517 else
1519 for(i = 0;i < SamplesToDo;i++)
1520 Values[i] = distbuf[i];
1521 memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
1522 memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
1524 for(i = 0;i < SamplesToDo;i++)
1525 inout[i] = Values[i]*gain;
1530 static void ApplyDither(ALfloatBUFFERSIZE *restrict Samples, ALuint *dither_seed,
1531 const ALfloat quant_scale, const ALsizei SamplesToDo,
1532 const ALsizei numchans)
1534 const ALfloat invscale = 1.0f / quant_scale;
1535 ALuint seed = *dither_seed;
1536 ALsizei c, i;
1538 /* Dithering. Step 1, generate whitenoise (uniform distribution of random
1539 * values between -1 and +1). Step 2 is to add the noise to the samples,
1540 * before rounding and after scaling up to the desired quantization depth.
1542 for(c = 0;c < numchans;c++)
1544 ALfloat *restrict samples = Samples[c];
1545 for(i = 0;i < SamplesToDo;i++)
1547 ALfloat val = samples[i] * quant_scale;
1548 ALuint rng0 = dither_rng(&seed);
1549 ALuint rng1 = dither_rng(&seed);
1550 val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
1551 samples[i] = roundf(val) * invscale;
1554 *dither_seed = seed;
1558 static inline ALfloat aluF2F(ALfloat val)
1559 { return val; }
1560 static inline ALint aluF2I(ALfloat val)
1562 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1563 * integer range normalized floats can be safely converted to (a bit of the
1564 * exponent helps out, effectively giving 25 bits).
1566 return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
1568 static inline ALshort aluF2S(ALfloat val)
1569 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
1570 static inline ALbyte aluF2B(ALfloat val)
1571 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
1573 /* Define unsigned output variations. */
1574 #define DECL_TEMPLATE(T, Name, func, O) \
1575 static inline T Name(ALfloat val) \
1576 { return func(val)+O; }
1578 DECL_TEMPLATE(ALubyte, aluF2UB, aluF2B, 128)
1579 DECL_TEMPLATE(ALushort, aluF2US, aluF2S, 32768)
1580 DECL_TEMPLATE(ALuint, aluF2UI, aluF2I, 2147483648u)
1582 #undef DECL_TEMPLATE
1584 #define DECL_TEMPLATE(T, func) \
1585 static void Write##T(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1586 ALsizei SamplesToDo, ALsizei numchans) \
1588 ALsizei i, j; \
1589 for(j = 0;j < numchans;j++) \
1591 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1592 T *restrict out = (T*)OutBuffer + j; \
1594 for(i = 0;i < SamplesToDo;i++) \
1595 out[i*numchans] = func(in[i]); \
1599 DECL_TEMPLATE(ALfloat, aluF2F)
1600 DECL_TEMPLATE(ALuint, aluF2UI)
1601 DECL_TEMPLATE(ALint, aluF2I)
1602 DECL_TEMPLATE(ALushort, aluF2US)
1603 DECL_TEMPLATE(ALshort, aluF2S)
1604 DECL_TEMPLATE(ALubyte, aluF2UB)
1605 DECL_TEMPLATE(ALbyte, aluF2B)
1607 #undef DECL_TEMPLATE
1610 void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1612 ALsizei SamplesToDo;
1613 ALCcontext *ctx;
1614 ALsizei i, c;
1616 START_MIXER_MODE();
1617 while(size > 0)
1619 SamplesToDo = mini(size, BUFFERSIZE);
1620 for(c = 0;c < device->Dry.NumChannels;c++)
1621 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1622 if(device->Dry.Buffer != device->FOAOut.Buffer)
1623 for(c = 0;c < device->FOAOut.NumChannels;c++)
1624 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1625 if(device->Dry.Buffer != device->RealOut.Buffer)
1626 for(c = 0;c < device->RealOut.NumChannels;c++)
1627 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1629 IncrementRef(&device->MixCount);
1631 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1632 while(ctx)
1634 const struct ALeffectslotArray *auxslots;
1636 if(ctx->DefaultSlot != NULL)
1638 ALeffectslot *slot = ctx->DefaultSlot;
1639 CalcEffectSlotParams(slot, device);
1640 for(c = 0;c < slot->NumChannels;c++)
1641 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1644 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1645 UpdateContextSources(ctx, auxslots);
1647 for(i = 0;i < auxslots->count;i++)
1649 ALeffectslot *slot = auxslots->slot[i];
1650 for(c = 0;c < slot->NumChannels;c++)
1651 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1654 /* source processing */
1655 for(i = 0;i < ctx->VoiceCount;i++)
1657 ALvoice *voice = ctx->Voices[i];
1658 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
1659 if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
1660 voice->Step > 0)
1662 if(!MixSource(voice, source, device, SamplesToDo))
1664 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
1665 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1670 /* effect slot processing */
1671 for(i = 0;i < auxslots->count;i++)
1673 const ALeffectslot *slot = auxslots->slot[i];
1674 ALeffectState *state = slot->Params.EffectState;
1675 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1676 state->OutChannels);
1679 if(ctx->DefaultSlot != NULL)
1681 const ALeffectslot *slot = ctx->DefaultSlot;
1682 ALeffectState *state = slot->Params.EffectState;
1683 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1684 state->OutChannels);
1687 ctx = ctx->next;
1690 /* Increment the clock time. Every second's worth of samples is
1691 * converted and added to clock base so that large sample counts don't
1692 * overflow during conversion. This also guarantees an exact, stable
1693 * conversion. */
1694 device->SamplesDone += SamplesToDo;
1695 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1696 device->SamplesDone %= device->Frequency;
1697 IncrementRef(&device->MixCount);
1699 if(device->HrtfHandle)
1701 HrtfDirectMixerFunc HrtfMix;
1702 DirectHrtfState *state;
1703 int lidx, ridx;
1705 if(device->AmbiUp)
1706 ambiup_process(device->AmbiUp,
1707 device->Dry.Buffer, device->Dry.NumChannels,
1708 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1711 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1712 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1713 assert(lidx != -1 && ridx != -1);
1715 HrtfMix = SelectHrtfMixer();
1716 state = device->Hrtf;
1717 for(c = 0;c < device->Dry.NumChannels;c++)
1719 HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1720 device->Dry.Buffer[c], state->Offset, state->IrSize,
1721 SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
1722 state->Chan[c].Values, SamplesToDo
1725 state->Offset += SamplesToDo;
1727 else if(device->AmbiDecoder)
1729 if(device->Dry.Buffer != device->FOAOut.Buffer)
1730 bformatdec_upSample(device->AmbiDecoder,
1731 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1732 device->FOAOut.NumChannels, SamplesToDo
1734 bformatdec_process(device->AmbiDecoder,
1735 device->RealOut.Buffer, device->RealOut.NumChannels,
1736 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1739 else if(device->AmbiUp)
1741 ambiup_process(device->AmbiUp,
1742 device->RealOut.Buffer, device->RealOut.NumChannels,
1743 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1746 else if(device->Uhj_Encoder)
1748 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1749 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1750 if(lidx != -1 && ridx != -1)
1752 /* Encode to stereo-compatible 2-channel UHJ output. */
1753 EncodeUhj2(device->Uhj_Encoder,
1754 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1755 device->Dry.Buffer, SamplesToDo
1759 else if(device->Bs2b)
1761 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1762 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1763 if(lidx != -1 && ridx != -1)
1765 /* Apply binaural/crossfeed filter */
1766 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1767 device->RealOut.Buffer[ridx], SamplesToDo);
1771 if(buffer)
1773 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1774 ALsizei OutChannels = device->RealOut.NumChannels;
1776 /* Use NFCtrlData for temp value storage. */
1777 ApplyDistanceComp(OutBuffer, device->ChannelDelay, device->NFCtrlData,
1778 SamplesToDo, OutChannels);
1780 if(device->Limiter)
1781 ApplyCompression(device->Limiter, OutChannels, SamplesToDo, OutBuffer);
1783 if(device->DitherDepth > 0.0f)
1784 ApplyDither(OutBuffer, &device->DitherSeed, device->DitherDepth, SamplesToDo,
1785 OutChannels);
1787 #define WRITE(T, a, b, c, d) do { \
1788 Write##T(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d)); \
1789 buffer = (T*)buffer + (c)*(d); \
1790 } while(0)
1791 switch(device->FmtType)
1793 case DevFmtByte:
1794 WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1795 break;
1796 case DevFmtUByte:
1797 WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
1798 break;
1799 case DevFmtShort:
1800 WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
1801 break;
1802 case DevFmtUShort:
1803 WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
1804 break;
1805 case DevFmtInt:
1806 WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
1807 break;
1808 case DevFmtUInt:
1809 WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
1810 break;
1811 case DevFmtFloat:
1812 WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
1813 break;
1815 #undef WRITE
1818 size -= SamplesToDo;
1820 END_MIXER_MODE();
1824 void aluHandleDisconnect(ALCdevice *device)
1826 ALCcontext *ctx;
1828 device->Connected = ALC_FALSE;
1830 ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
1831 while(ctx)
1833 ALsizei i;
1834 for(i = 0;i < ctx->VoiceCount;i++)
1836 ALvoice *voice = ctx->Voices[i];
1837 ALsource *source;
1839 source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_acq_rel);
1840 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
1842 if(source)
1844 ALenum playing = AL_PLAYING;
1845 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1848 ctx->VoiceCount = 0;
1850 ctx = ctx->next;