pa_context_errno can return a positive value
[openal-soft.git] / Alc / ALu.c
blobcc3f95cff3da6ab15a9d0901be1d55d4085217ca
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 "AL/al.h"
31 #include "AL/alc.h"
32 #include "alSource.h"
33 #include "alBuffer.h"
34 #include "alListener.h"
35 #include "alAuxEffectSlot.h"
36 #include "alu.h"
37 #include "bs2b.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));
58 if(length != 0.0f)
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])
69 ALfloat temp[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;
86 ALfloat cw;
87 ALint i;
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);
107 DryGainHF = 1.0f;
109 switch(ALSource->DirectFilter.type)
111 case AL_FILTER_LOWPASS:
112 DryGain *= ALSource->DirectFilter.Gain;
113 DryGainHF *= ALSource->DirectFilter.GainHF;
114 break;
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);
125 WetGainHF[i] = 1.0f;
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;
132 break;
135 ALSource->Params.WetGains[i] = WetGain[i] * ListenerGain;
137 for(i = NumSends;i < MAX_SENDS;i++)
139 ALSource->Params.WetGains[i] = 0.0f;
140 WetGainHF[i] = 1.0f;
143 /* Update filter coefficients. Calculations based on the I3DL2
144 * spec. */
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
149 * gain. */
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;
179 ALfloat length;
180 ALuint Frequency;
181 ALint NumSends;
182 ALint pos, s, i;
183 ALfloat cw;
185 for(i = 0;i < MAX_SENDS;i++)
186 WetGainHF[i] = 1.0f;
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);
243 else
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));
254 OrigDist = Distance;
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)
275 break;
276 //fall-through
277 case AL_INVERSE_DISTANCE:
278 if(MinDist > 0.0f)
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)));
288 break;
290 case AL_LINEAR_DISTANCE_CLAMPED:
291 Distance=__max(Distance,MinDist);
292 Distance=__min(Distance,MaxDist);
293 if(MaxDist < MinDist)
294 break;
295 //fall-through
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));
304 break;
306 case AL_EXPONENT_DISTANCE_CLAMPED:
307 Distance=__max(Distance,MinDist);
308 Distance=__min(Distance,MaxDist);
309 if(MaxDist < MinDist)
310 break;
311 //fall-through
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]);
319 break;
321 case AL_NONE:
322 break;
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;
331 if(MinDist > 0.0f)
332 effectiveDist = (MinDist/flAttenuation - MinDist)*MetersPerUnit;
334 // Distance-based air absorption
335 if(ALSource->AirAbsorptionFactor > 0.0f && effectiveDist > 0.0f)
337 ALfloat absorb;
339 // Absorption calculation is done in dB
340 absorb = (ALSource->AirAbsorptionFactor*AIRABSORBGAINDBHF) *
341 effectiveDist;
342 // Convert dB to linear gain before applying
343 absorb = aluPow(10.0f, absorb/20.0f);
345 DryGainHF *= absorb;
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));
361 else
363 ConeVolume = 1.0f;
364 ConeHF = 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;
375 if(Angle > 90.0f)
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)
383 DryGainHF *= ConeHF;
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;
396 WetGainHF[i] = 1.0f;
397 continue;
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) *
424 -60.0 / 20.0);
426 WetGainHF[i] *= aluPow(10.0f,
427 log10(Slot->effect.Reverb.AirAbsorptionGainHF) *
428 ALSource->AirAbsorptionFactor * effectiveDist);
431 else
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 */
435 WetGain[i] = DryMix;
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;
444 break;
446 ALSource->Params.WetGains[i] = WetGain[i] * ListenerGain;
448 for(i = NumSends;i < MAX_SENDS;i++)
450 ALSource->Params.WetGains[i] = 0.0f;
451 WetGainHF[i] = 1.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;
460 break;
462 DryMix *= ListenerGain;
464 // Calculate Velocity
465 if(DopplerFactor != 0.0f)
467 ALfloat flVSS, flVLS;
468 ALfloat flMaxVelocity = (DopplerVelocity * flSpeedOfSound) /
469 DopplerFactor;
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));
487 else
488 ALSource->Params.Pitch = ALSource->flPitch;
490 // Use energy-preserving panning algorithm for multi-speaker playback
491 length = __max(OrigDist, MinDist);
492 if(length > 0.0f)
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)
535 ALuint i;
537 SuspendContext(NULL);
538 for(i = 0;i < device->NumContexts;i++)
540 ALCcontext *Context = device->Contexts[i];
541 ALsource *source;
542 ALsizei pos;
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);