Store the output buffers in the DirectParams struct
[openal-soft.git] / Alc / ALu.c
blobd0c17c0a888c032bd2d1a5e7d541f6fc64626601
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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, 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"
37 #include "mixer_defs.h"
40 struct ChanMap {
41 enum Channel channel;
42 ALfloat angle;
45 /* Cone scalar */
46 ALfloat ConeScale = 1.0f;
48 /* Localized Z scalar for mono sources */
49 ALfloat ZScale = 1.0f;
52 static ResamplerFunc SelectResampler(enum Resampler Resampler, ALuint increment)
54 if(increment == FRACTIONONE)
55 return Resample_copy32_C;
56 switch(Resampler)
58 case PointResampler:
59 return Resample_point32_C;
60 case LinearResampler:
61 return Resample_lerp32_C;
62 case CubicResampler:
63 return Resample_cubic32_C;
64 case ResamplerMax:
65 /* Shouldn't happen */
66 break;
69 return Resample_point32_C;
73 static DryMixerFunc SelectHrtfMixer(void)
75 #ifdef HAVE_SSE
76 if((CPUCapFlags&CPU_CAP_SSE))
77 return MixDirect_Hrtf_SSE;
78 #endif
79 #ifdef HAVE_NEON
80 if((CPUCapFlags&CPU_CAP_NEON))
81 return MixDirect_Hrtf_Neon;
82 #endif
84 return MixDirect_Hrtf_C;
87 static DryMixerFunc SelectDirectMixer(void)
89 #ifdef HAVE_SSE
90 if((CPUCapFlags&CPU_CAP_SSE))
91 return MixDirect_SSE;
92 #endif
94 return MixDirect_C;
97 static WetMixerFunc SelectSendMixer(void)
99 #ifdef HAVE_SSE
100 if((CPUCapFlags&CPU_CAP_SSE))
101 return MixSend_SSE;
102 #endif
104 return MixSend_C;
108 static __inline ALvoid aluMatrixVector(ALfloat *vector, ALfloat w, ALfloat (*RESTRICT matrix)[4])
110 ALfloat temp[4] = {
111 vector[0], vector[1], vector[2], w
114 vector[0] = temp[0]*matrix[0][0] + temp[1]*matrix[1][0] + temp[2]*matrix[2][0] + temp[3]*matrix[3][0];
115 vector[1] = temp[0]*matrix[0][1] + temp[1]*matrix[1][1] + temp[2]*matrix[2][1] + temp[3]*matrix[3][1];
116 vector[2] = temp[0]*matrix[0][2] + temp[1]*matrix[1][2] + temp[2]*matrix[2][2] + temp[3]*matrix[3][2];
120 static ALvoid CalcListenerParams(ALlistener *Listener)
122 ALfloat N[3], V[3], U[3], P[3];
124 /* AT then UP */
125 N[0] = Listener->Forward[0];
126 N[1] = Listener->Forward[1];
127 N[2] = Listener->Forward[2];
128 aluNormalize(N);
129 V[0] = Listener->Up[0];
130 V[1] = Listener->Up[1];
131 V[2] = Listener->Up[2];
132 aluNormalize(V);
133 /* Build and normalize right-vector */
134 aluCrossproduct(N, V, U);
135 aluNormalize(U);
137 Listener->Params.Matrix[0][0] = U[0];
138 Listener->Params.Matrix[0][1] = V[0];
139 Listener->Params.Matrix[0][2] = -N[0];
140 Listener->Params.Matrix[0][3] = 0.0f;
141 Listener->Params.Matrix[1][0] = U[1];
142 Listener->Params.Matrix[1][1] = V[1];
143 Listener->Params.Matrix[1][2] = -N[1];
144 Listener->Params.Matrix[1][3] = 0.0f;
145 Listener->Params.Matrix[2][0] = U[2];
146 Listener->Params.Matrix[2][1] = V[2];
147 Listener->Params.Matrix[2][2] = -N[2];
148 Listener->Params.Matrix[2][3] = 0.0f;
149 Listener->Params.Matrix[3][0] = 0.0f;
150 Listener->Params.Matrix[3][1] = 0.0f;
151 Listener->Params.Matrix[3][2] = 0.0f;
152 Listener->Params.Matrix[3][3] = 1.0f;
154 P[0] = Listener->Position[0];
155 P[1] = Listener->Position[1];
156 P[2] = Listener->Position[2];
157 aluMatrixVector(P, 1.0f, Listener->Params.Matrix);
158 Listener->Params.Matrix[3][0] = -P[0];
159 Listener->Params.Matrix[3][1] = -P[1];
160 Listener->Params.Matrix[3][2] = -P[2];
162 Listener->Params.Velocity[0] = Listener->Velocity[0];
163 Listener->Params.Velocity[1] = Listener->Velocity[1];
164 Listener->Params.Velocity[2] = Listener->Velocity[2];
165 aluMatrixVector(Listener->Params.Velocity, 0.0f, Listener->Params.Matrix);
168 ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
170 static const struct ChanMap MonoMap[1] = { { FrontCenter, 0.0f } };
171 static const struct ChanMap StereoMap[2] = {
172 { FrontLeft, -30.0f * F_PI/180.0f },
173 { FrontRight, 30.0f * F_PI/180.0f }
175 static const struct ChanMap StereoWideMap[2] = {
176 { FrontLeft, -90.0f * F_PI/180.0f },
177 { FrontRight, 90.0f * F_PI/180.0f }
179 static const struct ChanMap RearMap[2] = {
180 { BackLeft, -150.0f * F_PI/180.0f },
181 { BackRight, 150.0f * F_PI/180.0f }
183 static const struct ChanMap QuadMap[4] = {
184 { FrontLeft, -45.0f * F_PI/180.0f },
185 { FrontRight, 45.0f * F_PI/180.0f },
186 { BackLeft, -135.0f * F_PI/180.0f },
187 { BackRight, 135.0f * F_PI/180.0f }
189 static const struct ChanMap X51Map[6] = {
190 { FrontLeft, -30.0f * F_PI/180.0f },
191 { FrontRight, 30.0f * F_PI/180.0f },
192 { FrontCenter, 0.0f * F_PI/180.0f },
193 { LFE, 0.0f },
194 { BackLeft, -110.0f * F_PI/180.0f },
195 { BackRight, 110.0f * F_PI/180.0f }
197 static const struct ChanMap X61Map[7] = {
198 { FrontLeft, -30.0f * F_PI/180.0f },
199 { FrontRight, 30.0f * F_PI/180.0f },
200 { FrontCenter, 0.0f * F_PI/180.0f },
201 { LFE, 0.0f },
202 { BackCenter, 180.0f * F_PI/180.0f },
203 { SideLeft, -90.0f * F_PI/180.0f },
204 { SideRight, 90.0f * F_PI/180.0f }
206 static const struct ChanMap X71Map[8] = {
207 { FrontLeft, -30.0f * F_PI/180.0f },
208 { FrontRight, 30.0f * F_PI/180.0f },
209 { FrontCenter, 0.0f * F_PI/180.0f },
210 { LFE, 0.0f },
211 { BackLeft, -150.0f * F_PI/180.0f },
212 { BackRight, 150.0f * F_PI/180.0f },
213 { SideLeft, -90.0f * F_PI/180.0f },
214 { SideRight, 90.0f * F_PI/180.0f }
217 ALCdevice *Device = ALContext->Device;
218 ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
219 ALbufferlistitem *BufferListItem;
220 enum FmtChannels Channels;
221 ALfloat (*SrcMatrix)[MaxChannels];
222 ALfloat DryGain, DryGainHF;
223 ALfloat WetGain[MAX_SENDS];
224 ALfloat WetGainHF[MAX_SENDS];
225 ALint NumSends, Frequency;
226 const struct ChanMap *chans = NULL;
227 enum Resampler Resampler;
228 ALint num_channels = 0;
229 ALboolean DirectChannels;
230 ALfloat hwidth = 0.0f;
231 ALfloat Pitch;
232 ALfloat cw;
233 ALint i, c;
235 /* Get device properties */
236 NumSends = Device->NumAuxSends;
237 Frequency = Device->Frequency;
239 /* Get listener properties */
240 ListenerGain = ALContext->Listener->Gain;
242 /* Get source properties */
243 SourceVolume = ALSource->Gain;
244 MinVolume = ALSource->MinGain;
245 MaxVolume = ALSource->MaxGain;
246 Pitch = ALSource->Pitch;
247 Resampler = ALSource->Resampler;
248 DirectChannels = ALSource->DirectChannels;
250 /* Calculate the stepping value */
251 Channels = FmtMono;
252 BufferListItem = ALSource->queue;
253 while(BufferListItem != NULL)
255 ALbuffer *ALBuffer;
256 if((ALBuffer=BufferListItem->buffer) != NULL)
258 ALsizei maxstep = BUFFERSIZE;
259 maxstep -= ResamplerPadding[Resampler] +
260 ResamplerPrePadding[Resampler] + 1;
261 maxstep = mini(maxstep, INT_MAX>>FRACTIONBITS);
263 Pitch = Pitch * ALBuffer->Frequency / Frequency;
264 if(Pitch > (ALfloat)maxstep)
265 ALSource->Params.Step = maxstep<<FRACTIONBITS;
266 else
268 ALSource->Params.Step = fastf2i(Pitch*FRACTIONONE);
269 if(ALSource->Params.Step == 0)
270 ALSource->Params.Step = 1;
272 ALSource->Params.Resample = SelectResampler(Resampler, ALSource->Params.Step);
274 Channels = ALBuffer->FmtChannels;
275 break;
277 BufferListItem = BufferListItem->next;
279 if(!DirectChannels && Device->Hrtf)
280 ALSource->Params.DryMix = SelectHrtfMixer();
281 else
282 ALSource->Params.DryMix = SelectDirectMixer();
283 ALSource->Params.WetMix = SelectSendMixer();
285 /* Calculate gains */
286 DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
287 DryGain *= ALSource->DirectGain * ListenerGain;
288 DryGainHF = ALSource->DirectGainHF;
289 for(i = 0;i < NumSends;i++)
291 WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
292 WetGain[i] *= ALSource->Send[i].Gain * ListenerGain;
293 WetGainHF[i] = ALSource->Send[i].GainHF;
296 SrcMatrix = ALSource->Params.Direct.Gains;
297 for(i = 0;i < MaxChannels;i++)
299 for(c = 0;c < MaxChannels;c++)
300 SrcMatrix[i][c] = 0.0f;
302 switch(Channels)
304 case FmtMono:
305 chans = MonoMap;
306 num_channels = 1;
307 break;
309 case FmtStereo:
310 if(!(Device->Flags&DEVICE_WIDE_STEREO))
311 chans = StereoMap;
312 else
314 chans = StereoWideMap;
315 hwidth = 60.0f * F_PI/180.0f;
317 num_channels = 2;
318 break;
320 case FmtRear:
321 chans = RearMap;
322 num_channels = 2;
323 break;
325 case FmtQuad:
326 chans = QuadMap;
327 num_channels = 4;
328 break;
330 case FmtX51:
331 chans = X51Map;
332 num_channels = 6;
333 break;
335 case FmtX61:
336 chans = X61Map;
337 num_channels = 7;
338 break;
340 case FmtX71:
341 chans = X71Map;
342 num_channels = 8;
343 break;
346 if(DirectChannels != AL_FALSE)
348 for(c = 0;c < num_channels;c++)
350 for(i = 0;i < (ALint)Device->NumChan;i++)
352 enum Channel chan = Device->Speaker2Chan[i];
353 if(chan == chans[c].channel)
355 SrcMatrix[c][chan] = DryGain;
356 break;
361 else if(Device->Hrtf)
363 for(c = 0;c < num_channels;c++)
365 if(chans[c].channel == LFE)
367 /* Skip LFE */
368 ALSource->Params.Direct.Hrtf.Delay[c][0] = 0;
369 ALSource->Params.Direct.Hrtf.Delay[c][1] = 0;
370 for(i = 0;i < HRIR_LENGTH;i++)
372 ALSource->Params.Direct.Hrtf.Coeffs[c][i][0] = 0.0f;
373 ALSource->Params.Direct.Hrtf.Coeffs[c][i][1] = 0.0f;
376 else
378 /* Get the static HRIR coefficients and delays for this
379 * channel. */
380 GetLerpedHrtfCoeffs(Device->Hrtf,
381 0.0f, chans[c].angle, DryGain,
382 ALSource->Params.Direct.Hrtf.Coeffs[c],
383 ALSource->Params.Direct.Hrtf.Delay[c]);
386 ALSource->Hrtf.Counter = 0;
387 ALSource->Params.Direct.Hrtf.IrSize = GetHrtfIrSize(Device->Hrtf);
389 ALSource->Params.Direct.hrtfState = &ALSource->Hrtf;
391 else
393 DryGain *= lerp(1.0f, 1.0f/sqrtf((float)Device->NumChan), hwidth/F_PI);
394 for(c = 0;c < num_channels;c++)
396 /* Special-case LFE */
397 if(chans[c].channel == LFE)
399 SrcMatrix[c][chans[c].channel] = DryGain;
400 continue;
402 ComputeAngleGains(Device, chans[c].angle, hwidth, DryGain,
403 SrcMatrix[c]);
407 ALSource->Params.Direct.OutBuffer = Device->DryBuffer;
408 ALSource->Params.Direct.ClickRemoval = Device->ClickRemoval;
409 ALSource->Params.Direct.PendingClicks = Device->PendingClicks;
410 for(i = 0;i < NumSends;i++)
412 ALeffectslot *Slot = ALSource->Send[i].Slot;
414 if(!Slot && i == 0)
415 Slot = Device->DefaultSlot;
416 if(Slot && Slot->effect.type == AL_EFFECT_NULL)
417 Slot = NULL;
418 ALSource->Params.Send[i].Slot = Slot;
419 ALSource->Params.Send[i].Gain = WetGain[i];
422 /* Update filter coefficients. Calculations based on the I3DL2
423 * spec. */
424 cw = cosf(F_PI*2.0f * LOWPASSFREQREF / Frequency);
426 /* We use two chained one-pole filters, so we need to take the
427 * square root of the squared gain, which is the same as the base
428 * gain. */
429 ALSource->Params.Direct.iirFilter.coeff = lpCoeffCalc(DryGainHF, cw);
430 for(i = 0;i < NumSends;i++)
432 ALfloat a = lpCoeffCalc(WetGainHF[i], cw);
433 ALSource->Params.Send[i].iirFilter.coeff = a;
437 ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
439 ALCdevice *Device = ALContext->Device;
440 ALfloat Velocity[3],Direction[3],Position[3],SourceToListener[3];
441 ALfloat InnerAngle,OuterAngle,Angle,Distance,ClampedDist;
442 ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
443 ALfloat ConeVolume,ConeHF,SourceVolume,ListenerGain;
444 ALfloat DopplerFactor, SpeedOfSound;
445 ALfloat AirAbsorptionFactor;
446 ALfloat RoomAirAbsorption[MAX_SENDS];
447 ALbufferlistitem *BufferListItem;
448 ALfloat Attenuation;
449 ALfloat RoomAttenuation[MAX_SENDS];
450 ALfloat MetersPerUnit;
451 ALfloat RoomRolloffBase;
452 ALfloat RoomRolloff[MAX_SENDS];
453 ALfloat DecayDistance[MAX_SENDS];
454 ALfloat DryGain;
455 ALfloat DryGainHF;
456 ALboolean DryGainHFAuto;
457 ALfloat WetGain[MAX_SENDS];
458 ALfloat WetGainHF[MAX_SENDS];
459 ALboolean WetGainAuto;
460 ALboolean WetGainHFAuto;
461 enum Resampler Resampler;
462 ALfloat Pitch;
463 ALuint Frequency;
464 ALint NumSends;
465 ALfloat cw;
466 ALint i, j;
468 DryGainHF = 1.0f;
469 for(i = 0;i < MAX_SENDS;i++)
470 WetGainHF[i] = 1.0f;
472 /* Get context/device properties */
473 DopplerFactor = ALContext->DopplerFactor * ALSource->DopplerFactor;
474 SpeedOfSound = ALContext->SpeedOfSound * ALContext->DopplerVelocity;
475 NumSends = Device->NumAuxSends;
476 Frequency = Device->Frequency;
478 /* Get listener properties */
479 ListenerGain = ALContext->Listener->Gain;
480 MetersPerUnit = ALContext->Listener->MetersPerUnit;
482 /* Get source properties */
483 SourceVolume = ALSource->Gain;
484 MinVolume = ALSource->MinGain;
485 MaxVolume = ALSource->MaxGain;
486 Pitch = ALSource->Pitch;
487 Resampler = ALSource->Resampler;
488 Position[0] = ALSource->Position[0];
489 Position[1] = ALSource->Position[1];
490 Position[2] = ALSource->Position[2];
491 Direction[0] = ALSource->Orientation[0];
492 Direction[1] = ALSource->Orientation[1];
493 Direction[2] = ALSource->Orientation[2];
494 Velocity[0] = ALSource->Velocity[0];
495 Velocity[1] = ALSource->Velocity[1];
496 Velocity[2] = ALSource->Velocity[2];
497 MinDist = ALSource->RefDistance;
498 MaxDist = ALSource->MaxDistance;
499 Rolloff = ALSource->RollOffFactor;
500 InnerAngle = ALSource->InnerAngle;
501 OuterAngle = ALSource->OuterAngle;
502 AirAbsorptionFactor = ALSource->AirAbsorptionFactor;
503 DryGainHFAuto = ALSource->DryGainHFAuto;
504 WetGainAuto = ALSource->WetGainAuto;
505 WetGainHFAuto = ALSource->WetGainHFAuto;
506 RoomRolloffBase = ALSource->RoomRolloffFactor;
508 ALSource->Params.Direct.OutBuffer = Device->DryBuffer;
509 ALSource->Params.Direct.ClickRemoval = Device->ClickRemoval;
510 ALSource->Params.Direct.PendingClicks = Device->PendingClicks;
511 for(i = 0;i < NumSends;i++)
513 ALeffectslot *Slot = ALSource->Send[i].Slot;
515 if(!Slot && i == 0)
516 Slot = Device->DefaultSlot;
517 if(!Slot || Slot->effect.type == AL_EFFECT_NULL)
519 Slot = NULL;
520 RoomRolloff[i] = 0.0f;
521 DecayDistance[i] = 0.0f;
522 RoomAirAbsorption[i] = 1.0f;
524 else if(Slot->AuxSendAuto)
526 RoomRolloff[i] = RoomRolloffBase;
527 if(IsReverbEffect(Slot->effect.type))
529 RoomRolloff[i] += Slot->effect.Reverb.RoomRolloffFactor;
530 DecayDistance[i] = Slot->effect.Reverb.DecayTime *
531 SPEEDOFSOUNDMETRESPERSEC;
532 RoomAirAbsorption[i] = Slot->effect.Reverb.AirAbsorptionGainHF;
534 else
536 DecayDistance[i] = 0.0f;
537 RoomAirAbsorption[i] = 1.0f;
540 else
542 /* If the slot's auxiliary send auto is off, the data sent to the
543 * effect slot is the same as the dry path, sans filter effects */
544 RoomRolloff[i] = Rolloff;
545 DecayDistance[i] = 0.0f;
546 RoomAirAbsorption[i] = AIRABSORBGAINHF;
549 ALSource->Params.Send[i].Slot = Slot;
552 /* Transform source to listener space (convert to head relative) */
553 if(ALSource->HeadRelative == AL_FALSE)
555 ALfloat (*RESTRICT Matrix)[4] = ALContext->Listener->Params.Matrix;
556 /* Transform source vectors */
557 aluMatrixVector(Position, 1.0f, Matrix);
558 aluMatrixVector(Direction, 0.0f, Matrix);
559 aluMatrixVector(Velocity, 0.0f, Matrix);
561 else
563 const ALfloat *ListenerVel = ALContext->Listener->Params.Velocity;
564 /* Offset the source velocity to be relative of the listener velocity */
565 Velocity[0] += ListenerVel[0];
566 Velocity[1] += ListenerVel[1];
567 Velocity[2] += ListenerVel[2];
570 SourceToListener[0] = -Position[0];
571 SourceToListener[1] = -Position[1];
572 SourceToListener[2] = -Position[2];
573 aluNormalize(SourceToListener);
574 aluNormalize(Direction);
576 /* Calculate distance attenuation */
577 Distance = sqrtf(aluDotproduct(Position, Position));
578 ClampedDist = Distance;
580 Attenuation = 1.0f;
581 for(i = 0;i < NumSends;i++)
582 RoomAttenuation[i] = 1.0f;
583 switch(ALContext->SourceDistanceModel ? ALSource->DistanceModel :
584 ALContext->DistanceModel)
586 case InverseDistanceClamped:
587 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
588 if(MaxDist < MinDist)
589 break;
590 /*fall-through*/
591 case InverseDistance:
592 if(MinDist > 0.0f)
594 if((MinDist + (Rolloff * (ClampedDist - MinDist))) > 0.0f)
595 Attenuation = MinDist / (MinDist + (Rolloff * (ClampedDist - MinDist)));
596 for(i = 0;i < NumSends;i++)
598 if((MinDist + (RoomRolloff[i] * (ClampedDist - MinDist))) > 0.0f)
599 RoomAttenuation[i] = MinDist / (MinDist + (RoomRolloff[i] * (ClampedDist - MinDist)));
602 break;
604 case LinearDistanceClamped:
605 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
606 if(MaxDist < MinDist)
607 break;
608 /*fall-through*/
609 case LinearDistance:
610 if(MaxDist != MinDist)
612 Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
613 Attenuation = maxf(Attenuation, 0.0f);
614 for(i = 0;i < NumSends;i++)
616 RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
617 RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
620 break;
622 case ExponentDistanceClamped:
623 ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
624 if(MaxDist < MinDist)
625 break;
626 /*fall-through*/
627 case ExponentDistance:
628 if(ClampedDist > 0.0f && MinDist > 0.0f)
630 Attenuation = powf(ClampedDist/MinDist, -Rolloff);
631 for(i = 0;i < NumSends;i++)
632 RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
634 break;
636 case DisableDistance:
637 ClampedDist = MinDist;
638 break;
641 /* Source Gain + Attenuation */
642 DryGain = SourceVolume * Attenuation;
643 for(i = 0;i < NumSends;i++)
644 WetGain[i] = SourceVolume * RoomAttenuation[i];
646 /* Distance-based air absorption */
647 if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
649 ALfloat meters = maxf(ClampedDist-MinDist, 0.0f) * MetersPerUnit;
650 DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
651 for(i = 0;i < NumSends;i++)
652 WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
655 if(WetGainAuto)
657 ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
659 /* Apply a decay-time transformation to the wet path, based on the
660 * attenuation of the dry path.
662 * Using the apparent distance, based on the distance attenuation, the
663 * initial decay of the reverb effect is calculated and applied to the
664 * wet path.
666 for(i = 0;i < NumSends;i++)
668 if(DecayDistance[i] > 0.0f)
669 WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
673 /* Calculate directional soundcones */
674 Angle = acosf(aluDotproduct(Direction,SourceToListener)) * ConeScale * (360.0f/F_PI);
675 if(Angle > InnerAngle && Angle <= OuterAngle)
677 ALfloat scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
678 ConeVolume = lerp(1.0f, ALSource->OuterGain, scale);
679 ConeHF = lerp(1.0f, ALSource->OuterGainHF, scale);
681 else if(Angle > OuterAngle)
683 ConeVolume = ALSource->OuterGain;
684 ConeHF = ALSource->OuterGainHF;
686 else
688 ConeVolume = 1.0f;
689 ConeHF = 1.0f;
692 DryGain *= ConeVolume;
693 if(WetGainAuto)
695 for(i = 0;i < NumSends;i++)
696 WetGain[i] *= ConeVolume;
698 if(DryGainHFAuto)
699 DryGainHF *= ConeHF;
700 if(WetGainHFAuto)
702 for(i = 0;i < NumSends;i++)
703 WetGainHF[i] *= ConeHF;
706 /* Clamp to Min/Max Gain */
707 DryGain = clampf(DryGain, MinVolume, MaxVolume);
708 for(i = 0;i < NumSends;i++)
709 WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
711 /* Apply gain and frequency filters */
712 DryGain *= ALSource->DirectGain * ListenerGain;
713 DryGainHF *= ALSource->DirectGainHF;
714 for(i = 0;i < NumSends;i++)
716 WetGain[i] *= ALSource->Send[i].Gain * ListenerGain;
717 WetGainHF[i] *= ALSource->Send[i].GainHF;
720 /* Calculate velocity-based doppler effect */
721 if(DopplerFactor > 0.0f)
723 const ALfloat *ListenerVel = ALContext->Listener->Params.Velocity;
724 ALfloat VSS, VLS;
726 if(SpeedOfSound < 1.0f)
728 DopplerFactor *= 1.0f/SpeedOfSound;
729 SpeedOfSound = 1.0f;
732 VSS = aluDotproduct(Velocity, SourceToListener) * DopplerFactor;
733 VLS = aluDotproduct(ListenerVel, SourceToListener) * DopplerFactor;
735 Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
736 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
739 BufferListItem = ALSource->queue;
740 while(BufferListItem != NULL)
742 ALbuffer *ALBuffer;
743 if((ALBuffer=BufferListItem->buffer) != NULL)
745 /* Calculate fixed-point stepping value, based on the pitch, buffer
746 * frequency, and output frequency. */
747 ALsizei maxstep = BUFFERSIZE;
748 maxstep -= ResamplerPadding[Resampler] +
749 ResamplerPrePadding[Resampler] + 1;
750 maxstep = mini(maxstep, INT_MAX>>FRACTIONBITS);
752 Pitch = Pitch * ALBuffer->Frequency / Frequency;
753 if(Pitch > (ALfloat)maxstep)
754 ALSource->Params.Step = maxstep<<FRACTIONBITS;
755 else
757 ALSource->Params.Step = fastf2i(Pitch*FRACTIONONE);
758 if(ALSource->Params.Step == 0)
759 ALSource->Params.Step = 1;
761 ALSource->Params.Resample = SelectResampler(Resampler, ALSource->Params.Step);
763 break;
765 BufferListItem = BufferListItem->next;
767 if(Device->Hrtf)
768 ALSource->Params.DryMix = SelectHrtfMixer();
769 else
770 ALSource->Params.DryMix = SelectDirectMixer();
771 ALSource->Params.WetMix = SelectSendMixer();
773 if(Device->Hrtf)
775 /* Use a binaural HRTF algorithm for stereo headphone playback */
776 ALfloat delta, ev = 0.0f, az = 0.0f;
778 if(Distance > FLT_EPSILON)
780 ALfloat invlen = 1.0f/Distance;
781 Position[0] *= invlen;
782 Position[1] *= invlen;
783 Position[2] *= invlen;
785 /* Calculate elevation and azimuth only when the source is not at
786 * the listener. This prevents +0 and -0 Z from producing
787 * inconsistent panning. Also, clamp Y in case FP precision errors
788 * cause it to land outside of -1..+1. */
789 ev = asinf(clampf(Position[1], -1.0f, 1.0f));
790 az = atan2f(Position[0], -Position[2]*ZScale);
793 /* Check to see if the HRIR is already moving. */
794 if(ALSource->Hrtf.Moving)
796 /* Calculate the normalized HRTF transition factor (delta). */
797 delta = CalcHrtfDelta(ALSource->Params.Direct.Hrtf.Gain, DryGain,
798 ALSource->Params.Direct.Hrtf.Dir, Position);
799 /* If the delta is large enough, get the moving HRIR target
800 * coefficients, target delays, steppping values, and counter. */
801 if(delta > 0.001f)
803 ALSource->Hrtf.Counter = GetMovingHrtfCoeffs(Device->Hrtf,
804 ev, az, DryGain, delta,
805 ALSource->Hrtf.Counter,
806 ALSource->Params.Direct.Hrtf.Coeffs[0],
807 ALSource->Params.Direct.Hrtf.Delay[0],
808 ALSource->Params.Direct.Hrtf.CoeffStep,
809 ALSource->Params.Direct.Hrtf.DelayStep);
810 ALSource->Params.Direct.Hrtf.Gain = DryGain;
811 ALSource->Params.Direct.Hrtf.Dir[0] = Position[0];
812 ALSource->Params.Direct.Hrtf.Dir[1] = Position[1];
813 ALSource->Params.Direct.Hrtf.Dir[2] = Position[2];
816 else
818 /* Get the initial (static) HRIR coefficients and delays. */
819 GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, DryGain,
820 ALSource->Params.Direct.Hrtf.Coeffs[0],
821 ALSource->Params.Direct.Hrtf.Delay[0]);
822 ALSource->Hrtf.Counter = 0;
823 ALSource->Hrtf.Moving = AL_TRUE;
824 ALSource->Params.Direct.Hrtf.Gain = DryGain;
825 ALSource->Params.Direct.Hrtf.Dir[0] = Position[0];
826 ALSource->Params.Direct.Hrtf.Dir[1] = Position[1];
827 ALSource->Params.Direct.Hrtf.Dir[2] = Position[2];
829 ALSource->Params.Direct.Hrtf.IrSize = GetHrtfIrSize(Device->Hrtf);
831 ALSource->Params.Direct.hrtfState = &ALSource->Hrtf;
833 else
835 ALfloat (*Matrix)[MaxChannels] = ALSource->Params.Direct.Gains;
836 ALfloat DirGain = 0.0f;
837 ALfloat AmbientGain;
839 for(i = 0;i < MaxChannels;i++)
841 for(j = 0;j < MaxChannels;j++)
842 Matrix[i][j] = 0.0f;
845 /* Normalize the length, and compute panned gains. */
846 if(Distance > FLT_EPSILON)
848 ALfloat invlen = 1.0f/Distance;
849 Position[0] *= invlen;
850 Position[1] *= invlen;
851 Position[2] *= invlen;
853 DirGain = sqrtf(Position[0]*Position[0] + Position[2]*Position[2]);
854 ComputeAngleGains(Device, atan2f(Position[0], -Position[2]*ZScale), 0.0f,
855 DryGain*DirGain, Matrix[0]);
858 /* Adjustment for vertical offsets. Not the greatest, but simple
859 * enough. */
860 AmbientGain = DryGain * sqrtf(1.0f/Device->NumChan) * (1.0f-DirGain);
861 for(i = 0;i < (ALint)Device->NumChan;i++)
863 enum Channel chan = Device->Speaker2Chan[i];
864 Matrix[0][chan] = maxf(Matrix[0][chan], AmbientGain);
867 for(i = 0;i < NumSends;i++)
868 ALSource->Params.Send[i].Gain = WetGain[i];
870 /* Update filter coefficients. */
871 cw = cosf(F_PI*2.0f * LOWPASSFREQREF / Frequency);
873 ALSource->Params.Direct.iirFilter.coeff = lpCoeffCalc(DryGainHF, cw);
874 for(i = 0;i < NumSends;i++)
876 ALfloat a = lpCoeffCalc(WetGainHF[i], cw);
877 ALSource->Params.Send[i].iirFilter.coeff = a;
882 static __inline ALfloat aluF2F(ALfloat val)
883 { return val; }
884 static __inline ALint aluF2I(ALfloat val)
886 if(val > 1.0f) return 2147483647;
887 if(val < -1.0f) return -2147483647-1;
888 return fastf2i((ALfloat)(val*2147483647.0));
890 static __inline ALuint aluF2UI(ALfloat val)
891 { return aluF2I(val)+2147483648u; }
892 static __inline ALshort aluF2S(ALfloat val)
893 { return aluF2I(val)>>16; }
894 static __inline ALushort aluF2US(ALfloat val)
895 { return aluF2S(val)+32768; }
896 static __inline ALbyte aluF2B(ALfloat val)
897 { return aluF2I(val)>>24; }
898 static __inline ALubyte aluF2UB(ALfloat val)
899 { return aluF2B(val)+128; }
901 #define DECL_TEMPLATE(T, func) \
902 static int Write_##T(ALCdevice *device, T *RESTRICT buffer, \
903 ALuint SamplesToDo) \
905 ALfloat (*RESTRICT DryBuffer)[BUFFERSIZE] = device->DryBuffer; \
906 ALuint numchans = ChannelsFromDevFmt(device->FmtChans); \
907 const enum Channel *ChanMap = device->DevChannels; \
908 ALuint i, j; \
910 for(j = 0;j < numchans;j++) \
912 T *RESTRICT out = buffer + j; \
913 enum Channel chan = ChanMap[j]; \
915 for(i = 0;i < SamplesToDo;i++) \
916 out[i*numchans] = func(DryBuffer[chan][i]); \
918 return SamplesToDo*numchans*sizeof(T); \
921 DECL_TEMPLATE(ALfloat, aluF2F)
922 DECL_TEMPLATE(ALuint, aluF2UI)
923 DECL_TEMPLATE(ALint, aluF2I)
924 DECL_TEMPLATE(ALushort, aluF2US)
925 DECL_TEMPLATE(ALshort, aluF2S)
926 DECL_TEMPLATE(ALubyte, aluF2UB)
927 DECL_TEMPLATE(ALbyte, aluF2B)
929 #undef DECL_TEMPLATE
932 ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
934 ALuint SamplesToDo;
935 ALeffectslot **slot, **slot_end;
936 ALsource **src, **src_end;
937 ALCcontext *ctx;
938 FPUCtl oldMode;
939 ALuint i, c;
941 SetMixerFPUMode(&oldMode);
943 while(size > 0)
945 SamplesToDo = minu(size, BUFFERSIZE);
946 for(c = 0;c < MaxChannels;c++)
947 memset(device->DryBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
949 ALCdevice_Lock(device);
950 ctx = device->ContextList;
951 while(ctx)
953 ALenum DeferUpdates = ctx->DeferUpdates;
954 ALenum UpdateSources = AL_FALSE;
956 if(!DeferUpdates)
957 UpdateSources = ExchangeInt(&ctx->UpdateSources, AL_FALSE);
959 if(UpdateSources)
960 CalcListenerParams(ctx->Listener);
962 /* source processing */
963 src = ctx->ActiveSources;
964 src_end = src + ctx->ActiveSourceCount;
965 while(src != src_end)
967 if((*src)->state != AL_PLAYING)
969 --(ctx->ActiveSourceCount);
970 *src = *(--src_end);
971 continue;
974 if(!DeferUpdates && (ExchangeInt(&(*src)->NeedsUpdate, AL_FALSE) ||
975 UpdateSources))
976 ALsource_Update(*src, ctx);
978 MixSource(*src, device, SamplesToDo);
979 src++;
982 /* effect slot processing */
983 slot = ctx->ActiveEffectSlots;
984 slot_end = slot + ctx->ActiveEffectSlotCount;
985 while(slot != slot_end)
987 ALfloat offset = (*slot)->ClickRemoval[0];
988 if(offset < (1.0f/32768.0f))
989 offset = 0.0f;
990 else for(i = 0;i < SamplesToDo;i++)
992 (*slot)->WetBuffer[0][i] += offset;
993 offset -= offset * (1.0f/256.0f);
995 (*slot)->ClickRemoval[0] = offset + (*slot)->PendingClicks[0];
996 (*slot)->PendingClicks[0] = 0.0f;
998 if(!DeferUpdates && ExchangeInt(&(*slot)->NeedsUpdate, AL_FALSE))
999 ALeffectState_Update((*slot)->EffectState, device, *slot);
1001 ALeffectState_Process((*slot)->EffectState, SamplesToDo,
1002 (*slot)->WetBuffer[0], device->DryBuffer);
1004 for(i = 0;i < SamplesToDo;i++)
1005 (*slot)->WetBuffer[0][i] = 0.0f;
1007 slot++;
1010 ctx = ctx->next;
1013 slot = &device->DefaultSlot;
1014 if(*slot != NULL)
1016 ALfloat offset = (*slot)->ClickRemoval[0];
1017 if(offset < (1.0f/32768.0f))
1018 offset = 0.0f;
1019 else for(i = 0;i < SamplesToDo;i++)
1021 (*slot)->WetBuffer[0][i] += offset;
1022 offset -= offset * (1.0f/256.0f);
1024 (*slot)->ClickRemoval[0] = offset + (*slot)->PendingClicks[0];
1025 (*slot)->PendingClicks[0] = 0.0f;
1027 if(ExchangeInt(&(*slot)->NeedsUpdate, AL_FALSE))
1028 ALeffectState_Update((*slot)->EffectState, device, *slot);
1030 ALeffectState_Process((*slot)->EffectState, SamplesToDo,
1031 (*slot)->WetBuffer[0], device->DryBuffer);
1033 for(i = 0;i < SamplesToDo;i++)
1034 (*slot)->WetBuffer[0][i] = 0.0f;
1036 ALCdevice_Unlock(device);
1038 /* Click-removal. Could do better; this only really handles immediate
1039 * changes between updates where a predictive sample could be
1040 * generated. Delays caused by effects and HRTF aren't caught. */
1041 if(device->FmtChans == DevFmtMono)
1043 ALfloat offset = device->ClickRemoval[FrontCenter];
1044 if(offset < (1.0f/32768.0f))
1045 offset = 0.0f;
1046 else for(i = 0;i < SamplesToDo;i++)
1048 device->DryBuffer[FrontCenter][i] += offset;
1049 offset -= offset * (1.0f/256.0f);
1051 device->ClickRemoval[FrontCenter] = offset + device->PendingClicks[FrontCenter];
1052 device->PendingClicks[FrontCenter] = 0.0f;
1054 else if(device->FmtChans == DevFmtStereo)
1056 /* Assumes the first two channels are FrontLeft and FrontRight */
1057 for(c = 0;c < 2;c++)
1059 ALfloat offset = device->ClickRemoval[c];
1060 if(offset < (1.0f/32768.0f))
1061 offset = 0.0f;
1062 else for(i = 0;i < SamplesToDo;i++)
1064 device->DryBuffer[c][i] += offset;
1065 offset -= offset * (1.0f/256.0f);
1067 device->ClickRemoval[c] = offset + device->PendingClicks[c];
1068 device->PendingClicks[c] = 0.0f;
1070 if(device->Bs2b)
1072 float samples[2];
1073 for(i = 0;i < SamplesToDo;i++)
1075 samples[0] = device->DryBuffer[FrontLeft][i];
1076 samples[1] = device->DryBuffer[FrontRight][i];
1077 bs2b_cross_feed(device->Bs2b, samples);
1078 device->DryBuffer[FrontLeft][i] = samples[0];
1079 device->DryBuffer[FrontRight][i] = samples[1];
1083 else
1085 for(c = 0;c < MaxChannels;c++)
1087 ALfloat offset = device->ClickRemoval[c];
1088 if(offset < (1.0f/32768.0f))
1089 offset = 0.0f;
1090 else for(i = 0;i < SamplesToDo;i++)
1092 device->DryBuffer[c][i] += offset;
1093 offset -= offset * (1.0f/256.0f);
1095 device->ClickRemoval[c] = offset + device->PendingClicks[c];
1096 device->PendingClicks[c] = 0.0f;
1100 if(buffer)
1102 int bytes = 0;
1103 switch(device->FmtType)
1105 case DevFmtByte:
1106 bytes = Write_ALbyte(device, buffer, SamplesToDo);
1107 break;
1108 case DevFmtUByte:
1109 bytes = Write_ALubyte(device, buffer, SamplesToDo);
1110 break;
1111 case DevFmtShort:
1112 bytes = Write_ALshort(device, buffer, SamplesToDo);
1113 break;
1114 case DevFmtUShort:
1115 bytes = Write_ALushort(device, buffer, SamplesToDo);
1116 break;
1117 case DevFmtInt:
1118 bytes = Write_ALint(device, buffer, SamplesToDo);
1119 break;
1120 case DevFmtUInt:
1121 bytes = Write_ALuint(device, buffer, SamplesToDo);
1122 break;
1123 case DevFmtFloat:
1124 bytes = Write_ALfloat(device, buffer, SamplesToDo);
1125 break;
1128 buffer = (ALubyte*)buffer + bytes;
1131 size -= SamplesToDo;
1134 RestoreFPUMode(&oldMode);
1138 ALvoid aluHandleDisconnect(ALCdevice *device)
1140 ALCcontext *Context;
1142 ALCdevice_Lock(device);
1143 device->Connected = ALC_FALSE;
1145 Context = device->ContextList;
1146 while(Context)
1148 ALsource **src, **src_end;
1150 src = Context->ActiveSources;
1151 src_end = src + Context->ActiveSourceCount;
1152 while(src != src_end)
1154 if((*src)->state == AL_PLAYING)
1156 (*src)->state = AL_STOPPED;
1157 (*src)->BuffersPlayed = (*src)->BuffersInQueue;
1158 (*src)->position = 0;
1159 (*src)->position_fraction = 0;
1161 src++;
1163 Context->ActiveSourceCount = 0;
1165 Context = Context->next;
1167 ALCdevice_Unlock(device);