Add a property to force source spatialization on or off
[openal-soft.git] / Alc / ALu.c
blob9ce452e9c489dd631700c22472c1528637a3a3db
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 static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
145 outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
146 outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
147 outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
150 static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
152 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
155 static ALfloat aluNormalize(ALfloat *vec)
157 ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
158 if(length > 0.0f)
160 ALfloat inv_length = 1.0f/length;
161 vec[0] *= inv_length;
162 vec[1] *= inv_length;
163 vec[2] *= inv_length;
165 return length;
168 static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
170 ALfloat v[4] = { vec[0], vec[1], vec[2], w };
172 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];
173 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];
174 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];
177 static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
179 aluVector v;
180 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];
181 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];
182 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];
183 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];
184 return v;
188 /* Prepares the interpolator for a given rate (determined by increment). A
189 * result of AL_FALSE indicates that the filter output will completely cut
190 * the input signal.
192 * With a bit of work, and a trade of memory for CPU cost, this could be
193 * modified for use with an interpolated increment for buttery-smooth pitch
194 * changes.
196 static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
198 static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
199 static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
200 static const ALuint to[4][BSINC_SCALE_COUNT] =
202 { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
203 { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
204 { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
205 { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
207 static const ALuint tm[2][BSINC_SCALE_COUNT] =
209 { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
210 { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
212 ALfloat sf;
213 ALuint si, pi;
214 ALboolean uncut = AL_TRUE;
216 if(increment > FRACTIONONE)
218 sf = (ALfloat)FRACTIONONE / increment;
219 if(sf < scaleBase)
221 /* Signal has been completely cut. The return result can be used
222 * to skip the filter (and output zeros) as an optimization.
224 sf = 0.0f;
225 si = 0;
226 uncut = AL_FALSE;
228 else
230 sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
231 si = fastf2u(sf);
232 /* The interpolation factor is fit to this diagonally-symmetric
233 * curve to reduce the transition ripple caused by interpolating
234 * different scales of the sinc function.
236 sf = 1.0f - cosf(asinf(sf - si));
239 else
241 sf = 0.0f;
242 si = BSINC_SCALE_COUNT - 1;
245 state->sf = sf;
246 state->m = m[si];
247 state->l = -(ALint)((m[si] / 2) - 1);
248 /* The CPU cost of this table re-mapping could be traded for the memory
249 * cost of a complete table map (1024 elements large).
251 for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
253 state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
254 state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
255 state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
256 state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
258 return uncut;
262 static ALboolean CalcListenerParams(ALCcontext *Context)
264 ALlistener *Listener = Context->Listener;
265 ALfloat N[3], V[3], U[3], P[3];
266 struct ALlistenerProps *props;
267 aluVector vel;
269 props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
270 if(!props) return AL_FALSE;
272 /* AT then UP */
273 N[0] = props->Forward[0];
274 N[1] = props->Forward[1];
275 N[2] = props->Forward[2];
276 aluNormalize(N);
277 V[0] = props->Up[0];
278 V[1] = props->Up[1];
279 V[2] = props->Up[2];
280 aluNormalize(V);
281 /* Build and normalize right-vector */
282 aluCrossproduct(N, V, U);
283 aluNormalize(U);
285 aluMatrixfSet(&Listener->Params.Matrix,
286 U[0], V[0], -N[0], 0.0,
287 U[1], V[1], -N[1], 0.0,
288 U[2], V[2], -N[2], 0.0,
289 0.0, 0.0, 0.0, 1.0
292 P[0] = props->Position[0];
293 P[1] = props->Position[1];
294 P[2] = props->Position[2];
295 aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
296 aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
298 aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
299 Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
301 Listener->Params.Gain = props->Gain * Context->GainBoost;
302 Listener->Params.MetersPerUnit = props->MetersPerUnit;
304 Listener->Params.DopplerFactor = props->DopplerFactor;
305 Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
307 Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
308 Listener->Params.DistanceModel = props->DistanceModel;
310 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props);
311 return AL_TRUE;
314 static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
316 struct ALeffectslotProps *props;
317 ALeffectState *state;
319 props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
320 if(!props) return AL_FALSE;
322 slot->Params.Gain = props->Gain;
323 slot->Params.AuxSendAuto = props->AuxSendAuto;
324 slot->Params.EffectType = props->Type;
325 if(IsReverbEffect(slot->Params.EffectType))
327 slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
328 slot->Params.DecayTime = props->Props.Reverb.DecayTime;
329 slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
331 else
333 slot->Params.RoomRolloff = 0.0f;
334 slot->Params.DecayTime = 0.0f;
335 slot->Params.AirAbsorptionGainHF = 1.0f;
338 /* Swap effect states. No need to play with the ref counts since they keep
339 * the same number of refs.
341 state = props->State;
342 props->State = slot->Params.EffectState;
343 slot->Params.EffectState = state;
345 V(state,update)(device, slot, &props->Props);
347 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
348 return AL_TRUE;
352 static const struct ChanMap MonoMap[1] = {
353 { FrontCenter, 0.0f, 0.0f }
354 }, RearMap[2] = {
355 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
356 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
357 }, QuadMap[4] = {
358 { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
359 { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
360 { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
361 { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
362 }, X51Map[6] = {
363 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
364 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
365 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
366 { LFE, 0.0f, 0.0f },
367 { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
368 { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
369 }, X61Map[7] = {
370 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
371 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
372 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
373 { LFE, 0.0f, 0.0f },
374 { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
375 { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
376 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
377 }, X71Map[8] = {
378 { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
379 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
380 { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
381 { LFE, 0.0f, 0.0f },
382 { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
383 { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
384 { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
385 { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
388 static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir,
389 const ALfloat Spread, const ALfloat DryGain,
390 const ALfloat DryGainHF, const ALfloat DryGainLF,
391 const ALfloat *WetGain, const ALfloat *WetGainLF,
392 const ALfloat *WetGainHF, ALeffectslot **SendSlots,
393 const ALbuffer *Buffer, const struct ALvoiceProps *props,
394 const ALlistener *Listener, const ALCdevice *Device)
396 struct ChanMap StereoMap[2] = {
397 { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
398 { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
400 bool DirectChannels = props->DirectChannels;
401 const ALsizei NumSends = Device->NumAuxSends;
402 const ALuint Frequency = Device->Frequency;
403 const struct ChanMap *chans = NULL;
404 ALsizei num_channels = 0;
405 bool isbformat = false;
406 ALsizei c, i, j;
408 switch(Buffer->FmtChannels)
410 case FmtMono:
411 chans = MonoMap;
412 num_channels = 1;
413 /* Mono buffers are never played direct. */
414 DirectChannels = false;
415 break;
417 case FmtStereo:
418 /* Convert counter-clockwise to clockwise. */
419 StereoMap[0].angle = -props->StereoPan[0];
420 StereoMap[1].angle = -props->StereoPan[1];
422 chans = StereoMap;
423 num_channels = 2;
424 break;
426 case FmtRear:
427 chans = RearMap;
428 num_channels = 2;
429 break;
431 case FmtQuad:
432 chans = QuadMap;
433 num_channels = 4;
434 break;
436 case FmtX51:
437 chans = X51Map;
438 num_channels = 6;
439 break;
441 case FmtX61:
442 chans = X61Map;
443 num_channels = 7;
444 break;
446 case FmtX71:
447 chans = X71Map;
448 num_channels = 8;
449 break;
451 case FmtBFormat2D:
452 num_channels = 3;
453 isbformat = true;
454 DirectChannels = false;
455 break;
457 case FmtBFormat3D:
458 num_channels = 4;
459 isbformat = true;
460 DirectChannels = false;
461 break;
464 voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
465 if(isbformat)
467 /* Special handling for B-Format sources. */
469 if(Distance > FLT_EPSILON)
471 /* Panning a B-Format sound toward some direction is easy. Just pan
472 * the first (W) channel as a normal mono sound and silence the
473 * others.
475 ALfloat coeffs[MAX_AMBI_COEFFS];
477 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
479 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
480 ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
481 (mdist * (ALfloat)Device->Frequency);
482 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
483 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
484 /* Clamp w0 for really close distances, to prevent excessive
485 * bass.
487 w0 = minf(w0, w1*4.0f);
489 /* Only need to adjust the first channel of a B-Format source. */
490 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
491 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
492 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
494 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
495 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
496 voice->Flags |= VOICE_HAS_NFC;
499 if(Device->Render_Mode == StereoPair)
501 ALfloat ev = asinf(Dir[1]);
502 ALfloat az = atan2f(Dir[0], -Dir[2]);
503 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
505 else
506 CalcDirectionCoeffs(Dir, Spread, coeffs);
508 /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
509 ComputePanningGains(Device->Dry, coeffs, DryGain*1.414213562f,
510 voice->Direct.Params[0].Gains.Target);
511 for(c = 1;c < num_channels;c++)
513 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
514 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
517 for(i = 0;i < NumSends;i++)
519 const ALeffectslot *Slot = SendSlots[i];
520 if(Slot)
521 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
522 coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
524 else
525 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
526 voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
527 for(c = 1;c < num_channels;c++)
529 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
530 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
534 else
536 /* Non-panned B-Format has its XYZ channels rotated according to
537 * the orientation.
539 ALfloat N[3], V[3], U[3];
540 aluMatrixf matrix;
541 ALfloat scale;
543 if(Device->AvgSpeakerDist > 0.0f)
545 /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
546 * is what we want for FOA input. The first channel may have
547 * been previously re-adjusted if panned, so reset it.
549 NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f);
550 NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f);
551 NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 0.0f);
553 voice->Direct.ChannelsPerOrder[0] = 1;
554 voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
555 for(i = 2;i < MAX_AMBI_ORDER+1;i++)
556 voice->Direct.ChannelsPerOrder[2] = 0;
557 voice->Flags |= VOICE_HAS_NFC;
560 /* AT then UP */
561 N[0] = props->Orientation[0][0];
562 N[1] = props->Orientation[0][1];
563 N[2] = props->Orientation[0][2];
564 aluNormalize(N);
565 V[0] = props->Orientation[1][0];
566 V[1] = props->Orientation[1][1];
567 V[2] = props->Orientation[1][2];
568 aluNormalize(V);
569 if(!props->HeadRelative)
571 const aluMatrixf *lmatrix = &Listener->Params.Matrix;
572 aluMatrixfFloat3(N, 0.0f, lmatrix);
573 aluMatrixfFloat3(V, 0.0f, lmatrix);
575 /* Build and normalize right-vector */
576 aluCrossproduct(N, V, U);
577 aluNormalize(U);
579 /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
580 scale = 1.732050808f;
581 aluMatrixfSet(&matrix,
582 1.414213562f, 0.0f, 0.0f, 0.0f,
583 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
584 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
585 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
588 voice->Direct.Buffer = Device->FOAOut.Buffer;
589 voice->Direct.Channels = Device->FOAOut.NumChannels;
590 for(c = 0;c < num_channels;c++)
591 ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
592 voice->Direct.Params[c].Gains.Target);
593 for(i = 0;i < NumSends;i++)
595 const ALeffectslot *Slot = SendSlots[i];
596 if(Slot)
598 for(c = 0;c < num_channels;c++)
599 ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
600 matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
603 else
605 for(c = 0;c < num_channels;c++)
606 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
607 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
612 else if(DirectChannels)
614 /* Skip the virtual channels and write inputs to the real output with
615 * no explicit panning.
617 voice->Direct.Buffer = Device->RealOut.Buffer;
618 voice->Direct.Channels = Device->RealOut.NumChannels;
620 for(c = 0;c < num_channels;c++)
622 int idx;
623 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
624 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
625 if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
626 voice->Direct.Params[c].Gains.Target[idx] = DryGain;
629 /* Auxiliary sends still use normal channel panning since they mix to
630 * B-Format, which can't channel-match.
632 for(c = 0;c < num_channels;c++)
634 ALfloat coeffs[MAX_AMBI_COEFFS];
635 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
637 for(i = 0;i < NumSends;i++)
639 const ALeffectslot *Slot = SendSlots[i];
640 if(Slot)
641 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
642 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
644 else
645 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
646 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
650 else if(Device->Render_Mode == HrtfRender)
652 /* Full HRTF rendering. Skip the virtual channels and render to the
653 * real outputs.
655 voice->Direct.Buffer = Device->RealOut.Buffer;
656 voice->Direct.Channels = Device->RealOut.NumChannels;
658 if(Distance > FLT_EPSILON)
660 ALfloat coeffs[MAX_AMBI_COEFFS];
661 ALfloat ev, az;
663 ev = asinf(Dir[1]);
664 az = atan2f(Dir[0], -Dir[2]);
666 /* Get the HRIR coefficients and delays just once, for the given
667 * source direction.
669 GetHrtfCoeffs(Device->HrtfHandle, ev, az, Spread,
670 voice->Direct.Params[0].Hrtf.Target.Coeffs,
671 voice->Direct.Params[0].Hrtf.Target.Delay);
672 voice->Direct.Params[0].Hrtf.Target.Gain = DryGain;
674 /* Remaining channels use the same results as the first. */
675 for(c = 1;c < num_channels;c++)
677 /* Skip LFE */
678 if(chans[c].channel == LFE)
679 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
680 sizeof(voice->Direct.Params[c].Hrtf.Target));
681 else
682 voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
685 /* Calculate the directional coefficients once, which apply to all
686 * input channels of the source sends.
688 CalcDirectionCoeffs(Dir, Spread, coeffs);
690 for(i = 0;i < NumSends;i++)
692 const ALeffectslot *Slot = SendSlots[i];
693 if(Slot)
694 for(c = 0;c < num_channels;c++)
696 /* Skip LFE */
697 if(chans[c].channel == LFE)
698 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
699 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
700 else
701 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
702 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
705 else
706 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
707 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
710 else
712 for(c = 0;c < num_channels;c++)
714 ALfloat coeffs[MAX_AMBI_COEFFS];
716 if(chans[c].channel == LFE)
718 /* Skip LFE */
719 memset(&voice->Direct.Params[c].Hrtf.Target, 0,
720 sizeof(voice->Direct.Params[c].Hrtf.Target));
721 for(i = 0;i < NumSends;i++)
723 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
724 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
726 continue;
729 /* Get the HRIR coefficients and delays for this channel
730 * position.
732 GetHrtfCoeffs(Device->HrtfHandle,
733 chans[c].elevation, chans[c].angle, Spread,
734 voice->Direct.Params[c].Hrtf.Target.Coeffs,
735 voice->Direct.Params[c].Hrtf.Target.Delay
737 voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
739 /* Normal panning for auxiliary sends. */
740 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
742 for(i = 0;i < NumSends;i++)
744 const ALeffectslot *Slot = SendSlots[i];
745 if(Slot)
746 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
747 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
749 else
750 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
751 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
756 voice->Flags |= VOICE_HAS_HRTF;
758 else
760 /* Non-HRTF rendering. Use normal panning to the output. */
762 if(Distance > FLT_EPSILON)
764 ALfloat coeffs[MAX_AMBI_COEFFS];
765 ALfloat w0 = 0.0f;
767 /* Calculate NFC filter coefficient if needed. */
768 if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f)
770 ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
771 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
772 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
773 w0 = SPEEDOFSOUNDMETRESPERSEC /
774 (mdist * (ALfloat)Device->Frequency);
775 /* Clamp w0 for really close distances, to prevent excessive
776 * bass.
778 w0 = minf(w0, w1*4.0f);
780 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
781 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
782 voice->Flags |= VOICE_HAS_NFC;
785 /* Calculate the directional coefficients once, which apply to all
786 * input channels.
788 if(Device->Render_Mode == StereoPair)
790 ALfloat ev = asinf(Dir[1]);
791 ALfloat az = atan2f(Dir[0], -Dir[2]);
792 CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs);
794 else
795 CalcDirectionCoeffs(Dir, Spread, coeffs);
797 for(c = 0;c < num_channels;c++)
799 /* Adjust NFC filters if needed. */
800 if((voice->Flags&VOICE_HAS_NFC))
802 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
803 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
804 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
807 /* Special-case LFE */
808 if(chans[c].channel == LFE)
810 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
811 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
812 if(Device->Dry.Buffer == Device->RealOut.Buffer)
814 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
815 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
817 continue;
820 ComputePanningGains(Device->Dry,
821 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
825 for(i = 0;i < NumSends;i++)
827 const ALeffectslot *Slot = SendSlots[i];
828 if(Slot)
829 for(c = 0;c < num_channels;c++)
831 /* Skip LFE */
832 if(chans[c].channel == LFE)
833 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
834 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
835 else
836 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
837 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
840 else
841 for(c = 0;c < num_channels;c++)
843 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
844 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
848 else
850 ALfloat w0 = 0.0f;
852 if(Device->AvgSpeakerDist > 0.0f)
854 /* If the source distance is 0, set w0 to w1 to act as a pass-
855 * through. We still want to pass the signal through the
856 * filters so they keep an appropriate history, in case the
857 * source moves away from the listener.
859 w0 = SPEEDOFSOUNDMETRESPERSEC /
860 (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
862 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
863 voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
864 voice->Flags |= VOICE_HAS_NFC;
867 for(c = 0;c < num_channels;c++)
869 ALfloat coeffs[MAX_AMBI_COEFFS];
871 if((voice->Flags&VOICE_HAS_NFC))
873 NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0);
874 NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0);
875 NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0);
878 /* Special-case LFE */
879 if(chans[c].channel == LFE)
881 for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
882 voice->Direct.Params[c].Gains.Target[j] = 0.0f;
883 if(Device->Dry.Buffer == Device->RealOut.Buffer)
885 int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
886 if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
889 for(i = 0;i < NumSends;i++)
891 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
892 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
894 continue;
897 if(Device->Render_Mode == StereoPair)
898 CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
899 else
900 CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
901 ComputePanningGains(Device->Dry,
902 coeffs, DryGain, voice->Direct.Params[c].Gains.Target
905 for(i = 0;i < NumSends;i++)
907 const ALeffectslot *Slot = SendSlots[i];
908 if(Slot)
909 ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
910 coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
912 else
913 for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
914 voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
921 ALfloat hfScale = props->Direct.HFReference / Frequency;
922 ALfloat lfScale = props->Direct.LFReference / Frequency;
923 ALfloat gainHF = maxf(DryGainHF, 0.0625f); /* Limit -24dB */
924 ALfloat gainLF = maxf(DryGainLF, 0.0625f);
925 for(c = 0;c < num_channels;c++)
927 voice->Direct.Params[c].FilterType = AF_None;
928 if(gainHF != 1.0f) voice->Direct.Params[c].FilterType |= AF_LowPass;
929 if(gainLF != 1.0f) voice->Direct.Params[c].FilterType |= AF_HighPass;
930 ALfilterState_setParams(
931 &voice->Direct.Params[c].LowPass, ALfilterType_HighShelf,
932 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
934 ALfilterState_setParams(
935 &voice->Direct.Params[c].HighPass, ALfilterType_LowShelf,
936 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
940 for(i = 0;i < NumSends;i++)
942 ALfloat hfScale = props->Send[i].HFReference / Frequency;
943 ALfloat lfScale = props->Send[i].LFReference / Frequency;
944 ALfloat gainHF = maxf(WetGainHF[i], 0.0625f);
945 ALfloat gainLF = maxf(WetGainLF[i], 0.0625f);
946 for(c = 0;c < num_channels;c++)
948 voice->Send[i].Params[c].FilterType = AF_None;
949 if(gainHF != 1.0f) voice->Send[i].Params[c].FilterType |= AF_LowPass;
950 if(gainLF != 1.0f) voice->Send[i].Params[c].FilterType |= AF_HighPass;
951 ALfilterState_setParams(
952 &voice->Send[i].Params[c].LowPass, ALfilterType_HighShelf,
953 gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
955 ALfilterState_setParams(
956 &voice->Send[i].Params[c].HighPass, ALfilterType_LowShelf,
957 gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
963 static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
965 static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
966 const ALCdevice *Device = ALContext->Device;
967 const ALlistener *Listener = ALContext->Listener;
968 ALfloat DryGain, DryGainHF, DryGainLF;
969 ALfloat WetGain[MAX_SENDS];
970 ALfloat WetGainHF[MAX_SENDS];
971 ALfloat WetGainLF[MAX_SENDS];
972 ALeffectslot *SendSlots[MAX_SENDS];
973 ALfloat Pitch;
974 ALsizei i;
976 voice->Direct.Buffer = Device->Dry.Buffer;
977 voice->Direct.Channels = Device->Dry.NumChannels;
978 for(i = 0;i < Device->NumAuxSends;i++)
980 SendSlots[i] = props->Send[i].Slot;
981 if(!SendSlots[i] && i == 0)
982 SendSlots[i] = Device->DefaultSlot;
983 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
985 SendSlots[i] = NULL;
986 voice->Send[i].Buffer = NULL;
987 voice->Send[i].Channels = 0;
989 else
991 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
992 voice->Send[i].Channels = SendSlots[i]->NumChannels;
996 /* Calculate the stepping value */
997 Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
998 if(Pitch > (ALfloat)MAX_PITCH)
999 voice->Step = MAX_PITCH<<FRACTIONBITS;
1000 else
1001 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1002 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1003 voice->Resampler = SelectResampler(props->Resampler);
1005 /* Calculate gains */
1006 DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
1007 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1008 DryGain = minf(DryGain, GAIN_MIX_MAX);
1009 DryGainHF = props->Direct.GainHF;
1010 DryGainLF = props->Direct.GainLF;
1011 for(i = 0;i < Device->NumAuxSends;i++)
1013 WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
1014 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1015 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1016 WetGainHF[i] = props->Send[i].GainHF;
1017 WetGainLF[i] = props->Send[i].GainLF;
1020 CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
1021 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1024 static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
1026 const ALCdevice *Device = ALContext->Device;
1027 const ALlistener *Listener = ALContext->Listener;
1028 const ALsizei NumSends = Device->NumAuxSends;
1029 aluVector Position, Velocity, Direction, SourceToListener;
1030 ALfloat Distance, ClampedDist;
1031 ALfloat DopplerFactor;
1032 ALfloat RoomAirAbsorption[MAX_SENDS];
1033 ALeffectslot *SendSlots[MAX_SENDS];
1034 ALfloat Attenuation;
1035 ALfloat RoomAttenuation[MAX_SENDS];
1036 ALfloat RoomRolloff[MAX_SENDS];
1037 ALfloat DecayDistance[MAX_SENDS];
1038 ALfloat DryGain, DryGainHF, DryGainLF;
1039 ALfloat WetGain[MAX_SENDS];
1040 ALfloat WetGainHF[MAX_SENDS];
1041 ALfloat WetGainLF[MAX_SENDS];
1042 ALfloat dir[3];
1043 ALfloat spread;
1044 ALfloat Pitch;
1045 ALint i;
1047 Pitch = 1.0f;
1048 aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
1049 aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
1050 aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
1052 voice->Direct.Buffer = Device->Dry.Buffer;
1053 voice->Direct.Channels = Device->Dry.NumChannels;
1054 for(i = 0;i < NumSends;i++)
1056 SendSlots[i] = props->Send[i].Slot;
1057 if(!SendSlots[i] && i == 0)
1058 SendSlots[i] = Device->DefaultSlot;
1059 if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
1061 SendSlots[i] = NULL;
1062 RoomRolloff[i] = 0.0f;
1063 DecayDistance[i] = 0.0f;
1064 RoomAirAbsorption[i] = 1.0f;
1066 else if(SendSlots[i]->Params.AuxSendAuto)
1068 RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
1069 DecayDistance[i] = SendSlots[i]->Params.DecayTime *
1070 SPEEDOFSOUNDMETRESPERSEC;
1071 RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
1073 else
1075 /* If the slot's auxiliary send auto is off, the data sent to the
1076 * effect slot is the same as the dry path, sans filter effects */
1077 RoomRolloff[i] = props->RollOffFactor;
1078 DecayDistance[i] = 0.0f;
1079 RoomAirAbsorption[i] = AIRABSORBGAINHF;
1082 if(!SendSlots[i])
1084 voice->Send[i].Buffer = NULL;
1085 voice->Send[i].Channels = 0;
1087 else
1089 voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
1090 voice->Send[i].Channels = SendSlots[i]->NumChannels;
1094 /* Transform source to listener space (convert to head relative) */
1095 if(props->HeadRelative == AL_FALSE)
1097 const aluMatrixf *Matrix = &Listener->Params.Matrix;
1098 /* Transform source vectors */
1099 Position = aluMatrixfVector(Matrix, &Position);
1100 Velocity = aluMatrixfVector(Matrix, &Velocity);
1101 Direction = aluMatrixfVector(Matrix, &Direction);
1103 else
1105 const aluVector *lvelocity = &Listener->Params.Velocity;
1106 /* Offset the source velocity to be relative of the listener velocity */
1107 Velocity.v[0] += lvelocity->v[0];
1108 Velocity.v[1] += lvelocity->v[1];
1109 Velocity.v[2] += lvelocity->v[2];
1112 aluNormalize(Direction.v);
1113 SourceToListener.v[0] = -Position.v[0];
1114 SourceToListener.v[1] = -Position.v[1];
1115 SourceToListener.v[2] = -Position.v[2];
1116 SourceToListener.v[3] = 0.0f;
1117 Distance = aluNormalize(SourceToListener.v);
1119 /* Calculate distance attenuation */
1120 ClampedDist = Distance;
1122 Attenuation = 1.0f;
1123 for(i = 0;i < NumSends;i++)
1124 RoomAttenuation[i] = 1.0f;
1125 switch(Listener->Params.SourceDistanceModel ?
1126 props->DistanceModel : Listener->Params.DistanceModel)
1128 case InverseDistanceClamped:
1129 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1130 if(props->MaxDistance < props->RefDistance)
1131 break;
1132 /*fall-through*/
1133 case InverseDistance:
1134 if(props->RefDistance > 0.0f)
1136 ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RollOffFactor);
1137 if(dist > 0.0f) Attenuation = props->RefDistance / dist;
1138 for(i = 0;i < NumSends;i++)
1140 dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
1141 if(dist > 0.0f) RoomAttenuation[i] = props->RefDistance / dist;
1144 break;
1146 case LinearDistanceClamped:
1147 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1148 if(props->MaxDistance < props->RefDistance)
1149 break;
1150 /*fall-through*/
1151 case LinearDistance:
1152 if(props->MaxDistance != props->RefDistance)
1154 Attenuation = props->RollOffFactor * (ClampedDist-props->RefDistance) /
1155 (props->MaxDistance-props->RefDistance);
1156 Attenuation = maxf(1.0f - Attenuation, 0.0f);
1157 for(i = 0;i < NumSends;i++)
1159 RoomAttenuation[i] = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
1160 (props->MaxDistance-props->RefDistance);
1161 RoomAttenuation[i] = maxf(1.0f - RoomAttenuation[i], 0.0f);
1164 break;
1166 case ExponentDistanceClamped:
1167 ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
1168 if(props->MaxDistance < props->RefDistance)
1169 break;
1170 /*fall-through*/
1171 case ExponentDistance:
1172 if(ClampedDist > 0.0f && props->RefDistance > 0.0f)
1174 Attenuation = powf(ClampedDist/props->RefDistance, -props->RollOffFactor);
1175 for(i = 0;i < NumSends;i++)
1176 RoomAttenuation[i] = powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
1178 break;
1180 case DisableDistance:
1181 ClampedDist = props->RefDistance;
1182 break;
1185 /* Source Gain + Attenuation */
1186 DryGain = props->Gain * Attenuation;
1187 DryGainHF = 1.0f;
1188 DryGainLF = 1.0f;
1189 for(i = 0;i < NumSends;i++)
1191 WetGain[i] = props->Gain * RoomAttenuation[i];
1192 WetGainHF[i] = 1.0f;
1193 WetGainLF[i] = 1.0f;
1196 /* Distance-based air absorption */
1197 if(props->AirAbsorptionFactor > 0.0f && ClampedDist > props->RefDistance)
1199 ALfloat meters = (ClampedDist-props->RefDistance) * Listener->Params.MetersPerUnit;
1200 DryGainHF *= powf(AIRABSORBGAINHF, props->AirAbsorptionFactor*meters);
1201 for(i = 0;i < NumSends;i++)
1202 WetGainHF[i] *= powf(RoomAirAbsorption[i], props->AirAbsorptionFactor*meters);
1205 if(props->WetGainAuto)
1207 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
1209 /* Apply a decay-time transformation to the wet path, based on the
1210 * attenuation of the dry path.
1212 * Using the apparent distance, based on the distance attenuation, the
1213 * initial decay of the reverb effect is calculated and applied to the
1214 * wet path.
1216 for(i = 0;i < NumSends;i++)
1218 if(DecayDistance[i] > 0.0f)
1219 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
1223 /* Calculate directional soundcones */
1224 if(props->InnerAngle < 360.0f)
1226 ALfloat ConeVolume;
1227 ALfloat ConeHF;
1228 ALfloat Angle;
1229 ALfloat scale;
1231 Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
1232 if(Angle > props->InnerAngle)
1234 if(Angle < props->OuterAngle)
1236 scale = (Angle-props->InnerAngle) / (props->OuterAngle-props->InnerAngle);
1237 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1238 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1240 else
1242 ConeVolume = props->OuterGain;
1243 ConeHF = props->OuterGainHF;
1245 DryGain *= ConeVolume;
1246 if(props->DryGainHFAuto)
1247 DryGainHF *= ConeHF;
1250 /* Wet path uses the total area of the cone emitter (the room will
1251 * receive the same amount of sound regardless of its direction).
1253 scale = (asinf(maxf((props->OuterAngle-props->InnerAngle)/360.0f, 0.0f)) / F_PI) +
1254 (props->InnerAngle/360.0f);
1255 if(props->WetGainAuto)
1257 ConeVolume = lerp(1.0f, props->OuterGain, scale);
1258 for(i = 0;i < NumSends;i++)
1259 WetGain[i] *= ConeVolume;
1261 if(props->WetGainHFAuto)
1263 ConeHF = lerp(1.0f, props->OuterGainHF, scale);
1264 for(i = 0;i < NumSends;i++)
1265 WetGainHF[i] *= ConeHF;
1269 /* Apply gain and frequency filters */
1270 DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
1271 DryGain *= props->Direct.Gain * Listener->Params.Gain;
1272 DryGain = minf(DryGain, GAIN_MIX_MAX);
1273 DryGainHF *= props->Direct.GainHF;
1274 DryGainLF *= props->Direct.GainLF;
1275 for(i = 0;i < NumSends;i++)
1277 WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
1278 WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
1279 WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
1280 WetGainHF[i] *= props->Send[i].GainHF;
1281 WetGainLF[i] *= props->Send[i].GainLF;
1284 /* Calculate velocity-based doppler effect */
1285 DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
1286 if(DopplerFactor > 0.0f)
1288 const aluVector *lvelocity = &Listener->Params.Velocity;
1289 ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
1290 ALfloat VSS, VLS;
1292 if(SpeedOfSound < 1.0f)
1294 DopplerFactor *= 1.0f/SpeedOfSound;
1295 SpeedOfSound = 1.0f;
1298 VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
1299 VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
1301 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
1302 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
1305 /* Calculate fixed-point stepping value, based on the pitch, buffer
1306 * frequency, and output frequency.
1308 Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
1309 if(Pitch > (ALfloat)MAX_PITCH)
1310 voice->Step = MAX_PITCH<<FRACTIONBITS;
1311 else
1312 voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
1313 BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
1314 voice->Resampler = SelectResampler(props->Resampler);
1316 if(Distance > FLT_EPSILON)
1318 dir[0] = -SourceToListener.v[0];
1319 /* Clamp Y, in case rounding errors caused it to end up outside of
1320 * -1...+1.
1322 dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f);
1323 dir[2] = -SourceToListener.v[2] * ZScale;
1325 else
1327 dir[0] = 0.0f;
1328 dir[1] = 0.0f;
1329 dir[2] = -1.0f;
1331 if(props->Radius > Distance)
1332 spread = F_TAU - Distance/props->Radius*F_PI;
1333 else if(Distance > FLT_EPSILON)
1334 spread = asinf(props->Radius / Distance) * 2.0f;
1335 else
1336 spread = 0.0f;
1338 CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain,
1339 WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
1342 static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
1344 ALbufferlistitem *BufferListItem;
1345 struct ALvoiceProps *props;
1347 props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
1348 if(!props && !force) return;
1350 if(props)
1352 memcpy(voice->Props, props,
1353 FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
1356 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &voice->FreeList, props);
1358 props = voice->Props;
1360 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1361 while(BufferListItem != NULL)
1363 const ALbuffer *buffer;
1364 if((buffer=BufferListItem->buffer) != NULL)
1366 if(props->SpatializeMode == SpatializeOn ||
1367 (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
1368 CalcAttnSourceParams(voice, props, buffer, context);
1369 else
1370 CalcNonAttnSourceParams(voice, props, buffer, context);
1371 break;
1373 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
1378 static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots)
1380 ALvoice **voice, **voice_end;
1381 ALsource *source;
1382 ALsizei i;
1384 IncrementRef(&ctx->UpdateCount);
1385 if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
1387 ALboolean force = CalcListenerParams(ctx);
1388 for(i = 0;i < slots->count;i++)
1389 force |= CalcEffectSlotParams(slots->slot[i], ctx->Device);
1391 voice = ctx->Voices;
1392 voice_end = voice + ctx->VoiceCount;
1393 for(;voice != voice_end;++voice)
1395 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1396 if(source) CalcSourceParams(*voice, ctx, force);
1399 IncrementRef(&ctx->UpdateCount);
1403 static ALfloat ApplyLimiter(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALsizei NumChans,
1404 const ALfloat AttackRate, const ALfloat ReleaseRate,
1405 const ALfloat InGain, ALfloat (*restrict Gains),
1406 const ALsizei SamplesToDo)
1408 bool do_limit = false;
1409 ALsizei c, i;
1411 OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
1412 Gains = ASSUME_ALIGNED(Gains, 16);
1414 for(i = 0;i < SamplesToDo;i++)
1415 Gains[i] = 1.0f;
1417 for(c = 0;c < NumChans;c++)
1419 ALfloat lastgain = InGain;
1420 for(i = 0;i < SamplesToDo;i++)
1422 /* Clamp limiter range to 0dB...-80dB. */
1423 ALfloat gain = 1.0f / clampf(fabsf(OutBuffer[c][i]), 1.0f, 1000.0f);
1424 if(lastgain >= gain)
1425 lastgain = maxf(lastgain*AttackRate, gain);
1426 else
1427 lastgain = minf(lastgain/ReleaseRate, gain);
1428 do_limit |= (lastgain < 1.0f);
1430 lastgain = minf(lastgain, Gains[i]);
1431 Gains[i] = lastgain;
1434 if(do_limit)
1436 for(c = 0;c < NumChans;c++)
1438 for(i = 0;i < SamplesToDo;i++)
1439 OutBuffer[c][i] *= Gains[i];
1443 return Gains[SamplesToDo-1];
1446 static inline ALfloat aluF2F(ALfloat val)
1447 { return val; }
1449 #define S25_MAX_NORM (16777215.0f/16777216.0f)
1450 static inline ALint aluF2I(ALfloat val)
1452 /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
1453 * integer range normalized floats can be safely converted to (a bit of the
1454 * exponent helps out, effectively giving 25 bits).
1456 return fastf2i(clampf(val, -1.0f, S25_MAX_NORM)*16777216.0f)<<7;
1458 static inline ALuint aluF2UI(ALfloat val)
1459 { return aluF2I(val)+2147483648u; }
1461 #define S16_MAX_NORM (32767.0f/32768.0f)
1462 static inline ALshort aluF2S(ALfloat val)
1463 { return fastf2i(clampf(val, -1.0f, S16_MAX_NORM)*32768.0f); }
1464 static inline ALushort aluF2US(ALfloat val)
1465 { return aluF2S(val)+32768; }
1467 #define S8_MAX_NORM (127.0f/128.0f)
1468 static inline ALbyte aluF2B(ALfloat val)
1469 { return fastf2i(clampf(val, -1.0f, S8_MAX_NORM)*128.0f); }
1470 static inline ALubyte aluF2UB(ALfloat val)
1471 { return aluF2B(val)+128; }
1473 #define DECL_TEMPLATE(T, func) \
1474 static void Write_##T(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
1475 DistanceComp *distcomp, ALsizei SamplesToDo, \
1476 ALsizei numchans) \
1478 ALsizei i, j; \
1479 for(j = 0;j < numchans;j++) \
1481 const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
1482 T *restrict out = (T*)OutBuffer + j; \
1483 const ALfloat gain = distcomp[j].Gain; \
1484 const ALsizei base = distcomp[j].Length; \
1485 ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[j].Buffer, 16); \
1486 if(base > 0 || gain != 1.0f) \
1488 if(SamplesToDo >= base) \
1490 for(i = 0;i < base;i++) \
1491 out[i*numchans] = func(distbuf[i]*gain); \
1492 for(;i < SamplesToDo;i++) \
1493 out[i*numchans] = func(in[i-base]*gain); \
1494 memcpy(distbuf, &in[SamplesToDo-base], base*sizeof(ALfloat)); \
1496 else \
1498 for(i = 0;i < SamplesToDo;i++) \
1499 out[i*numchans] = func(distbuf[i]*gain); \
1500 memmove(distbuf, distbuf+SamplesToDo, \
1501 (base-SamplesToDo)*sizeof(ALfloat)); \
1502 memcpy(distbuf+base-SamplesToDo, in, \
1503 SamplesToDo*sizeof(ALfloat)); \
1506 else for(i = 0;i < SamplesToDo;i++) \
1507 out[i*numchans] = func(in[i]); \
1511 DECL_TEMPLATE(ALfloat, aluF2F)
1512 DECL_TEMPLATE(ALuint, aluF2UI)
1513 DECL_TEMPLATE(ALint, aluF2I)
1514 DECL_TEMPLATE(ALushort, aluF2US)
1515 DECL_TEMPLATE(ALshort, aluF2S)
1516 DECL_TEMPLATE(ALubyte, aluF2UB)
1517 DECL_TEMPLATE(ALbyte, aluF2B)
1519 #undef DECL_TEMPLATE
1522 void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
1524 ALsizei SamplesToDo;
1525 ALvoice **voice, **voice_end;
1526 ALeffectslot *slot;
1527 ALsource *source;
1528 ALCcontext *ctx;
1529 FPUCtl oldMode;
1530 ALsizei i, c;
1532 SetMixerFPUMode(&oldMode);
1534 while(size > 0)
1536 SamplesToDo = mini(size, BUFFERSIZE);
1537 for(c = 0;c < device->Dry.NumChannels;c++)
1538 memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1539 if(device->Dry.Buffer != device->FOAOut.Buffer)
1540 for(c = 0;c < device->FOAOut.NumChannels;c++)
1541 memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1542 if(device->Dry.Buffer != device->RealOut.Buffer)
1543 for(c = 0;c < device->RealOut.NumChannels;c++)
1544 memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
1546 IncrementRef(&device->MixCount);
1548 if((slot=device->DefaultSlot) != NULL)
1550 CalcEffectSlotParams(device->DefaultSlot, device);
1551 for(c = 0;c < slot->NumChannels;c++)
1552 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1555 ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
1556 while(ctx)
1558 const struct ALeffectslotArray *auxslots;
1560 auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
1561 UpdateContextSources(ctx, auxslots);
1563 for(i = 0;i < auxslots->count;i++)
1565 ALeffectslot *slot = auxslots->slot[i];
1566 for(c = 0;c < slot->NumChannels;c++)
1567 memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
1570 /* source processing */
1571 voice = ctx->Voices;
1572 voice_end = voice + ctx->VoiceCount;
1573 for(;voice != voice_end;++voice)
1575 source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
1576 if(source && ATOMIC_LOAD(&(*voice)->Playing, almemory_order_relaxed) &&
1577 (*voice)->Step > 0)
1579 if(!MixSource(*voice, source, device, SamplesToDo))
1581 ATOMIC_STORE(&(*voice)->Source, NULL, almemory_order_relaxed);
1582 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1587 /* effect slot processing */
1588 for(i = 0;i < auxslots->count;i++)
1590 const ALeffectslot *slot = auxslots->slot[i];
1591 ALeffectState *state = slot->Params.EffectState;
1592 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1593 state->OutChannels);
1596 ctx = ctx->next;
1599 if(device->DefaultSlot != NULL)
1601 const ALeffectslot *slot = device->DefaultSlot;
1602 ALeffectState *state = slot->Params.EffectState;
1603 V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
1604 state->OutChannels);
1607 /* Increment the clock time. Every second's worth of samples is
1608 * converted and added to clock base so that large sample counts don't
1609 * overflow during conversion. This also guarantees an exact, stable
1610 * conversion. */
1611 device->SamplesDone += SamplesToDo;
1612 device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
1613 device->SamplesDone %= device->Frequency;
1614 IncrementRef(&device->MixCount);
1616 if(device->HrtfHandle)
1618 HrtfDirectMixerFunc HrtfMix;
1619 DirectHrtfState *state;
1620 int lidx, ridx;
1622 if(device->AmbiUp)
1623 ambiup_process(device->AmbiUp,
1624 device->Dry.Buffer, device->Dry.NumChannels,
1625 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1628 lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1629 ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1630 assert(lidx != -1 && ridx != -1);
1632 HrtfMix = SelectHrtfMixer();
1633 state = device->Hrtf;
1634 for(c = 0;c < device->Dry.NumChannels;c++)
1636 HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1637 device->Dry.Buffer[c], state->Offset, state->IrSize,
1638 SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
1639 state->Chan[c].Values, SamplesToDo
1642 state->Offset += SamplesToDo;
1644 else if(device->AmbiDecoder)
1646 if(device->Dry.Buffer != device->FOAOut.Buffer)
1647 bformatdec_upSample(device->AmbiDecoder,
1648 device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
1649 device->FOAOut.NumChannels, SamplesToDo
1651 bformatdec_process(device->AmbiDecoder,
1652 device->RealOut.Buffer, device->RealOut.NumChannels,
1653 SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
1656 else if(device->AmbiUp)
1658 ambiup_process(device->AmbiUp,
1659 device->RealOut.Buffer, device->RealOut.NumChannels,
1660 SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
1663 else if(device->Uhj_Encoder)
1665 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1666 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1667 if(lidx != -1 && ridx != -1)
1669 /* Encode to stereo-compatible 2-channel UHJ output. */
1670 EncodeUhj2(device->Uhj_Encoder,
1671 device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
1672 device->Dry.Buffer, SamplesToDo
1676 else if(device->Bs2b)
1678 int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
1679 int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
1680 if(lidx != -1 && ridx != -1)
1682 /* Apply binaural/crossfeed filter */
1683 bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
1684 device->RealOut.Buffer[ridx], SamplesToDo);
1688 if(buffer)
1690 ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
1691 ALsizei OutChannels = device->RealOut.NumChannels;
1692 DistanceComp *DistComp;
1694 if(device->LimiterGain > 0.0f)
1696 /* Limiter attack drops -80dB in 50ms. */
1697 const ALfloat AttackRate = powf(0.0001f, 1.0f/(device->Frequency*0.05f));
1698 /* Limiter release raises +80dB in 1s. */
1699 const ALfloat ReleaseRate = powf(0.0001f, 1.0f/(device->Frequency*1.0f));
1701 /* Use NFCtrlData for temp gain storage. */
1702 device->LimiterGain = ApplyLimiter(OutBuffer, OutChannels,
1703 AttackRate, ReleaseRate, device->LimiterGain, device->NFCtrlData,
1704 SamplesToDo
1708 DistComp = device->ChannelDelay;
1709 #define WRITE(T, a, b, c, d, e) do { \
1710 Write_##T(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \
1711 buffer = (T*)buffer + (d)*(e); \
1712 } while(0)
1713 switch(device->FmtType)
1715 case DevFmtByte:
1716 WRITE(ALbyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1717 break;
1718 case DevFmtUByte:
1719 WRITE(ALubyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1720 break;
1721 case DevFmtShort:
1722 WRITE(ALshort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1723 break;
1724 case DevFmtUShort:
1725 WRITE(ALushort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1726 break;
1727 case DevFmtInt:
1728 WRITE(ALint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1729 break;
1730 case DevFmtUInt:
1731 WRITE(ALuint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1732 break;
1733 case DevFmtFloat:
1734 WRITE(ALfloat, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels);
1735 break;
1737 #undef WRITE
1740 size -= SamplesToDo;
1743 RestoreFPUMode(&oldMode);
1747 void aluHandleDisconnect(ALCdevice *device)
1749 ALCcontext *Context;
1751 device->Connected = ALC_FALSE;
1753 Context = ATOMIC_LOAD_SEQ(&device->ContextList);
1754 while(Context)
1756 ALvoice **voice, **voice_end;
1758 voice = Context->Voices;
1759 voice_end = voice + Context->VoiceCount;
1760 while(voice != voice_end)
1762 ALsource *source = ATOMIC_EXCHANGE_PTR(&(*voice)->Source, NULL,
1763 almemory_order_acq_rel);
1764 ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
1766 if(source)
1768 ALenum playing = AL_PLAYING;
1769 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
1772 voice++;
1774 Context->VoiceCount = 0;
1776 Context = Context->next;