2007-11-14 02:02:18 +00:00
|
|
|
/**
|
|
|
|
* OpenAL cross platform audio library
|
|
|
|
* Copyright (C) 1999-2007 by authors.
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this library; if not, write to the
|
|
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 02111-1307, USA.
|
|
|
|
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <math.h>
|
2009-01-24 18:38:04 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2009-01-26 03:20:47 +00:00
|
|
|
#include <ctype.h>
|
2009-02-02 19:18:33 +00:00
|
|
|
#include <assert.h>
|
2009-01-26 03:20:47 +00:00
|
|
|
|
2007-11-14 02:02:18 +00:00
|
|
|
#include "alMain.h"
|
|
|
|
#include "AL/al.h"
|
|
|
|
#include "AL/alc.h"
|
2007-12-31 09:09:57 +00:00
|
|
|
#include "alSource.h"
|
|
|
|
#include "alBuffer.h"
|
|
|
|
#include "alListener.h"
|
2008-01-16 22:01:24 +00:00
|
|
|
#include "alAuxEffectSlot.h"
|
2008-08-14 12:43:52 +00:00
|
|
|
#include "alu.h"
|
2008-01-03 13:36:51 +00:00
|
|
|
#include "bs2b.h"
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2009-08-15 18:33:38 +00:00
|
|
|
|
2009-03-14 06:08:15 +00:00
|
|
|
static __inline ALvoid aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
|
2007-11-14 02:02:18 +00:00
|
|
|
{
|
|
|
|
outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
|
|
|
|
outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
|
|
|
|
outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
|
|
|
|
}
|
|
|
|
|
2009-03-14 06:08:15 +00:00
|
|
|
static __inline ALfloat aluDotproduct(const ALfloat *inVector1, const ALfloat *inVector2)
|
2007-11-14 02:02:18 +00:00
|
|
|
{
|
|
|
|
return inVector1[0]*inVector2[0] + inVector1[1]*inVector2[1] +
|
|
|
|
inVector1[2]*inVector2[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline ALvoid aluNormalize(ALfloat *inVector)
|
|
|
|
{
|
|
|
|
ALfloat length, inverse_length;
|
|
|
|
|
2008-02-09 05:01:05 +00:00
|
|
|
length = aluSqrt(aluDotproduct(inVector, inVector));
|
|
|
|
if(length != 0.0f)
|
2007-11-14 02:02:18 +00:00
|
|
|
{
|
|
|
|
inverse_length = 1.0f/length;
|
|
|
|
inVector[0] *= inverse_length;
|
|
|
|
inVector[1] *= inverse_length;
|
|
|
|
inVector[2] *= inverse_length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-23 12:13:51 +00:00
|
|
|
static __inline ALvoid aluMatrixVector(ALfloat *vector,ALfloat w,ALfloat matrix[4][4])
|
2007-11-14 02:02:18 +00:00
|
|
|
{
|
2009-11-23 12:13:51 +00:00
|
|
|
ALfloat temp[4] = {
|
|
|
|
vector[0], vector[1], vector[2], w
|
|
|
|
};
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2009-11-23 12:13:51 +00:00
|
|
|
vector[0] = temp[0]*matrix[0][0] + temp[1]*matrix[1][0] + temp[2]*matrix[2][0] + temp[3]*matrix[3][0];
|
|
|
|
vector[1] = temp[0]*matrix[0][1] + temp[1]*matrix[1][1] + temp[2]*matrix[2][1] + temp[3]*matrix[3][1];
|
|
|
|
vector[2] = temp[0]*matrix[0][2] + temp[1]*matrix[1][2] + temp[2]*matrix[2][2] + temp[3]*matrix[3][2];
|
2007-11-14 02:02:18 +00:00
|
|
|
}
|
|
|
|
|
2010-08-04 06:19:36 +00:00
|
|
|
|
2010-08-04 06:10:00 +00:00
|
|
|
ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
|
2010-08-05 08:07:20 +00:00
|
|
|
{
|
|
|
|
ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
|
2010-08-07 07:38:02 +00:00
|
|
|
ALbufferlistitem *BufferListItem;
|
2010-08-05 08:07:20 +00:00
|
|
|
ALfloat DryGain, DryGainHF;
|
|
|
|
ALfloat WetGain[MAX_SENDS];
|
|
|
|
ALfloat WetGainHF[MAX_SENDS];
|
|
|
|
ALint NumSends, Frequency;
|
|
|
|
ALboolean DupStereo;
|
2010-08-07 13:57:31 +00:00
|
|
|
ALint Channels;
|
2010-08-07 12:43:16 +00:00
|
|
|
ALfloat Pitch;
|
2010-08-05 08:07:20 +00:00
|
|
|
ALenum Format;
|
|
|
|
ALfloat cw;
|
|
|
|
ALint i;
|
|
|
|
|
|
|
|
//Get context properties
|
|
|
|
Format = ALContext->Device->Format;
|
|
|
|
DupStereo = ALContext->Device->DuplicateStereo;
|
|
|
|
NumSends = ALContext->Device->NumAuxSends;
|
|
|
|
Frequency = ALContext->Device->Frequency;
|
|
|
|
|
|
|
|
//Get listener properties
|
|
|
|
ListenerGain = ALContext->Listener.Gain;
|
|
|
|
|
|
|
|
//Get source properties
|
|
|
|
SourceVolume = ALSource->flGain;
|
|
|
|
MinVolume = ALSource->flMinGain;
|
|
|
|
MaxVolume = ALSource->flMaxGain;
|
|
|
|
|
|
|
|
//1. Multi-channel buffers always play "normal"
|
2010-08-07 13:57:31 +00:00
|
|
|
Channels = 0;
|
2010-08-07 12:43:16 +00:00
|
|
|
Pitch = ALSource->flPitch;
|
2010-08-07 07:38:02 +00:00
|
|
|
BufferListItem = ALSource->queue;
|
|
|
|
while(BufferListItem != NULL)
|
|
|
|
{
|
|
|
|
ALbuffer *ALBuffer;
|
|
|
|
if((ALBuffer=BufferListItem->buffer) != NULL)
|
|
|
|
{
|
2010-08-07 13:57:31 +00:00
|
|
|
Channels = aluChannelsFromFormat(ALBuffer->format);
|
2010-08-07 12:43:16 +00:00
|
|
|
Pitch = Pitch * ALBuffer->frequency / Frequency;
|
2010-08-07 07:38:02 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
BufferListItem = BufferListItem->next;
|
|
|
|
}
|
2010-08-05 08:07:20 +00:00
|
|
|
|
2010-08-07 12:43:16 +00:00
|
|
|
if(Pitch > (float)MAX_PITCH)
|
|
|
|
ALSource->Params.Step = MAX_PITCH<<FRACTIONBITS;
|
|
|
|
else if(!(Pitch > 0.0f))
|
|
|
|
ALSource->Params.Step = 1<<FRACTIONBITS;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ALSource->Params.Step = Pitch*(1<<FRACTIONBITS);
|
|
|
|
if(ALSource->Params.Step == 0)
|
|
|
|
ALSource->Params.Step = 1;
|
|
|
|
}
|
|
|
|
|
2010-08-05 08:07:20 +00:00
|
|
|
DryGain = SourceVolume;
|
|
|
|
DryGain = __min(DryGain,MaxVolume);
|
|
|
|
DryGain = __max(DryGain,MinVolume);
|
|
|
|
DryGainHF = 1.0f;
|
|
|
|
|
|
|
|
switch(ALSource->DirectFilter.type)
|
|
|
|
{
|
|
|
|
case AL_FILTER_LOWPASS:
|
|
|
|
DryGain *= ALSource->DirectFilter.Gain;
|
|
|
|
DryGainHF *= ALSource->DirectFilter.GainHF;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-08-07 13:57:31 +00:00
|
|
|
if(Channels == 2)
|
2010-08-05 08:07:20 +00:00
|
|
|
{
|
2010-08-07 13:57:31 +00:00
|
|
|
for(i = 0;i < OUTPUTCHANNELS;i++)
|
|
|
|
ALSource->Params.DryGains[i] = 0.0f;
|
|
|
|
|
|
|
|
if(DupStereo == AL_FALSE)
|
2010-08-05 08:07:20 +00:00
|
|
|
{
|
|
|
|
ALSource->Params.DryGains[FRONT_LEFT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[FRONT_RIGHT] = DryGain * ListenerGain;
|
2010-08-07 13:57:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch(Format)
|
|
|
|
{
|
|
|
|
case AL_FORMAT_MONO8:
|
|
|
|
case AL_FORMAT_MONO16:
|
|
|
|
case AL_FORMAT_MONO_FLOAT32:
|
|
|
|
case AL_FORMAT_STEREO8:
|
|
|
|
case AL_FORMAT_STEREO16:
|
|
|
|
case AL_FORMAT_STEREO_FLOAT32:
|
|
|
|
ALSource->Params.DryGains[FRONT_LEFT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[FRONT_RIGHT] = DryGain * ListenerGain;
|
|
|
|
break;
|
2010-08-05 08:07:20 +00:00
|
|
|
|
2010-08-07 13:57:31 +00:00
|
|
|
case AL_FORMAT_QUAD8:
|
|
|
|
case AL_FORMAT_QUAD16:
|
|
|
|
case AL_FORMAT_QUAD32:
|
|
|
|
case AL_FORMAT_51CHN8:
|
|
|
|
case AL_FORMAT_51CHN16:
|
|
|
|
case AL_FORMAT_51CHN32:
|
|
|
|
DryGain *= aluSqrt(2.0f/4.0f);
|
|
|
|
ALSource->Params.DryGains[FRONT_LEFT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[FRONT_RIGHT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[BACK_LEFT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[BACK_RIGHT] = DryGain * ListenerGain;
|
|
|
|
break;
|
2010-08-05 08:07:20 +00:00
|
|
|
|
2010-08-07 13:57:31 +00:00
|
|
|
case AL_FORMAT_61CHN8:
|
|
|
|
case AL_FORMAT_61CHN16:
|
|
|
|
case AL_FORMAT_61CHN32:
|
|
|
|
DryGain *= aluSqrt(2.0f/4.0f);
|
|
|
|
ALSource->Params.DryGains[FRONT_LEFT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[FRONT_RIGHT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[SIDE_LEFT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[SIDE_RIGHT] = DryGain * ListenerGain;
|
|
|
|
break;
|
2010-08-05 08:07:20 +00:00
|
|
|
|
2010-08-07 13:57:31 +00:00
|
|
|
case AL_FORMAT_71CHN8:
|
|
|
|
case AL_FORMAT_71CHN16:
|
|
|
|
case AL_FORMAT_71CHN32:
|
|
|
|
DryGain *= aluSqrt(2.0f/6.0f);
|
|
|
|
ALSource->Params.DryGains[FRONT_LEFT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[FRONT_RIGHT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[BACK_LEFT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[BACK_RIGHT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[SIDE_LEFT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[SIDE_RIGHT] = DryGain * ListenerGain;
|
|
|
|
break;
|
2010-08-05 08:07:20 +00:00
|
|
|
|
2010-08-07 13:57:31 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2010-08-05 08:07:20 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-07 13:57:31 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
for(i = 0;i < OUTPUTCHANNELS;i++)
|
|
|
|
ALSource->Params.DryGains[i] = DryGain * ListenerGain;
|
|
|
|
}
|
2010-08-05 08:07:20 +00:00
|
|
|
|
2009-12-09 15:02:26 +00:00
|
|
|
for(i = 0;i < NumSends;i++)
|
|
|
|
{
|
|
|
|
WetGain[i] = SourceVolume;
|
|
|
|
WetGain[i] = __min(WetGain[i],MaxVolume);
|
|
|
|
WetGain[i] = __max(WetGain[i],MinVolume);
|
|
|
|
WetGainHF[i] = 1.0f;
|
|
|
|
|
|
|
|
switch(ALSource->Send[i].WetFilter.type)
|
|
|
|
{
|
|
|
|
case AL_FILTER_LOWPASS:
|
|
|
|
WetGain[i] *= ALSource->Send[i].WetFilter.Gain;
|
|
|
|
WetGainHF[i] *= ALSource->Send[i].WetFilter.GainHF;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ALSource->Params.WetGains[i] = WetGain[i] * ListenerGain;
|
|
|
|
}
|
|
|
|
for(i = NumSends;i < MAX_SENDS;i++)
|
|
|
|
{
|
|
|
|
ALSource->Params.WetGains[i] = 0.0f;
|
|
|
|
WetGainHF[i] = 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update filter coefficients. Calculations based on the I3DL2
|
|
|
|
* spec. */
|
|
|
|
cw = cos(2.0*M_PI * LOWPASSFREQCUTOFF / Frequency);
|
2009-12-09 15:21:59 +00:00
|
|
|
|
2009-12-09 15:02:26 +00:00
|
|
|
/* We use two chained one-pole filters, so we need to take the
|
|
|
|
* square root of the squared gain, which is the same as the base
|
|
|
|
* gain. */
|
2009-12-09 15:21:59 +00:00
|
|
|
ALSource->Params.iirFilter.coeff = lpCoeffCalc(DryGainHF, cw);
|
2009-12-09 15:02:26 +00:00
|
|
|
|
|
|
|
for(i = 0;i < NumSends;i++)
|
|
|
|
{
|
|
|
|
/* We use a one-pole filter, so we need to take the squared gain */
|
2009-12-09 15:21:59 +00:00
|
|
|
ALfloat a = lpCoeffCalc(WetGainHF[i]*WetGainHF[i], cw);
|
2009-12-09 15:02:26 +00:00
|
|
|
ALSource->Params.Send[i].iirFilter.coeff = a;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-04 06:10:00 +00:00
|
|
|
ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
|
2007-11-14 02:02:18 +00:00
|
|
|
{
|
2010-04-27 18:39:54 +00:00
|
|
|
const ALCdevice *Device = ALContext->Device;
|
2009-12-08 18:12:18 +00:00
|
|
|
ALfloat InnerAngle,OuterAngle,Angle,Distance,DryMix,OrigDist;
|
2008-01-16 05:57:50 +00:00
|
|
|
ALfloat Direction[3],Position[3],SourceToListener[3];
|
2009-11-23 06:36:20 +00:00
|
|
|
ALfloat Velocity[3],ListenerVel[3];
|
2007-12-18 05:56:31 +00:00
|
|
|
ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff,OuterGainHF;
|
2009-04-13 09:50:40 +00:00
|
|
|
ALfloat ConeVolume,ConeHF,SourceVolume,ListenerGain;
|
2009-10-20 18:03:16 +00:00
|
|
|
ALfloat DopplerFactor, DopplerVelocity, flSpeedOfSound;
|
2010-08-07 07:38:02 +00:00
|
|
|
ALbufferlistitem *BufferListItem;
|
2009-11-23 12:13:51 +00:00
|
|
|
ALfloat Matrix[4][4];
|
2009-12-11 02:22:45 +00:00
|
|
|
ALfloat flAttenuation, effectiveDist;
|
2009-04-12 03:04:46 +00:00
|
|
|
ALfloat RoomAttenuation[MAX_SENDS];
|
2007-12-18 03:40:43 +00:00
|
|
|
ALfloat MetersPerUnit;
|
2009-04-12 03:04:46 +00:00
|
|
|
ALfloat RoomRolloff[MAX_SENDS];
|
2007-12-18 05:56:31 +00:00
|
|
|
ALfloat DryGainHF = 1.0f;
|
2009-10-21 22:31:21 +00:00
|
|
|
ALfloat WetGain[MAX_SENDS];
|
2009-10-21 20:08:50 +00:00
|
|
|
ALfloat WetGainHF[MAX_SENDS];
|
2009-01-24 18:38:04 +00:00
|
|
|
ALfloat DirGain, AmbientGain;
|
|
|
|
const ALfloat *SpeakerGain;
|
2010-08-07 07:38:02 +00:00
|
|
|
ALfloat Pitch;
|
2010-04-27 18:39:54 +00:00
|
|
|
ALfloat length;
|
2009-10-21 20:08:50 +00:00
|
|
|
ALuint Frequency;
|
2009-04-14 03:33:41 +00:00
|
|
|
ALint NumSends;
|
2009-04-12 03:04:46 +00:00
|
|
|
ALint pos, s, i;
|
2009-12-09 15:21:59 +00:00
|
|
|
ALfloat cw;
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2009-12-01 11:32:04 +00:00
|
|
|
for(i = 0;i < MAX_SENDS;i++)
|
|
|
|
WetGainHF[i] = 1.0f;
|
|
|
|
|
2007-11-14 02:02:18 +00:00
|
|
|
//Get context properties
|
2008-07-15 09:33:05 +00:00
|
|
|
DopplerFactor = ALContext->DopplerFactor * ALSource->DopplerFactor;
|
2007-11-14 02:02:18 +00:00
|
|
|
DopplerVelocity = ALContext->DopplerVelocity;
|
|
|
|
flSpeedOfSound = ALContext->flSpeedOfSound;
|
2010-04-27 18:39:54 +00:00
|
|
|
NumSends = Device->NumAuxSends;
|
|
|
|
Frequency = Device->Frequency;
|
2007-11-14 02:02:18 +00:00
|
|
|
|
|
|
|
//Get listener properties
|
|
|
|
ListenerGain = ALContext->Listener.Gain;
|
2007-12-18 03:40:43 +00:00
|
|
|
MetersPerUnit = ALContext->Listener.MetersPerUnit;
|
2009-11-23 06:36:20 +00:00
|
|
|
memcpy(ListenerVel, ALContext->Listener.Velocity, sizeof(ALContext->Listener.Velocity));
|
2007-11-14 02:02:18 +00:00
|
|
|
|
|
|
|
//Get source properties
|
|
|
|
SourceVolume = ALSource->flGain;
|
|
|
|
memcpy(Position, ALSource->vPosition, sizeof(ALSource->vPosition));
|
|
|
|
memcpy(Direction, ALSource->vOrientation, sizeof(ALSource->vOrientation));
|
2009-11-23 06:36:20 +00:00
|
|
|
memcpy(Velocity, ALSource->vVelocity, sizeof(ALSource->vVelocity));
|
2007-11-14 02:02:18 +00:00
|
|
|
MinVolume = ALSource->flMinGain;
|
|
|
|
MaxVolume = ALSource->flMaxGain;
|
|
|
|
MinDist = ALSource->flRefDistance;
|
|
|
|
MaxDist = ALSource->flMaxDistance;
|
|
|
|
Rolloff = ALSource->flRollOffFactor;
|
|
|
|
InnerAngle = ALSource->flInnerAngle;
|
|
|
|
OuterAngle = ALSource->flOuterAngle;
|
2007-12-19 00:54:22 +00:00
|
|
|
OuterGainHF = ALSource->OuterGainHF;
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2009-10-19 20:25:40 +00:00
|
|
|
//1. Translate Listener to origin (convert to head relative)
|
|
|
|
if(ALSource->bHeadRelative==AL_FALSE)
|
|
|
|
{
|
2010-04-16 09:09:53 +00:00
|
|
|
ALfloat U[3],V[3],N[3];
|
2009-11-23 06:36:20 +00:00
|
|
|
|
2009-10-19 20:25:40 +00:00
|
|
|
// Build transform matrix
|
|
|
|
memcpy(N, ALContext->Listener.Forward, sizeof(N)); // At-vector
|
|
|
|
aluNormalize(N); // Normalized At-vector
|
2009-11-23 12:13:51 +00:00
|
|
|
memcpy(V, ALContext->Listener.Up, sizeof(V)); // Up-vector
|
|
|
|
aluNormalize(V); // Normalized Up-vector
|
|
|
|
aluCrossproduct(N, V, U); // Right-vector
|
|
|
|
aluNormalize(U); // Normalized Right-vector
|
|
|
|
Matrix[0][0] = U[0]; Matrix[0][1] = V[0]; Matrix[0][2] = -N[0]; Matrix[0][3] = 0.0f;
|
|
|
|
Matrix[1][0] = U[1]; Matrix[1][1] = V[1]; Matrix[1][2] = -N[1]; Matrix[1][3] = 0.0f;
|
|
|
|
Matrix[2][0] = U[2]; Matrix[2][1] = V[2]; Matrix[2][2] = -N[2]; Matrix[2][3] = 0.0f;
|
2010-04-16 09:09:53 +00:00
|
|
|
Matrix[3][0] = 0.0f; Matrix[3][1] = 0.0f; Matrix[3][2] = 0.0f; Matrix[3][3] = 1.0f;
|
|
|
|
|
|
|
|
// Translate position
|
|
|
|
Position[0] -= ALContext->Listener.Position[0];
|
|
|
|
Position[1] -= ALContext->Listener.Position[1];
|
|
|
|
Position[2] -= ALContext->Listener.Position[2];
|
2009-11-23 12:13:51 +00:00
|
|
|
|
2009-11-23 06:36:20 +00:00
|
|
|
// Transform source position and direction into listener space
|
2009-11-23 12:13:51 +00:00
|
|
|
aluMatrixVector(Position, 1.0f, Matrix);
|
|
|
|
aluMatrixVector(Direction, 0.0f, Matrix);
|
2009-11-23 06:36:20 +00:00
|
|
|
// Transform source and listener velocity into listener space
|
2009-11-23 12:13:51 +00:00
|
|
|
aluMatrixVector(Velocity, 0.0f, Matrix);
|
|
|
|
aluMatrixVector(ListenerVel, 0.0f, Matrix);
|
2009-10-19 20:25:40 +00:00
|
|
|
}
|
|
|
|
else
|
2009-11-23 06:36:20 +00:00
|
|
|
ListenerVel[0] = ListenerVel[1] = ListenerVel[2] = 0.0f;
|
|
|
|
|
|
|
|
SourceToListener[0] = -Position[0];
|
|
|
|
SourceToListener[1] = -Position[1];
|
|
|
|
SourceToListener[2] = -Position[2];
|
2009-10-19 20:25:40 +00:00
|
|
|
aluNormalize(SourceToListener);
|
|
|
|
aluNormalize(Direction);
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2009-10-19 20:25:40 +00:00
|
|
|
//2. Calculate distance attenuation
|
|
|
|
Distance = aluSqrt(aluDotproduct(Position, Position));
|
2009-12-08 18:12:18 +00:00
|
|
|
OrigDist = Distance;
|
2009-10-19 20:25:40 +00:00
|
|
|
|
|
|
|
flAttenuation = 1.0f;
|
2009-12-06 11:59:12 +00:00
|
|
|
for(i = 0;i < NumSends;i++)
|
2009-10-19 20:25:40 +00:00
|
|
|
{
|
|
|
|
RoomAttenuation[i] = 1.0f;
|
|
|
|
|
|
|
|
RoomRolloff[i] = ALSource->RoomRolloffFactor;
|
|
|
|
if(ALSource->Send[i].Slot &&
|
2009-11-19 18:29:10 +00:00
|
|
|
(ALSource->Send[i].Slot->effect.type == AL_EFFECT_REVERB ||
|
|
|
|
ALSource->Send[i].Slot->effect.type == AL_EFFECT_EAXREVERB))
|
2009-10-19 20:25:40 +00:00
|
|
|
RoomRolloff[i] += ALSource->Send[i].Slot->effect.Reverb.RoomRolloffFactor;
|
|
|
|
}
|
|
|
|
|
2009-11-28 04:05:21 +00:00
|
|
|
switch(ALContext->SourceDistanceModel ? ALSource->DistanceModel :
|
|
|
|
ALContext->DistanceModel)
|
2009-10-19 20:25:40 +00:00
|
|
|
{
|
|
|
|
case AL_INVERSE_DISTANCE_CLAMPED:
|
|
|
|
Distance=__max(Distance,MinDist);
|
|
|
|
Distance=__min(Distance,MaxDist);
|
|
|
|
if(MaxDist < MinDist)
|
|
|
|
break;
|
|
|
|
//fall-through
|
|
|
|
case AL_INVERSE_DISTANCE:
|
|
|
|
if(MinDist > 0.0f)
|
|
|
|
{
|
|
|
|
if((MinDist + (Rolloff * (Distance - MinDist))) > 0.0f)
|
|
|
|
flAttenuation = MinDist / (MinDist + (Rolloff * (Distance - MinDist)));
|
|
|
|
for(i = 0;i < NumSends;i++)
|
2007-12-19 03:03:40 +00:00
|
|
|
{
|
2009-10-19 20:25:40 +00:00
|
|
|
if((MinDist + (RoomRolloff[i] * (Distance - MinDist))) > 0.0f)
|
|
|
|
RoomAttenuation[i] = MinDist / (MinDist + (RoomRolloff[i] * (Distance - MinDist)));
|
2007-12-19 03:03:40 +00:00
|
|
|
}
|
2009-10-19 20:25:40 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AL_LINEAR_DISTANCE_CLAMPED:
|
|
|
|
Distance=__max(Distance,MinDist);
|
|
|
|
Distance=__min(Distance,MaxDist);
|
|
|
|
if(MaxDist < MinDist)
|
2007-11-14 02:02:18 +00:00
|
|
|
break;
|
2009-10-19 20:25:40 +00:00
|
|
|
//fall-through
|
|
|
|
case AL_LINEAR_DISTANCE:
|
|
|
|
Distance=__min(Distance,MaxDist);
|
|
|
|
if(MaxDist != MinDist)
|
|
|
|
{
|
|
|
|
flAttenuation = 1.0f - (Rolloff*(Distance-MinDist)/(MaxDist - MinDist));
|
|
|
|
for(i = 0;i < NumSends;i++)
|
|
|
|
RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(Distance-MinDist)/(MaxDist - MinDist));
|
|
|
|
}
|
|
|
|
break;
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2009-10-19 20:25:40 +00:00
|
|
|
case AL_EXPONENT_DISTANCE_CLAMPED:
|
|
|
|
Distance=__max(Distance,MinDist);
|
|
|
|
Distance=__min(Distance,MaxDist);
|
|
|
|
if(MaxDist < MinDist)
|
2007-11-14 02:02:18 +00:00
|
|
|
break;
|
2009-10-19 20:25:40 +00:00
|
|
|
//fall-through
|
|
|
|
case AL_EXPONENT_DISTANCE:
|
|
|
|
if(Distance > 0.0f && MinDist > 0.0f)
|
|
|
|
{
|
2010-03-08 06:12:33 +00:00
|
|
|
flAttenuation = aluPow(Distance/MinDist, -Rolloff);
|
2009-10-19 20:25:40 +00:00
|
|
|
for(i = 0;i < NumSends;i++)
|
2010-03-08 06:12:33 +00:00
|
|
|
RoomAttenuation[i] = aluPow(Distance/MinDist, -RoomRolloff[i]);
|
2009-10-19 20:25:40 +00:00
|
|
|
}
|
|
|
|
break;
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2009-10-19 20:25:40 +00:00
|
|
|
case AL_NONE:
|
|
|
|
break;
|
|
|
|
}
|
2009-04-12 03:27:55 +00:00
|
|
|
|
2009-12-01 11:32:04 +00:00
|
|
|
// Source Gain + Attenuation
|
2009-10-19 20:25:40 +00:00
|
|
|
DryMix = SourceVolume * flAttenuation;
|
|
|
|
for(i = 0;i < NumSends;i++)
|
2009-12-01 11:32:04 +00:00
|
|
|
WetGain[i] = SourceVolume * RoomAttenuation[i];
|
2008-09-16 14:36:48 +00:00
|
|
|
|
2009-12-11 02:22:45 +00:00
|
|
|
effectiveDist = 0.0f;
|
|
|
|
if(MinDist > 0.0f)
|
|
|
|
effectiveDist = (MinDist/flAttenuation - MinDist)*MetersPerUnit;
|
|
|
|
|
2009-10-19 20:25:40 +00:00
|
|
|
// Distance-based air absorption
|
2009-12-11 02:22:45 +00:00
|
|
|
if(ALSource->AirAbsorptionFactor > 0.0f && effectiveDist > 0.0f)
|
2009-10-19 20:25:40 +00:00
|
|
|
{
|
2009-12-11 02:22:45 +00:00
|
|
|
ALfloat absorb;
|
2009-10-19 20:25:40 +00:00
|
|
|
|
2009-11-24 09:39:34 +00:00
|
|
|
// Absorption calculation is done in dB
|
2009-12-11 02:22:45 +00:00
|
|
|
absorb = (ALSource->AirAbsorptionFactor*AIRABSORBGAINDBHF) *
|
|
|
|
effectiveDist;
|
|
|
|
// Convert dB to linear gain before applying
|
2010-03-08 06:12:33 +00:00
|
|
|
absorb = aluPow(10.0f, absorb/20.0f);
|
2009-12-11 02:22:45 +00:00
|
|
|
|
2009-11-24 09:39:34 +00:00
|
|
|
DryGainHF *= absorb;
|
2009-10-19 20:25:40 +00:00
|
|
|
}
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2009-10-19 20:25:40 +00:00
|
|
|
//3. Apply directional soundcones
|
|
|
|
Angle = aluAcos(aluDotproduct(Direction,SourceToListener)) * 180.0f/M_PI;
|
|
|
|
if(Angle >= InnerAngle && Angle <= OuterAngle)
|
|
|
|
{
|
|
|
|
ALfloat scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
|
|
|
|
ConeVolume = (1.0f+(ALSource->flOuterGain-1.0f)*scale);
|
|
|
|
ConeHF = (1.0f+(OuterGainHF-1.0f)*scale);
|
|
|
|
}
|
|
|
|
else if(Angle > OuterAngle)
|
|
|
|
{
|
|
|
|
ConeVolume = (1.0f+(ALSource->flOuterGain-1.0f));
|
|
|
|
ConeHF = (1.0f+(OuterGainHF-1.0f));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ConeVolume = 1.0f;
|
|
|
|
ConeHF = 1.0f;
|
|
|
|
}
|
2008-01-16 05:57:50 +00:00
|
|
|
|
2009-12-08 22:18:07 +00:00
|
|
|
// Apply some high-frequency attenuation for sources behind the listener
|
|
|
|
// NOTE: This should be aluDotproduct({0,0,-1}, ListenerToSource), however
|
|
|
|
// that is equivalent to aluDotproduct({0,0,1}, SourceToListener), which is
|
|
|
|
// the same as SourceToListener[2]
|
|
|
|
Angle = aluAcos(SourceToListener[2]) * 180.0f/M_PI;
|
|
|
|
// Sources within the minimum distance attenuate less
|
|
|
|
if(OrigDist < MinDist)
|
|
|
|
Angle *= OrigDist/MinDist;
|
|
|
|
if(Angle > 90.0f)
|
|
|
|
{
|
|
|
|
ALfloat scale = (Angle-90.0f) / (180.1f-90.0f); // .1 to account for fp errors
|
2010-04-27 18:39:54 +00:00
|
|
|
ConeHF *= 1.0f - (Device->HeadDampen*scale);
|
2009-12-08 22:18:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DryMix *= ConeVolume;
|
|
|
|
if(ALSource->DryGainHFAuto)
|
|
|
|
DryGainHF *= ConeHF;
|
|
|
|
|
2009-12-01 11:32:04 +00:00
|
|
|
// Clamp to Min/Max Gain
|
|
|
|
DryMix = __min(DryMix,MaxVolume);
|
|
|
|
DryMix = __max(DryMix,MinVolume);
|
2009-04-13 09:50:40 +00:00
|
|
|
|
2009-10-19 20:25:40 +00:00
|
|
|
for(i = 0;i < NumSends;i++)
|
|
|
|
{
|
2009-12-11 02:22:45 +00:00
|
|
|
ALeffectslot *Slot = ALSource->Send[i].Slot;
|
|
|
|
|
2010-03-08 06:12:33 +00:00
|
|
|
if(!Slot || Slot->effect.type == AL_EFFECT_NULL)
|
2009-10-19 20:25:40 +00:00
|
|
|
{
|
2010-03-08 06:12:33 +00:00
|
|
|
ALSource->Params.WetGains[i] = 0.0f;
|
|
|
|
WetGainHF[i] = 1.0f;
|
|
|
|
continue;
|
|
|
|
}
|
2009-10-21 22:31:21 +00:00
|
|
|
|
2010-03-08 06:12:33 +00:00
|
|
|
if(Slot->AuxSendAuto)
|
|
|
|
{
|
|
|
|
if(ALSource->WetGainAuto)
|
|
|
|
WetGain[i] *= ConeVolume;
|
|
|
|
if(ALSource->WetGainHFAuto)
|
|
|
|
WetGainHF[i] *= ConeHF;
|
2009-12-01 11:32:04 +00:00
|
|
|
|
2010-03-08 06:12:33 +00:00
|
|
|
// Clamp to Min/Max Gain
|
|
|
|
WetGain[i] = __min(WetGain[i],MaxVolume);
|
|
|
|
WetGain[i] = __max(WetGain[i],MinVolume);
|
|
|
|
|
|
|
|
if(Slot->effect.type == AL_EFFECT_REVERB ||
|
|
|
|
Slot->effect.type == AL_EFFECT_EAXREVERB)
|
|
|
|
{
|
|
|
|
/* Apply a decay-time transformation to the wet path, based on
|
|
|
|
* the attenuation of the dry path.
|
|
|
|
*
|
|
|
|
* Using the approximate (effective) source to listener
|
|
|
|
* distance, the initial decay of the reverb effect is
|
|
|
|
* calculated and applied to the wet path.
|
|
|
|
*/
|
|
|
|
WetGain[i] *= aluPow(10.0f, effectiveDist /
|
2009-12-11 02:37:36 +00:00
|
|
|
(SPEEDOFSOUNDMETRESPERSEC *
|
|
|
|
Slot->effect.Reverb.DecayTime) *
|
|
|
|
-60.0 / 20.0);
|
2009-12-11 02:22:45 +00:00
|
|
|
|
2010-03-08 06:12:33 +00:00
|
|
|
WetGainHF[i] *= aluPow(10.0f,
|
|
|
|
log10(Slot->effect.Reverb.AirAbsorptionGainHF) *
|
|
|
|
ALSource->AirAbsorptionFactor * effectiveDist);
|
2009-10-19 20:25:40 +00:00
|
|
|
}
|
2008-01-19 05:39:09 +00:00
|
|
|
}
|
2009-10-19 20:25:40 +00:00
|
|
|
else
|
2009-04-14 03:33:41 +00:00
|
|
|
{
|
2010-03-08 06:12:33 +00:00
|
|
|
/* If the slot's auxiliary send auto is off, the data sent to the
|
|
|
|
* effect slot is the same as the dry path, sans filter effects */
|
|
|
|
WetGain[i] = DryMix;
|
|
|
|
WetGainHF[i] = DryGainHF;
|
2009-04-14 03:33:41 +00:00
|
|
|
}
|
2010-03-08 06:12:33 +00:00
|
|
|
|
|
|
|
switch(ALSource->Send[i].WetFilter.type)
|
|
|
|
{
|
|
|
|
case AL_FILTER_LOWPASS:
|
|
|
|
WetGain[i] *= ALSource->Send[i].WetFilter.Gain;
|
|
|
|
WetGainHF[i] *= ALSource->Send[i].WetFilter.GainHF;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ALSource->Params.WetGains[i] = WetGain[i] * ListenerGain;
|
2007-11-14 02:02:18 +00:00
|
|
|
}
|
2009-10-19 20:25:40 +00:00
|
|
|
for(i = NumSends;i < MAX_SENDS;i++)
|
2007-11-14 02:02:18 +00:00
|
|
|
{
|
2009-10-21 22:31:21 +00:00
|
|
|
ALSource->Params.WetGains[i] = 0.0f;
|
2009-10-21 20:08:50 +00:00
|
|
|
WetGainHF[i] = 1.0f;
|
2009-10-19 20:25:40 +00:00
|
|
|
}
|
2008-01-19 08:49:05 +00:00
|
|
|
|
2009-12-01 11:32:04 +00:00
|
|
|
// Apply filter gains and filters
|
2009-10-19 20:25:40 +00:00
|
|
|
switch(ALSource->DirectFilter.type)
|
|
|
|
{
|
|
|
|
case AL_FILTER_LOWPASS:
|
|
|
|
DryMix *= ALSource->DirectFilter.Gain;
|
|
|
|
DryGainHF *= ALSource->DirectFilter.GainHF;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
DryMix *= ListenerGain;
|
2008-12-10 19:54:13 +00:00
|
|
|
|
2009-12-01 11:32:04 +00:00
|
|
|
// Calculate Velocity
|
|
|
|
if(DopplerFactor != 0.0f)
|
|
|
|
{
|
|
|
|
ALfloat flVSS, flVLS;
|
|
|
|
ALfloat flMaxVelocity = (DopplerVelocity * flSpeedOfSound) /
|
|
|
|
DopplerFactor;
|
|
|
|
|
|
|
|
flVSS = aluDotproduct(Velocity, SourceToListener);
|
|
|
|
if(flVSS >= flMaxVelocity)
|
|
|
|
flVSS = (flMaxVelocity - 1.0f);
|
|
|
|
else if(flVSS <= -flMaxVelocity)
|
|
|
|
flVSS = -flMaxVelocity + 1.0f;
|
|
|
|
|
|
|
|
flVLS = aluDotproduct(ListenerVel, SourceToListener);
|
|
|
|
if(flVLS >= flMaxVelocity)
|
|
|
|
flVLS = (flMaxVelocity - 1.0f);
|
|
|
|
else if(flVLS <= -flMaxVelocity)
|
|
|
|
flVLS = -flMaxVelocity + 1.0f;
|
|
|
|
|
2010-08-07 07:38:02 +00:00
|
|
|
Pitch = ALSource->flPitch *
|
2009-12-01 11:32:04 +00:00
|
|
|
((flSpeedOfSound * DopplerVelocity) - (DopplerFactor * flVLS)) /
|
|
|
|
((flSpeedOfSound * DopplerVelocity) - (DopplerFactor * flVSS));
|
|
|
|
}
|
|
|
|
else
|
2010-08-07 07:38:02 +00:00
|
|
|
Pitch = ALSource->flPitch;
|
|
|
|
|
|
|
|
BufferListItem = ALSource->queue;
|
|
|
|
while(BufferListItem != NULL)
|
|
|
|
{
|
|
|
|
ALbuffer *ALBuffer;
|
|
|
|
if((ALBuffer=BufferListItem->buffer) != NULL)
|
|
|
|
{
|
2010-08-07 12:43:16 +00:00
|
|
|
Pitch = Pitch * ALBuffer->frequency / Frequency;
|
2010-08-07 07:38:02 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
BufferListItem = BufferListItem->next;
|
|
|
|
}
|
2009-12-01 11:32:04 +00:00
|
|
|
|
2010-08-07 12:43:16 +00:00
|
|
|
if(Pitch > (float)MAX_PITCH)
|
|
|
|
ALSource->Params.Step = MAX_PITCH<<FRACTIONBITS;
|
|
|
|
else if(!(Pitch > 0.0f))
|
|
|
|
ALSource->Params.Step = 1<<FRACTIONBITS;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ALSource->Params.Step = Pitch*(1<<FRACTIONBITS);
|
|
|
|
if(ALSource->Params.Step == 0)
|
|
|
|
ALSource->Params.Step = 1;
|
|
|
|
}
|
|
|
|
|
2009-10-19 20:25:40 +00:00
|
|
|
// Use energy-preserving panning algorithm for multi-speaker playback
|
2009-12-08 18:12:18 +00:00
|
|
|
length = __max(OrigDist, MinDist);
|
2009-10-19 20:25:40 +00:00
|
|
|
if(length > 0.0f)
|
|
|
|
{
|
|
|
|
ALfloat invlen = 1.0f/length;
|
|
|
|
Position[0] *= invlen;
|
|
|
|
Position[1] *= invlen;
|
|
|
|
Position[2] *= invlen;
|
|
|
|
}
|
2008-12-10 19:54:13 +00:00
|
|
|
|
2009-10-19 20:25:40 +00:00
|
|
|
pos = aluCart2LUTpos(-Position[2], Position[0]);
|
2010-04-27 18:39:54 +00:00
|
|
|
SpeakerGain = &Device->PanningLUT[OUTPUTCHANNELS * pos];
|
2008-12-10 19:54:13 +00:00
|
|
|
|
2009-10-19 20:25:40 +00:00
|
|
|
DirGain = aluSqrt(Position[0]*Position[0] + Position[2]*Position[2]);
|
|
|
|
// elevation adjustment for directional gain. this sucks, but
|
|
|
|
// has low complexity
|
2010-04-27 18:39:54 +00:00
|
|
|
AmbientGain = 1.0/aluSqrt(Device->NumChan) * (1.0-DirGain);
|
|
|
|
for(s = 0;s < OUTPUTCHANNELS;s++)
|
|
|
|
ALSource->Params.DryGains[s] = 0.0f;
|
|
|
|
for(s = 0;s < (ALsizei)Device->NumChan;s++)
|
2009-10-19 20:25:40 +00:00
|
|
|
{
|
2010-04-27 18:39:54 +00:00
|
|
|
Channel chan = Device->Speaker2Chan[s];
|
|
|
|
ALfloat gain = SpeakerGain[chan]*DirGain + AmbientGain;
|
|
|
|
ALSource->Params.DryGains[chan] = DryMix * gain;
|
2007-11-14 02:02:18 +00:00
|
|
|
}
|
2009-10-21 20:08:50 +00:00
|
|
|
|
|
|
|
/* Update filter coefficients. */
|
|
|
|
cw = cos(2.0*M_PI * LOWPASSFREQCUTOFF / Frequency);
|
2009-12-09 15:21:59 +00:00
|
|
|
|
2009-10-21 20:08:50 +00:00
|
|
|
/* Spatialized sources use four chained one-pole filters, so we need to
|
|
|
|
* take the fourth root of the squared gain, which is the same as the
|
|
|
|
* square root of the base gain. */
|
2009-12-09 15:21:59 +00:00
|
|
|
ALSource->Params.iirFilter.coeff = lpCoeffCalc(aluSqrt(DryGainHF), cw);
|
2009-10-21 20:08:50 +00:00
|
|
|
|
|
|
|
for(i = 0;i < NumSends;i++)
|
|
|
|
{
|
|
|
|
/* The wet path uses two chained one-pole filters, so take the
|
|
|
|
* base gain (square root of the squared gain) */
|
2009-12-09 15:21:59 +00:00
|
|
|
ALSource->Params.Send[i].iirFilter.coeff = lpCoeffCalc(WetGainHF[i], cw);
|
2009-10-21 20:08:50 +00:00
|
|
|
}
|
2007-11-14 02:02:18 +00:00
|
|
|
}
|
|
|
|
|
2009-08-27 02:15:17 +00:00
|
|
|
|
|
|
|
ALvoid aluHandleDisconnect(ALCdevice *device)
|
|
|
|
{
|
2009-10-20 18:54:04 +00:00
|
|
|
ALuint i;
|
|
|
|
|
2009-11-01 17:29:20 +00:00
|
|
|
SuspendContext(NULL);
|
2009-10-20 18:54:04 +00:00
|
|
|
for(i = 0;i < device->NumContexts;i++)
|
2009-08-27 02:15:17 +00:00
|
|
|
{
|
2010-05-02 02:59:41 +00:00
|
|
|
ALCcontext *Context = device->Contexts[i];
|
2009-09-22 06:31:04 +00:00
|
|
|
ALsource *source;
|
2010-05-02 02:59:41 +00:00
|
|
|
ALsizei pos;
|
2009-08-27 02:15:17 +00:00
|
|
|
|
2010-05-02 02:59:41 +00:00
|
|
|
SuspendContext(Context);
|
2009-08-27 02:15:17 +00:00
|
|
|
|
2010-05-02 02:59:41 +00:00
|
|
|
for(pos = 0;pos < Context->SourceMap.size;pos++)
|
2009-08-27 02:15:17 +00:00
|
|
|
{
|
2010-05-02 02:59:41 +00:00
|
|
|
source = Context->SourceMap.array[pos].value;
|
2009-08-27 02:15:17 +00:00
|
|
|
if(source->state == AL_PLAYING)
|
|
|
|
{
|
|
|
|
source->state = AL_STOPPED;
|
|
|
|
source->BuffersPlayed = source->BuffersInQueue;
|
|
|
|
source->position = 0;
|
|
|
|
source->position_fraction = 0;
|
|
|
|
}
|
|
|
|
}
|
2010-05-02 02:59:41 +00:00
|
|
|
ProcessContext(Context);
|
2009-08-27 02:15:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
device->Connected = ALC_FALSE;
|
2009-11-01 17:29:20 +00:00
|
|
|
ProcessContext(NULL);
|
2009-08-27 02:15:17 +00:00
|
|
|
}
|