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
34 #include "alListener.h"
35 #include "alAuxEffectSlot.h"
40 static __inline ALvoid
aluCrossproduct(const ALfloat
*inVector1
, const ALfloat
*inVector2
, ALfloat
*outVector
)
42 outVector
[0] = inVector1
[1]*inVector2
[2] - inVector1
[2]*inVector2
[1];
43 outVector
[1] = inVector1
[2]*inVector2
[0] - inVector1
[0]*inVector2
[2];
44 outVector
[2] = inVector1
[0]*inVector2
[1] - inVector1
[1]*inVector2
[0];
47 static __inline ALfloat
aluDotproduct(const ALfloat
*inVector1
, const ALfloat
*inVector2
)
49 return inVector1
[0]*inVector2
[0] + inVector1
[1]*inVector2
[1] +
50 inVector1
[2]*inVector2
[2];
53 static __inline ALvoid
aluNormalize(ALfloat
*inVector
)
55 ALfloat length
, inverse_length
;
57 length
= aluSqrt(aluDotproduct(inVector
, inVector
));
60 inverse_length
= 1.0f
/length
;
61 inVector
[0] *= inverse_length
;
62 inVector
[1] *= inverse_length
;
63 inVector
[2] *= inverse_length
;
67 static __inline ALvoid
aluMatrixVector(ALfloat
*vector
,ALfloat w
,ALfloat matrix
[4][4])
70 vector
[0], vector
[1], vector
[2], w
73 vector
[0] = temp
[0]*matrix
[0][0] + temp
[1]*matrix
[1][0] + temp
[2]*matrix
[2][0] + temp
[3]*matrix
[3][0];
74 vector
[1] = temp
[0]*matrix
[0][1] + temp
[1]*matrix
[1][1] + temp
[2]*matrix
[2][1] + temp
[3]*matrix
[3][1];
75 vector
[2] = temp
[0]*matrix
[0][2] + temp
[1]*matrix
[1][2] + temp
[2]*matrix
[2][2] + temp
[3]*matrix
[3][2];
79 ALvoid
CalcNonAttnSourceParams(ALsource
*ALSource
, const ALCcontext
*ALContext
)
81 ALfloat SourceVolume
,ListenerGain
,MinVolume
,MaxVolume
;
82 ALfloat DryGain
, DryGainHF
;
83 ALfloat WetGain
[MAX_SENDS
];
84 ALfloat WetGainHF
[MAX_SENDS
];
85 ALint NumSends
, Frequency
;
89 //Get context properties
90 NumSends
= ALContext
->Device
->NumAuxSends
;
91 Frequency
= ALContext
->Device
->Frequency
;
93 //Get listener properties
94 ListenerGain
= ALContext
->Listener
.Gain
;
96 //Get source properties
97 SourceVolume
= ALSource
->flGain
;
98 MinVolume
= ALSource
->flMinGain
;
99 MaxVolume
= ALSource
->flMaxGain
;
101 //1. Multi-channel buffers always play "normal"
102 ALSource
->Params
.Pitch
= ALSource
->flPitch
;
104 DryGain
= SourceVolume
;
105 DryGain
= __min(DryGain
,MaxVolume
);
106 DryGain
= __max(DryGain
,MinVolume
);
109 switch(ALSource
->DirectFilter
.type
)
111 case AL_FILTER_LOWPASS
:
112 DryGain
*= ALSource
->DirectFilter
.Gain
;
113 DryGainHF
*= ALSource
->DirectFilter
.GainHF
;
117 for(i
= 0;i
< OUTPUTCHANNELS
;i
++)
118 ALSource
->Params
.DryGains
[i
] = DryGain
* ListenerGain
;
120 for(i
= 0;i
< NumSends
;i
++)
122 WetGain
[i
] = SourceVolume
;
123 WetGain
[i
] = __min(WetGain
[i
],MaxVolume
);
124 WetGain
[i
] = __max(WetGain
[i
],MinVolume
);
127 switch(ALSource
->Send
[i
].WetFilter
.type
)
129 case AL_FILTER_LOWPASS
:
130 WetGain
[i
] *= ALSource
->Send
[i
].WetFilter
.Gain
;
131 WetGainHF
[i
] *= ALSource
->Send
[i
].WetFilter
.GainHF
;
135 ALSource
->Params
.WetGains
[i
] = WetGain
[i
] * ListenerGain
;
137 for(i
= NumSends
;i
< MAX_SENDS
;i
++)
139 ALSource
->Params
.WetGains
[i
] = 0.0f
;
143 /* Update filter coefficients. Calculations based on the I3DL2
145 cw
= cos(2.0*M_PI
* LOWPASSFREQCUTOFF
/ Frequency
);
147 /* We use two chained one-pole filters, so we need to take the
148 * square root of the squared gain, which is the same as the base
150 ALSource
->Params
.iirFilter
.coeff
= lpCoeffCalc(DryGainHF
, cw
);
152 for(i
= 0;i
< NumSends
;i
++)
154 /* We use a one-pole filter, so we need to take the squared gain */
155 ALfloat a
= lpCoeffCalc(WetGainHF
[i
]*WetGainHF
[i
], cw
);
156 ALSource
->Params
.Send
[i
].iirFilter
.coeff
= a
;
160 ALvoid
CalcSourceParams(ALsource
*ALSource
, const ALCcontext
*ALContext
)
162 const ALCdevice
*Device
= ALContext
->Device
;
163 ALfloat InnerAngle
,OuterAngle
,Angle
,Distance
,DryMix
,OrigDist
;
164 ALfloat Direction
[3],Position
[3],SourceToListener
[3];
165 ALfloat Velocity
[3],ListenerVel
[3];
166 ALfloat MinVolume
,MaxVolume
,MinDist
,MaxDist
,Rolloff
,OuterGainHF
;
167 ALfloat ConeVolume
,ConeHF
,SourceVolume
,ListenerGain
;
168 ALfloat DopplerFactor
, DopplerVelocity
, flSpeedOfSound
;
169 ALfloat Matrix
[4][4];
170 ALfloat flAttenuation
, effectiveDist
;
171 ALfloat RoomAttenuation
[MAX_SENDS
];
172 ALfloat MetersPerUnit
;
173 ALfloat RoomRolloff
[MAX_SENDS
];
174 ALfloat DryGainHF
= 1.0f
;
175 ALfloat WetGain
[MAX_SENDS
];
176 ALfloat WetGainHF
[MAX_SENDS
];
177 ALfloat DirGain
, AmbientGain
;
178 const ALfloat
*SpeakerGain
;
185 for(i
= 0;i
< MAX_SENDS
;i
++)
188 //Get context properties
189 DopplerFactor
= ALContext
->DopplerFactor
* ALSource
->DopplerFactor
;
190 DopplerVelocity
= ALContext
->DopplerVelocity
;
191 flSpeedOfSound
= ALContext
->flSpeedOfSound
;
192 NumSends
= Device
->NumAuxSends
;
193 Frequency
= Device
->Frequency
;
195 //Get listener properties
196 ListenerGain
= ALContext
->Listener
.Gain
;
197 MetersPerUnit
= ALContext
->Listener
.MetersPerUnit
;
198 memcpy(ListenerVel
, ALContext
->Listener
.Velocity
, sizeof(ALContext
->Listener
.Velocity
));
200 //Get source properties
201 SourceVolume
= ALSource
->flGain
;
202 memcpy(Position
, ALSource
->vPosition
, sizeof(ALSource
->vPosition
));
203 memcpy(Direction
, ALSource
->vOrientation
, sizeof(ALSource
->vOrientation
));
204 memcpy(Velocity
, ALSource
->vVelocity
, sizeof(ALSource
->vVelocity
));
205 MinVolume
= ALSource
->flMinGain
;
206 MaxVolume
= ALSource
->flMaxGain
;
207 MinDist
= ALSource
->flRefDistance
;
208 MaxDist
= ALSource
->flMaxDistance
;
209 Rolloff
= ALSource
->flRollOffFactor
;
210 InnerAngle
= ALSource
->flInnerAngle
;
211 OuterAngle
= ALSource
->flOuterAngle
;
212 OuterGainHF
= ALSource
->OuterGainHF
;
214 //1. Translate Listener to origin (convert to head relative)
215 if(ALSource
->bHeadRelative
==AL_FALSE
)
217 ALfloat U
[3],V
[3],N
[3];
219 // Build transform matrix
220 memcpy(N
, ALContext
->Listener
.Forward
, sizeof(N
)); // At-vector
221 aluNormalize(N
); // Normalized At-vector
222 memcpy(V
, ALContext
->Listener
.Up
, sizeof(V
)); // Up-vector
223 aluNormalize(V
); // Normalized Up-vector
224 aluCrossproduct(N
, V
, U
); // Right-vector
225 aluNormalize(U
); // Normalized Right-vector
226 Matrix
[0][0] = U
[0]; Matrix
[0][1] = V
[0]; Matrix
[0][2] = -N
[0]; Matrix
[0][3] = 0.0f
;
227 Matrix
[1][0] = U
[1]; Matrix
[1][1] = V
[1]; Matrix
[1][2] = -N
[1]; Matrix
[1][3] = 0.0f
;
228 Matrix
[2][0] = U
[2]; Matrix
[2][1] = V
[2]; Matrix
[2][2] = -N
[2]; Matrix
[2][3] = 0.0f
;
229 Matrix
[3][0] = 0.0f
; Matrix
[3][1] = 0.0f
; Matrix
[3][2] = 0.0f
; Matrix
[3][3] = 1.0f
;
231 // Translate position
232 Position
[0] -= ALContext
->Listener
.Position
[0];
233 Position
[1] -= ALContext
->Listener
.Position
[1];
234 Position
[2] -= ALContext
->Listener
.Position
[2];
236 // Transform source position and direction into listener space
237 aluMatrixVector(Position
, 1.0f
, Matrix
);
238 aluMatrixVector(Direction
, 0.0f
, Matrix
);
239 // Transform source and listener velocity into listener space
240 aluMatrixVector(Velocity
, 0.0f
, Matrix
);
241 aluMatrixVector(ListenerVel
, 0.0f
, Matrix
);
244 ListenerVel
[0] = ListenerVel
[1] = ListenerVel
[2] = 0.0f
;
246 SourceToListener
[0] = -Position
[0];
247 SourceToListener
[1] = -Position
[1];
248 SourceToListener
[2] = -Position
[2];
249 aluNormalize(SourceToListener
);
250 aluNormalize(Direction
);
252 //2. Calculate distance attenuation
253 Distance
= aluSqrt(aluDotproduct(Position
, Position
));
256 flAttenuation
= 1.0f
;
257 for(i
= 0;i
< NumSends
;i
++)
259 RoomAttenuation
[i
] = 1.0f
;
261 RoomRolloff
[i
] = ALSource
->RoomRolloffFactor
;
262 if(ALSource
->Send
[i
].Slot
&&
263 (ALSource
->Send
[i
].Slot
->effect
.type
== AL_EFFECT_REVERB
||
264 ALSource
->Send
[i
].Slot
->effect
.type
== AL_EFFECT_EAXREVERB
))
265 RoomRolloff
[i
] += ALSource
->Send
[i
].Slot
->effect
.Reverb
.RoomRolloffFactor
;
268 switch(ALContext
->SourceDistanceModel
? ALSource
->DistanceModel
:
269 ALContext
->DistanceModel
)
271 case AL_INVERSE_DISTANCE_CLAMPED
:
272 Distance
=__max(Distance
,MinDist
);
273 Distance
=__min(Distance
,MaxDist
);
274 if(MaxDist
< MinDist
)
277 case AL_INVERSE_DISTANCE
:
280 if((MinDist
+ (Rolloff
* (Distance
- MinDist
))) > 0.0f
)
281 flAttenuation
= MinDist
/ (MinDist
+ (Rolloff
* (Distance
- MinDist
)));
282 for(i
= 0;i
< NumSends
;i
++)
284 if((MinDist
+ (RoomRolloff
[i
] * (Distance
- MinDist
))) > 0.0f
)
285 RoomAttenuation
[i
] = MinDist
/ (MinDist
+ (RoomRolloff
[i
] * (Distance
- MinDist
)));
290 case AL_LINEAR_DISTANCE_CLAMPED
:
291 Distance
=__max(Distance
,MinDist
);
292 Distance
=__min(Distance
,MaxDist
);
293 if(MaxDist
< MinDist
)
296 case AL_LINEAR_DISTANCE
:
297 Distance
=__min(Distance
,MaxDist
);
298 if(MaxDist
!= MinDist
)
300 flAttenuation
= 1.0f
- (Rolloff
*(Distance
-MinDist
)/(MaxDist
- MinDist
));
301 for(i
= 0;i
< NumSends
;i
++)
302 RoomAttenuation
[i
] = 1.0f
- (RoomRolloff
[i
]*(Distance
-MinDist
)/(MaxDist
- MinDist
));
306 case AL_EXPONENT_DISTANCE_CLAMPED
:
307 Distance
=__max(Distance
,MinDist
);
308 Distance
=__min(Distance
,MaxDist
);
309 if(MaxDist
< MinDist
)
312 case AL_EXPONENT_DISTANCE
:
313 if(Distance
> 0.0f
&& MinDist
> 0.0f
)
315 flAttenuation
= aluPow(Distance
/MinDist
, -Rolloff
);
316 for(i
= 0;i
< NumSends
;i
++)
317 RoomAttenuation
[i
] = aluPow(Distance
/MinDist
, -RoomRolloff
[i
]);
325 // Source Gain + Attenuation
326 DryMix
= SourceVolume
* flAttenuation
;
327 for(i
= 0;i
< NumSends
;i
++)
328 WetGain
[i
] = SourceVolume
* RoomAttenuation
[i
];
330 effectiveDist
= 0.0f
;
332 effectiveDist
= (MinDist
/flAttenuation
- MinDist
)*MetersPerUnit
;
334 // Distance-based air absorption
335 if(ALSource
->AirAbsorptionFactor
> 0.0f
&& effectiveDist
> 0.0f
)
339 // Absorption calculation is done in dB
340 absorb
= (ALSource
->AirAbsorptionFactor
*AIRABSORBGAINDBHF
) *
342 // Convert dB to linear gain before applying
343 absorb
= aluPow(10.0f
, absorb
/20.0f
);
348 //3. Apply directional soundcones
349 Angle
= aluAcos(aluDotproduct(Direction
,SourceToListener
)) * 180.0f
/M_PI
;
350 if(Angle
>= InnerAngle
&& Angle
<= OuterAngle
)
352 ALfloat scale
= (Angle
-InnerAngle
) / (OuterAngle
-InnerAngle
);
353 ConeVolume
= (1.0f
+(ALSource
->flOuterGain
-1.0f
)*scale
);
354 ConeHF
= (1.0f
+(OuterGainHF
-1.0f
)*scale
);
356 else if(Angle
> OuterAngle
)
358 ConeVolume
= (1.0f
+(ALSource
->flOuterGain
-1.0f
));
359 ConeHF
= (1.0f
+(OuterGainHF
-1.0f
));
367 // Apply some high-frequency attenuation for sources behind the listener
368 // NOTE: This should be aluDotproduct({0,0,-1}, ListenerToSource), however
369 // that is equivalent to aluDotproduct({0,0,1}, SourceToListener), which is
370 // the same as SourceToListener[2]
371 Angle
= aluAcos(SourceToListener
[2]) * 180.0f
/M_PI
;
372 // Sources within the minimum distance attenuate less
373 if(OrigDist
< MinDist
)
374 Angle
*= OrigDist
/MinDist
;
377 ALfloat scale
= (Angle
-90.0f
) / (180.1f
-90.0f
); // .1 to account for fp errors
378 ConeHF
*= 1.0f
- (Device
->HeadDampen
*scale
);
381 DryMix
*= ConeVolume
;
382 if(ALSource
->DryGainHFAuto
)
385 // Clamp to Min/Max Gain
386 DryMix
= __min(DryMix
,MaxVolume
);
387 DryMix
= __max(DryMix
,MinVolume
);
389 for(i
= 0;i
< NumSends
;i
++)
391 ALeffectslot
*Slot
= ALSource
->Send
[i
].Slot
;
393 if(!Slot
|| Slot
->effect
.type
== AL_EFFECT_NULL
)
395 ALSource
->Params
.WetGains
[i
] = 0.0f
;
400 if(Slot
->AuxSendAuto
)
402 if(ALSource
->WetGainAuto
)
403 WetGain
[i
] *= ConeVolume
;
404 if(ALSource
->WetGainHFAuto
)
405 WetGainHF
[i
] *= ConeHF
;
407 // Clamp to Min/Max Gain
408 WetGain
[i
] = __min(WetGain
[i
],MaxVolume
);
409 WetGain
[i
] = __max(WetGain
[i
],MinVolume
);
411 if(Slot
->effect
.type
== AL_EFFECT_REVERB
||
412 Slot
->effect
.type
== AL_EFFECT_EAXREVERB
)
414 /* Apply a decay-time transformation to the wet path, based on
415 * the attenuation of the dry path.
417 * Using the approximate (effective) source to listener
418 * distance, the initial decay of the reverb effect is
419 * calculated and applied to the wet path.
421 WetGain
[i
] *= aluPow(10.0f
, effectiveDist
/
422 (SPEEDOFSOUNDMETRESPERSEC
*
423 Slot
->effect
.Reverb
.DecayTime
) *
426 WetGainHF
[i
] *= aluPow(10.0f
,
427 log10(Slot
->effect
.Reverb
.AirAbsorptionGainHF
) *
428 ALSource
->AirAbsorptionFactor
* effectiveDist
);
433 /* If the slot's auxiliary send auto is off, the data sent to the
434 * effect slot is the same as the dry path, sans filter effects */
436 WetGainHF
[i
] = DryGainHF
;
439 switch(ALSource
->Send
[i
].WetFilter
.type
)
441 case AL_FILTER_LOWPASS
:
442 WetGain
[i
] *= ALSource
->Send
[i
].WetFilter
.Gain
;
443 WetGainHF
[i
] *= ALSource
->Send
[i
].WetFilter
.GainHF
;
446 ALSource
->Params
.WetGains
[i
] = WetGain
[i
] * ListenerGain
;
448 for(i
= NumSends
;i
< MAX_SENDS
;i
++)
450 ALSource
->Params
.WetGains
[i
] = 0.0f
;
454 // Apply filter gains and filters
455 switch(ALSource
->DirectFilter
.type
)
457 case AL_FILTER_LOWPASS
:
458 DryMix
*= ALSource
->DirectFilter
.Gain
;
459 DryGainHF
*= ALSource
->DirectFilter
.GainHF
;
462 DryMix
*= ListenerGain
;
464 // Calculate Velocity
465 if(DopplerFactor
!= 0.0f
)
467 ALfloat flVSS
, flVLS
;
468 ALfloat flMaxVelocity
= (DopplerVelocity
* flSpeedOfSound
) /
471 flVSS
= aluDotproduct(Velocity
, SourceToListener
);
472 if(flVSS
>= flMaxVelocity
)
473 flVSS
= (flMaxVelocity
- 1.0f
);
474 else if(flVSS
<= -flMaxVelocity
)
475 flVSS
= -flMaxVelocity
+ 1.0f
;
477 flVLS
= aluDotproduct(ListenerVel
, SourceToListener
);
478 if(flVLS
>= flMaxVelocity
)
479 flVLS
= (flMaxVelocity
- 1.0f
);
480 else if(flVLS
<= -flMaxVelocity
)
481 flVLS
= -flMaxVelocity
+ 1.0f
;
483 ALSource
->Params
.Pitch
= ALSource
->flPitch
*
484 ((flSpeedOfSound
* DopplerVelocity
) - (DopplerFactor
* flVLS
)) /
485 ((flSpeedOfSound
* DopplerVelocity
) - (DopplerFactor
* flVSS
));
488 ALSource
->Params
.Pitch
= ALSource
->flPitch
;
490 // Use energy-preserving panning algorithm for multi-speaker playback
491 length
= __max(OrigDist
, MinDist
);
494 ALfloat invlen
= 1.0f
/length
;
495 Position
[0] *= invlen
;
496 Position
[1] *= invlen
;
497 Position
[2] *= invlen
;
500 pos
= aluCart2LUTpos(-Position
[2], Position
[0]);
501 SpeakerGain
= &Device
->PanningLUT
[OUTPUTCHANNELS
* pos
];
503 DirGain
= aluSqrt(Position
[0]*Position
[0] + Position
[2]*Position
[2]);
504 // elevation adjustment for directional gain. this sucks, but
505 // has low complexity
506 AmbientGain
= 1.0/aluSqrt(Device
->NumChan
) * (1.0-DirGain
);
507 for(s
= 0;s
< OUTPUTCHANNELS
;s
++)
508 ALSource
->Params
.DryGains
[s
] = 0.0f
;
509 for(s
= 0;s
< (ALsizei
)Device
->NumChan
;s
++)
511 Channel chan
= Device
->Speaker2Chan
[s
];
512 ALfloat gain
= SpeakerGain
[chan
]*DirGain
+ AmbientGain
;
513 ALSource
->Params
.DryGains
[chan
] = DryMix
* gain
;
516 /* Update filter coefficients. */
517 cw
= cos(2.0*M_PI
* LOWPASSFREQCUTOFF
/ Frequency
);
519 /* Spatialized sources use four chained one-pole filters, so we need to
520 * take the fourth root of the squared gain, which is the same as the
521 * square root of the base gain. */
522 ALSource
->Params
.iirFilter
.coeff
= lpCoeffCalc(aluSqrt(DryGainHF
), cw
);
524 for(i
= 0;i
< NumSends
;i
++)
526 /* The wet path uses two chained one-pole filters, so take the
527 * base gain (square root of the squared gain) */
528 ALSource
->Params
.Send
[i
].iirFilter
.coeff
= lpCoeffCalc(WetGainHF
[i
], cw
);
533 ALvoid
aluHandleDisconnect(ALCdevice
*device
)
537 SuspendContext(NULL
);
538 for(i
= 0;i
< device
->NumContexts
;i
++)
540 ALCcontext
*Context
= device
->Contexts
[i
];
544 SuspendContext(Context
);
546 for(pos
= 0;pos
< Context
->SourceMap
.size
;pos
++)
548 source
= Context
->SourceMap
.array
[pos
].value
;
549 if(source
->state
== AL_PLAYING
)
551 source
->state
= AL_STOPPED
;
552 source
->BuffersPlayed
= source
->BuffersInQueue
;
553 source
->position
= 0;
554 source
->position_fraction
= 0;
557 ProcessContext(Context
);
560 device
->Connected
= ALC_FALSE
;
561 ProcessContext(NULL
);