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 "alThunk.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
|
|
|
|
2007-12-31 09:16:13 +00:00
|
|
|
#define FRACTIONBITS 14
|
|
|
|
#define FRACTIONMASK ((1L<<FRACTIONBITS)-1)
|
2008-10-09 09:50:00 +00:00
|
|
|
#define MAX_PITCH 65536
|
2007-12-31 09:47:10 +00:00
|
|
|
|
2008-08-14 12:43:52 +00:00
|
|
|
/* Minimum ramp length in milliseconds. The value below was chosen to
|
|
|
|
* adequately reduce clicks and pops from harsh gain changes. */
|
|
|
|
#define MIN_RAMP_LENGTH 16
|
2007-12-31 09:16:13 +00:00
|
|
|
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2009-08-15 20:20:35 +00:00
|
|
|
static __inline ALfloat aluF2F(ALfloat Value)
|
|
|
|
{
|
2009-12-07 18:35:30 +00:00
|
|
|
return Value;
|
2009-08-15 20:20:35 +00:00
|
|
|
}
|
|
|
|
|
2007-11-14 02:02:18 +00:00
|
|
|
static __inline ALshort aluF2S(ALfloat Value)
|
|
|
|
{
|
|
|
|
ALint i;
|
|
|
|
|
2009-12-07 18:35:30 +00:00
|
|
|
if(Value < 0.0f)
|
|
|
|
{
|
|
|
|
i = (ALint)(Value*32768.0f);
|
|
|
|
i = max(-32768, i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
i = (ALint)(Value*32767.0f);
|
|
|
|
i = min( 32767, i);
|
|
|
|
}
|
2007-11-14 02:02:18 +00:00
|
|
|
return ((ALshort)i);
|
|
|
|
}
|
|
|
|
|
2009-08-15 18:33:38 +00:00
|
|
|
static __inline ALubyte aluF2UB(ALfloat Value)
|
|
|
|
{
|
|
|
|
ALshort i = aluF2S(Value);
|
|
|
|
return (i>>8)+128;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2009-01-26 03:20:47 +00:00
|
|
|
static ALvoid SetSpeakerArrangement(const char *name, ALfloat SpeakerAngle[OUTPUTCHANNELS],
|
2010-04-09 01:05:24 +00:00
|
|
|
Channel Speaker2Chan[OUTPUTCHANNELS], ALint chans)
|
2009-01-26 03:20:47 +00:00
|
|
|
{
|
2009-12-28 19:49:55 +00:00
|
|
|
char layout_str[256];
|
|
|
|
char *confkey, *next;
|
|
|
|
char *sep, *end;
|
2010-04-11 21:33:22 +00:00
|
|
|
Channel val;
|
|
|
|
int i;
|
2009-01-26 03:20:47 +00:00
|
|
|
|
2009-12-28 19:49:55 +00:00
|
|
|
strncpy(layout_str, GetConfigValue(NULL, name, ""), sizeof(layout_str));
|
|
|
|
layout_str[255] = 0;
|
|
|
|
|
2010-04-12 06:03:36 +00:00
|
|
|
if(!layout_str[0])
|
|
|
|
return;
|
|
|
|
|
2009-12-28 19:49:55 +00:00
|
|
|
next = confkey = layout_str;
|
2009-01-26 03:20:47 +00:00
|
|
|
while(next && *next)
|
|
|
|
{
|
|
|
|
confkey = next;
|
|
|
|
next = strchr(confkey, ',');
|
|
|
|
if(next)
|
|
|
|
{
|
2009-12-28 19:49:55 +00:00
|
|
|
*next = 0;
|
2009-01-26 03:20:47 +00:00
|
|
|
do {
|
|
|
|
next++;
|
2009-12-28 19:49:55 +00:00
|
|
|
} while(isspace(*next) || *next == ',');
|
2009-01-26 03:20:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sep = strchr(confkey, '=');
|
|
|
|
if(!sep || confkey == sep)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
end = sep - 1;
|
|
|
|
while(isspace(*end) && end != confkey)
|
|
|
|
end--;
|
2009-12-28 19:49:55 +00:00
|
|
|
*(++end) = 0;
|
2009-01-26 03:20:47 +00:00
|
|
|
|
2009-12-28 19:49:55 +00:00
|
|
|
if(strcmp(confkey, "fl") == 0 || strcmp(confkey, "front-left") == 0)
|
2009-01-26 03:20:47 +00:00
|
|
|
val = FRONT_LEFT;
|
2009-12-28 19:49:55 +00:00
|
|
|
else if(strcmp(confkey, "fr") == 0 || strcmp(confkey, "front-right") == 0)
|
2009-01-26 03:20:47 +00:00
|
|
|
val = FRONT_RIGHT;
|
2009-12-28 19:49:55 +00:00
|
|
|
else if(strcmp(confkey, "fc") == 0 || strcmp(confkey, "front-center") == 0)
|
2009-01-26 03:20:47 +00:00
|
|
|
val = FRONT_CENTER;
|
2009-12-28 19:49:55 +00:00
|
|
|
else if(strcmp(confkey, "bl") == 0 || strcmp(confkey, "back-left") == 0)
|
2009-01-26 03:20:47 +00:00
|
|
|
val = BACK_LEFT;
|
2009-12-28 19:49:55 +00:00
|
|
|
else if(strcmp(confkey, "br") == 0 || strcmp(confkey, "back-right") == 0)
|
2009-01-26 03:20:47 +00:00
|
|
|
val = BACK_RIGHT;
|
2009-12-28 19:49:55 +00:00
|
|
|
else if(strcmp(confkey, "bc") == 0 || strcmp(confkey, "back-center") == 0)
|
2009-01-26 03:20:47 +00:00
|
|
|
val = BACK_CENTER;
|
2009-12-28 19:49:55 +00:00
|
|
|
else if(strcmp(confkey, "sl") == 0 || strcmp(confkey, "side-left") == 0)
|
2009-01-26 03:20:47 +00:00
|
|
|
val = SIDE_LEFT;
|
2009-12-28 19:49:55 +00:00
|
|
|
else if(strcmp(confkey, "sr") == 0 || strcmp(confkey, "side-right") == 0)
|
2009-01-26 03:20:47 +00:00
|
|
|
val = SIDE_RIGHT;
|
|
|
|
else
|
|
|
|
{
|
2009-12-28 19:49:55 +00:00
|
|
|
AL_PRINT("Unknown speaker for %s: \"%s\"\n", name, confkey);
|
2009-01-26 03:20:47 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-12-28 19:49:55 +00:00
|
|
|
*(sep++) = 0;
|
2009-01-26 03:20:47 +00:00
|
|
|
while(isspace(*sep))
|
|
|
|
sep++;
|
|
|
|
|
|
|
|
for(i = 0;i < chans;i++)
|
|
|
|
{
|
2010-04-11 21:33:22 +00:00
|
|
|
if(Speaker2Chan[i] == val)
|
2009-01-26 03:20:47 +00:00
|
|
|
{
|
2010-04-11 21:33:22 +00:00
|
|
|
long angle = strtol(sep, NULL, 10);
|
|
|
|
if(angle >= -180 && angle <= 180)
|
|
|
|
SpeakerAngle[i] = angle * M_PI/180.0f;
|
2009-01-26 03:20:47 +00:00
|
|
|
else
|
2010-04-11 21:33:22 +00:00
|
|
|
AL_PRINT("Invalid angle for speaker \"%s\": %ld\n", confkey, angle);
|
2009-01-26 03:20:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-20 07:54:44 +00:00
|
|
|
for(i = 0;i < chans;i++)
|
2009-01-26 03:20:47 +00:00
|
|
|
{
|
2010-03-20 07:54:44 +00:00
|
|
|
int min = i;
|
|
|
|
int i2;
|
|
|
|
|
|
|
|
for(i2 = i+1;i2 < chans;i2++)
|
|
|
|
{
|
|
|
|
if(SpeakerAngle[i2] < SpeakerAngle[min])
|
|
|
|
min = i2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(min != i)
|
2009-01-26 03:20:47 +00:00
|
|
|
{
|
2010-03-20 07:54:44 +00:00
|
|
|
ALfloat tmpf;
|
2010-04-12 03:17:43 +00:00
|
|
|
Channel tmpc;
|
2010-03-20 07:54:44 +00:00
|
|
|
|
|
|
|
tmpf = SpeakerAngle[i];
|
|
|
|
SpeakerAngle[i] = SpeakerAngle[min];
|
|
|
|
SpeakerAngle[min] = tmpf;
|
|
|
|
|
2010-04-12 03:17:43 +00:00
|
|
|
tmpc = Speaker2Chan[i];
|
2010-03-20 07:54:44 +00:00
|
|
|
Speaker2Chan[i] = Speaker2Chan[min];
|
2010-04-12 03:17:43 +00:00
|
|
|
Speaker2Chan[min] = tmpc;
|
2009-01-26 03:20:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-24 18:38:04 +00:00
|
|
|
static __inline ALfloat aluLUTpos2Angle(ALint pos)
|
|
|
|
{
|
|
|
|
if(pos < QUADRANT_NUM)
|
|
|
|
return aluAtan((ALfloat)pos / (ALfloat)(QUADRANT_NUM - pos));
|
|
|
|
if(pos < 2 * QUADRANT_NUM)
|
|
|
|
return M_PI_2 + aluAtan((ALfloat)(pos - QUADRANT_NUM) / (ALfloat)(2 * QUADRANT_NUM - pos));
|
|
|
|
if(pos < 3 * QUADRANT_NUM)
|
|
|
|
return aluAtan((ALfloat)(pos - 2 * QUADRANT_NUM) / (ALfloat)(3 * QUADRANT_NUM - pos)) - M_PI;
|
|
|
|
return aluAtan((ALfloat)(pos - 3 * QUADRANT_NUM) / (ALfloat)(4 * QUADRANT_NUM - pos)) - M_PI_2;
|
|
|
|
}
|
|
|
|
|
2010-04-08 22:58:11 +00:00
|
|
|
ALvoid aluInitPanning(ALCdevice *Device)
|
2009-01-24 18:38:04 +00:00
|
|
|
{
|
|
|
|
ALfloat SpeakerAngle[OUTPUTCHANNELS];
|
2010-04-27 18:39:54 +00:00
|
|
|
Channel *Speaker2Chan;
|
2010-04-08 22:58:11 +00:00
|
|
|
ALfloat Alpha, Theta;
|
|
|
|
ALint pos, offset;
|
|
|
|
ALuint s, s2;
|
|
|
|
|
|
|
|
for(s = 0;s < OUTPUTCHANNELS;s++)
|
|
|
|
{
|
|
|
|
for(s2 = 0;s2 < OUTPUTCHANNELS;s2++)
|
|
|
|
Device->ChannelMatrix[s][s2] = ((s==s2) ? 1.0f : 0.0f);
|
|
|
|
}
|
|
|
|
|
2010-04-27 18:39:54 +00:00
|
|
|
Speaker2Chan = Device->Speaker2Chan;
|
2010-04-08 22:58:11 +00:00
|
|
|
switch(Device->Format)
|
|
|
|
{
|
|
|
|
case AL_FORMAT_MONO8:
|
|
|
|
case AL_FORMAT_MONO16:
|
|
|
|
case AL_FORMAT_MONO_FLOAT32:
|
2010-04-20 10:57:40 +00:00
|
|
|
Device->DuplicateStereo = AL_FALSE;
|
2010-04-08 22:58:11 +00:00
|
|
|
Device->ChannelMatrix[FRONT_LEFT][FRONT_CENTER] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[FRONT_RIGHT][FRONT_CENTER] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[SIDE_LEFT][FRONT_CENTER] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[SIDE_RIGHT][FRONT_CENTER] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[BACK_LEFT][FRONT_CENTER] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[BACK_RIGHT][FRONT_CENTER] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[BACK_CENTER][FRONT_CENTER] = 1.0f;
|
2010-04-11 21:23:10 +00:00
|
|
|
Device->NumChan = 1;
|
|
|
|
Speaker2Chan[0] = FRONT_CENTER;
|
|
|
|
SpeakerAngle[0] = 0.0f * M_PI/180.0f;
|
2010-04-08 22:58:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case AL_FORMAT_STEREO8:
|
|
|
|
case AL_FORMAT_STEREO16:
|
|
|
|
case AL_FORMAT_STEREO_FLOAT32:
|
2010-04-20 10:57:40 +00:00
|
|
|
Device->DuplicateStereo = AL_FALSE;
|
2010-04-08 22:58:11 +00:00
|
|
|
Device->ChannelMatrix[FRONT_CENTER][FRONT_LEFT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[FRONT_CENTER][FRONT_RIGHT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[SIDE_LEFT][FRONT_LEFT] = 1.0f;
|
|
|
|
Device->ChannelMatrix[SIDE_RIGHT][FRONT_RIGHT] = 1.0f;
|
|
|
|
Device->ChannelMatrix[BACK_LEFT][FRONT_LEFT] = 1.0f;
|
|
|
|
Device->ChannelMatrix[BACK_RIGHT][FRONT_RIGHT] = 1.0f;
|
|
|
|
Device->ChannelMatrix[BACK_CENTER][FRONT_LEFT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[BACK_CENTER][FRONT_RIGHT] = aluSqrt(0.5);
|
2010-04-11 21:23:10 +00:00
|
|
|
Device->NumChan = 2;
|
|
|
|
Speaker2Chan[0] = FRONT_LEFT;
|
|
|
|
Speaker2Chan[1] = FRONT_RIGHT;
|
|
|
|
SpeakerAngle[0] = -90.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[1] = 90.0f * M_PI/180.0f;
|
|
|
|
SetSpeakerArrangement("layout", SpeakerAngle, Speaker2Chan, Device->NumChan);
|
2010-04-08 22:58:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case AL_FORMAT_QUAD8:
|
|
|
|
case AL_FORMAT_QUAD16:
|
|
|
|
case AL_FORMAT_QUAD32:
|
2010-04-20 10:57:40 +00:00
|
|
|
Device->DuplicateStereo = GetConfigValueBool(NULL, "stereodup", 0);
|
2010-04-08 22:58:11 +00:00
|
|
|
Device->ChannelMatrix[FRONT_CENTER][FRONT_LEFT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[FRONT_CENTER][FRONT_RIGHT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[SIDE_LEFT][FRONT_LEFT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[SIDE_LEFT][BACK_LEFT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[SIDE_RIGHT][FRONT_RIGHT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[SIDE_RIGHT][BACK_RIGHT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[BACK_CENTER][BACK_LEFT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[BACK_CENTER][BACK_RIGHT] = aluSqrt(0.5);
|
2010-04-11 21:23:10 +00:00
|
|
|
Device->NumChan = 4;
|
|
|
|
Speaker2Chan[0] = BACK_LEFT;
|
|
|
|
Speaker2Chan[1] = FRONT_LEFT;
|
|
|
|
Speaker2Chan[2] = FRONT_RIGHT;
|
|
|
|
Speaker2Chan[3] = BACK_RIGHT;
|
|
|
|
SpeakerAngle[0] = -135.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[1] = -45.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[2] = 45.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[3] = 135.0f * M_PI/180.0f;
|
|
|
|
SetSpeakerArrangement("layout", SpeakerAngle, Speaker2Chan, Device->NumChan);
|
2010-04-08 22:58:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case AL_FORMAT_51CHN8:
|
|
|
|
case AL_FORMAT_51CHN16:
|
|
|
|
case AL_FORMAT_51CHN32:
|
2010-04-20 10:57:40 +00:00
|
|
|
Device->DuplicateStereo = GetConfigValueBool(NULL, "stereodup", 0);
|
2010-04-08 22:58:11 +00:00
|
|
|
Device->ChannelMatrix[SIDE_LEFT][FRONT_LEFT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[SIDE_LEFT][BACK_LEFT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[SIDE_RIGHT][FRONT_RIGHT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[SIDE_RIGHT][BACK_RIGHT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[BACK_CENTER][BACK_LEFT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[BACK_CENTER][BACK_RIGHT] = aluSqrt(0.5);
|
2010-04-11 21:23:10 +00:00
|
|
|
Device->NumChan = 5;
|
|
|
|
Speaker2Chan[0] = BACK_LEFT;
|
|
|
|
Speaker2Chan[1] = FRONT_LEFT;
|
|
|
|
Speaker2Chan[2] = FRONT_CENTER;
|
|
|
|
Speaker2Chan[3] = FRONT_RIGHT;
|
|
|
|
Speaker2Chan[4] = BACK_RIGHT;
|
|
|
|
SpeakerAngle[0] = -110.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[1] = -30.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[2] = 0.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[3] = 30.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[4] = 110.0f * M_PI/180.0f;
|
|
|
|
SetSpeakerArrangement("layout", SpeakerAngle, Speaker2Chan, Device->NumChan);
|
2010-04-08 22:58:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case AL_FORMAT_61CHN8:
|
|
|
|
case AL_FORMAT_61CHN16:
|
|
|
|
case AL_FORMAT_61CHN32:
|
2010-04-20 10:57:40 +00:00
|
|
|
Device->DuplicateStereo = GetConfigValueBool(NULL, "stereodup", 0);
|
2010-04-08 22:58:11 +00:00
|
|
|
Device->ChannelMatrix[BACK_LEFT][BACK_CENTER] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[BACK_LEFT][SIDE_LEFT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[BACK_RIGHT][BACK_CENTER] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[BACK_RIGHT][SIDE_RIGHT] = aluSqrt(0.5);
|
2010-04-11 21:23:10 +00:00
|
|
|
Device->NumChan = 6;
|
|
|
|
Speaker2Chan[0] = SIDE_LEFT;
|
|
|
|
Speaker2Chan[1] = FRONT_LEFT;
|
|
|
|
Speaker2Chan[2] = FRONT_CENTER;
|
|
|
|
Speaker2Chan[3] = FRONT_RIGHT;
|
|
|
|
Speaker2Chan[4] = SIDE_RIGHT;
|
|
|
|
Speaker2Chan[5] = BACK_CENTER;
|
|
|
|
SpeakerAngle[0] = -90.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[1] = -30.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[2] = 0.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[3] = 30.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[4] = 90.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[5] = 180.0f * M_PI/180.0f;
|
|
|
|
SetSpeakerArrangement("layout", SpeakerAngle, Speaker2Chan, Device->NumChan);
|
2010-04-08 22:58:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case AL_FORMAT_71CHN8:
|
|
|
|
case AL_FORMAT_71CHN16:
|
|
|
|
case AL_FORMAT_71CHN32:
|
2010-04-20 10:57:40 +00:00
|
|
|
Device->DuplicateStereo = GetConfigValueBool(NULL, "stereodup", 0);
|
2010-04-08 22:58:11 +00:00
|
|
|
Device->ChannelMatrix[BACK_CENTER][BACK_LEFT] = aluSqrt(0.5);
|
|
|
|
Device->ChannelMatrix[BACK_CENTER][BACK_RIGHT] = aluSqrt(0.5);
|
2010-04-11 21:23:10 +00:00
|
|
|
Device->NumChan = 7;
|
|
|
|
Speaker2Chan[0] = BACK_LEFT;
|
|
|
|
Speaker2Chan[1] = SIDE_LEFT;
|
|
|
|
Speaker2Chan[2] = FRONT_LEFT;
|
|
|
|
Speaker2Chan[3] = FRONT_CENTER;
|
|
|
|
Speaker2Chan[4] = FRONT_RIGHT;
|
|
|
|
Speaker2Chan[5] = SIDE_RIGHT;
|
|
|
|
Speaker2Chan[6] = BACK_RIGHT;
|
|
|
|
SpeakerAngle[0] = -150.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[1] = -90.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[2] = -30.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[3] = 0.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[4] = 30.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[5] = 90.0f * M_PI/180.0f;
|
|
|
|
SpeakerAngle[6] = 150.0f * M_PI/180.0f;
|
|
|
|
SetSpeakerArrangement("layout", SpeakerAngle, Speaker2Chan, Device->NumChan);
|
2010-04-08 22:58:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
|
2010-04-23 14:54:43 +00:00
|
|
|
if(GetConfigValueBool(NULL, "scalemix", 0))
|
2010-04-08 22:58:11 +00:00
|
|
|
{
|
2010-04-23 14:54:43 +00:00
|
|
|
ALfloat maxout = 1.0f;
|
|
|
|
for(s = 0;s < OUTPUTCHANNELS;s++)
|
|
|
|
{
|
|
|
|
ALfloat out = 0.0f;
|
|
|
|
for(s2 = 0;s2 < OUTPUTCHANNELS;s2++)
|
|
|
|
out += Device->ChannelMatrix[s2][s];
|
|
|
|
maxout = __max(maxout, out);
|
|
|
|
}
|
2010-04-08 22:58:11 +00:00
|
|
|
|
2010-04-23 14:54:43 +00:00
|
|
|
maxout = 1.0f/maxout;
|
|
|
|
for(s = 0;s < OUTPUTCHANNELS;s++)
|
|
|
|
{
|
|
|
|
for(s2 = 0;s2 < OUTPUTCHANNELS;s2++)
|
|
|
|
Device->ChannelMatrix[s2][s] *= maxout;
|
|
|
|
}
|
2010-04-08 22:58:11 +00:00
|
|
|
}
|
2009-01-24 18:38:04 +00:00
|
|
|
|
|
|
|
for(pos = 0; pos < LUT_NUM; pos++)
|
|
|
|
{
|
|
|
|
/* clear all values */
|
|
|
|
offset = OUTPUTCHANNELS * pos;
|
|
|
|
for(s = 0; s < OUTPUTCHANNELS; s++)
|
2010-04-08 22:58:11 +00:00
|
|
|
Device->PanningLUT[offset+s] = 0.0f;
|
2009-01-24 18:38:04 +00:00
|
|
|
|
2010-04-11 21:23:10 +00:00
|
|
|
if(Device->NumChan == 1)
|
|
|
|
{
|
|
|
|
Device->PanningLUT[offset + Speaker2Chan[0]] = 1.0f;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-12-21 10:59:30 +00:00
|
|
|
/* source angle */
|
|
|
|
Theta = aluLUTpos2Angle(pos);
|
|
|
|
|
2009-01-24 18:38:04 +00:00
|
|
|
/* set panning values */
|
2010-04-08 22:58:11 +00:00
|
|
|
for(s = 0; s < Device->NumChan - 1; s++)
|
2009-01-24 18:38:04 +00:00
|
|
|
{
|
|
|
|
if(Theta >= SpeakerAngle[s] && Theta < SpeakerAngle[s+1])
|
|
|
|
{
|
|
|
|
/* source between speaker s and speaker s+1 */
|
|
|
|
Alpha = M_PI_2 * (Theta-SpeakerAngle[s]) /
|
|
|
|
(SpeakerAngle[s+1]-SpeakerAngle[s]);
|
2010-04-08 22:58:11 +00:00
|
|
|
Device->PanningLUT[offset + Speaker2Chan[s]] = cos(Alpha);
|
|
|
|
Device->PanningLUT[offset + Speaker2Chan[s+1]] = sin(Alpha);
|
2009-01-24 18:38:04 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-04-08 22:58:11 +00:00
|
|
|
if(s == Device->NumChan - 1)
|
2009-01-24 18:38:04 +00:00
|
|
|
{
|
|
|
|
/* source between last and first speaker */
|
|
|
|
if(Theta < SpeakerAngle[0])
|
|
|
|
Theta += 2.0f * M_PI;
|
|
|
|
Alpha = M_PI_2 * (Theta-SpeakerAngle[s]) /
|
|
|
|
(2.0f * M_PI + SpeakerAngle[0]-SpeakerAngle[s]);
|
2010-04-08 22:58:11 +00:00
|
|
|
Device->PanningLUT[offset + Speaker2Chan[s]] = cos(Alpha);
|
|
|
|
Device->PanningLUT[offset + Speaker2Chan[0]] = sin(Alpha);
|
2009-01-24 18:38:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-09 15:02:26 +00:00
|
|
|
static ALvoid CalcNonAttnSourceParams(const ALCcontext *ALContext, ALsource *ALSource)
|
|
|
|
{
|
|
|
|
ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
|
|
|
|
ALfloat DryGain, DryGainHF;
|
|
|
|
ALfloat WetGain[MAX_SENDS];
|
|
|
|
ALfloat WetGainHF[MAX_SENDS];
|
|
|
|
ALint NumSends, Frequency;
|
2009-12-09 15:21:59 +00:00
|
|
|
ALfloat cw;
|
2009-12-09 15:02:26 +00:00
|
|
|
ALint i;
|
|
|
|
|
|
|
|
//Get context properties
|
|
|
|
NumSends = ALContext->Device->NumAuxSends;
|
2009-12-21 05:19:57 +00:00
|
|
|
Frequency = ALContext->Device->Frequency;
|
2009-12-09 15:02:26 +00:00
|
|
|
|
|
|
|
//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"
|
|
|
|
ALSource->Params.Pitch = ALSource->flPitch;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
ALSource->Params.DryGains[BACK_LEFT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[BACK_RIGHT] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[FRONT_CENTER] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[BACK_CENTER] = DryGain * ListenerGain;
|
|
|
|
ALSource->Params.DryGains[LFE] = DryGain * ListenerGain;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static ALvoid CalcSourceParams(const ALCcontext *ALContext, ALsource *ALSource)
|
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;
|
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-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;
|
|
|
|
|
|
|
|
ALSource->Params.Pitch = ALSource->flPitch *
|
|
|
|
((flSpeedOfSound * DopplerVelocity) - (DopplerFactor * flVLS)) /
|
|
|
|
((flSpeedOfSound * DopplerVelocity) - (DopplerFactor * flVSS));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ALSource->Params.Pitch = ALSource->flPitch;
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2010-01-11 13:37:20 +00:00
|
|
|
static __inline ALfloat point(ALfloat val1, ALfloat val2, ALint frac)
|
|
|
|
{
|
|
|
|
return val1;
|
|
|
|
(void)val2;
|
|
|
|
(void)frac;
|
|
|
|
}
|
2009-12-07 18:35:30 +00:00
|
|
|
static __inline ALfloat lerp(ALfloat val1, ALfloat val2, ALint frac)
|
2008-10-03 06:53:46 +00:00
|
|
|
{
|
2009-12-07 18:35:30 +00:00
|
|
|
return val1 + ((val2-val1)*(frac * (1.0f/(1<<FRACTIONBITS))));
|
2008-10-03 06:53:46 +00:00
|
|
|
}
|
2010-01-11 14:19:54 +00:00
|
|
|
static __inline ALfloat cos_lerp(ALfloat val1, ALfloat val2, ALint frac)
|
|
|
|
{
|
|
|
|
ALfloat mult = (1.0f-cos(frac * (1.0f/(1<<FRACTIONBITS)) * M_PI)) * 0.5f;
|
|
|
|
return val1 + ((val2-val1)*mult);
|
|
|
|
}
|
2008-10-03 06:53:46 +00:00
|
|
|
|
2009-09-16 00:56:31 +00:00
|
|
|
static void MixSomeSources(ALCcontext *ALContext, float (*DryBuffer)[OUTPUTCHANNELS], ALuint SamplesToDo)
|
2007-11-14 02:02:18 +00:00
|
|
|
{
|
2009-04-12 00:04:55 +00:00
|
|
|
static float DummyBuffer[BUFFERSIZE];
|
2009-04-12 03:04:46 +00:00
|
|
|
ALfloat *WetBuffer[MAX_SENDS];
|
2009-10-21 21:25:54 +00:00
|
|
|
ALfloat DrySend[OUTPUTCHANNELS];
|
2008-08-14 12:43:52 +00:00
|
|
|
ALfloat dryGainStep[OUTPUTCHANNELS];
|
2009-04-12 03:04:46 +00:00
|
|
|
ALfloat wetGainStep[MAX_SENDS];
|
2009-09-16 00:56:31 +00:00
|
|
|
ALuint i, j, k, out;
|
|
|
|
ALsource *ALSource;
|
2009-11-26 08:39:32 +00:00
|
|
|
ALfloat value, outsamp;
|
2007-11-14 02:02:18 +00:00
|
|
|
ALbufferlistitem *BufferListItem;
|
|
|
|
ALint64 DataSize64,DataPos64;
|
2009-04-12 03:04:46 +00:00
|
|
|
FILTER *DryFilter, *WetFilter[MAX_SENDS];
|
2009-09-16 00:56:31 +00:00
|
|
|
ALfloat WetSend[MAX_SENDS];
|
|
|
|
ALuint rampLength;
|
2010-04-20 10:57:40 +00:00
|
|
|
ALboolean DuplicateStereo;
|
2009-10-25 13:26:19 +00:00
|
|
|
ALuint DeviceFreq;
|
2009-09-16 00:56:31 +00:00
|
|
|
ALint increment;
|
2009-10-25 11:39:36 +00:00
|
|
|
ALuint DataPosInt, DataPosFrac;
|
2009-10-25 12:03:22 +00:00
|
|
|
ALuint Channels, Bytes;
|
2009-10-25 13:26:19 +00:00
|
|
|
ALuint Frequency;
|
2010-01-11 13:37:20 +00:00
|
|
|
resampler_t Resampler;
|
2009-10-25 11:39:36 +00:00
|
|
|
ALuint BuffersPlayed;
|
2009-10-25 12:03:22 +00:00
|
|
|
ALfloat Pitch;
|
2009-10-25 11:39:36 +00:00
|
|
|
ALenum State;
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2010-03-16 22:37:41 +00:00
|
|
|
if(!(ALSource=ALContext->SourceList))
|
2009-09-16 00:56:31 +00:00
|
|
|
return;
|
2008-08-08 14:32:21 +00:00
|
|
|
|
2010-04-20 10:57:40 +00:00
|
|
|
DuplicateStereo = ALContext->Device->DuplicateStereo;
|
2009-10-25 13:26:19 +00:00
|
|
|
DeviceFreq = ALContext->Device->Frequency;
|
2009-10-20 20:47:55 +00:00
|
|
|
|
2009-10-25 13:26:19 +00:00
|
|
|
rampLength = DeviceFreq * MIN_RAMP_LENGTH / 1000;
|
2009-09-16 00:56:31 +00:00
|
|
|
rampLength = max(rampLength, SamplesToDo);
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2009-09-16 00:56:31 +00:00
|
|
|
another_source:
|
2009-12-09 19:56:53 +00:00
|
|
|
if(ALSource->state != AL_PLAYING)
|
2009-10-25 11:39:36 +00:00
|
|
|
{
|
|
|
|
if((ALSource=ALSource->next) != NULL)
|
|
|
|
goto another_source;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
j = 0;
|
|
|
|
|
2009-10-25 13:26:19 +00:00
|
|
|
/* Find buffer format */
|
|
|
|
Frequency = 0;
|
|
|
|
Channels = 0;
|
|
|
|
Bytes = 0;
|
|
|
|
BufferListItem = ALSource->queue;
|
|
|
|
while(BufferListItem != NULL)
|
|
|
|
{
|
|
|
|
ALbuffer *ALBuffer;
|
|
|
|
if((ALBuffer=BufferListItem->buffer) != NULL)
|
|
|
|
{
|
|
|
|
Channels = aluChannelsFromFormat(ALBuffer->format);
|
|
|
|
Bytes = aluBytesFromFormat(ALBuffer->format);
|
|
|
|
Frequency = ALBuffer->frequency;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
BufferListItem = BufferListItem->next;
|
|
|
|
}
|
|
|
|
|
2009-11-26 00:21:47 +00:00
|
|
|
if(ALSource->NeedsUpdate)
|
|
|
|
{
|
2009-12-09 15:02:26 +00:00
|
|
|
//Only apply 3D calculations for mono buffers
|
|
|
|
if(Channels == 1)
|
|
|
|
CalcSourceParams(ALContext, ALSource);
|
|
|
|
else
|
|
|
|
CalcNonAttnSourceParams(ALContext, ALSource);
|
2009-11-26 00:21:47 +00:00
|
|
|
ALSource->NeedsUpdate = AL_FALSE;
|
|
|
|
}
|
2009-10-25 13:16:21 +00:00
|
|
|
|
2009-12-09 19:56:53 +00:00
|
|
|
/* Get source info */
|
2010-01-11 13:37:20 +00:00
|
|
|
Resampler = ALSource->Resampler;
|
2009-12-09 19:56:53 +00:00
|
|
|
State = ALSource->state;
|
|
|
|
BuffersPlayed = ALSource->BuffersPlayed;
|
|
|
|
DataPosInt = ALSource->position;
|
|
|
|
DataPosFrac = ALSource->position_fraction;
|
|
|
|
|
2009-10-25 12:34:47 +00:00
|
|
|
/* Compute 18.14 fixed point step */
|
2009-10-25 13:26:19 +00:00
|
|
|
Pitch = (ALSource->Params.Pitch*Frequency) / DeviceFreq;
|
2009-10-25 12:34:47 +00:00
|
|
|
if(Pitch > (float)MAX_PITCH) Pitch = (float)MAX_PITCH;
|
|
|
|
increment = (ALint)(Pitch*(ALfloat)(1L<<FRACTIONBITS));
|
|
|
|
if(increment <= 0) increment = (1<<FRACTIONBITS);
|
2009-10-25 12:03:22 +00:00
|
|
|
|
|
|
|
if(ALSource->FirstStart)
|
|
|
|
{
|
|
|
|
for(i = 0;i < OUTPUTCHANNELS;i++)
|
|
|
|
DrySend[i] = ALSource->Params.DryGains[i];
|
|
|
|
for(i = 0;i < MAX_SENDS;i++)
|
|
|
|
WetSend[i] = ALSource->Params.WetGains[i];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for(i = 0;i < OUTPUTCHANNELS;i++)
|
|
|
|
DrySend[i] = ALSource->DryGains[i];
|
|
|
|
for(i = 0;i < MAX_SENDS;i++)
|
|
|
|
WetSend[i] = ALSource->WetGains[i];
|
|
|
|
}
|
2009-10-25 11:39:36 +00:00
|
|
|
|
|
|
|
DryFilter = &ALSource->Params.iirFilter;
|
|
|
|
for(i = 0;i < MAX_SENDS;i++)
|
|
|
|
{
|
|
|
|
WetFilter[i] = &ALSource->Params.Send[i].iirFilter;
|
|
|
|
WetBuffer[i] = (ALSource->Send[i].Slot ?
|
|
|
|
ALSource->Send[i].Slot->WetBuffer :
|
|
|
|
DummyBuffer);
|
|
|
|
}
|
|
|
|
|
2009-10-25 13:26:19 +00:00
|
|
|
/* Get current buffer queue item */
|
2009-10-25 11:39:36 +00:00
|
|
|
BufferListItem = ALSource->queue;
|
|
|
|
for(i = 0;i < BuffersPlayed && BufferListItem;i++)
|
|
|
|
BufferListItem = BufferListItem->next;
|
|
|
|
|
2009-09-16 00:56:31 +00:00
|
|
|
while(State == AL_PLAYING && j < SamplesToDo)
|
2008-01-21 22:54:15 +00:00
|
|
|
{
|
2009-09-16 00:56:31 +00:00
|
|
|
ALuint DataSize = 0;
|
|
|
|
ALbuffer *ALBuffer;
|
2009-12-07 18:35:30 +00:00
|
|
|
ALfloat *Data;
|
2009-09-16 00:56:31 +00:00
|
|
|
ALuint BufferSize;
|
|
|
|
|
|
|
|
/* Get buffer info */
|
2009-10-25 11:39:36 +00:00
|
|
|
if((ALBuffer=BufferListItem->buffer) != NULL)
|
2008-08-14 12:43:52 +00:00
|
|
|
{
|
2009-10-25 11:39:36 +00:00
|
|
|
Data = ALBuffer->data;
|
|
|
|
DataSize = ALBuffer->size;
|
|
|
|
DataSize /= Channels * Bytes;
|
2008-08-14 12:43:52 +00:00
|
|
|
}
|
2009-10-25 11:39:36 +00:00
|
|
|
if(DataPosInt >= DataSize)
|
|
|
|
goto skipmix;
|
2008-01-21 22:54:15 +00:00
|
|
|
|
2009-10-25 11:39:36 +00:00
|
|
|
if(BufferListItem->next)
|
2009-09-16 00:56:31 +00:00
|
|
|
{
|
2009-10-25 11:39:36 +00:00
|
|
|
ALbuffer *NextBuf = BufferListItem->next->buffer;
|
2010-01-11 18:58:33 +00:00
|
|
|
if(NextBuf && NextBuf->size)
|
2009-09-16 00:56:31 +00:00
|
|
|
{
|
2009-10-25 15:31:28 +00:00
|
|
|
ALint ulExtraSamples = BUFFER_PADDING*Channels*Bytes;
|
|
|
|
ulExtraSamples = min(NextBuf->size, ulExtraSamples);
|
2009-10-25 11:39:36 +00:00
|
|
|
memcpy(&Data[DataSize*Channels], NextBuf->data, ulExtraSamples);
|
2009-09-16 00:56:31 +00:00
|
|
|
}
|
2009-10-25 11:39:36 +00:00
|
|
|
}
|
|
|
|
else if(ALSource->bLooping)
|
|
|
|
{
|
|
|
|
ALbuffer *NextBuf = ALSource->queue->buffer;
|
2010-01-11 18:58:33 +00:00
|
|
|
if(NextBuf && NextBuf->size)
|
2009-09-16 00:56:31 +00:00
|
|
|
{
|
2009-10-25 15:31:28 +00:00
|
|
|
ALint ulExtraSamples = BUFFER_PADDING*Channels*Bytes;
|
|
|
|
ulExtraSamples = min(NextBuf->size, ulExtraSamples);
|
2009-10-25 11:39:36 +00:00
|
|
|
memcpy(&Data[DataSize*Channels], NextBuf->data, ulExtraSamples);
|
2009-09-16 00:56:31 +00:00
|
|
|
}
|
|
|
|
}
|
2009-10-25 11:39:36 +00:00
|
|
|
else
|
2009-10-25 15:31:28 +00:00
|
|
|
memset(&Data[DataSize*Channels], 0, (BUFFER_PADDING*Channels*Bytes));
|
2008-07-24 07:41:25 +00:00
|
|
|
|
2009-10-25 13:16:21 +00:00
|
|
|
/* Compute the gain steps for each output channel */
|
|
|
|
for(i = 0;i < OUTPUTCHANNELS;i++)
|
2009-12-21 05:50:00 +00:00
|
|
|
dryGainStep[i] = (ALSource->Params.DryGains[i]-DrySend[i]) /
|
|
|
|
rampLength;
|
2009-10-25 13:16:21 +00:00
|
|
|
for(i = 0;i < MAX_SENDS;i++)
|
2009-12-21 05:50:00 +00:00
|
|
|
wetGainStep[i] = (ALSource->Params.WetGains[i]-WetSend[i]) /
|
|
|
|
rampLength;
|
2009-10-25 13:16:21 +00:00
|
|
|
|
2009-10-25 12:34:47 +00:00
|
|
|
/* Figure out how many samples we can mix. */
|
|
|
|
DataSize64 = DataSize;
|
|
|
|
DataSize64 <<= FRACTIONBITS;
|
|
|
|
DataPos64 = DataPosInt;
|
|
|
|
DataPos64 <<= FRACTIONBITS;
|
|
|
|
DataPos64 += DataPosFrac;
|
|
|
|
BufferSize = (ALuint)((DataSize64-DataPos64+(increment-1)) / increment);
|
|
|
|
|
|
|
|
BufferSize = min(BufferSize, (SamplesToDo-j));
|
2009-10-25 11:39:36 +00:00
|
|
|
|
2009-09-16 00:56:31 +00:00
|
|
|
/* Actual sample mixing loop */
|
|
|
|
k = 0;
|
|
|
|
Data += DataPosInt*Channels;
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2009-09-16 00:56:31 +00:00
|
|
|
if(Channels == 1) /* Mono */
|
|
|
|
{
|
2010-01-11 13:37:20 +00:00
|
|
|
#define DO_MIX(resampler) do { \
|
|
|
|
while(BufferSize--) \
|
|
|
|
{ \
|
|
|
|
for(i = 0;i < OUTPUTCHANNELS;i++) \
|
|
|
|
DrySend[i] += dryGainStep[i]; \
|
|
|
|
for(i = 0;i < MAX_SENDS;i++) \
|
|
|
|
WetSend[i] += wetGainStep[i]; \
|
|
|
|
\
|
|
|
|
/* First order interpolator */ \
|
|
|
|
value = (resampler)(Data[k], Data[k+1], DataPosFrac); \
|
|
|
|
\
|
|
|
|
/* Direct path final mix buffer and panning */ \
|
|
|
|
outsamp = lpFilter4P(DryFilter, 0, value); \
|
|
|
|
DryBuffer[j][FRONT_LEFT] += outsamp*DrySend[FRONT_LEFT]; \
|
|
|
|
DryBuffer[j][FRONT_RIGHT] += outsamp*DrySend[FRONT_RIGHT]; \
|
|
|
|
DryBuffer[j][SIDE_LEFT] += outsamp*DrySend[SIDE_LEFT]; \
|
|
|
|
DryBuffer[j][SIDE_RIGHT] += outsamp*DrySend[SIDE_RIGHT]; \
|
|
|
|
DryBuffer[j][BACK_LEFT] += outsamp*DrySend[BACK_LEFT]; \
|
|
|
|
DryBuffer[j][BACK_RIGHT] += outsamp*DrySend[BACK_RIGHT]; \
|
|
|
|
DryBuffer[j][FRONT_CENTER] += outsamp*DrySend[FRONT_CENTER]; \
|
|
|
|
DryBuffer[j][BACK_CENTER] += outsamp*DrySend[BACK_CENTER]; \
|
|
|
|
\
|
|
|
|
/* Room path final mix buffer and panning */ \
|
|
|
|
for(i = 0;i < MAX_SENDS;i++) \
|
|
|
|
{ \
|
|
|
|
outsamp = lpFilter2P(WetFilter[i], 0, value); \
|
|
|
|
WetBuffer[i][j] += outsamp*WetSend[i]; \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
DataPosFrac += increment; \
|
|
|
|
k += DataPosFrac>>FRACTIONBITS; \
|
|
|
|
DataPosFrac &= FRACTIONMASK; \
|
|
|
|
j++; \
|
|
|
|
} \
|
|
|
|
} while(0)
|
2008-08-14 12:43:52 +00:00
|
|
|
|
2010-01-11 13:37:20 +00:00
|
|
|
switch(Resampler)
|
|
|
|
{
|
2010-01-12 07:47:03 +00:00
|
|
|
case POINT_RESAMPLER:
|
|
|
|
DO_MIX(point); break;
|
|
|
|
case LINEAR_RESAMPLER:
|
|
|
|
DO_MIX(lerp); break;
|
|
|
|
case COSINE_RESAMPLER:
|
|
|
|
DO_MIX(cos_lerp); break;
|
2010-01-11 15:02:29 +00:00
|
|
|
case RESAMPLER_MIN:
|
2010-01-11 13:37:20 +00:00
|
|
|
case RESAMPLER_MAX:
|
|
|
|
break;
|
2009-09-16 00:56:31 +00:00
|
|
|
}
|
2010-01-11 13:37:20 +00:00
|
|
|
#undef DO_MIX
|
2010-04-09 06:00:14 +00:00
|
|
|
}
|
|
|
|
else if(Channels == 2 && DuplicateStereo) /* Stereo */
|
|
|
|
{
|
|
|
|
const int chans[] = {
|
|
|
|
FRONT_LEFT, FRONT_RIGHT
|
|
|
|
};
|
|
|
|
const int chans2[] = {
|
|
|
|
BACK_LEFT, SIDE_LEFT, BACK_RIGHT, SIDE_RIGHT
|
|
|
|
};
|
2010-04-11 22:20:46 +00:00
|
|
|
const ALfloat scaler = 1.0f/Channels;
|
2010-04-09 06:00:14 +00:00
|
|
|
const ALfloat dupscaler = aluSqrt(1.0f/3.0f);
|
|
|
|
|
|
|
|
#define DO_MIX(resampler) do { \
|
|
|
|
while(BufferSize--) \
|
|
|
|
{ \
|
|
|
|
for(i = 0;i < OUTPUTCHANNELS;i++) \
|
|
|
|
DrySend[i] += dryGainStep[i]; \
|
|
|
|
for(i = 0;i < MAX_SENDS;i++) \
|
|
|
|
WetSend[i] += wetGainStep[i]; \
|
|
|
|
\
|
|
|
|
for(i = 0;i < Channels;i++) \
|
|
|
|
{ \
|
|
|
|
value = (resampler)(Data[k*Channels + i],Data[(k+1)*Channels + i],\
|
|
|
|
DataPosFrac); \
|
|
|
|
outsamp = lpFilter2P(DryFilter, chans[i]*2, value) * dupscaler; \
|
|
|
|
DryBuffer[j][chans[i]] += outsamp*DrySend[chans[i]]; \
|
|
|
|
DryBuffer[j][chans2[i*2+0]] += outsamp*DrySend[chans2[i*2+0]]; \
|
|
|
|
DryBuffer[j][chans2[i*2+1]] += outsamp*DrySend[chans2[i*2+1]]; \
|
|
|
|
for(out = 0;out < MAX_SENDS;out++) \
|
|
|
|
{ \
|
|
|
|
outsamp = lpFilter1P(WetFilter[out], chans[i], value); \
|
|
|
|
WetBuffer[out][j] += outsamp*WetSend[out]*scaler; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
DataPosFrac += increment; \
|
|
|
|
k += DataPosFrac>>FRACTIONBITS; \
|
|
|
|
DataPosFrac &= FRACTIONMASK; \
|
|
|
|
j++; \
|
|
|
|
} \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
switch(Resampler)
|
|
|
|
{
|
|
|
|
case POINT_RESAMPLER:
|
|
|
|
DO_MIX(point); break;
|
|
|
|
case LINEAR_RESAMPLER:
|
|
|
|
DO_MIX(lerp); break;
|
|
|
|
case COSINE_RESAMPLER:
|
|
|
|
DO_MIX(cos_lerp); break;
|
|
|
|
case RESAMPLER_MIN:
|
|
|
|
case RESAMPLER_MAX:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#undef DO_MIX
|
2009-09-16 00:56:31 +00:00
|
|
|
}
|
|
|
|
else if(Channels == 2) /* Stereo */
|
|
|
|
{
|
|
|
|
const int chans[] = {
|
|
|
|
FRONT_LEFT, FRONT_RIGHT
|
|
|
|
};
|
2010-04-11 22:20:46 +00:00
|
|
|
const ALfloat scaler = 1.0f/Channels;
|
2009-01-24 21:57:01 +00:00
|
|
|
|
2010-04-08 21:47:12 +00:00
|
|
|
#define DO_MIX(resampler) do { \
|
|
|
|
while(BufferSize--) \
|
|
|
|
{ \
|
|
|
|
for(i = 0;i < OUTPUTCHANNELS;i++) \
|
|
|
|
DrySend[i] += dryGainStep[i]; \
|
|
|
|
for(i = 0;i < MAX_SENDS;i++) \
|
|
|
|
WetSend[i] += wetGainStep[i]; \
|
|
|
|
\
|
|
|
|
for(i = 0;i < Channels;i++) \
|
|
|
|
{ \
|
|
|
|
value = (resampler)(Data[k*Channels + i],Data[(k+1)*Channels + i],\
|
|
|
|
DataPosFrac); \
|
|
|
|
outsamp = lpFilter2P(DryFilter, chans[i]*2, value); \
|
|
|
|
DryBuffer[j][chans[i]] += outsamp*DrySend[chans[i]]; \
|
|
|
|
for(out = 0;out < MAX_SENDS;out++) \
|
|
|
|
{ \
|
|
|
|
outsamp = lpFilter1P(WetFilter[out], chans[i], value); \
|
|
|
|
WetBuffer[out][j] += outsamp*WetSend[out]*scaler; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
DataPosFrac += increment; \
|
|
|
|
k += DataPosFrac>>FRACTIONBITS; \
|
|
|
|
DataPosFrac &= FRACTIONMASK; \
|
|
|
|
j++; \
|
|
|
|
} \
|
2009-01-24 21:57:01 +00:00
|
|
|
} while(0)
|
|
|
|
|
2010-01-11 13:37:20 +00:00
|
|
|
switch(Resampler)
|
|
|
|
{
|
2010-01-12 07:47:03 +00:00
|
|
|
case POINT_RESAMPLER:
|
|
|
|
DO_MIX(point); break;
|
|
|
|
case LINEAR_RESAMPLER:
|
|
|
|
DO_MIX(lerp); break;
|
|
|
|
case COSINE_RESAMPLER:
|
|
|
|
DO_MIX(cos_lerp); break;
|
2010-01-11 15:02:29 +00:00
|
|
|
case RESAMPLER_MIN:
|
2010-01-11 13:37:20 +00:00
|
|
|
case RESAMPLER_MAX:
|
|
|
|
break;
|
|
|
|
}
|
2009-09-16 00:56:31 +00:00
|
|
|
}
|
|
|
|
else if(Channels == 4) /* Quad */
|
|
|
|
{
|
|
|
|
const int chans[] = {
|
|
|
|
FRONT_LEFT, FRONT_RIGHT,
|
|
|
|
BACK_LEFT, BACK_RIGHT
|
|
|
|
};
|
2010-04-11 22:20:46 +00:00
|
|
|
const ALfloat scaler = 1.0f/Channels;
|
2009-01-26 06:11:07 +00:00
|
|
|
|
2010-01-11 13:37:20 +00:00
|
|
|
switch(Resampler)
|
|
|
|
{
|
2010-01-12 07:47:03 +00:00
|
|
|
case POINT_RESAMPLER:
|
|
|
|
DO_MIX(point); break;
|
|
|
|
case LINEAR_RESAMPLER:
|
|
|
|
DO_MIX(lerp); break;
|
|
|
|
case COSINE_RESAMPLER:
|
|
|
|
DO_MIX(cos_lerp); break;
|
2010-01-11 15:02:29 +00:00
|
|
|
case RESAMPLER_MIN:
|
2010-01-11 13:37:20 +00:00
|
|
|
case RESAMPLER_MAX:
|
|
|
|
break;
|
|
|
|
}
|
2009-09-16 00:56:31 +00:00
|
|
|
}
|
|
|
|
else if(Channels == 6) /* 5.1 */
|
|
|
|
{
|
|
|
|
const int chans[] = {
|
|
|
|
FRONT_LEFT, FRONT_RIGHT,
|
|
|
|
FRONT_CENTER, LFE,
|
|
|
|
BACK_LEFT, BACK_RIGHT
|
|
|
|
};
|
2010-04-11 22:20:46 +00:00
|
|
|
const ALfloat scaler = 1.0f/Channels;
|
2009-01-24 21:57:01 +00:00
|
|
|
|
2010-01-11 13:37:20 +00:00
|
|
|
switch(Resampler)
|
|
|
|
{
|
2010-01-12 07:47:03 +00:00
|
|
|
case POINT_RESAMPLER:
|
|
|
|
DO_MIX(point); break;
|
|
|
|
case LINEAR_RESAMPLER:
|
|
|
|
DO_MIX(lerp); break;
|
|
|
|
case COSINE_RESAMPLER:
|
|
|
|
DO_MIX(cos_lerp); break;
|
2010-01-11 15:02:29 +00:00
|
|
|
case RESAMPLER_MIN:
|
2010-01-11 13:37:20 +00:00
|
|
|
case RESAMPLER_MAX:
|
|
|
|
break;
|
|
|
|
}
|
2009-09-16 00:56:31 +00:00
|
|
|
}
|
|
|
|
else if(Channels == 7) /* 6.1 */
|
|
|
|
{
|
|
|
|
const int chans[] = {
|
|
|
|
FRONT_LEFT, FRONT_RIGHT,
|
|
|
|
FRONT_CENTER, LFE,
|
|
|
|
BACK_CENTER,
|
|
|
|
SIDE_LEFT, SIDE_RIGHT
|
|
|
|
};
|
2010-04-11 22:20:46 +00:00
|
|
|
const ALfloat scaler = 1.0f/Channels;
|
2009-09-16 00:56:31 +00:00
|
|
|
|
2010-01-11 13:37:20 +00:00
|
|
|
switch(Resampler)
|
|
|
|
{
|
2010-01-12 07:47:03 +00:00
|
|
|
case POINT_RESAMPLER:
|
|
|
|
DO_MIX(point); break;
|
|
|
|
case LINEAR_RESAMPLER:
|
|
|
|
DO_MIX(lerp); break;
|
|
|
|
case COSINE_RESAMPLER:
|
|
|
|
DO_MIX(cos_lerp); break;
|
2010-01-11 15:02:29 +00:00
|
|
|
case RESAMPLER_MIN:
|
2010-01-11 13:37:20 +00:00
|
|
|
case RESAMPLER_MAX:
|
|
|
|
break;
|
|
|
|
}
|
2009-09-16 00:56:31 +00:00
|
|
|
}
|
|
|
|
else if(Channels == 8) /* 7.1 */
|
|
|
|
{
|
|
|
|
const int chans[] = {
|
|
|
|
FRONT_LEFT, FRONT_RIGHT,
|
|
|
|
FRONT_CENTER, LFE,
|
|
|
|
BACK_LEFT, BACK_RIGHT,
|
|
|
|
SIDE_LEFT, SIDE_RIGHT
|
|
|
|
};
|
2010-04-11 22:20:46 +00:00
|
|
|
const ALfloat scaler = 1.0f/Channels;
|
2009-09-16 00:56:31 +00:00
|
|
|
|
2010-01-11 13:37:20 +00:00
|
|
|
switch(Resampler)
|
|
|
|
{
|
2010-01-12 07:47:03 +00:00
|
|
|
case POINT_RESAMPLER:
|
|
|
|
DO_MIX(point); break;
|
|
|
|
case LINEAR_RESAMPLER:
|
|
|
|
DO_MIX(lerp); break;
|
|
|
|
case COSINE_RESAMPLER:
|
|
|
|
DO_MIX(cos_lerp); break;
|
2010-01-11 15:02:29 +00:00
|
|
|
case RESAMPLER_MIN:
|
2010-01-11 13:37:20 +00:00
|
|
|
case RESAMPLER_MAX:
|
|
|
|
break;
|
|
|
|
}
|
2009-01-24 21:57:01 +00:00
|
|
|
#undef DO_MIX
|
2009-09-16 00:56:31 +00:00
|
|
|
}
|
|
|
|
else /* Unknown? */
|
|
|
|
{
|
|
|
|
for(i = 0;i < OUTPUTCHANNELS;i++)
|
|
|
|
DrySend[i] += dryGainStep[i]*BufferSize;
|
|
|
|
for(i = 0;i < MAX_SENDS;i++)
|
|
|
|
WetSend[i] += wetGainStep[i]*BufferSize;
|
|
|
|
while(BufferSize--)
|
|
|
|
{
|
|
|
|
DataPosFrac += increment;
|
|
|
|
k += DataPosFrac>>FRACTIONBITS;
|
|
|
|
DataPosFrac &= FRACTIONMASK;
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DataPosInt += k;
|
|
|
|
|
|
|
|
skipmix:
|
|
|
|
/* Handle looping sources */
|
2009-10-25 11:39:36 +00:00
|
|
|
if(DataPosInt >= DataSize)
|
2009-09-16 00:56:31 +00:00
|
|
|
{
|
2009-10-25 11:39:36 +00:00
|
|
|
if(BuffersPlayed < (ALSource->BuffersInQueue-1))
|
2009-09-16 00:56:31 +00:00
|
|
|
{
|
2009-10-25 11:39:36 +00:00
|
|
|
BufferListItem = BufferListItem->next;
|
|
|
|
BuffersPlayed++;
|
|
|
|
DataPosInt -= DataSize;
|
|
|
|
}
|
2009-12-21 05:50:00 +00:00
|
|
|
else if(ALSource->bLooping)
|
2009-10-25 11:39:36 +00:00
|
|
|
{
|
2009-12-21 05:50:00 +00:00
|
|
|
BufferListItem = ALSource->queue;
|
|
|
|
BuffersPlayed = 0;
|
|
|
|
if(ALSource->BuffersInQueue == 1)
|
|
|
|
DataPosInt %= DataSize;
|
2009-09-16 00:56:31 +00:00
|
|
|
else
|
2009-12-21 05:50:00 +00:00
|
|
|
DataPosInt -= DataSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
State = AL_STOPPED;
|
|
|
|
BufferListItem = ALSource->queue;
|
|
|
|
BuffersPlayed = ALSource->BuffersInQueue;
|
|
|
|
DataPosInt = 0;
|
|
|
|
DataPosFrac = 0;
|
2007-11-14 02:02:18 +00:00
|
|
|
}
|
2008-01-21 22:54:15 +00:00
|
|
|
}
|
2009-09-16 00:56:31 +00:00
|
|
|
}
|
|
|
|
|
2009-10-25 11:39:36 +00:00
|
|
|
/* Update source info */
|
|
|
|
ALSource->state = State;
|
|
|
|
ALSource->BuffersPlayed = BuffersPlayed;
|
|
|
|
ALSource->position = DataPosInt;
|
|
|
|
ALSource->position_fraction = DataPosFrac;
|
|
|
|
ALSource->Buffer = BufferListItem->buffer;
|
|
|
|
|
|
|
|
for(i = 0;i < OUTPUTCHANNELS;i++)
|
|
|
|
ALSource->DryGains[i] = DrySend[i];
|
|
|
|
for(i = 0;i < MAX_SENDS;i++)
|
|
|
|
ALSource->WetGains[i] = WetSend[i];
|
|
|
|
|
2009-10-25 12:03:22 +00:00
|
|
|
ALSource->FirstStart = AL_FALSE;
|
2009-10-25 11:39:36 +00:00
|
|
|
|
2009-09-16 00:56:31 +00:00
|
|
|
if((ALSource=ALSource->next) != NULL)
|
|
|
|
goto another_source;
|
|
|
|
}
|
|
|
|
|
2009-09-16 02:30:27 +00:00
|
|
|
ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
|
2009-09-16 00:56:31 +00:00
|
|
|
{
|
2009-10-09 08:06:33 +00:00
|
|
|
float (*DryBuffer)[OUTPUTCHANNELS];
|
2010-04-08 21:47:12 +00:00
|
|
|
ALfloat (*Matrix)[OUTPUTCHANNELS];
|
|
|
|
const ALuint *ChanMap;
|
2009-09-16 00:56:31 +00:00
|
|
|
ALuint SamplesToDo;
|
|
|
|
ALeffectslot *ALEffectSlot;
|
2009-09-16 02:30:27 +00:00
|
|
|
ALCcontext *ALContext;
|
2010-04-08 21:47:12 +00:00
|
|
|
ALfloat samp;
|
2009-09-16 00:56:31 +00:00
|
|
|
int fpuState;
|
2010-04-08 21:47:12 +00:00
|
|
|
ALuint i, j, c;
|
2009-09-16 00:56:31 +00:00
|
|
|
|
|
|
|
#if defined(HAVE_FESETROUND)
|
|
|
|
fpuState = fegetround();
|
|
|
|
fesetround(FE_TOWARDZERO);
|
|
|
|
#elif defined(HAVE__CONTROLFP)
|
|
|
|
fpuState = _controlfp(0, 0);
|
|
|
|
_controlfp(_RC_CHOP, _MCW_RC);
|
|
|
|
#else
|
|
|
|
(void)fpuState;
|
|
|
|
#endif
|
|
|
|
|
2009-10-09 08:06:33 +00:00
|
|
|
DryBuffer = device->DryBuffer;
|
2009-09-16 00:56:31 +00:00
|
|
|
while(size > 0)
|
|
|
|
{
|
|
|
|
/* Setup variables */
|
|
|
|
SamplesToDo = min(size, BUFFERSIZE);
|
|
|
|
|
|
|
|
/* Clear mixing buffer */
|
|
|
|
memset(DryBuffer, 0, SamplesToDo*OUTPUTCHANNELS*sizeof(ALfloat));
|
|
|
|
|
2010-01-11 11:57:49 +00:00
|
|
|
SuspendContext(NULL);
|
2009-10-20 18:54:04 +00:00
|
|
|
for(c = 0;c < device->NumContexts;c++)
|
2008-01-21 22:54:15 +00:00
|
|
|
{
|
2009-10-20 18:54:04 +00:00
|
|
|
ALContext = device->Contexts[c];
|
2009-10-20 20:38:02 +00:00
|
|
|
SuspendContext(ALContext);
|
|
|
|
|
2009-09-16 00:56:31 +00:00
|
|
|
MixSomeSources(ALContext, DryBuffer, SamplesToDo);
|
|
|
|
|
|
|
|
/* effect slot processing */
|
2010-03-16 22:37:41 +00:00
|
|
|
ALEffectSlot = ALContext->EffectSlotList;
|
2009-09-16 00:56:31 +00:00
|
|
|
while(ALEffectSlot)
|
|
|
|
{
|
|
|
|
if(ALEffectSlot->EffectState)
|
|
|
|
ALEffect_Process(ALEffectSlot->EffectState, ALEffectSlot, SamplesToDo, ALEffectSlot->WetBuffer, DryBuffer);
|
Implement AL_EFFECT_REVERB
Here is a quick description of how the reverb effect works:
+--->---+*(4)
| V new sample
+-----+---+---+ |
|extra|ltr|ref| <- +*(1)
+-----+---+---+
(3,5)*| |*(2)
+-->|
V
out sample
1) Apply master reverb gain to incoming sample and place it at the head of the
buffer. The master reverb gainhf was already applied when the source was
initially mixed.
2) Copy the delayed reflection sample to an output sample and apply the
reflection gain.
3) Apply the late reverb gain to the late reverb sample
4) Copy the end of the buffer, applying a decay gain and the decay hf ratio,
and add to the late reverb.
5) Copy the late reverb sample, adding to the output sample.
Then the head and sampling points are shifted forward, and done again for each
new sample. The extra buffer length is determined by the Reverb Density
property. A value of 0 gives a length of 0.1 seconds (long, with fairly
distinct echos) , and 1 gives 0.075 seconds (short, indistinct echos).
The decay gain is calculated such that after a number of loops to satisfy the
Decay Time, a sample will be 1/32768th as powerful (virtually insignificant to
the resulting output, and only getting further reduced). It is calculated as:
DecayGain = pow(1.0f/32768.0f, 1.0/(DecayTime/ExtraLength));
Things to note: Reverb Diffusion is not currently handled, nor is Decay HF
Limit. Decay HF Ratios above 1 probably give incorrect results. Also, this
method likely sucks, but it's the best I can come up with before release. :)
2008-01-19 05:25:40 +00:00
|
|
|
|
2009-09-16 00:56:31 +00:00
|
|
|
for(i = 0;i < SamplesToDo;i++)
|
|
|
|
ALEffectSlot->WetBuffer[i] = 0.0f;
|
|
|
|
ALEffectSlot = ALEffectSlot->next;
|
|
|
|
}
|
2009-10-20 20:38:02 +00:00
|
|
|
ProcessContext(ALContext);
|
2008-01-21 22:54:15 +00:00
|
|
|
}
|
2010-01-11 11:57:49 +00:00
|
|
|
ProcessContext(NULL);
|
2008-01-21 22:54:15 +00:00
|
|
|
|
|
|
|
//Post processing loop
|
2009-12-02 12:03:51 +00:00
|
|
|
ChanMap = device->DevChannels;
|
2010-04-08 21:47:12 +00:00
|
|
|
Matrix = device->ChannelMatrix;
|
2009-09-16 02:30:27 +00:00
|
|
|
switch(device->Format)
|
2008-01-21 22:54:15 +00:00
|
|
|
{
|
2009-12-02 12:03:51 +00:00
|
|
|
#define CHECK_WRITE_FORMAT(bits, type, func) \
|
2009-08-15 20:20:35 +00:00
|
|
|
case AL_FORMAT_MONO##bits: \
|
|
|
|
for(i = 0;i < SamplesToDo;i++) \
|
|
|
|
{ \
|
2010-04-08 21:47:12 +00:00
|
|
|
samp = 0.0f; \
|
|
|
|
for(c = 0;c < OUTPUTCHANNELS;c++) \
|
|
|
|
samp += DryBuffer[i][c] * Matrix[c][FRONT_CENTER]; \
|
|
|
|
((type*)buffer)[ChanMap[FRONT_CENTER]] = (func)(samp); \
|
2009-08-15 20:20:35 +00:00
|
|
|
buffer = ((type*)buffer) + 1; \
|
|
|
|
} \
|
|
|
|
break; \
|
|
|
|
case AL_FORMAT_STEREO##bits: \
|
2009-09-16 02:30:27 +00:00
|
|
|
if(device->Bs2b) \
|
2009-08-15 20:20:35 +00:00
|
|
|
{ \
|
|
|
|
for(i = 0;i < SamplesToDo;i++) \
|
|
|
|
{ \
|
2010-04-08 21:47:12 +00:00
|
|
|
float samples[2] = { 0.0f, 0.0f }; \
|
|
|
|
for(c = 0;c < OUTPUTCHANNELS;c++) \
|
|
|
|
{ \
|
|
|
|
samples[0] += DryBuffer[i][c]*Matrix[c][FRONT_LEFT]; \
|
|
|
|
samples[1] += DryBuffer[i][c]*Matrix[c][FRONT_RIGHT]; \
|
|
|
|
} \
|
2009-09-16 02:30:27 +00:00
|
|
|
bs2b_cross_feed(device->Bs2b, samples); \
|
2010-04-08 20:11:21 +00:00
|
|
|
((type*)buffer)[ChanMap[FRONT_LEFT]] = (func)(samples[0]);\
|
2010-04-08 21:47:12 +00:00
|
|
|
((type*)buffer)[ChanMap[FRONT_RIGHT]]= (func)(samples[1]);\
|
2009-08-15 20:20:35 +00:00
|
|
|
buffer = ((type*)buffer) + 2; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
|
|
|
for(i = 0;i < SamplesToDo;i++) \
|
|
|
|
{ \
|
2010-04-08 21:47:12 +00:00
|
|
|
static const Channel chans[] = { \
|
|
|
|
FRONT_LEFT, FRONT_RIGHT \
|
|
|
|
}; \
|
|
|
|
for(j = 0;j < 2;j++) \
|
|
|
|
{ \
|
|
|
|
samp = 0.0f; \
|
|
|
|
for(c = 0;c < OUTPUTCHANNELS;c++) \
|
|
|
|
samp += DryBuffer[i][c] * Matrix[c][chans[j]]; \
|
|
|
|
((type*)buffer)[ChanMap[chans[j]]] = (func)(samp); \
|
|
|
|
} \
|
2009-08-15 20:20:35 +00:00
|
|
|
buffer = ((type*)buffer) + 2; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
break; \
|
|
|
|
case AL_FORMAT_QUAD##bits: \
|
|
|
|
for(i = 0;i < SamplesToDo;i++) \
|
|
|
|
{ \
|
2010-04-08 21:47:12 +00:00
|
|
|
static const Channel chans[] = { \
|
|
|
|
FRONT_LEFT, FRONT_RIGHT, \
|
|
|
|
BACK_LEFT, BACK_RIGHT, \
|
|
|
|
}; \
|
|
|
|
for(j = 0;j < 4;j++) \
|
|
|
|
{ \
|
|
|
|
samp = 0.0f; \
|
|
|
|
for(c = 0;c < OUTPUTCHANNELS;c++) \
|
|
|
|
samp += DryBuffer[i][c] * Matrix[c][chans[j]]; \
|
|
|
|
((type*)buffer)[ChanMap[chans[j]]] = (func)(samp); \
|
|
|
|
} \
|
2009-08-15 20:20:35 +00:00
|
|
|
buffer = ((type*)buffer) + 4; \
|
|
|
|
} \
|
|
|
|
break; \
|
|
|
|
case AL_FORMAT_51CHN##bits: \
|
|
|
|
for(i = 0;i < SamplesToDo;i++) \
|
|
|
|
{ \
|
2010-04-08 21:47:12 +00:00
|
|
|
static const Channel chans[] = { \
|
|
|
|
FRONT_LEFT, FRONT_RIGHT, \
|
|
|
|
FRONT_CENTER, LFE, \
|
|
|
|
BACK_LEFT, BACK_RIGHT, \
|
|
|
|
}; \
|
|
|
|
for(j = 0;j < 6;j++) \
|
|
|
|
{ \
|
|
|
|
samp = 0.0f; \
|
|
|
|
for(c = 0;c < OUTPUTCHANNELS;c++) \
|
|
|
|
samp += DryBuffer[i][c] * Matrix[c][chans[j]]; \
|
|
|
|
((type*)buffer)[ChanMap[chans[j]]] = (func)(samp); \
|
|
|
|
} \
|
2009-08-15 20:20:35 +00:00
|
|
|
buffer = ((type*)buffer) + 6; \
|
|
|
|
} \
|
|
|
|
break; \
|
|
|
|
case AL_FORMAT_61CHN##bits: \
|
|
|
|
for(i = 0;i < SamplesToDo;i++) \
|
|
|
|
{ \
|
2010-04-08 21:47:12 +00:00
|
|
|
static const Channel chans[] = { \
|
|
|
|
FRONT_LEFT, FRONT_RIGHT, \
|
|
|
|
FRONT_CENTER, LFE, BACK_CENTER, \
|
|
|
|
SIDE_LEFT, SIDE_RIGHT, \
|
|
|
|
}; \
|
|
|
|
for(j = 0;j < 7;j++) \
|
|
|
|
{ \
|
|
|
|
samp = 0.0f; \
|
|
|
|
for(c = 0;c < OUTPUTCHANNELS;c++) \
|
|
|
|
samp += DryBuffer[i][c] * Matrix[c][chans[j]]; \
|
|
|
|
((type*)buffer)[ChanMap[chans[j]]] = (func)(samp); \
|
|
|
|
} \
|
2009-08-15 20:20:35 +00:00
|
|
|
buffer = ((type*)buffer) + 7; \
|
|
|
|
} \
|
|
|
|
break; \
|
|
|
|
case AL_FORMAT_71CHN##bits: \
|
|
|
|
for(i = 0;i < SamplesToDo;i++) \
|
|
|
|
{ \
|
2010-04-08 21:47:12 +00:00
|
|
|
static const Channel chans[] = { \
|
|
|
|
FRONT_LEFT, FRONT_RIGHT, \
|
|
|
|
FRONT_CENTER, LFE, \
|
|
|
|
BACK_LEFT, BACK_RIGHT, \
|
|
|
|
SIDE_LEFT, SIDE_RIGHT \
|
|
|
|
}; \
|
|
|
|
for(j = 0;j < 8;j++) \
|
|
|
|
{ \
|
|
|
|
samp = 0.0f; \
|
|
|
|
for(c = 0;c < OUTPUTCHANNELS;c++) \
|
|
|
|
samp += DryBuffer[i][c] * Matrix[c][chans[j]]; \
|
|
|
|
((type*)buffer)[ChanMap[chans[j]]] = (func)(samp); \
|
|
|
|
} \
|
2009-08-15 20:20:35 +00:00
|
|
|
buffer = ((type*)buffer) + 8; \
|
|
|
|
} \
|
|
|
|
break;
|
2007-12-31 11:29:14 +00:00
|
|
|
|
2009-08-15 20:20:35 +00:00
|
|
|
#define AL_FORMAT_MONO32 AL_FORMAT_MONO_FLOAT32
|
|
|
|
#define AL_FORMAT_STEREO32 AL_FORMAT_STEREO_FLOAT32
|
2009-12-02 12:03:51 +00:00
|
|
|
CHECK_WRITE_FORMAT(8, ALubyte, aluF2UB)
|
|
|
|
CHECK_WRITE_FORMAT(16, ALshort, aluF2S)
|
|
|
|
CHECK_WRITE_FORMAT(32, ALfloat, aluF2F)
|
2009-08-15 20:20:35 +00:00
|
|
|
#undef AL_FORMAT_STEREO32
|
|
|
|
#undef AL_FORMAT_MONO32
|
|
|
|
#undef CHECK_WRITE_FORMAT
|
2007-11-14 02:02:18 +00:00
|
|
|
|
2008-01-21 22:54:15 +00:00
|
|
|
default:
|
|
|
|
break;
|
2007-11-14 02:02:18 +00:00
|
|
|
}
|
2008-01-21 22:54:15 +00:00
|
|
|
|
|
|
|
size -= SamplesToDo;
|
2007-11-14 02:02:18 +00:00
|
|
|
}
|
|
|
|
|
2008-08-08 14:32:21 +00:00
|
|
|
#if defined(HAVE_FESETROUND)
|
|
|
|
fesetround(fpuState);
|
|
|
|
#elif defined(HAVE__CONTROLFP)
|
|
|
|
_controlfp(fpuState, 0xfffff);
|
|
|
|
#endif
|
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
|
|
|
{
|
2009-09-22 06:31:04 +00:00
|
|
|
ALsource *source;
|
2009-08-27 02:15:17 +00:00
|
|
|
|
2009-10-20 18:54:04 +00:00
|
|
|
SuspendContext(device->Contexts[i]);
|
2009-08-27 02:15:17 +00:00
|
|
|
|
2010-03-16 22:37:41 +00:00
|
|
|
source = device->Contexts[i]->SourceList;
|
2009-08-27 02:15:17 +00:00
|
|
|
while(source)
|
|
|
|
{
|
|
|
|
if(source->state == AL_PLAYING)
|
|
|
|
{
|
|
|
|
source->state = AL_STOPPED;
|
|
|
|
source->BuffersPlayed = source->BuffersInQueue;
|
|
|
|
source->position = 0;
|
|
|
|
source->position_fraction = 0;
|
|
|
|
}
|
|
|
|
source = source->next;
|
|
|
|
}
|
2009-10-20 18:54:04 +00:00
|
|
|
ProcessContext(device->Contexts[i]);
|
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
|
|
|
}
|