Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 61122a5093 | |||
| a8a4ff8af1 | |||
| 8a857c35ee | |||
| 89ddd7d8e5 | |||
| 8d0c4ccb3b | |||
| 8ee47d5573 | |||
| 9e88011417 | |||
| f8949ee7a1 | |||
| 62aa2d0ba7 | |||
| ace047d625 | |||
| d6277db209 | |||
| 7ebb28327f | |||
| 86931cbde4 | |||
| 6d7be151dc | |||
| 07227b9806 | |||
| 8348d719cd | |||
| 0fcefd865b | |||
| 57c2e9b5f8 | |||
| 7d7fc39035 | |||
| 351105b3df | |||
| cbfc33215b | |||
| 5a93b56673 | |||
| 1f4c69c17a | |||
| f5b19fad20 | |||
| bc60818e9a | |||
| f82c88f016 | |||
| 98e86decad | |||
| aaf2c0ebd4 | |||
| 778b74cae1 | |||
| 43ee1edd97 | |||
| dd7e23740b | |||
| 1acd6da745 | |||
| 3056f91ec5 | |||
| ed03570e1a | |||
| 2ec0e48d06 | |||
| 8b54d59b8c | |||
| f6a4dbabdd | |||
| a71c291bcb | |||
| 55b9ccc2de |
@@ -37,6 +37,7 @@
|
||||
#include "alExtension.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "bs2b.h"
|
||||
#include "alu.h"
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// DEBUG INFORMATION
|
||||
@@ -67,6 +68,9 @@ static struct {
|
||||
#ifdef HAVE_WINMM
|
||||
{ "winmm", alcWinMMInit, EmptyFuncs },
|
||||
#endif
|
||||
#ifdef HAVE_PORTAUDIO
|
||||
{ "port", alc_pa_init, EmptyFuncs },
|
||||
#endif
|
||||
|
||||
{ "wave", alc_wave_init, EmptyFuncs },
|
||||
|
||||
@@ -475,7 +479,7 @@ static ALvoid InitContext(ALCcontext *pContext)
|
||||
pContext->lNumStereoSources = 1;
|
||||
pContext->lNumMonoSources = pContext->Device->MaxNoOfSources - pContext->lNumStereoSources;
|
||||
|
||||
pContext->ExtensionList = "AL_EXTX_buffer_sub_data AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_OFFSET AL_LOKI_quadriphonic";
|
||||
pContext->ExtensionList = "AL_EXTX_buffer_sub_data AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_OFFSET AL_EXTX_source_distance_model AL_LOKI_quadriphonic";
|
||||
|
||||
level = GetConfigValueInt(NULL, "cf_level", 0);
|
||||
if(level > 0 && level <= 6)
|
||||
@@ -484,6 +488,8 @@ static ALvoid InitContext(ALCcontext *pContext)
|
||||
bs2b_set_srate(pContext->bs2b, pContext->Frequency);
|
||||
bs2b_set_level(pContext->bs2b, level);
|
||||
}
|
||||
|
||||
aluInitPanning(pContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -1263,21 +1269,21 @@ ALCAPI ALCdevice* ALCAPIENTRY alcOpenDevice(const ALCchar *deviceName)
|
||||
device->MaxNoOfSources = 256;
|
||||
|
||||
// Find a playback device to open
|
||||
SuspendContext(NULL);
|
||||
for(i = 0;BackendList[i].Init;i++)
|
||||
{
|
||||
device->Funcs = &BackendList[i].Funcs;
|
||||
if(ALCdevice_OpenPlayback(device, deviceName))
|
||||
{
|
||||
SuspendContext(NULL);
|
||||
device->next = g_pDeviceList;
|
||||
g_pDeviceList = device;
|
||||
g_ulDeviceCount++;
|
||||
ProcessContext(NULL);
|
||||
|
||||
bDeviceFound = AL_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ProcessContext(NULL);
|
||||
|
||||
if (!bDeviceFound)
|
||||
{
|
||||
|
||||
@@ -23,6 +23,11 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
@@ -39,6 +44,11 @@
|
||||
#include <float.h>
|
||||
#endif
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846 /* pi */
|
||||
#define M_PI_2 1.57079632679489661923 /* pi/2 */
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_STDINT_H)
|
||||
#include <stdint.h>
|
||||
typedef int64_t ALint64;
|
||||
@@ -62,6 +72,18 @@ typedef long long ALint64;
|
||||
#define aluAcos(x) ((ALfloat)acos((double)(x)))
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ATANF
|
||||
#define aluAtan(x) ((ALfloat)atanf((float)(x)))
|
||||
#else
|
||||
#define aluAtan(x) ((ALfloat)atan((double)(x)))
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FABSF
|
||||
#define aluFabs(x) ((ALfloat)fabsf((float)(x)))
|
||||
#else
|
||||
#define aluFabs(x) ((ALfloat)fabs((double)(x)))
|
||||
#endif
|
||||
|
||||
// fixes for mingw32.
|
||||
#if defined(max) && !defined(__max)
|
||||
#define __max max
|
||||
@@ -178,6 +200,20 @@ static __inline ALfloat lpFilter(FILTER *iir, ALfloat input)
|
||||
return output;
|
||||
}
|
||||
|
||||
static __inline ALfloat lpFilterMC(FILTER *iir, ALuint chan, ALfloat input)
|
||||
{
|
||||
ALfloat *history = &iir->history[chan*2];
|
||||
ALfloat a = iir->coeff;
|
||||
ALfloat output = input;
|
||||
|
||||
output = output + (history[0]-output)*a;
|
||||
history[0] = output;
|
||||
output = output + (history[1]-output)*a;
|
||||
history[1] = output;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
static __inline ALshort aluF2S(ALfloat Value)
|
||||
{
|
||||
@@ -189,14 +225,14 @@ static __inline ALshort aluF2S(ALfloat Value)
|
||||
return ((ALshort)i);
|
||||
}
|
||||
|
||||
static __inline ALvoid aluCrossproduct(ALfloat *inVector1,ALfloat *inVector2,ALfloat *outVector)
|
||||
static __inline ALvoid aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
|
||||
{
|
||||
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];
|
||||
}
|
||||
|
||||
static __inline ALfloat aluDotproduct(ALfloat *inVector1,ALfloat *inVector2)
|
||||
static __inline ALfloat aluDotproduct(const ALfloat *inVector1, const ALfloat *inVector2)
|
||||
{
|
||||
return inVector1[0]*inVector2[0] + inVector1[1]*inVector2[1] +
|
||||
inVector1[2]*inVector2[2];
|
||||
@@ -226,9 +262,300 @@ static __inline ALvoid aluMatrixVector(ALfloat *vector,ALfloat matrix[3][3])
|
||||
memcpy(vector, result, sizeof(result));
|
||||
}
|
||||
|
||||
static ALvoid SetSpeakerArrangement(const char *name, ALfloat SpeakerAngle[OUTPUTCHANNELS],
|
||||
ALint Speaker2Chan[OUTPUTCHANNELS], ALint chans)
|
||||
{
|
||||
const char *confkey;
|
||||
const char *next;
|
||||
const char *sep;
|
||||
const char *end;
|
||||
int i, val;
|
||||
|
||||
static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
|
||||
ALenum isMono, ALenum OutputFormat,
|
||||
confkey = GetConfigValue(NULL, name, "");
|
||||
next = confkey;
|
||||
while(next && *next)
|
||||
{
|
||||
confkey = next;
|
||||
next = strchr(confkey, ',');
|
||||
if(next)
|
||||
{
|
||||
do {
|
||||
next++;
|
||||
} while(isspace(*next));
|
||||
}
|
||||
|
||||
sep = strchr(confkey, '=');
|
||||
if(!sep || confkey == sep)
|
||||
continue;
|
||||
|
||||
end = sep - 1;
|
||||
while(isspace(*end) && end != confkey)
|
||||
end--;
|
||||
|
||||
if(strncmp(confkey, "fl", end-confkey) == 0)
|
||||
val = FRONT_LEFT;
|
||||
else if(strncmp(confkey, "fr", end-confkey) == 0)
|
||||
val = FRONT_RIGHT;
|
||||
else if(strncmp(confkey, "fc", end-confkey) == 0)
|
||||
val = FRONT_CENTER;
|
||||
else if(strncmp(confkey, "bl", end-confkey) == 0)
|
||||
val = BACK_LEFT;
|
||||
else if(strncmp(confkey, "br", end-confkey) == 0)
|
||||
val = BACK_RIGHT;
|
||||
else if(strncmp(confkey, "bc", end-confkey) == 0)
|
||||
val = BACK_CENTER;
|
||||
else if(strncmp(confkey, "sl", end-confkey) == 0)
|
||||
val = SIDE_LEFT;
|
||||
else if(strncmp(confkey, "sr", end-confkey) == 0)
|
||||
val = SIDE_RIGHT;
|
||||
else
|
||||
{
|
||||
AL_PRINT("Unknown speaker for %s: \"%c%c\"\n", name, confkey[0], confkey[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
sep++;
|
||||
while(isspace(*sep))
|
||||
sep++;
|
||||
|
||||
for(i = 0;i < chans;i++)
|
||||
{
|
||||
if(Speaker2Chan[i] == val)
|
||||
{
|
||||
val = strtol(sep, NULL, 10);
|
||||
if(val >= -180 && val <= 180)
|
||||
SpeakerAngle[i] = val * M_PI/180.0f;
|
||||
else
|
||||
AL_PRINT("Invalid angle for speaker \"%c%c\": %d\n", confkey[0], confkey[1], val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 1;i < chans;i++)
|
||||
{
|
||||
if(SpeakerAngle[i] <= SpeakerAngle[i-1])
|
||||
{
|
||||
AL_PRINT("Speaker %d of %d does not follow previous: %f > %f\n", i, chans,
|
||||
SpeakerAngle[i-1] * 180.0f/M_PI, SpeakerAngle[i] * 180.0f/M_PI);
|
||||
SpeakerAngle[i] = SpeakerAngle[i-1] + 1 * 180.0f/M_PI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ALvoid aluInitPanning(ALCcontext *Context)
|
||||
{
|
||||
ALint pos, offset, s;
|
||||
ALfloat Alpha, Theta;
|
||||
ALfloat SpeakerAngle[OUTPUTCHANNELS];
|
||||
ALint Speaker2Chan[OUTPUTCHANNELS];
|
||||
|
||||
for(s = 0;s < OUTPUTCHANNELS;s++)
|
||||
{
|
||||
int s2;
|
||||
for(s2 = 0;s2 < OUTPUTCHANNELS;s2++)
|
||||
Context->ChannelMatrix[s][s2] = ((s==s2) ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
switch(Context->Device->Format)
|
||||
{
|
||||
/* Mono is rendered as stereo, then downmixed during post-process */
|
||||
case AL_FORMAT_MONO8:
|
||||
case AL_FORMAT_MONO16:
|
||||
case AL_FORMAT_MONO_FLOAT32:
|
||||
Context->ChannelMatrix[FRONT_CENTER][FRONT_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[FRONT_CENTER][FRONT_RIGHT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[SIDE_LEFT][FRONT_LEFT] = 1.0f;
|
||||
Context->ChannelMatrix[SIDE_RIGHT][FRONT_RIGHT] = 1.0f;
|
||||
Context->ChannelMatrix[BACK_LEFT][FRONT_LEFT] = 1.0f;
|
||||
Context->ChannelMatrix[BACK_RIGHT][FRONT_RIGHT] = 1.0f;
|
||||
Context->ChannelMatrix[BACK_CENTER][FRONT_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[BACK_CENTER][FRONT_RIGHT] = aluSqrt(0.5);
|
||||
Context->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;
|
||||
break;
|
||||
|
||||
case AL_FORMAT_STEREO8:
|
||||
case AL_FORMAT_STEREO16:
|
||||
case AL_FORMAT_STEREO_FLOAT32:
|
||||
Context->ChannelMatrix[FRONT_CENTER][FRONT_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[FRONT_CENTER][FRONT_RIGHT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[SIDE_LEFT][FRONT_LEFT] = 1.0f;
|
||||
Context->ChannelMatrix[SIDE_RIGHT][FRONT_RIGHT] = 1.0f;
|
||||
Context->ChannelMatrix[BACK_LEFT][FRONT_LEFT] = 1.0f;
|
||||
Context->ChannelMatrix[BACK_RIGHT][FRONT_RIGHT] = 1.0f;
|
||||
Context->ChannelMatrix[BACK_CENTER][FRONT_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[BACK_CENTER][FRONT_RIGHT] = aluSqrt(0.5);
|
||||
Context->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_STEREO", SpeakerAngle, Speaker2Chan, Context->NumChan);
|
||||
break;
|
||||
|
||||
case AL_FORMAT_QUAD8:
|
||||
case AL_FORMAT_QUAD16:
|
||||
case AL_FORMAT_QUAD32:
|
||||
Context->ChannelMatrix[FRONT_CENTER][FRONT_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[FRONT_CENTER][FRONT_RIGHT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[SIDE_LEFT][FRONT_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[SIDE_LEFT][BACK_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[SIDE_RIGHT][FRONT_RIGHT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[SIDE_RIGHT][BACK_RIGHT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[BACK_CENTER][BACK_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[BACK_CENTER][BACK_RIGHT] = aluSqrt(0.5);
|
||||
Context->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_QUAD", SpeakerAngle, Speaker2Chan, Context->NumChan);
|
||||
break;
|
||||
|
||||
case AL_FORMAT_51CHN8:
|
||||
case AL_FORMAT_51CHN16:
|
||||
case AL_FORMAT_51CHN32:
|
||||
Context->ChannelMatrix[SIDE_LEFT][FRONT_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[SIDE_LEFT][BACK_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[SIDE_RIGHT][FRONT_RIGHT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[SIDE_RIGHT][BACK_RIGHT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[BACK_CENTER][BACK_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[BACK_CENTER][BACK_RIGHT] = aluSqrt(0.5);
|
||||
Context->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_51CHN", SpeakerAngle, Speaker2Chan, Context->NumChan);
|
||||
break;
|
||||
|
||||
case AL_FORMAT_61CHN8:
|
||||
case AL_FORMAT_61CHN16:
|
||||
case AL_FORMAT_61CHN32:
|
||||
Context->ChannelMatrix[BACK_LEFT][BACK_CENTER] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[BACK_LEFT][SIDE_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[BACK_RIGHT][BACK_CENTER] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[BACK_RIGHT][SIDE_RIGHT] = aluSqrt(0.5);
|
||||
Context->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_61CHN", SpeakerAngle, Speaker2Chan, Context->NumChan);
|
||||
break;
|
||||
|
||||
case AL_FORMAT_71CHN8:
|
||||
case AL_FORMAT_71CHN16:
|
||||
case AL_FORMAT_71CHN32:
|
||||
Context->ChannelMatrix[BACK_CENTER][BACK_LEFT] = aluSqrt(0.5);
|
||||
Context->ChannelMatrix[BACK_CENTER][BACK_RIGHT] = aluSqrt(0.5);
|
||||
Context->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_71CHN", SpeakerAngle, Speaker2Chan, Context->NumChan);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
for(pos = 0; pos < LUT_NUM; pos++)
|
||||
{
|
||||
/* source angle */
|
||||
Theta = aluLUTpos2Angle(pos);
|
||||
|
||||
/* clear all values */
|
||||
offset = OUTPUTCHANNELS * pos;
|
||||
for(s = 0; s < OUTPUTCHANNELS; s++)
|
||||
Context->PanningLUT[offset+s] = 0.0f;
|
||||
|
||||
/* set panning values */
|
||||
for(s = 0; s < Context->NumChan - 1; s++)
|
||||
{
|
||||
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]);
|
||||
Context->PanningLUT[offset + Speaker2Chan[s]] = cos(Alpha);
|
||||
Context->PanningLUT[offset + Speaker2Chan[s+1]] = sin(Alpha);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(s == Context->NumChan - 1)
|
||||
{
|
||||
/* 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]);
|
||||
Context->PanningLUT[offset + Speaker2Chan[s]] = cos(Alpha);
|
||||
Context->PanningLUT[offset + Speaker2Chan[0]] = sin(Alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static __inline ALint aluCart2LUTpos(ALfloat re, ALfloat im)
|
||||
{
|
||||
ALint pos = 0;
|
||||
ALfloat denom = aluFabs(re) + aluFabs(im);
|
||||
if(denom > 0.0f)
|
||||
pos = (ALint)(QUADRANT_NUM*aluFabs(im) / denom + 0.5);
|
||||
|
||||
if(re < 0.0)
|
||||
pos = 2 * QUADRANT_NUM - pos;
|
||||
if(im < 0.0)
|
||||
pos = LUT_NUM - pos;
|
||||
return pos%LUT_NUM;
|
||||
}
|
||||
|
||||
static ALvoid CalcSourceParams(const ALCcontext *ALContext,
|
||||
const ALsource *ALSource, ALenum isMono,
|
||||
ALfloat *drysend, ALfloat *wetsend,
|
||||
ALfloat *pitch, ALfloat *drygainhf,
|
||||
ALfloat *wetgainhf)
|
||||
@@ -236,7 +563,7 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
|
||||
ALfloat InnerAngle,OuterAngle,Angle,Distance,DryMix,WetMix=0.0f;
|
||||
ALfloat Direction[3],Position[3],SourceToListener[3];
|
||||
ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff,OuterGainHF;
|
||||
ALfloat ConeVolume,SourceVolume,PanningFB,PanningLR,ListenerGain;
|
||||
ALfloat ConeVolume,SourceVolume,ListenerGain;
|
||||
ALfloat U[3],V[3],N[3];
|
||||
ALfloat DopplerFactor, DopplerVelocity, flSpeedOfSound, flMaxVelocity;
|
||||
ALfloat Matrix[3][3];
|
||||
@@ -246,7 +573,9 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
|
||||
ALfloat RoomRolloff;
|
||||
ALfloat DryGainHF = 1.0f;
|
||||
ALfloat WetGainHF = 1.0f;
|
||||
ALfloat cw, a, g;
|
||||
ALfloat DirGain, AmbientGain;
|
||||
const ALfloat *SpeakerGain;
|
||||
ALint pos, s;
|
||||
|
||||
//Get context properties
|
||||
DopplerFactor = ALContext->DopplerFactor * ALSource->DopplerFactor;
|
||||
@@ -301,7 +630,7 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
|
||||
SourceToListener[1] = -Position[1];
|
||||
SourceToListener[2] = -Position[2];
|
||||
|
||||
// Transform source position and direction into listener space
|
||||
// Transform source position into listener space
|
||||
aluMatrixVector(Position, Matrix);
|
||||
}
|
||||
else
|
||||
@@ -324,7 +653,7 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
|
||||
|
||||
flAttenuation = 1.0f;
|
||||
RoomAttenuation = 1.0f;
|
||||
switch (ALContext->DistanceModel)
|
||||
switch (ALSource->DistanceModel)
|
||||
{
|
||||
case AL_INVERSE_DISTANCE_CLAMPED:
|
||||
Distance=__max(Distance,MinDist);
|
||||
@@ -386,7 +715,7 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
|
||||
if(dist < 0.0f) dist = 0.0f;
|
||||
// Absorption calculation is done in dB
|
||||
absorb = (ALSource->AirAbsorptionFactor*AIRABSORBGAINDBHF) *
|
||||
(Distance*MetersPerUnit);
|
||||
(dist*MetersPerUnit);
|
||||
// Convert dB to linear gain before applying
|
||||
absorb = pow(10.0, absorb/20.0);
|
||||
DryGainHF *= absorb;
|
||||
@@ -403,8 +732,7 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
|
||||
WetMix = __max(WetMix,MinVolume);
|
||||
|
||||
//3. Apply directional soundcones
|
||||
Angle = aluAcos(aluDotproduct(Direction,SourceToListener)) * 180.0f /
|
||||
3.141592654f;
|
||||
Angle = aluAcos(aluDotproduct(Direction,SourceToListener)) * 180.0f/M_PI;
|
||||
if(Angle >= InnerAngle && Angle <= OuterAngle)
|
||||
{
|
||||
ALfloat scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
|
||||
@@ -505,87 +833,23 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
|
||||
DryMix *= ListenerGain;
|
||||
WetMix *= ListenerGain;
|
||||
|
||||
//6. Convert normalized position into pannings, then into channel volumes
|
||||
// Use energy-preserving panning algorithm for multi-speaker playback
|
||||
aluNormalize(Position);
|
||||
switch(aluChannelsFromFormat(OutputFormat))
|
||||
|
||||
pos = aluCart2LUTpos(-Position[2], Position[0]);
|
||||
SpeakerGain = &ALContext->PanningLUT[OUTPUTCHANNELS * pos];
|
||||
|
||||
DirGain = aluSqrt(Position[0]*Position[0] + Position[2]*Position[2]);
|
||||
// elevation adjustment for directional gain. this sucks, but
|
||||
// has low complexity
|
||||
AmbientGain = 1.0/aluSqrt(ALContext->NumChan) * (1.0-DirGain);
|
||||
for(s = 0; s < OUTPUTCHANNELS; s++)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
PanningLR = 0.5f + 0.5f*Position[0];
|
||||
drysend[FRONT_LEFT] = DryMix * aluSqrt(1.0f-PanningLR); //L Direct
|
||||
drysend[FRONT_RIGHT] = DryMix * aluSqrt( PanningLR); //R Direct
|
||||
drysend[BACK_LEFT] = 0.0f;
|
||||
drysend[BACK_RIGHT] = 0.0f;
|
||||
drysend[SIDE_LEFT] = 0.0f;
|
||||
drysend[SIDE_RIGHT] = 0.0f;
|
||||
break;
|
||||
case 4:
|
||||
/* TODO: Add center/lfe channel in spatial calculations? */
|
||||
case 6:
|
||||
// Apply a scalar so each individual speaker has more weight
|
||||
PanningLR = 0.5f + (0.5f*Position[0]*1.41421356f);
|
||||
PanningLR = __min(1.0f, PanningLR);
|
||||
PanningLR = __max(0.0f, PanningLR);
|
||||
PanningFB = 0.5f + (0.5f*Position[2]*1.41421356f);
|
||||
PanningFB = __min(1.0f, PanningFB);
|
||||
PanningFB = __max(0.0f, PanningFB);
|
||||
drysend[FRONT_LEFT] = DryMix * aluSqrt((1.0f-PanningLR)*(1.0f-PanningFB));
|
||||
drysend[FRONT_RIGHT] = DryMix * aluSqrt(( PanningLR)*(1.0f-PanningFB));
|
||||
drysend[BACK_LEFT] = DryMix * aluSqrt((1.0f-PanningLR)*( PanningFB));
|
||||
drysend[BACK_RIGHT] = DryMix * aluSqrt(( PanningLR)*( PanningFB));
|
||||
drysend[SIDE_LEFT] = 0.0f;
|
||||
drysend[SIDE_RIGHT] = 0.0f;
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
PanningFB = 1.0f - fabs(Position[2]*1.15470054f);
|
||||
PanningFB = __min(1.0f, PanningFB);
|
||||
PanningFB = __max(0.0f, PanningFB);
|
||||
PanningLR = 0.5f + (0.5*Position[0]*((1.0f-PanningFB)*2.0f));
|
||||
PanningLR = __min(1.0f, PanningLR);
|
||||
PanningLR = __max(0.0f, PanningLR);
|
||||
if(Position[2] > 0.0f)
|
||||
{
|
||||
drysend[BACK_LEFT] = DryMix * aluSqrt((1.0f-PanningLR)*(1.0f-PanningFB));
|
||||
drysend[BACK_RIGHT] = DryMix * aluSqrt(( PanningLR)*(1.0f-PanningFB));
|
||||
drysend[SIDE_LEFT] = DryMix * aluSqrt((1.0f-PanningLR)*( PanningFB));
|
||||
drysend[SIDE_RIGHT] = DryMix * aluSqrt(( PanningLR)*( PanningFB));
|
||||
drysend[FRONT_LEFT] = 0.0f;
|
||||
drysend[FRONT_RIGHT] = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
drysend[FRONT_LEFT] = DryMix * aluSqrt((1.0f-PanningLR)*(1.0f-PanningFB));
|
||||
drysend[FRONT_RIGHT] = DryMix * aluSqrt(( PanningLR)*(1.0f-PanningFB));
|
||||
drysend[SIDE_LEFT] = DryMix * aluSqrt((1.0f-PanningLR)*( PanningFB));
|
||||
drysend[SIDE_RIGHT] = DryMix * aluSqrt(( PanningLR)*( PanningFB));
|
||||
drysend[BACK_LEFT] = 0.0f;
|
||||
drysend[BACK_RIGHT] = 0.0f;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
ALfloat gain = SpeakerGain[s]*DirGain + AmbientGain;
|
||||
drysend[s] = DryMix * gain;
|
||||
}
|
||||
*wetsend = WetMix;
|
||||
|
||||
// Update filter coefficients. Calculations based on the I3DL2 spec.
|
||||
cw = cos(2.0f*3.141592654f * LOWPASSFREQCUTOFF / ALContext->Frequency);
|
||||
// We 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.
|
||||
// Be careful with gains < 0.0001, as that causes the coefficient to
|
||||
// head towards 1, which will flatten the signal
|
||||
g = aluSqrt(__max(DryGainHF, 0.0001f));
|
||||
a = 0.0f;
|
||||
if(g < 0.9999f) // 1-epsilon
|
||||
a = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
|
||||
ALSource->iirFilter.coeff = a;
|
||||
|
||||
g = aluSqrt(__max(WetGainHF, 0.0001f));
|
||||
a = 0.0f;
|
||||
if(g < 0.9999f) // 1-epsilon
|
||||
a = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
|
||||
ALSource->Send[0].iirFilter.coeff = a;
|
||||
|
||||
*drygainhf = DryGainHF;
|
||||
*wetgainhf = WetGainHF;
|
||||
}
|
||||
@@ -594,16 +858,28 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
|
||||
//1. Multi-channel buffers always play "normal"
|
||||
pitch[0] = ALSource->flPitch;
|
||||
|
||||
drysend[FRONT_LEFT] = SourceVolume * ListenerGain;
|
||||
drysend[FRONT_RIGHT] = SourceVolume * ListenerGain;
|
||||
drysend[SIDE_LEFT] = SourceVolume * ListenerGain;
|
||||
drysend[SIDE_RIGHT] = SourceVolume * ListenerGain;
|
||||
drysend[BACK_LEFT] = SourceVolume * ListenerGain;
|
||||
drysend[BACK_RIGHT] = SourceVolume * ListenerGain;
|
||||
drysend[CENTER] = SourceVolume * ListenerGain;
|
||||
drysend[LFE] = SourceVolume * ListenerGain;
|
||||
*wetsend = 0.0f;
|
||||
WetGainHF = 1.0f;
|
||||
DryMix = SourceVolume;
|
||||
DryMix = __min(DryMix,MaxVolume);
|
||||
DryMix = __max(DryMix,MinVolume);
|
||||
|
||||
switch(ALSource->DirectFilter.type)
|
||||
{
|
||||
case AL_FILTER_LOWPASS:
|
||||
DryMix *= ALSource->DirectFilter.Gain;
|
||||
DryGainHF *= ALSource->DirectFilter.GainHF;
|
||||
break;
|
||||
}
|
||||
|
||||
drysend[FRONT_LEFT] = DryMix * ListenerGain;
|
||||
drysend[FRONT_RIGHT] = DryMix * ListenerGain;
|
||||
drysend[SIDE_LEFT] = DryMix * ListenerGain;
|
||||
drysend[SIDE_RIGHT] = DryMix * ListenerGain;
|
||||
drysend[BACK_LEFT] = DryMix * ListenerGain;
|
||||
drysend[BACK_RIGHT] = DryMix * ListenerGain;
|
||||
drysend[FRONT_CENTER] = DryMix * ListenerGain;
|
||||
drysend[BACK_CENTER] = DryMix * ListenerGain;
|
||||
drysend[LFE] = DryMix * ListenerGain;
|
||||
*wetsend = 0.0f;
|
||||
|
||||
*drygainhf = DryGainHF;
|
||||
*wetgainhf = WetGainHF;
|
||||
@@ -619,6 +895,7 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
|
||||
{
|
||||
static float DryBuffer[BUFFERSIZE][OUTPUTCHANNELS];
|
||||
static float WetBuffer[BUFFERSIZE];
|
||||
ALfloat (*Matrix)[OUTPUTCHANNELS] = ALContext->ChannelMatrix;
|
||||
ALfloat newDrySend[OUTPUTCHANNELS] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
ALfloat newWetSend = 0.0f;
|
||||
ALfloat DryGainHF = 0.0f;
|
||||
@@ -639,9 +916,11 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
|
||||
ALsource *ALSource;
|
||||
ALbuffer *ALBuffer;
|
||||
ALeffectslot *ALEffectSlot;
|
||||
ALfloat values[OUTPUTCHANNELS];
|
||||
ALfloat value;
|
||||
ALshort *Data;
|
||||
ALuint i,j,k;
|
||||
ALuint i,j,k,out;
|
||||
ALfloat cw, a, g;
|
||||
ALbufferlistitem *BufferListItem;
|
||||
ALuint loop;
|
||||
ALint64 DataSize64,DataPos64;
|
||||
@@ -715,19 +994,70 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
|
||||
if(DataPosInt >= DataSize)
|
||||
goto skipmix;
|
||||
|
||||
CalcSourceParams(ALContext, ALSource,
|
||||
(Channels==1) ? AL_TRUE : AL_FALSE,
|
||||
format, newDrySend, &newWetSend, &Pitch,
|
||||
&DryGainHF, &WetGainHF);
|
||||
|
||||
Pitch = (Pitch*Frequency) / ALContext->Frequency;
|
||||
|
||||
//Get source info
|
||||
DryFilter = &ALSource->iirFilter;
|
||||
WetFilter = &ALSource->Send[0].iirFilter;
|
||||
DrySend = ALSource->DryGains;
|
||||
WetSend = &ALSource->WetGain;
|
||||
|
||||
CalcSourceParams(ALContext, ALSource,
|
||||
(Channels==1) ? AL_TRUE : AL_FALSE,
|
||||
newDrySend, &newWetSend, &Pitch,
|
||||
&DryGainHF, &WetGainHF);
|
||||
Pitch = (Pitch*Frequency) / ALContext->Frequency;
|
||||
|
||||
if(Channels == 1)
|
||||
{
|
||||
// Update filter coefficients. Calculations based on
|
||||
// the I3DL2 spec.
|
||||
cw = cos(2.0*M_PI * LOWPASSFREQCUTOFF / ALContext->Frequency);
|
||||
// We 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.
|
||||
// Be careful with gains < 0.0001, as that causes the
|
||||
// coefficient to head towards 1, which will flatten
|
||||
// the signal
|
||||
g = aluSqrt(__max(DryGainHF, 0.0001f));
|
||||
a = 0.0f;
|
||||
if(g < 0.9999f) // 1-epsilon
|
||||
a = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
|
||||
DryFilter->coeff = a;
|
||||
|
||||
g = aluSqrt(__max(WetGainHF, 0.0001f));
|
||||
a = 0.0f;
|
||||
if(g < 0.9999f) // 1-epsilon
|
||||
a = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
|
||||
WetFilter->coeff = a;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Multi-channel sources use two chained one-pole
|
||||
// filters, so take the base gain (square root of the
|
||||
// squared gain)
|
||||
cw = cos(2.0*M_PI * LOWPASSFREQCUTOFF / ALContext->Frequency);
|
||||
g = __max(DryGainHF, 0.01f);
|
||||
a = 0.0f;
|
||||
if(g < 0.9999f) // 1-epsilon
|
||||
a = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
|
||||
DryFilter->coeff = a;
|
||||
WetFilter->coeff = 0.0f;
|
||||
|
||||
if(DuplicateStereo && Channels == 2)
|
||||
{
|
||||
Matrix[FRONT_LEFT][SIDE_LEFT] = 1.0f;
|
||||
Matrix[FRONT_RIGHT][SIDE_RIGHT] = 1.0f;
|
||||
Matrix[FRONT_LEFT][BACK_LEFT] = 1.0f;
|
||||
Matrix[FRONT_RIGHT][BACK_RIGHT] = 1.0f;
|
||||
}
|
||||
else if(DuplicateStereo)
|
||||
{
|
||||
Matrix[FRONT_LEFT][SIDE_LEFT] = 0.0f;
|
||||
Matrix[FRONT_RIGHT][SIDE_RIGHT] = 0.0f;
|
||||
Matrix[FRONT_LEFT][BACK_LEFT] = 0.0f;
|
||||
Matrix[FRONT_RIGHT][BACK_RIGHT] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
//Compute the gain steps for each output channel
|
||||
if(ALSource->FirstStart && DataPosInt == 0 && DataPosFrac == 0)
|
||||
{
|
||||
@@ -796,87 +1126,130 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
|
||||
//Actual sample mixing loop
|
||||
k = 0;
|
||||
Data += DataPosInt*Channels;
|
||||
while(BufferSize--)
|
||||
{
|
||||
for(i = 0;i < OUTPUTCHANNELS;i++)
|
||||
DrySend[i] += dryGainStep[i];
|
||||
*WetSend += wetGainStep;
|
||||
|
||||
if(Channels==1)
|
||||
if(Channels == 1) /* Mono */
|
||||
{
|
||||
ALfloat outsamp;
|
||||
|
||||
while(BufferSize--)
|
||||
{
|
||||
ALfloat sample, outsamp;
|
||||
for(i = 0;i < OUTPUTCHANNELS;i++)
|
||||
DrySend[i] += dryGainStep[i];
|
||||
*WetSend += wetGainStep;
|
||||
|
||||
//First order interpolator
|
||||
sample = lerp(Data[k], Data[k+1], DataPosFrac);
|
||||
value = lerp(Data[k], Data[k+1], DataPosFrac);
|
||||
|
||||
//Direct path final mix buffer and panning
|
||||
outsamp = lpFilter(DryFilter, sample);
|
||||
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];
|
||||
outsamp = lpFilter(DryFilter, 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
|
||||
outsamp = lpFilter(WetFilter, sample);
|
||||
outsamp = lpFilter(WetFilter, value);
|
||||
WetBuffer[j] += outsamp*(*WetSend);
|
||||
|
||||
DataPosFrac += increment;
|
||||
k += DataPosFrac>>FRACTIONBITS;
|
||||
DataPosFrac &= FRACTIONMASK;
|
||||
j++;
|
||||
}
|
||||
else
|
||||
}
|
||||
else if(Channels == 2) /* Stereo */
|
||||
{
|
||||
const int chans[] = {
|
||||
FRONT_LEFT, FRONT_RIGHT
|
||||
};
|
||||
|
||||
#define DO_MIX() do { \
|
||||
*WetSend += wetGainStep*BufferSize; \
|
||||
while(BufferSize--) \
|
||||
{ \
|
||||
for(i = 0;i < OUTPUTCHANNELS;i++) \
|
||||
DrySend[i] += dryGainStep[i]; \
|
||||
\
|
||||
for(i = 0;i < Channels;i++) \
|
||||
{ \
|
||||
value = lerp(Data[k*Channels + i], Data[(k+1)*Channels + i], DataPosFrac); \
|
||||
values[i] = lpFilterMC(DryFilter, chans[i], value)*DrySend[chans[i]]; \
|
||||
} \
|
||||
for(out = 0;out < OUTPUTCHANNELS;out++) \
|
||||
{ \
|
||||
ALfloat sum = 0.0f; \
|
||||
for(i = 0;i < Channels;i++) \
|
||||
sum += values[i]*Matrix[chans[i]][out]; \
|
||||
DryBuffer[j][out] += sum; \
|
||||
} \
|
||||
\
|
||||
DataPosFrac += increment; \
|
||||
k += DataPosFrac>>FRACTIONBITS; \
|
||||
DataPosFrac &= FRACTIONMASK; \
|
||||
j++; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
DO_MIX();
|
||||
}
|
||||
else if(Channels == 4) /* Quad */
|
||||
{
|
||||
const int chans[] = {
|
||||
FRONT_LEFT, FRONT_RIGHT,
|
||||
BACK_LEFT, BACK_RIGHT
|
||||
};
|
||||
|
||||
DO_MIX();
|
||||
}
|
||||
else if(Channels == 6) /* 5.1 */
|
||||
{
|
||||
const int chans[] = {
|
||||
FRONT_LEFT, FRONT_RIGHT,
|
||||
FRONT_CENTER, LFE,
|
||||
BACK_LEFT, BACK_RIGHT
|
||||
};
|
||||
|
||||
DO_MIX();
|
||||
}
|
||||
else if(Channels == 7) /* 6.1 */
|
||||
{
|
||||
const int chans[] = {
|
||||
FRONT_LEFT, FRONT_RIGHT,
|
||||
FRONT_CENTER, LFE,
|
||||
BACK_CENTER,
|
||||
SIDE_LEFT, SIDE_RIGHT
|
||||
};
|
||||
|
||||
DO_MIX();
|
||||
}
|
||||
else if(Channels == 8) /* 7.1 */
|
||||
{
|
||||
const int chans[] = {
|
||||
FRONT_LEFT, FRONT_RIGHT,
|
||||
FRONT_CENTER, LFE,
|
||||
BACK_LEFT, BACK_RIGHT,
|
||||
SIDE_LEFT, SIDE_RIGHT
|
||||
};
|
||||
|
||||
DO_MIX();
|
||||
#undef DO_MIX
|
||||
}
|
||||
else /* Unknown? */
|
||||
{
|
||||
*WetSend += wetGainStep*BufferSize;
|
||||
for(i = 0;i < OUTPUTCHANNELS;i++)
|
||||
DrySend[i] += dryGainStep[i]*BufferSize;
|
||||
while(BufferSize--)
|
||||
{
|
||||
ALfloat samp1, samp2;
|
||||
//First order interpolator (front left)
|
||||
samp1 = lerp(Data[k*Channels], Data[(k+1)*Channels], DataPosFrac);
|
||||
DryBuffer[j][FRONT_LEFT] += samp1*DrySend[FRONT_LEFT];
|
||||
//First order interpolator (front right)
|
||||
samp2 = lerp(Data[k*Channels+1], Data[(k+1)*Channels+1], DataPosFrac);
|
||||
DryBuffer[j][FRONT_RIGHT] += samp2*DrySend[FRONT_RIGHT];
|
||||
if(Channels >= 4)
|
||||
{
|
||||
int i = 2;
|
||||
if(Channels >= 6)
|
||||
{
|
||||
if(Channels != 7)
|
||||
{
|
||||
//First order interpolator (center)
|
||||
value = lerp(Data[k*Channels+i], Data[(k+1)*Channels+i], DataPosFrac);
|
||||
DryBuffer[j][CENTER] += value*DrySend[CENTER];
|
||||
i++;
|
||||
}
|
||||
//First order interpolator (lfe)
|
||||
value = lerp(Data[k*Channels+i], Data[(k+1)*Channels+i], DataPosFrac);
|
||||
DryBuffer[j][LFE] += value*DrySend[LFE];
|
||||
i++;
|
||||
}
|
||||
//First order interpolator (back left)
|
||||
value = lerp(Data[k*Channels+i], Data[(k+1)*Channels+i], DataPosFrac);
|
||||
DryBuffer[j][BACK_LEFT] += value*DrySend[BACK_LEFT];
|
||||
i++;
|
||||
//First order interpolator (back right)
|
||||
value = lerp(Data[k*Channels+i], Data[(k+1)*Channels+i], DataPosFrac);
|
||||
DryBuffer[j][BACK_RIGHT] += value*DrySend[BACK_RIGHT];
|
||||
i++;
|
||||
if(Channels >= 7)
|
||||
{
|
||||
//First order interpolator (side left)
|
||||
value = lerp(Data[k*Channels+i], Data[(k+1)*Channels+i], DataPosFrac);
|
||||
DryBuffer[j][SIDE_LEFT] += value*DrySend[SIDE_LEFT];
|
||||
i++;
|
||||
//First order interpolator (side right)
|
||||
value = lerp(Data[k*Channels+i], Data[(k+1)*Channels+i], DataPosFrac);
|
||||
DryBuffer[j][SIDE_RIGHT] += value*DrySend[SIDE_RIGHT];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if(DuplicateStereo)
|
||||
{
|
||||
//Duplicate stereo channels on the back speakers
|
||||
DryBuffer[j][BACK_LEFT] += samp1*DrySend[BACK_LEFT];
|
||||
DryBuffer[j][BACK_RIGHT] += samp2*DrySend[BACK_RIGHT];
|
||||
}
|
||||
DataPosFrac += increment;
|
||||
k += DataPosFrac>>FRACTIONBITS;
|
||||
DataPosFrac &= FRACTIONMASK;
|
||||
j++;
|
||||
}
|
||||
DataPosFrac += increment;
|
||||
k += DataPosFrac>>FRACTIONBITS;
|
||||
DataPosFrac &= FRACTIONMASK;
|
||||
j++;
|
||||
}
|
||||
DataPosInt += k;
|
||||
|
||||
@@ -1021,14 +1394,14 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
|
||||
((ALubyte*)buffer)[0] = (ALubyte)((aluF2S(DryBuffer[i][FRONT_LEFT])>>8)+128);
|
||||
((ALubyte*)buffer)[1] = (ALubyte)((aluF2S(DryBuffer[i][FRONT_RIGHT])>>8)+128);
|
||||
#ifdef _WIN32 /* Of course, Windows can't use the same ordering... */
|
||||
((ALubyte*)buffer)[2] = (ALubyte)((aluF2S(DryBuffer[i][CENTER])>>8)+128);
|
||||
((ALubyte*)buffer)[2] = (ALubyte)((aluF2S(DryBuffer[i][FRONT_CENTER])>>8)+128);
|
||||
((ALubyte*)buffer)[3] = (ALubyte)((aluF2S(DryBuffer[i][LFE])>>8)+128);
|
||||
((ALubyte*)buffer)[4] = (ALubyte)((aluF2S(DryBuffer[i][BACK_LEFT])>>8)+128);
|
||||
((ALubyte*)buffer)[5] = (ALubyte)((aluF2S(DryBuffer[i][BACK_RIGHT])>>8)+128);
|
||||
#else
|
||||
((ALubyte*)buffer)[2] = (ALubyte)((aluF2S(DryBuffer[i][BACK_LEFT])>>8)+128);
|
||||
((ALubyte*)buffer)[3] = (ALubyte)((aluF2S(DryBuffer[i][BACK_RIGHT])>>8)+128);
|
||||
((ALubyte*)buffer)[4] = (ALubyte)((aluF2S(DryBuffer[i][CENTER])>>8)+128);
|
||||
((ALubyte*)buffer)[4] = (ALubyte)((aluF2S(DryBuffer[i][FRONT_CENTER])>>8)+128);
|
||||
((ALubyte*)buffer)[5] = (ALubyte)((aluF2S(DryBuffer[i][LFE])>>8)+128);
|
||||
#endif
|
||||
buffer = ((ALubyte*)buffer) + 6;
|
||||
@@ -1039,15 +1412,9 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
|
||||
{
|
||||
((ALubyte*)buffer)[0] = (ALubyte)((aluF2S(DryBuffer[i][FRONT_LEFT])>>8)+128);
|
||||
((ALubyte*)buffer)[1] = (ALubyte)((aluF2S(DryBuffer[i][FRONT_RIGHT])>>8)+128);
|
||||
#ifdef _WIN32
|
||||
((ALubyte*)buffer)[2] = (ALubyte)((aluF2S(DryBuffer[i][LFE])>>8)+128);
|
||||
((ALubyte*)buffer)[3] = (ALubyte)((aluF2S(DryBuffer[i][BACK_LEFT])>>8)+128);
|
||||
((ALubyte*)buffer)[4] = (ALubyte)((aluF2S(DryBuffer[i][BACK_RIGHT])>>8)+128);
|
||||
#else
|
||||
((ALubyte*)buffer)[2] = (ALubyte)((aluF2S(DryBuffer[i][BACK_LEFT])>>8)+128);
|
||||
((ALubyte*)buffer)[3] = (ALubyte)((aluF2S(DryBuffer[i][BACK_RIGHT])>>8)+128);
|
||||
((ALubyte*)buffer)[4] = (ALubyte)((aluF2S(DryBuffer[i][LFE])>>8)+128);
|
||||
#endif
|
||||
((ALubyte*)buffer)[2] = (ALubyte)((aluF2S(DryBuffer[i][FRONT_CENTER])>>8)+128);
|
||||
((ALubyte*)buffer)[3] = (ALubyte)((aluF2S(DryBuffer[i][LFE])>>8)+128);
|
||||
((ALubyte*)buffer)[4] = (ALubyte)((aluF2S(DryBuffer[i][BACK_CENTER])>>8)+128);
|
||||
((ALubyte*)buffer)[5] = (ALubyte)((aluF2S(DryBuffer[i][SIDE_LEFT])>>8)+128);
|
||||
((ALubyte*)buffer)[6] = (ALubyte)((aluF2S(DryBuffer[i][SIDE_RIGHT])>>8)+128);
|
||||
buffer = ((ALubyte*)buffer) + 7;
|
||||
@@ -1059,14 +1426,14 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
|
||||
((ALubyte*)buffer)[0] = (ALubyte)((aluF2S(DryBuffer[i][FRONT_LEFT])>>8)+128);
|
||||
((ALubyte*)buffer)[1] = (ALubyte)((aluF2S(DryBuffer[i][FRONT_RIGHT])>>8)+128);
|
||||
#ifdef _WIN32
|
||||
((ALubyte*)buffer)[2] = (ALubyte)((aluF2S(DryBuffer[i][CENTER])>>8)+128);
|
||||
((ALubyte*)buffer)[2] = (ALubyte)((aluF2S(DryBuffer[i][FRONT_CENTER])>>8)+128);
|
||||
((ALubyte*)buffer)[3] = (ALubyte)((aluF2S(DryBuffer[i][LFE])>>8)+128);
|
||||
((ALubyte*)buffer)[4] = (ALubyte)((aluF2S(DryBuffer[i][BACK_LEFT])>>8)+128);
|
||||
((ALubyte*)buffer)[5] = (ALubyte)((aluF2S(DryBuffer[i][BACK_RIGHT])>>8)+128);
|
||||
#else
|
||||
((ALubyte*)buffer)[2] = (ALubyte)((aluF2S(DryBuffer[i][BACK_LEFT])>>8)+128);
|
||||
((ALubyte*)buffer)[3] = (ALubyte)((aluF2S(DryBuffer[i][BACK_RIGHT])>>8)+128);
|
||||
((ALubyte*)buffer)[4] = (ALubyte)((aluF2S(DryBuffer[i][CENTER])>>8)+128);
|
||||
((ALubyte*)buffer)[4] = (ALubyte)((aluF2S(DryBuffer[i][FRONT_CENTER])>>8)+128);
|
||||
((ALubyte*)buffer)[5] = (ALubyte)((aluF2S(DryBuffer[i][LFE])>>8)+128);
|
||||
#endif
|
||||
((ALubyte*)buffer)[6] = (ALubyte)((aluF2S(DryBuffer[i][SIDE_LEFT])>>8)+128);
|
||||
@@ -1122,14 +1489,14 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
|
||||
((ALshort*)buffer)[0] = aluF2S(DryBuffer[i][FRONT_LEFT]);
|
||||
((ALshort*)buffer)[1] = aluF2S(DryBuffer[i][FRONT_RIGHT]);
|
||||
#ifdef _WIN32
|
||||
((ALshort*)buffer)[2] = aluF2S(DryBuffer[i][CENTER]);
|
||||
((ALshort*)buffer)[2] = aluF2S(DryBuffer[i][FRONT_CENTER]);
|
||||
((ALshort*)buffer)[3] = aluF2S(DryBuffer[i][LFE]);
|
||||
((ALshort*)buffer)[4] = aluF2S(DryBuffer[i][BACK_LEFT]);
|
||||
((ALshort*)buffer)[5] = aluF2S(DryBuffer[i][BACK_RIGHT]);
|
||||
#else
|
||||
((ALshort*)buffer)[2] = aluF2S(DryBuffer[i][BACK_LEFT]);
|
||||
((ALshort*)buffer)[3] = aluF2S(DryBuffer[i][BACK_RIGHT]);
|
||||
((ALshort*)buffer)[4] = aluF2S(DryBuffer[i][CENTER]);
|
||||
((ALshort*)buffer)[4] = aluF2S(DryBuffer[i][FRONT_CENTER]);
|
||||
((ALshort*)buffer)[5] = aluF2S(DryBuffer[i][LFE]);
|
||||
#endif
|
||||
buffer = ((ALshort*)buffer) + 6;
|
||||
@@ -1140,15 +1507,9 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
|
||||
{
|
||||
((ALshort*)buffer)[0] = aluF2S(DryBuffer[i][FRONT_LEFT]);
|
||||
((ALshort*)buffer)[1] = aluF2S(DryBuffer[i][FRONT_RIGHT]);
|
||||
#ifdef _WIN32
|
||||
((ALshort*)buffer)[2] = aluF2S(DryBuffer[i][LFE]);
|
||||
((ALshort*)buffer)[3] = aluF2S(DryBuffer[i][BACK_LEFT]);
|
||||
((ALshort*)buffer)[4] = aluF2S(DryBuffer[i][BACK_RIGHT]);
|
||||
#else
|
||||
((ALshort*)buffer)[2] = aluF2S(DryBuffer[i][BACK_LEFT]);
|
||||
((ALshort*)buffer)[3] = aluF2S(DryBuffer[i][BACK_RIGHT]);
|
||||
((ALshort*)buffer)[4] = aluF2S(DryBuffer[i][LFE]);
|
||||
#endif
|
||||
((ALshort*)buffer)[2] = aluF2S(DryBuffer[i][FRONT_CENTER]);
|
||||
((ALshort*)buffer)[3] = aluF2S(DryBuffer[i][LFE]);
|
||||
((ALshort*)buffer)[4] = aluF2S(DryBuffer[i][BACK_CENTER]);
|
||||
((ALshort*)buffer)[5] = aluF2S(DryBuffer[i][SIDE_LEFT]);
|
||||
((ALshort*)buffer)[6] = aluF2S(DryBuffer[i][SIDE_RIGHT]);
|
||||
buffer = ((ALshort*)buffer) + 7;
|
||||
@@ -1160,14 +1521,14 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
|
||||
((ALshort*)buffer)[0] = aluF2S(DryBuffer[i][FRONT_LEFT]);
|
||||
((ALshort*)buffer)[1] = aluF2S(DryBuffer[i][FRONT_RIGHT]);
|
||||
#ifdef _WIN32
|
||||
((ALshort*)buffer)[2] = aluF2S(DryBuffer[i][CENTER]);
|
||||
((ALshort*)buffer)[2] = aluF2S(DryBuffer[i][FRONT_CENTER]);
|
||||
((ALshort*)buffer)[3] = aluF2S(DryBuffer[i][LFE]);
|
||||
((ALshort*)buffer)[4] = aluF2S(DryBuffer[i][BACK_LEFT]);
|
||||
((ALshort*)buffer)[5] = aluF2S(DryBuffer[i][BACK_RIGHT]);
|
||||
#else
|
||||
((ALshort*)buffer)[2] = aluF2S(DryBuffer[i][BACK_LEFT]);
|
||||
((ALshort*)buffer)[3] = aluF2S(DryBuffer[i][BACK_RIGHT]);
|
||||
((ALshort*)buffer)[4] = aluF2S(DryBuffer[i][CENTER]);
|
||||
((ALshort*)buffer)[4] = aluF2S(DryBuffer[i][FRONT_CENTER]);
|
||||
((ALshort*)buffer)[5] = aluF2S(DryBuffer[i][LFE]);
|
||||
#endif
|
||||
((ALshort*)buffer)[6] = aluF2S(DryBuffer[i][SIDE_LEFT]);
|
||||
|
||||
+316
-215
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2008 by Christopher Fitzgerald.
|
||||
* Reverb for the OpenAL cross platform audio library
|
||||
* Copyright (C) 2008-2009 by Christopher Fitzgerald.
|
||||
* 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
|
||||
@@ -46,8 +46,8 @@
|
||||
|
||||
typedef struct DelayLine
|
||||
{
|
||||
// The delay lines use lengths that are powers of 2 to allow bitmasking
|
||||
// instead of modulus wrapping.
|
||||
// The delay lines use sample lengths that are powers of 2 to allow
|
||||
// bitmasking instead of modulus wrapping.
|
||||
ALuint Mask;
|
||||
ALfloat *Line;
|
||||
} DelayLine;
|
||||
@@ -55,15 +55,15 @@ typedef struct DelayLine
|
||||
struct ALverbState
|
||||
{
|
||||
// All delay lines are allocated as a single buffer to reduce memory
|
||||
// fragmentation and teardown code.
|
||||
// fragmentation and management code.
|
||||
ALfloat *SampleBuffer;
|
||||
// Master reverb gain.
|
||||
// Master effect gain.
|
||||
ALfloat Gain;
|
||||
// Initial reverb delay.
|
||||
// Initial effect delay and decorrelation.
|
||||
DelayLine Delay;
|
||||
// The tap points for the initial delay. First tap goes to early
|
||||
// reflections, the second to late reverb.
|
||||
ALuint Tap[2];
|
||||
// reflections, the last four decorrelate to late reverb.
|
||||
ALuint Tap[5];
|
||||
struct {
|
||||
// Gain for early reflections.
|
||||
ALfloat Gain;
|
||||
@@ -75,42 +75,64 @@ struct ALverbState
|
||||
struct {
|
||||
// Gain for late reverb.
|
||||
ALfloat Gain;
|
||||
// Diffusion of late reverb.
|
||||
ALfloat Diffusion;
|
||||
// Late reverb is done with 8 delay lines.
|
||||
ALfloat Coeff[8];
|
||||
DelayLine Delay[8];
|
||||
ALuint Offset[8];
|
||||
// The input and last 4 delay lines are low-pass filtered.
|
||||
ALfloat LpCoeff[5];
|
||||
ALfloat LpSample[5];
|
||||
// Attenuation to compensate for modal density and decay rate.
|
||||
ALfloat DensityGain;
|
||||
// The feed-back and feed-forward all-pass coefficient.
|
||||
ALfloat ApFeedCoeff;
|
||||
// Mixing matrix coefficient.
|
||||
ALfloat MixCoeff;
|
||||
// Late reverb has 4 parallel all-pass filters.
|
||||
ALfloat ApCoeff[4];
|
||||
DelayLine ApDelay[4];
|
||||
ALuint ApOffset[4];
|
||||
// In addition to 4 cyclical delay lines.
|
||||
ALfloat Coeff[4];
|
||||
DelayLine Delay[4];
|
||||
ALuint Offset[4];
|
||||
// The cyclical delay lines are low-pass filtered.
|
||||
ALfloat LpCoeff[4][2];
|
||||
ALfloat LpSample[4];
|
||||
} Late;
|
||||
// The current read offset for all delay lines.
|
||||
ALuint Offset;
|
||||
};
|
||||
|
||||
// All delay line lengths are specified in seconds.
|
||||
|
||||
// The length of the initial delay line (a sum of the maximum delay before
|
||||
// early reflections and late reverb; 0.3 + 0.1).
|
||||
static const ALfloat MASTER_LINE_LENGTH = 0.4000f;
|
||||
|
||||
// The lengths of the early delay lines.
|
||||
static const ALfloat EARLY_LINE_LENGTH[4] =
|
||||
{
|
||||
0.0015f, 0.0045f, 0.0135f, 0.0405f
|
||||
};
|
||||
|
||||
// The lengths of the late delay lines.
|
||||
static const ALfloat LATE_LINE_LENGTH[8] =
|
||||
// The lengths of the late all-pass delay lines.
|
||||
static const ALfloat ALLPASS_LINE_LENGTH[4] =
|
||||
{
|
||||
0.0015f, 0.0037f, 0.0093f, 0.0234f,
|
||||
0.0100f, 0.0150f, 0.0225f, 0.0337f
|
||||
0.0151f, 0.0167f, 0.0183f, 0.0200f,
|
||||
};
|
||||
|
||||
// The last 4 late delay lines have a variable length dependent on the effect
|
||||
// density parameter and this multiplier.
|
||||
static const ALfloat LATE_LINE_MULTIPLIER = 9.0f;
|
||||
// The lengths of the late cyclical delay lines.
|
||||
static const ALfloat LATE_LINE_LENGTH[4] =
|
||||
{
|
||||
0.0211f, 0.0311f, 0.0461f, 0.0680f
|
||||
};
|
||||
|
||||
// The late cyclical delay lines have a variable length dependent on the
|
||||
// effect's density parameter (inverted for some reason) and this multiplier.
|
||||
static const ALfloat LATE_LINE_MULTIPLIER = 4.0f;
|
||||
|
||||
// Input into the late reverb is decorrelated between four channels. Their
|
||||
// timings are dependent on a fraction and multiplier. See VerbUpdate() for
|
||||
// the calculations involved.
|
||||
static const ALfloat DECO_FRACTION = 1.0f / 32.0f;
|
||||
static const ALfloat DECO_MULTIPLIER = 2.0f;
|
||||
|
||||
// The maximum length of initial delay for the master delay line (a sum of
|
||||
// the maximum early reflection and late reverb delays).
|
||||
static const ALfloat MASTER_LINE_LENGTH = 0.3f + 0.1f;
|
||||
|
||||
// Find the next power of 2. Actually, this will return the input value if
|
||||
// it is already a power of 2.
|
||||
static ALuint NextPowerOf2(ALuint value)
|
||||
{
|
||||
ALuint powerOf2 = 1;
|
||||
@@ -146,8 +168,8 @@ static __inline ALfloat EarlyDelayLineOut(ALverbState *State, ALuint index)
|
||||
State->Offset - State->Early.Offset[index]);
|
||||
}
|
||||
|
||||
// Given an input sample, this function produces a decorrelated stereo output
|
||||
// for early reflections.
|
||||
// Given an input sample, this function produces stereo output for early
|
||||
// reflections.
|
||||
static __inline ALvoid EarlyReflection(ALverbState *State, ALfloat in, ALfloat *out)
|
||||
{
|
||||
ALfloat d[4], v, f[4];
|
||||
@@ -161,11 +183,11 @@ static __inline ALvoid EarlyReflection(ALverbState *State, ALfloat in, ALfloat *
|
||||
/* The following uses a lossless scattering junction from waveguide
|
||||
* theory. It actually amounts to a householder mixing matrix, which
|
||||
* will produce a maximally diffuse response, and means this can probably
|
||||
* be considered a simple FDN.
|
||||
* be considered a simple feedback delay network (FDN).
|
||||
* N
|
||||
* ---
|
||||
* \
|
||||
* v = 2/N / di
|
||||
* v = 2/N / d_i
|
||||
* ---
|
||||
* i=1
|
||||
*/
|
||||
@@ -194,6 +216,20 @@ static __inline ALvoid EarlyReflection(ALverbState *State, ALfloat in, ALfloat *
|
||||
out[1] = State->Early.Gain * f[3];
|
||||
}
|
||||
|
||||
// All-pass input/output routine for late reverb.
|
||||
static __inline ALfloat LateAllPassInOut(ALverbState *State, ALuint index, ALfloat in)
|
||||
{
|
||||
ALfloat out;
|
||||
|
||||
out = State->Late.ApCoeff[index] *
|
||||
DelayLineOut(&State->Late.ApDelay[index],
|
||||
State->Offset - State->Late.ApOffset[index]);
|
||||
out -= (State->Late.ApFeedCoeff * in);
|
||||
DelayLineIn(&State->Late.ApDelay[index], State->Offset,
|
||||
(State->Late.ApFeedCoeff * out) + in);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Delay line output routine for late reverb.
|
||||
static __inline ALfloat LateDelayLineOut(ALverbState *State, ALuint index)
|
||||
{
|
||||
@@ -205,94 +241,81 @@ static __inline ALfloat LateDelayLineOut(ALverbState *State, ALuint index)
|
||||
// Low-pass filter input/output routine for late reverb.
|
||||
static __inline ALfloat LateLowPassInOut(ALverbState *State, ALuint index, ALfloat in)
|
||||
{
|
||||
State->Late.LpSample[index] = in + ((State->Late.LpSample[index] - in) *
|
||||
State->Late.LpCoeff[index]);
|
||||
State->Late.LpSample[index] = (State->Late.LpCoeff[index][0] * in) +
|
||||
(State->Late.LpCoeff[index][1] * State->Late.LpSample[index]);
|
||||
return State->Late.LpSample[index];
|
||||
}
|
||||
|
||||
// Given an input sample, this function produces a decorrelated stereo output
|
||||
// for late reverb.
|
||||
static __inline ALvoid LateReverb(ALverbState *State, ALfloat in, ALfloat *out)
|
||||
// Given four decorrelated input samples, this function produces stereo
|
||||
// output for late reverb.
|
||||
static __inline ALvoid LateReverb(ALverbState *State, ALfloat *in, ALfloat *out)
|
||||
{
|
||||
ALfloat din, d[8], v, dv, f[8];
|
||||
ALfloat d[4], f[4];
|
||||
|
||||
// Since the input will be sent directly to the output as in the early
|
||||
// reflections function, it needs to take into account some immediate
|
||||
// absorption.
|
||||
in = LateLowPassInOut(State, 0, in);
|
||||
// Obtain the decayed results of the cyclical delay lines, and add the
|
||||
// corresponding input channels attenuated by density. Then pass the
|
||||
// results through the low-pass filters.
|
||||
d[0] = LateLowPassInOut(State, 0, (State->Late.DensityGain * in[0]) +
|
||||
LateDelayLineOut(State, 0));
|
||||
d[1] = LateLowPassInOut(State, 1, (State->Late.DensityGain * in[1]) +
|
||||
LateDelayLineOut(State, 1));
|
||||
d[2] = LateLowPassInOut(State, 2, (State->Late.DensityGain * in[2]) +
|
||||
LateDelayLineOut(State, 2));
|
||||
d[3] = LateLowPassInOut(State, 3, (State->Late.DensityGain * in[3]) +
|
||||
LateDelayLineOut(State, 3));
|
||||
|
||||
// When diffusion is full, no input is directly passed to the variable-
|
||||
// length delay lines (the last 4).
|
||||
din = (1.0f - State->Late.Diffusion) * in;
|
||||
// To help increase diffusion, run each line through an all-pass filter.
|
||||
// The order of the all-pass filters is selected so that the shortest
|
||||
// all-pass filter will feed the shortest delay line.
|
||||
d[0] = LateAllPassInOut(State, 1, d[0]);
|
||||
d[1] = LateAllPassInOut(State, 3, d[1]);
|
||||
d[2] = LateAllPassInOut(State, 0, d[2]);
|
||||
d[3] = LateAllPassInOut(State, 2, d[3]);
|
||||
|
||||
// Obtain the decayed results of the fixed-length delay lines.
|
||||
d[0] = LateDelayLineOut(State, 0);
|
||||
d[1] = LateDelayLineOut(State, 1);
|
||||
d[2] = LateDelayLineOut(State, 2);
|
||||
d[3] = LateDelayLineOut(State, 3);
|
||||
// Obtain the decayed and low-pass filtered results of the variable-
|
||||
// length delay lines.
|
||||
d[4] = LateLowPassInOut(State, 1, LateDelayLineOut(State, 4));
|
||||
d[5] = LateLowPassInOut(State, 2, LateDelayLineOut(State, 5));
|
||||
d[6] = LateLowPassInOut(State, 3, LateDelayLineOut(State, 6));
|
||||
d[7] = LateLowPassInOut(State, 4, LateDelayLineOut(State, 7));
|
||||
/* Late reverb is done with a modified feedback delay network (FDN)
|
||||
* topology. Four input lines are each fed through their own all-pass
|
||||
* filter and then into the mixing matrix. The four outputs of the
|
||||
* mixing matrix are then cycled back to the inputs. Each output feeds
|
||||
* a different input to form a circlular feed cycle.
|
||||
*
|
||||
* The mixing matrix used is a 4D skew-symmetric rotation matrix derived
|
||||
* using a single unitary rotational parameter:
|
||||
*
|
||||
* [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
|
||||
* [ -a, d, c, -b ]
|
||||
* [ -b, -c, d, a ]
|
||||
* [ -c, b, -a, d ]
|
||||
*
|
||||
* The rotation is constructed from the effect's diffusion parameter,
|
||||
* yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y
|
||||
* with differing signs, and d is the coefficient x. The matrix is thus:
|
||||
*
|
||||
* [ x, y, -y, y ] x = 1 - (0.5 diffusion^3)
|
||||
* [ -y, x, y, y ] y = sqrt((1 - x^2) / 3)
|
||||
* [ y, -y, x, y ]
|
||||
* [ -y, -y, -y, x ]
|
||||
*
|
||||
* To reduce the number of multiplies, the x coefficient is applied with
|
||||
* the cyclical delay line coefficients. Thus only the y coefficient is
|
||||
* applied when mixing, and is modified to be: y / x.
|
||||
*/
|
||||
f[0] = d[0] + (State->Late.MixCoeff * ( d[1] - d[2] + d[3]));
|
||||
f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3]));
|
||||
f[2] = d[2] + (State->Late.MixCoeff * ( d[0] - d[1] + d[3]));
|
||||
f[3] = d[3] + (State->Late.MixCoeff * (-d[0] - d[1] - d[2]));
|
||||
|
||||
// The waveguide formula used in the early reflections function works
|
||||
// great for high diffusion, but it is not obviously paramerized to allow
|
||||
// a variable diffusion. With only limited time and resources, what
|
||||
// follows is the best variation of that formula I could come up with.
|
||||
// First, there are 8 delay lines used. The first 4 are fixed-length and
|
||||
// generate the highest density of the diffuse response. The last 4 are
|
||||
// variable-length, and are used to smooth out the diffuse response. The
|
||||
// density effect parameter alters their length. The inner two delay
|
||||
// lines of each group have their signs reversed (more about this later).
|
||||
v = (d[0] - d[1] - d[2] + d[3] +
|
||||
d[4] - d[5] - d[6] + d[7]) * 0.25f;
|
||||
// Diffusion is applied as a reduction of the junction pressure for all
|
||||
// branches. This presents two problems. When the diffusion factor (0
|
||||
// to 1) reaches 0.5, the average feed value is reduced (the junction
|
||||
// becomes lossy). Thus, at 0.5 the signal decays almost twice as fast
|
||||
// as it should. The second problem is the introduction of some
|
||||
// resonant frequencies (coloration). The reversed signs above are used
|
||||
// to help combat some of the coloration by adding variations along the
|
||||
// feed cycle.
|
||||
v *= State->Late.Diffusion;
|
||||
// Load the junction with the input. To reduce the noticeable echo of
|
||||
// the longer delay lines (the variable-length ones) the input is loaded
|
||||
// with the inverse of the effect diffusion. So at full diffusion, the
|
||||
// input is not applied to the last 4 delay lines. Input signs reversed
|
||||
// to balance the equation.
|
||||
dv = v + din;
|
||||
v += in;
|
||||
// Output is tapped at the input to the shortest two cyclical delay
|
||||
// lines, attenuated by the late reverb gain (which is attenuated by the
|
||||
// mixing coefficient x).
|
||||
out[0] = State->Late.Gain * f[0];
|
||||
out[1] = State->Late.Gain * f[1];
|
||||
|
||||
// As with the reversed signs above, to balance the equation the signs
|
||||
// need to be reversed here, too.
|
||||
f[0] = d[0] - v;
|
||||
f[1] = d[1] + v;
|
||||
f[2] = d[2] + v;
|
||||
f[3] = d[3] - v;
|
||||
f[4] = d[4] - dv;
|
||||
f[5] = d[5] + dv;
|
||||
f[6] = d[6] + dv;
|
||||
f[7] = d[7] - dv;
|
||||
|
||||
// Feed the fixed-length delay lines with their own cycle (0 -> 1 -> 3 ->
|
||||
// 2 -> 0...).
|
||||
// The delay lines are fed circularly in the order:
|
||||
// 0 -> 1 -> 3 -> 2 -> 0 ...
|
||||
DelayLineIn(&State->Late.Delay[0], State->Offset, f[2]);
|
||||
DelayLineIn(&State->Late.Delay[1], State->Offset, f[0]);
|
||||
DelayLineIn(&State->Late.Delay[2], State->Offset, f[3]);
|
||||
DelayLineIn(&State->Late.Delay[3], State->Offset, f[1]);
|
||||
// Feed the variable-length delay lines with their cycle (4 -> 6 -> 7 ->
|
||||
// 5 -> 4...).
|
||||
DelayLineIn(&State->Late.Delay[4], State->Offset, f[5]);
|
||||
DelayLineIn(&State->Late.Delay[5], State->Offset, f[7]);
|
||||
DelayLineIn(&State->Late.Delay[6], State->Offset, f[4]);
|
||||
DelayLineIn(&State->Late.Delay[7], State->Offset, f[6]);
|
||||
|
||||
// Output is derived from the values fed to the inner two variable-length
|
||||
// delay lines (5 and 6).
|
||||
out[0] = State->Late.Gain * f[7];
|
||||
out[1] = State->Late.Gain * f[4];
|
||||
}
|
||||
|
||||
// This creates the reverb state. It should be called only when the reverb
|
||||
@@ -300,33 +323,46 @@ static __inline ALvoid LateReverb(ALverbState *State, ALfloat in, ALfloat *out)
|
||||
ALverbState *VerbCreate(ALCcontext *Context)
|
||||
{
|
||||
ALverbState *State = NULL;
|
||||
ALuint length[13], totalLength, index;
|
||||
ALuint samples, length[13], totalLength, index;
|
||||
|
||||
State = malloc(sizeof(ALverbState));
|
||||
if(!State)
|
||||
return NULL;
|
||||
|
||||
// All line lengths are powers of 2, calculated from the line timings and
|
||||
// the addition of an extra sample (for safety).
|
||||
length[0] = NextPowerOf2((ALuint)(MASTER_LINE_LENGTH*Context->Frequency) + 1);
|
||||
// All line lengths are powers of 2, calculated from their lengths, with
|
||||
// an additional sample in case of rounding errors.
|
||||
|
||||
// See VerbUpdate() for an explanation of the additional calculation
|
||||
// added to the master line length.
|
||||
samples = (ALuint)
|
||||
((MASTER_LINE_LENGTH +
|
||||
(LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER) *
|
||||
(DECO_FRACTION * ((DECO_MULTIPLIER * DECO_MULTIPLIER *
|
||||
DECO_MULTIPLIER) - 1.0f)))) *
|
||||
Context->Frequency) + 1;
|
||||
length[0] = NextPowerOf2(samples);
|
||||
totalLength = length[0];
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
length[1+index] = NextPowerOf2((ALuint)(EARLY_LINE_LENGTH[index]*Context->Frequency) + 1);
|
||||
totalLength += length[1+index];
|
||||
samples = (ALuint)(EARLY_LINE_LENGTH[index] * Context->Frequency) + 1;
|
||||
length[1 + index] = NextPowerOf2(samples);
|
||||
totalLength += length[1 + index];
|
||||
}
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
length[5+index] = NextPowerOf2((ALuint)(LATE_LINE_LENGTH[index]*Context->Frequency) + 1);
|
||||
totalLength += length[5+index];
|
||||
samples = (ALuint)(ALLPASS_LINE_LENGTH[index] * Context->Frequency) + 1;
|
||||
length[5 + index] = NextPowerOf2(samples);
|
||||
totalLength += length[5 + index];
|
||||
}
|
||||
for(index = 4;index < 8;index++)
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
length[5+index] = NextPowerOf2((ALuint)(LATE_LINE_LENGTH[index]*(1.0f + LATE_LINE_MULTIPLIER)*Context->Frequency) + 1);
|
||||
totalLength += length[5+index];
|
||||
samples = (ALuint)(LATE_LINE_LENGTH[index] *
|
||||
(1.0f + LATE_LINE_MULTIPLIER) * Context->Frequency) + 1;
|
||||
length[9 + index] = NextPowerOf2(samples);
|
||||
totalLength += length[9 + index];
|
||||
}
|
||||
|
||||
// They all share a single sample buffer.
|
||||
// All lines share a single sample buffer.
|
||||
State->SampleBuffer = malloc(totalLength * sizeof(ALfloat));
|
||||
if(!State->SampleBuffer)
|
||||
{
|
||||
@@ -344,10 +380,11 @@ ALverbState *VerbCreate(ALCcontext *Context)
|
||||
|
||||
State->Tap[0] = 0;
|
||||
State->Tap[1] = 0;
|
||||
State->Tap[2] = 0;
|
||||
State->Tap[3] = 0;
|
||||
State->Tap[4] = 0;
|
||||
|
||||
State->Early.Gain = 0.0f;
|
||||
// All fixed-length delay lines have their read-write offsets calculated
|
||||
// one time.
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
State->Early.Coeff[index] = 0.0f;
|
||||
@@ -355,30 +392,40 @@ ALverbState *VerbCreate(ALCcontext *Context)
|
||||
State->Early.Delay[index].Line = &State->SampleBuffer[totalLength];
|
||||
totalLength += length[1 + index];
|
||||
|
||||
State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] * Context->Frequency);
|
||||
// The early delay lines have their read offsets calculated once.
|
||||
State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] *
|
||||
Context->Frequency);
|
||||
}
|
||||
|
||||
State->Late.Gain = 0.0f;
|
||||
State->Late.Diffusion = 0.0f;
|
||||
for(index = 0;index < 8;index++)
|
||||
State->Late.DensityGain = 0.0f;
|
||||
State->Late.ApFeedCoeff = 0.0f;
|
||||
State->Late.MixCoeff = 0.0f;
|
||||
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
State->Late.Coeff[index] = 0.0f;
|
||||
State->Late.Delay[index].Mask = length[5 + index] - 1;
|
||||
State->Late.Delay[index].Line = &State->SampleBuffer[totalLength];
|
||||
State->Late.ApCoeff[index] = 0.0f;
|
||||
State->Late.ApDelay[index].Mask = length[5 + index] - 1;
|
||||
State->Late.ApDelay[index].Line = &State->SampleBuffer[totalLength];
|
||||
totalLength += length[5 + index];
|
||||
|
||||
// The late all-pass lines have their read offsets calculated once.
|
||||
State->Late.ApOffset[index] = (ALuint)(ALLPASS_LINE_LENGTH[index] *
|
||||
Context->Frequency);
|
||||
}
|
||||
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
State->Late.Coeff[index] = 0.0f;
|
||||
State->Late.Delay[index].Mask = length[9 + index] - 1;
|
||||
State->Late.Delay[index].Line = &State->SampleBuffer[totalLength];
|
||||
totalLength += length[9 + index];
|
||||
|
||||
State->Late.Offset[index] = 0;
|
||||
if(index < 4)
|
||||
{
|
||||
State->Late.Offset[index] = (ALuint)(LATE_LINE_LENGTH[index] * Context->Frequency);
|
||||
State->Late.LpCoeff[index] = 0.0f;
|
||||
State->Late.LpSample[index] = 0.0f;
|
||||
}
|
||||
else if(index == 4)
|
||||
{
|
||||
State->Late.LpCoeff[index] = 0.0f;
|
||||
State->Late.LpSample[index] = 0.0f;
|
||||
}
|
||||
|
||||
State->Late.LpCoeff[index][0] = 0.0f;
|
||||
State->Late.LpCoeff[index][1] = 0.0f;
|
||||
State->Late.LpSample[index] = 0.0f;
|
||||
}
|
||||
|
||||
State->Offset = 0;
|
||||
@@ -402,24 +449,37 @@ ALvoid VerbDestroy(ALverbState *State)
|
||||
ALvoid VerbUpdate(ALCcontext *Context, ALeffectslot *Slot, ALeffect *Effect)
|
||||
{
|
||||
ALverbState *State = Slot->ReverbState;
|
||||
ALuint index, index2;
|
||||
ALfloat length, lpcoeff, cw, g;
|
||||
ALuint index;
|
||||
ALfloat length, mixCoeff, cw, g, lpCoeff;
|
||||
ALfloat hfRatio = Effect->Reverb.DecayHFRatio;
|
||||
|
||||
// Calculate the master gain (from the slot and master reverb gain).
|
||||
// Calculate the master gain (from the slot and master effect gain).
|
||||
State->Gain = Slot->Gain * Effect->Reverb.Gain;
|
||||
|
||||
// Calculate the initial delay taps.
|
||||
length = Effect->Reverb.ReflectionsDelay;
|
||||
State->Tap[0] = (ALuint)(length * Context->Frequency);
|
||||
length += Effect->Reverb.LateReverbDelay;
|
||||
State->Tap[1] = (ALuint)(length * Context->Frequency);
|
||||
|
||||
// Calculate the early reflections gain. Right now this uses a gain of
|
||||
// 0.75 to compensate for the increase in density. It should probably
|
||||
// use a power (RMS) based measurement from the resulting distribution of
|
||||
// early delay lines.
|
||||
State->Early.Gain = Effect->Reverb.ReflectionsGain * 0.75f;
|
||||
length += Effect->Reverb.LateReverbDelay;
|
||||
|
||||
/* The four inputs to the late reverb are decorrelated to smooth the
|
||||
* initial reverb and reduce harsh echos. The timings are calculated as
|
||||
* multiples of a fraction of the smallest cyclical delay time. This
|
||||
* result is then adjusted so that the first tap occurs immediately (all
|
||||
* taps are reduced by the shortest fraction).
|
||||
*
|
||||
* offset[index] = ((FRACTION MULTIPLIER^index) - 1) delay
|
||||
*/
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
length += LATE_LINE_LENGTH[0] *
|
||||
(1.0f + (Effect->Reverb.Density * LATE_LINE_MULTIPLIER)) *
|
||||
(DECO_FRACTION * (pow(DECO_MULTIPLIER, (ALfloat)index) - 1.0f));
|
||||
State->Tap[1 + index] = (ALuint)(length * Context->Frequency);
|
||||
}
|
||||
|
||||
// Set the early reflections gain.
|
||||
State->Early.Gain = Effect->Reverb.ReflectionsGain;
|
||||
|
||||
// Calculate the gain (coefficient) for each early delay line.
|
||||
for(index = 0;index < 4;index++)
|
||||
@@ -427,29 +487,68 @@ ALvoid VerbUpdate(ALCcontext *Context, ALeffectslot *Slot, ALeffect *Effect)
|
||||
Effect->Reverb.LateReverbDelay *
|
||||
-60.0f / 20.0f);
|
||||
|
||||
// Calculate the late reverb gain, adjusted by density, diffusion, and
|
||||
// decay time. To be accurate, the adjustments should probably use power
|
||||
// measurements for each contribution, but they are not too bad as they
|
||||
// are.
|
||||
State->Late.Gain = Effect->Reverb.LateReverbGain *
|
||||
(0.45f + (0.55f * Effect->Reverb.Density)) *
|
||||
(1.0f - (0.25f * Effect->Reverb.Diffusion)) *
|
||||
(1.0f - (0.025f * Effect->Reverb.DecayTime));
|
||||
State->Late.Diffusion = Effect->Reverb.Diffusion;
|
||||
// Calculate the first mixing matrix coefficient (x).
|
||||
mixCoeff = 1.0f - (0.5f * pow(Effect->Reverb.Diffusion, 3.0f));
|
||||
|
||||
// Set the late reverb gain. Since the output is tapped prior to the
|
||||
// application of the delay line coefficients, this gain needs to be
|
||||
// attenuated by the mix coefficient from above.
|
||||
State->Late.Gain = Effect->Reverb.LateReverbGain * mixCoeff;
|
||||
|
||||
/* To compensate for changes in modal density and decay time of the late
|
||||
* reverb signal, the input is attenuated based on the maximal energy of
|
||||
* the outgoing signal. This is calculated as the ratio between a
|
||||
* reference value and the current approximation of energy for the output
|
||||
* signal.
|
||||
*
|
||||
* Reverb output matches exponential decay of the form Sum(a^n), where a
|
||||
* is the attenuation coefficient, and n is the sample ranging from 0 to
|
||||
* infinity. The signal energy can thus be approximated using the area
|
||||
* under this curve, calculated as: 1 / (1 - a).
|
||||
*
|
||||
* The reference energy is calculated from a signal at the lowest (effect
|
||||
* at 1.0) density with a decay time of one second.
|
||||
*
|
||||
* The coefficient is calculated as the average length of the cyclical
|
||||
* delay lines. This produces a better result than calculating the gain
|
||||
* for each line individually (most likely a side effect of diffusion).
|
||||
*
|
||||
* The final result is the square root of the ratio bound to a maximum
|
||||
* value of 1 (no amplification) and attenuated by 1 / sqrt(2) to
|
||||
* compensate for the four decorrelated inputs.
|
||||
*/
|
||||
length = (LATE_LINE_LENGTH[0] + LATE_LINE_LENGTH[1] +
|
||||
LATE_LINE_LENGTH[2] + LATE_LINE_LENGTH[3]);
|
||||
g = length * (1.0f + LATE_LINE_MULTIPLIER) * 0.25f;
|
||||
g = pow(10.0f, g * -60.0f / 20.0f);
|
||||
g = 1.0f / (1.0f - g);
|
||||
length *= 1.0f + (Effect->Reverb.Density * LATE_LINE_MULTIPLIER) * 0.25f;
|
||||
length = pow(10.0f, length / Effect->Reverb.DecayTime * -60.0f / 20.0f);
|
||||
length = 1.0f / (1.0f - length);
|
||||
State->Late.DensityGain = 0.707106f * __min(aluSqrt(g / length), 1.0f);
|
||||
|
||||
// Calculate the all-pass feed-back and feed-forward coefficient.
|
||||
State->Late.ApFeedCoeff = 0.6f * pow(Effect->Reverb.Diffusion, 3.0f);
|
||||
|
||||
// Calculate the mixing matrix coefficient (y / x).
|
||||
g = aluSqrt((1.0f - (mixCoeff * mixCoeff)) / 3.0f);
|
||||
State->Late.MixCoeff = g / mixCoeff;
|
||||
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
// Calculate the gain (coefficient) for each all-pass line.
|
||||
State->Late.ApCoeff[index] = pow(10.0f, ALLPASS_LINE_LENGTH[index] /
|
||||
Effect->Reverb.DecayTime *
|
||||
-60.0f / 20.0f);
|
||||
}
|
||||
|
||||
// The EFX specification does not make it clear whether the air
|
||||
// absorption parameter should always take effect. Both Generic Software
|
||||
// and Generic Hardware only apply it when HF limit is flagged, so that's
|
||||
// what is done here.
|
||||
// If the HF limit parameter is flagged, calculate an appropriate limit
|
||||
// based on the air absorption parameter.
|
||||
if(Effect->Reverb.DecayHFLimit && Effect->Reverb.AirAbsorptionGainHF < 1.0f)
|
||||
{
|
||||
ALfloat limitRatio;
|
||||
|
||||
// The following is my best guess at how to limit the HF ratio by the
|
||||
// air absorption parameter.
|
||||
// For each of the last 4 delays, find the attenuation due to air
|
||||
// For each of the cyclical delays, find the attenuation due to air
|
||||
// absorption in dB (converting delay time to meters using the speed
|
||||
// of sound). Then reversing the decay equation, solve for HF ratio.
|
||||
// The delay length is cancelled out of the equation, so it can be
|
||||
@@ -466,59 +565,58 @@ ALvoid VerbUpdate(ALCcontext *Context, ALeffectslot *Slot, ALeffect *Effect)
|
||||
hfRatio = __min(hfRatio, limitRatio);
|
||||
}
|
||||
|
||||
cw = cos(2.0f*3.141592654f * LOWPASSFREQCUTOFF / Context->Frequency);
|
||||
// Calculate the filter frequency for low-pass or high-pass depending on
|
||||
// whether the HF ratio is above 1.
|
||||
cw = 2.0f * M_PI * LOWPASSFREQCUTOFF / Context->Frequency;
|
||||
if(hfRatio > 1.0f)
|
||||
cw = M_PI - cw;
|
||||
cw = cos(cw);
|
||||
|
||||
for(index = 0;index < 8;index++)
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
// Calculate the length (in seconds) of each delay line.
|
||||
length = LATE_LINE_LENGTH[index];
|
||||
if(index >= 4)
|
||||
{
|
||||
// Calculate the delay offset for the variable-length delay
|
||||
// lines.
|
||||
length *= 1.0f + (Effect->Reverb.Density * LATE_LINE_MULTIPLIER);
|
||||
State->Late.Offset[index] = (ALuint)(length * Context->Frequency);
|
||||
}
|
||||
// Calculate the gain (coefficient) for each line.
|
||||
// Calculate the length (in seconds) of each cyclical delay line.
|
||||
length = LATE_LINE_LENGTH[index] * (1.0f + (Effect->Reverb.Density *
|
||||
LATE_LINE_MULTIPLIER));
|
||||
// Calculate the delay offset for the cyclical delay lines.
|
||||
State->Late.Offset[index] = (ALuint)(length * Context->Frequency);
|
||||
|
||||
// Calculate the gain (coefficient) for each cyclical line.
|
||||
State->Late.Coeff[index] = pow(10.0f, length / Effect->Reverb.DecayTime *
|
||||
-60.0f / 20.0f);
|
||||
if(index >= 4)
|
||||
{
|
||||
index2 = index - 3;
|
||||
|
||||
// Calculate the decay equation for each low-pass filter.
|
||||
g = pow(10.0f, length / (Effect->Reverb.DecayTime * hfRatio) *
|
||||
-60.0f / 20.0f) /
|
||||
State->Late.Coeff[index];
|
||||
g = __max(g, 0.1f);
|
||||
g *= g;
|
||||
// Calculate the gain (coefficient) for each low-pass filter.
|
||||
lpcoeff = 0.0f;
|
||||
if(g < 0.9999f) // 1-epsilon
|
||||
lpcoeff = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
|
||||
// Calculate the decay equation for each low-pass filter.
|
||||
g = pow(10.0f, length / (Effect->Reverb.DecayTime * hfRatio) *
|
||||
-60.0f / 20.0f);
|
||||
if (hfRatio > 1.0f)
|
||||
g = State->Late.Coeff[index] / g;
|
||||
else
|
||||
g = g / State->Late.Coeff[index];
|
||||
g = __max(g, 0.1f);
|
||||
g *= g;
|
||||
|
||||
// Very low decay times will produce minimal output, so apply an
|
||||
// upper bound to the coefficient.
|
||||
State->Late.LpCoeff[index2] = __min(lpcoeff, 0.98f);
|
||||
// Calculate the gain (coefficient) for each low-pass filter.
|
||||
lpCoeff = 0.0f;
|
||||
if(g < 0.9999f) // 1-epsilon
|
||||
lpCoeff = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
|
||||
|
||||
// Very low decay times will produce minimal output, so apply an
|
||||
// upper bound to the coefficient.
|
||||
lpCoeff = __min(lpCoeff, 0.98f);
|
||||
|
||||
// Calculate the filter coefficients for high-pass or low-pass
|
||||
// dependent on HF ratio being above 1.
|
||||
if(hfRatio > 1.0f) {
|
||||
State->Late.LpCoeff[index][0] = 1.0f + lpCoeff;
|
||||
State->Late.LpCoeff[index][1] = -lpCoeff;
|
||||
} else {
|
||||
State->Late.LpCoeff[index][0] = 1.0f - lpCoeff;
|
||||
State->Late.LpCoeff[index][1] = lpCoeff;
|
||||
}
|
||||
|
||||
// Attenuate the cyclical line coefficients by the mixing coefficient
|
||||
// (x).
|
||||
State->Late.Coeff[index] *= mixCoeff;
|
||||
}
|
||||
|
||||
// This just calculates the coefficient for the late reverb input low-
|
||||
// pass filter. It is calculated based the average (hence -30 instead
|
||||
// of -60) length of the inner two variable-length delay lines.
|
||||
length = LATE_LINE_LENGTH[5] * (1.0f + Effect->Reverb.Density * LATE_LINE_MULTIPLIER) +
|
||||
LATE_LINE_LENGTH[6] * (1.0f + Effect->Reverb.Density * LATE_LINE_MULTIPLIER);
|
||||
|
||||
g = pow(10.0f, ((length / (Effect->Reverb.DecayTime * hfRatio))-
|
||||
(length / Effect->Reverb.DecayTime)) * -30.0f / 20.0f);
|
||||
g = __max(g, 0.1f);
|
||||
g *= g;
|
||||
|
||||
lpcoeff = 0.0f;
|
||||
if(g < 0.9999f) // 1-epsilon
|
||||
lpcoeff = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
|
||||
|
||||
State->Late.LpCoeff[0] = __min(lpcoeff, 0.98f);
|
||||
}
|
||||
|
||||
// This processes the reverb state, given the input samples and an output
|
||||
@@ -526,7 +624,7 @@ ALvoid VerbUpdate(ALCcontext *Context, ALeffectslot *Slot, ALeffect *Effect)
|
||||
ALvoid VerbProcess(ALverbState *State, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS])
|
||||
{
|
||||
ALuint index;
|
||||
ALfloat in, early[2], late[2], out[2];
|
||||
ALfloat in[4], early[2], late[2], out[2];
|
||||
|
||||
for(index = 0;index < SamplesToDo;index++)
|
||||
{
|
||||
@@ -534,11 +632,14 @@ ALvoid VerbProcess(ALverbState *State, ALuint SamplesToDo, const ALfloat *Sample
|
||||
DelayLineIn(&State->Delay, State->Offset, SamplesIn[index]);
|
||||
|
||||
// Calculate the early reflection from the first delay tap.
|
||||
in = DelayLineOut(&State->Delay, State->Offset - State->Tap[0]);
|
||||
EarlyReflection(State, in, early);
|
||||
in[0] = DelayLineOut(&State->Delay, State->Offset - State->Tap[0]);
|
||||
EarlyReflection(State, in[0], early);
|
||||
|
||||
// Calculate the late reverb from the second delay tap.
|
||||
in = DelayLineOut(&State->Delay, State->Offset - State->Tap[1]);
|
||||
// Calculate the late reverb from the last four delay taps.
|
||||
in[0] = DelayLineOut(&State->Delay, State->Offset - State->Tap[1]);
|
||||
in[1] = DelayLineOut(&State->Delay, State->Offset - State->Tap[2]);
|
||||
in[2] = DelayLineOut(&State->Delay, State->Offset - State->Tap[3]);
|
||||
in[3] = DelayLineOut(&State->Delay, State->Offset - State->Tap[4]);
|
||||
LateReverb(State, in, late);
|
||||
|
||||
// Mix early reflections and late reverb.
|
||||
|
||||
+19
-34
@@ -103,11 +103,10 @@ MAKE_FUNC(snd_ctl_card_info_get_name);
|
||||
MAKE_FUNC(snd_card_next);
|
||||
#undef MAKE_FUNC
|
||||
|
||||
#define MAX_DEVICES 16
|
||||
#define MAX_ALL_DEVICES 32
|
||||
|
||||
static ALCchar *alsaDevice;
|
||||
static DevMap allDevNameMap[MAX_ALL_DEVICES];
|
||||
static ALCchar *alsaDeviceList[MAX_DEVICES];
|
||||
static DevMap allCaptureDevNameMap[MAX_ALL_DEVICES];
|
||||
|
||||
static int xrun_recovery(snd_pcm_t *handle, int err)
|
||||
@@ -321,6 +320,7 @@ static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceNam
|
||||
snd_pcm_hw_params_t *p = NULL;
|
||||
snd_pcm_access_t access;
|
||||
unsigned int periods;
|
||||
unsigned int rate;
|
||||
alsa_data *data;
|
||||
char driver[64];
|
||||
const char *str;
|
||||
@@ -348,21 +348,15 @@ static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceNam
|
||||
goto open_alsa;
|
||||
}
|
||||
}
|
||||
for(idx = 0;idx < MAX_DEVICES;idx++)
|
||||
if(strcmp(deviceName, alsaDevice) == 0)
|
||||
{
|
||||
if(alsaDeviceList[idx] &&
|
||||
strcmp(deviceName, alsaDeviceList[idx]) == 0)
|
||||
{
|
||||
device->szDeviceName = alsaDeviceList[idx];
|
||||
if(idx > 0)
|
||||
sprintf(driver, "hw:%zd,0", idx-1);
|
||||
goto open_alsa;
|
||||
}
|
||||
device->szDeviceName = alsaDevice;
|
||||
goto open_alsa;
|
||||
}
|
||||
return ALC_FALSE;
|
||||
}
|
||||
else
|
||||
device->szDeviceName = alsaDeviceList[0];
|
||||
device->szDeviceName = alsaDevice;
|
||||
|
||||
open_alsa:
|
||||
data = (alsa_data*)calloc(1, sizeof(alsa_data));
|
||||
@@ -401,6 +395,7 @@ open_alsa:
|
||||
|
||||
periods = GetConfigValueInt("alsa", "periods", 0);
|
||||
bufferSizeInFrames = device->UpdateSize;
|
||||
rate = device->Frequency;
|
||||
|
||||
str = GetConfigValue("alsa", "mmap", "true");
|
||||
allowmmap = (strcasecmp(str, "true") == 0 ||
|
||||
@@ -422,7 +417,7 @@ open_alsa:
|
||||
/* set periods (implicitly constrains period/buffer parameters) */
|
||||
(!periods || ok(psnd_pcm_hw_params_set_periods_near(data->pcmHandle, p, &periods, NULL), "set periods near")) &&
|
||||
/* set rate (implicitly constrains period/buffer parameters) */
|
||||
ok(psnd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &device->Frequency, NULL), "set rate near") &&
|
||||
ok(psnd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &rate, NULL), "set rate near") &&
|
||||
/* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
|
||||
ok(psnd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames), "set buffer size near") &&
|
||||
/* install and prepare hardware configuration */
|
||||
@@ -456,8 +451,6 @@ open_alsa:
|
||||
|
||||
psnd_pcm_hw_params_free(p);
|
||||
|
||||
device->UpdateSize = bufferSizeInFrames;
|
||||
|
||||
data->size = psnd_pcm_frames_to_bytes(data->pcmHandle, device->UpdateSize);
|
||||
if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
|
||||
{
|
||||
@@ -498,6 +491,9 @@ open_alsa:
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->UpdateSize = bufferSizeInFrames;
|
||||
device->Frequency = rate;
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
@@ -901,7 +897,7 @@ void alc_alsa_init(BackendFuncs *func_list)
|
||||
} while(0)
|
||||
#else
|
||||
str = NULL;
|
||||
alsa_handle = 0xDEADBEEF;
|
||||
alsa_handle = (void*)0xDEADBEEF;
|
||||
#define LOAD_FUNC(f) p##f = f
|
||||
#endif
|
||||
|
||||
@@ -962,15 +958,11 @@ LOAD_FUNC(snd_card_next);
|
||||
card = -1;
|
||||
if(psnd_card_next(&card) < 0 || card < 0)
|
||||
AL_PRINT("no playback cards found...\n");
|
||||
else
|
||||
{
|
||||
alsaDeviceList[0] = AppendDeviceList("ALSA Software on default");
|
||||
allDevNameMap[0].name = AppendAllDeviceList("ALSA Software on default");
|
||||
}
|
||||
|
||||
alsaDevice = AppendDeviceList("ALSA Software");
|
||||
allDevNameMap[0].name = AppendAllDeviceList("ALSA Software on default");
|
||||
|
||||
while (card >= 0) {
|
||||
int firstDev = 1;
|
||||
|
||||
sprintf(name, "hw:%d", card);
|
||||
if ((err = psnd_ctl_open(&handle, name, 0)) < 0) {
|
||||
AL_PRINT("control open (%i): %s\n", card, psnd_strerror(err));
|
||||
@@ -991,13 +983,6 @@ LOAD_FUNC(snd_card_next);
|
||||
if (dev < 0)
|
||||
break;
|
||||
|
||||
if(firstDev && card < MAX_DEVICES-1) {
|
||||
firstDev = 0;
|
||||
snprintf(name, sizeof(name), "ALSA Software on %s",
|
||||
psnd_ctl_card_info_get_name(info));
|
||||
alsaDeviceList[card+1] = AppendDeviceList(name);
|
||||
}
|
||||
|
||||
psnd_pcm_info_set_device(pcminfo, dev);
|
||||
psnd_pcm_info_set_subdevice(pcminfo, 0);
|
||||
psnd_pcm_info_set_stream(pcminfo, stream);
|
||||
@@ -1009,8 +994,8 @@ LOAD_FUNC(snd_card_next);
|
||||
|
||||
cname = psnd_ctl_card_info_get_name(info);
|
||||
dname = psnd_pcm_info_get_name(pcminfo);
|
||||
snprintf(name, sizeof(name), "ALSA Software on %s [%s]",
|
||||
cname, dname);
|
||||
snprintf(name, sizeof(name), "ALSA Software on %s [%s] (hw:%d,%d)",
|
||||
cname, dname, card, dev);
|
||||
allDevNameMap[idx].name = AppendAllDeviceList(name);
|
||||
allDevNameMap[idx].card = card;
|
||||
allDevNameMap[idx].dev = dev;
|
||||
@@ -1068,8 +1053,8 @@ next_card:
|
||||
|
||||
cname = psnd_ctl_card_info_get_name(info);
|
||||
dname = psnd_pcm_info_get_name(pcminfo);
|
||||
snprintf(name, sizeof(name), "ALSA Capture on %s [%s]",
|
||||
cname, dname);
|
||||
snprintf(name, sizeof(name), "ALSA Capture on %s [%s] (hw:%d,%d)",
|
||||
cname, dname, card, dev);
|
||||
allCaptureDevNameMap[idx].name = AppendCaptureDeviceList(name);
|
||||
allCaptureDevNameMap[idx].card = card;
|
||||
allCaptureDevNameMap[idx].dev = dev;
|
||||
|
||||
+36
-2
@@ -39,6 +39,10 @@
|
||||
|
||||
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
|
||||
|
||||
static void *ds_handle;
|
||||
static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter);
|
||||
static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
|
||||
|
||||
// Since DSound doesn't report the fragment size, emulate it
|
||||
static int num_frags;
|
||||
|
||||
@@ -136,6 +140,9 @@ static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceNam
|
||||
DWORD speakers;
|
||||
HRESULT hr;
|
||||
|
||||
if(ds_handle == NULL)
|
||||
return ALC_FALSE;
|
||||
|
||||
if(deviceName)
|
||||
{
|
||||
int i;
|
||||
@@ -167,7 +174,7 @@ static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceNam
|
||||
}
|
||||
|
||||
//DirectSound Init code
|
||||
hr = DirectSoundCreate(guid, &pData->lpDS, NULL);
|
||||
hr = pDirectSoundCreate(guid, &pData->lpDS, NULL);
|
||||
if(SUCCEEDED(hr))
|
||||
hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
|
||||
|
||||
@@ -411,10 +418,37 @@ void alcDSoundInit(BackendFuncs *FuncList)
|
||||
|
||||
*FuncList = DSoundFuncs;
|
||||
|
||||
#ifdef _WIN32
|
||||
ds_handle = LoadLibraryA("dsound.dll");
|
||||
if(ds_handle == NULL)
|
||||
{
|
||||
AL_PRINT("Failed to load dsound.dll\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = (void*)GetProcAddress((HMODULE)ds_handle, #f); \
|
||||
if(p##f == NULL) \
|
||||
{ \
|
||||
FreeLibrary(ds_handle); \
|
||||
ds_handle = NULL; \
|
||||
AL_PRINT("Could not load %s from dsound.dll\n", #f); \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
#else
|
||||
ds_handle = (void*)0xDEADBEEF;
|
||||
#define LOAD_FUNC(f) p##f = f
|
||||
#endif
|
||||
|
||||
LOAD_FUNC(DirectSoundCreate);
|
||||
LOAD_FUNC(DirectSoundEnumerateA);
|
||||
#undef LOAD_FUNC
|
||||
|
||||
num_frags = GetConfigValueInt("dsound", "periods", 4);
|
||||
if(num_frags < 2) num_frags = 2;
|
||||
|
||||
hr = DirectSoundEnumerate(DSoundEnumDevices, &iter);
|
||||
hr = pDirectSoundEnumerateA(DSoundEnumDevices, &iter);
|
||||
if(FAILED(hr))
|
||||
AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
|
||||
}
|
||||
|
||||
@@ -221,8 +221,6 @@ static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName
|
||||
}
|
||||
#undef ok
|
||||
|
||||
device->Frequency = ossSpeed;
|
||||
|
||||
if((int)aluChannelsFromFormat(device->Format) != numChannels)
|
||||
{
|
||||
AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), numChannels);
|
||||
@@ -240,8 +238,6 @@ static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->UpdateSize = info.fragsize / frameSize;
|
||||
|
||||
data->data_size = device->UpdateSize * frameSize;
|
||||
data->mix_data = calloc(1, data->data_size);
|
||||
|
||||
@@ -255,6 +251,9 @@ static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->Frequency = ossSpeed;
|
||||
device->UpdateSize = info.fragsize / frameSize;
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#ifdef HAVE_DLFCN_H
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <portaudio.h>
|
||||
|
||||
static void *pa_handle;
|
||||
#define MAKE_FUNC(x) static typeof(x) * p##x
|
||||
MAKE_FUNC(Pa_Initialize);
|
||||
MAKE_FUNC(Pa_GetErrorText);
|
||||
MAKE_FUNC(Pa_StartStream);
|
||||
MAKE_FUNC(Pa_StopStream);
|
||||
MAKE_FUNC(Pa_OpenStream);
|
||||
MAKE_FUNC(Pa_CloseStream);
|
||||
MAKE_FUNC(Pa_GetDefaultOutputDevice);
|
||||
#undef MAKE_FUNC
|
||||
|
||||
|
||||
static char *pa_device;
|
||||
|
||||
typedef struct {
|
||||
PaStream *stream;
|
||||
} pa_data;
|
||||
|
||||
|
||||
static int pa_callback(const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||
const PaStreamCallbackFlags statusFlags, void *userData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)userData;
|
||||
int frameSize;
|
||||
|
||||
(void)inputBuffer;
|
||||
(void)timeInfo;
|
||||
(void)statusFlags;
|
||||
|
||||
frameSize = aluBytesFromFormat(device->Format);
|
||||
frameSize *= aluChannelsFromFormat(device->Format);
|
||||
|
||||
SuspendContext(NULL);
|
||||
aluMixData(device->Context, outputBuffer, framesPerBuffer*frameSize, device->Format);
|
||||
ProcessContext(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
PaStreamParameters outParams;
|
||||
pa_data *data;
|
||||
int periods;
|
||||
PaError err;
|
||||
|
||||
if(pa_handle == NULL)
|
||||
return ALC_FALSE;
|
||||
|
||||
if(deviceName)
|
||||
{
|
||||
if(strcmp(deviceName, pa_device) != 0)
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->szDeviceName = pa_device;
|
||||
|
||||
data = (pa_data*)calloc(1, sizeof(pa_data));
|
||||
device->ExtraData = data;
|
||||
|
||||
outParams.device = GetConfigValueInt("port", "device", -1);
|
||||
if(outParams.device < 0)
|
||||
outParams.device = pPa_GetDefaultOutputDevice();
|
||||
outParams.suggestedLatency = (float)device->UpdateSize /
|
||||
(float)device->Frequency;
|
||||
outParams.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
switch(aluBytesFromFormat(device->Format))
|
||||
{
|
||||
case 1:
|
||||
outParams.sampleFormat = paUInt8;
|
||||
break;
|
||||
case 2:
|
||||
outParams.sampleFormat = paInt16;
|
||||
break;
|
||||
default:
|
||||
outParams.sampleFormat = -1;
|
||||
AL_PRINT("Unknown format?! %x\n", device->Format);
|
||||
}
|
||||
|
||||
periods = GetConfigValueInt("port", "periods", 4);
|
||||
if((int)periods <= 0)
|
||||
periods = 4;
|
||||
outParams.channelCount = aluChannelsFromFormat(device->Format);
|
||||
|
||||
err = pPa_OpenStream(&data->stream, NULL, &outParams, device->Frequency,
|
||||
device->UpdateSize/periods, paNoFlag,
|
||||
pa_callback, device);
|
||||
if(err != paNoError)
|
||||
{
|
||||
AL_PRINT("Pa_OpenStream() returned an error: %s\n", pPa_GetErrorText(err));
|
||||
device->ExtraData = NULL;
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
err = pPa_StartStream(data->stream);
|
||||
if(err != paNoError)
|
||||
{
|
||||
AL_PRINT("Pa_StartStream() returned an error: %s\n", pPa_GetErrorText(err));
|
||||
pPa_CloseStream(data->stream);
|
||||
device->ExtraData = NULL;
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->UpdateSize /= periods;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void pa_close_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = pPa_StopStream(data->stream);
|
||||
if(err != paNoError)
|
||||
fprintf(stderr, "Error stopping stream: %s\n", pPa_GetErrorText(err));
|
||||
|
||||
err = pPa_CloseStream(data->stream);
|
||||
if(err != paNoError)
|
||||
fprintf(stderr, "Error closing stream: %s\n", pPa_GetErrorText(err));
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean pa_open_capture(ALCdevice *device, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
|
||||
{
|
||||
return ALC_FALSE;
|
||||
(void)device;
|
||||
(void)deviceName;
|
||||
(void)frequency;
|
||||
(void)format;
|
||||
(void)SampleSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const BackendFuncs pa_funcs = {
|
||||
pa_open_playback,
|
||||
pa_close_playback,
|
||||
pa_open_capture,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
void alc_pa_init(BackendFuncs *func_list)
|
||||
{
|
||||
const char *str;
|
||||
PaError err;
|
||||
|
||||
*func_list = pa_funcs;
|
||||
|
||||
#ifdef HAVE_DLFCN_H
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
# define PALIB "libportaudio.2.dylib"
|
||||
#else
|
||||
# define PALIB "libportaudio.so.2"
|
||||
#endif
|
||||
pa_handle = dlopen(PALIB, RTLD_NOW);
|
||||
if(!pa_handle)
|
||||
return;
|
||||
dlerror();
|
||||
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = (typeof(f)*)dlsym(pa_handle, #f); \
|
||||
if((str=dlerror()) != NULL) \
|
||||
{ \
|
||||
dlclose(pa_handle); \
|
||||
pa_handle = NULL; \
|
||||
AL_PRINT("Could not load %s from "PALIB": %s\n", #f, str); \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
#else
|
||||
str = NULL;
|
||||
pa_handle = (void*)0xDEADBEEF;
|
||||
#define LOAD_FUNC(f) p##f = f
|
||||
#endif
|
||||
|
||||
LOAD_FUNC(Pa_Initialize);
|
||||
LOAD_FUNC(Pa_GetErrorText);
|
||||
LOAD_FUNC(Pa_StartStream);
|
||||
LOAD_FUNC(Pa_StopStream);
|
||||
LOAD_FUNC(Pa_OpenStream);
|
||||
LOAD_FUNC(Pa_CloseStream);
|
||||
LOAD_FUNC(Pa_GetDefaultOutputDevice);
|
||||
#undef LOAD_FUNC
|
||||
|
||||
if((err=pPa_Initialize()) != paNoError)
|
||||
{
|
||||
AL_PRINT("Pa_Initialize() returned an error: %s\n", pPa_GetErrorText(err));
|
||||
return;
|
||||
}
|
||||
|
||||
pa_device = AppendDeviceList("PortAudio Software");
|
||||
AppendAllDeviceList(pa_device);
|
||||
}
|
||||
+2
-1
@@ -50,6 +50,7 @@ static ALuint WaveProc(ALvoid *ptr)
|
||||
ALuint frameSize;
|
||||
ALuint now, last;
|
||||
size_t WriteCnt;
|
||||
size_t fs;
|
||||
ALuint avail;
|
||||
union {
|
||||
short s;
|
||||
@@ -89,7 +90,7 @@ static ALuint WaveProc(ALvoid *ptr)
|
||||
fputc(bytes[i^1], data->f);
|
||||
}
|
||||
else
|
||||
fwrite(data->buffer, frameSize, WriteCnt, data->f);
|
||||
fs = fwrite(data->buffer, frameSize, WriteCnt, data->f);
|
||||
if(ferror(data->f))
|
||||
{
|
||||
AL_PRINT("Error writing to file\n");
|
||||
|
||||
+47
-31
@@ -26,6 +26,7 @@ OPTION(OSS "Check for OSS backend" ON)
|
||||
OPTION(SOLARIS "Check for Solaris backend" ON)
|
||||
OPTION(DSOUND "Check for DirectSound backend" ON)
|
||||
OPTION(WINMM "Check for Windows Multimedia backend" ON)
|
||||
OPTION(PORTAUDIO "Check for PortAudio backend" ON)
|
||||
|
||||
OPTION(DLOPEN "Check for the dlopen API for loading optional libs" ON)
|
||||
|
||||
@@ -33,19 +34,17 @@ OPTION(WERROR "Treat compile warnings as errors" OFF)
|
||||
|
||||
OPTION(EXAMPLES "Build example programs" ON)
|
||||
|
||||
OPTION(XCOMPILEWIN32 "Cross-compile to Win32" OFF)
|
||||
|
||||
|
||||
IF(WIN32 OR XCOMPILEWIN32)
|
||||
SET(LIBNAME openal32)
|
||||
IF(WIN32)
|
||||
SET(LIBNAME OpenAL32)
|
||||
ADD_DEFINITIONS("-D_WIN32")
|
||||
ELSE()
|
||||
SET(LIBNAME openal)
|
||||
ENDIF()
|
||||
|
||||
SET(LIB_MAJOR_VERSION "1")
|
||||
SET(LIB_MINOR_VERSION "6")
|
||||
SET(LIB_BUILD_VERSION "372")
|
||||
SET(LIB_MINOR_VERSION "7")
|
||||
SET(LIB_BUILD_VERSION "411")
|
||||
SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_BUILD_VERSION}")
|
||||
IF(NOT DEFINED LIB_INSTALL_DIR)
|
||||
SET(LIB_INSTALL_DIR "lib")
|
||||
@@ -66,6 +65,11 @@ IF(NOT CMAKE_BUILD_TYPE)
|
||||
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel."
|
||||
FORCE)
|
||||
ENDIF()
|
||||
IF(NOT CMAKE_DEBUG_POSTFIX)
|
||||
SET(CMAKE_DEBUG_POSTFIX "" CACHE STRING
|
||||
"Library postfix for debug builds. Normally left blank."
|
||||
FORCE)
|
||||
ENDIF()
|
||||
|
||||
IF(MSVC)
|
||||
# ???
|
||||
@@ -76,7 +80,7 @@ IF(MSVC)
|
||||
ELSE()
|
||||
ADD_DEFINITIONS(-Wall)
|
||||
CHECK_C_COMPILER_FLAG(-Wextra HAVE_W_EXTRA)
|
||||
IF("${HAVE_W_EXTRA}")
|
||||
IF(HAVE_W_EXTRA)
|
||||
ADD_DEFINITIONS(-Wextra)
|
||||
ENDIF()
|
||||
|
||||
@@ -98,15 +102,15 @@ ELSE()
|
||||
FORCE)
|
||||
|
||||
# Set visibility options if available
|
||||
IF(NOT WIN32 AND NOT XCOMPILEWIN32)
|
||||
IF(NOT WIN32)
|
||||
CHECK_C_SOURCE_COMPILES("int foo() __attribute__((destructor));
|
||||
int main() {return 0;}" HAVE_GCC_DESTRUCTOR)
|
||||
|
||||
CHECK_C_COMPILER_FLAG(-fvisibility=hidden HAVE_VISIBILITY_SWITCH)
|
||||
IF("${HAVE_VISIBILITY_SWITCH}")
|
||||
IF(HAVE_VISIBILITY_SWITCH)
|
||||
CHECK_C_SOURCE_COMPILES("int foo() __attribute__((visibility(\"default\")));
|
||||
int main() {return 0;}" HAVE_GCC_VISIBILITY)
|
||||
IF("${HAVE_GCC_VISIBILITY}")
|
||||
IF(HAVE_GCC_VISIBILITY)
|
||||
ADD_DEFINITIONS(-fvisibility=hidden -DHAVE_GCC_VISIBILITY)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
@@ -118,10 +122,12 @@ CHECK_INCLUDE_FILE(float.h HAVE_FLOAT_H)
|
||||
|
||||
CHECK_LIBRARY_EXISTS(m sqrtf "" HAVE_SQRTF)
|
||||
CHECK_LIBRARY_EXISTS(m acosf "" HAVE_ACOSF)
|
||||
CHECK_LIBRARY_EXISTS(m atanf "" HAVE_ATANF)
|
||||
CHECK_LIBRARY_EXISTS(m fabsf "" HAVE_FABSF)
|
||||
IF(HAVE_FENV_H)
|
||||
CHECK_LIBRARY_EXISTS(m fesetround "" HAVE_FESETROUND)
|
||||
ENDIF()
|
||||
IF(HAVE_SQRTF OR HAVE_ACOSF OR HAVE_FESETROUND)
|
||||
IF(HAVE_SQRTF OR HAVE_ACOSF OR HAVE_ATANF OR HAVE_FABSF OR HAVE_FESETROUND)
|
||||
SET(EXTRA_LIBS m ${EXTRA_LIBS})
|
||||
ENDIF()
|
||||
CHECK_FUNCTION_EXISTS(strtof HAVE_STRTOF)
|
||||
@@ -283,16 +289,17 @@ ENDIF()
|
||||
IF(DSOUND)
|
||||
CHECK_INCLUDE_FILE(dsound.h HAVE_DSOUND_H)
|
||||
IF(HAVE_DSOUND_H)
|
||||
SET(HAVE_DSOUND 1)
|
||||
SET(ALC_OBJS ${ALC_OBJS} Alc/dsound.c)
|
||||
SET(BACKENDS "${BACKENDS} DirectSound,")
|
||||
CHECK_LIBRARY_EXISTS(dsound DirectSoundCreate "" HAVE_LIBDSOUND)
|
||||
IF(HAVE_LIBDSOUND OR WIN32)
|
||||
SET(HAVE_DSOUND 1)
|
||||
SET(ALC_OBJS ${ALC_OBJS} Alc/dsound.c)
|
||||
|
||||
SET(CMAKE_REQUIRED_LIBRARIES dsound)
|
||||
CHECK_C_SOURCE_COMPILES("int main() {return 0;}" HAVE_LIBDSOUND)
|
||||
SET(CMAKE_REQUIRED_LIBRARIES "")
|
||||
# CHECK_LIBRARY_EXISTS(dsound DirectSoundCreate "" HAVE_LIBDSOUND)
|
||||
IF(HAVE_LIBDSOUND)
|
||||
SET(EXTRA_LIBS dsound ${EXTRA_LIBS})
|
||||
IF(WIN32)
|
||||
SET(BACKENDS "${BACKENDS} DirectSound,")
|
||||
ELSE()
|
||||
SET(BACKENDS "${BACKENDS} DirectSound \(linked\),")
|
||||
SET(EXTRA_LIBS dsound ${EXTRA_LIBS})
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
@@ -315,6 +322,24 @@ IF(HAVE_WINDOWS_H)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
# Check PortAudio backend
|
||||
IF(PORTAUDIO)
|
||||
CHECK_INCLUDE_FILE(portaudio.h HAVE_PORTAUDIO_H)
|
||||
IF(HAVE_PORTAUDIO_H)
|
||||
CHECK_LIBRARY_EXISTS(portaudio Pa_Initialize "" HAVE_LIBPORTAUDIO)
|
||||
IF(HAVE_LIBPORTAUDIO)
|
||||
SET(HAVE_PORTAUDIO 1)
|
||||
SET(ALC_OBJS ${ALC_OBJS} Alc/portaudio.c)
|
||||
IF(HAVE_DLFCN_H)
|
||||
SET(BACKENDS "${BACKENDS} PortAudio,")
|
||||
ELSE()
|
||||
SET(BACKENDS "${BACKENDS} PortAudio \(linked\),")
|
||||
SET(EXTRA_LIBS portaudio ${EXTRA_LIBS})
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
# This is always available
|
||||
SET(BACKENDS "${BACKENDS} WaveFile")
|
||||
|
||||
@@ -347,15 +372,6 @@ SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES VERSION ${LIB_VERSION}
|
||||
IF(WIN32)
|
||||
SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES PREFIX "")
|
||||
ENDIF()
|
||||
IF(XCOMPILEWIN32)
|
||||
SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES PREFIX "" SUFFIX .dll)
|
||||
IF(EXAMPLES)
|
||||
SET(EXAMPLES OFF)
|
||||
MESSAGE(STATUS "")
|
||||
MESSAGE(STATUS "Building examples disabled when cross-compiling")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES OUTPUT_NAME ${LIBNAME})
|
||||
|
||||
TARGET_LINK_LIBRARIES(${LIBNAME} ${EXTRA_LIBS})
|
||||
|
||||
@@ -371,7 +387,7 @@ INSTALL(FILES include/AL/al.h
|
||||
DESTINATION include/AL
|
||||
)
|
||||
INSTALL(FILES "${OpenAL_BINARY_DIR}/admin/pkgconfig/openal.pc"
|
||||
DESTINATION lib/pkgconfig)
|
||||
DESTINATION "${LIB_INSTALL_DIR}/pkgconfig")
|
||||
|
||||
IF(EXAMPLES)
|
||||
ADD_EXECUTABLE(openal-info examples/openal-info.c)
|
||||
@@ -388,7 +404,7 @@ MESSAGE(STATUS "Building OpenAL with support for the following backends:")
|
||||
MESSAGE(STATUS " ${BACKENDS}")
|
||||
MESSAGE(STATUS "")
|
||||
|
||||
IF(WIN32 OR XCOMPILEWIN32)
|
||||
IF(WIN32)
|
||||
IF(NOT HAVE_DSOUND)
|
||||
MESSAGE(STATUS "WARNING: Building the Windows version without DirectSound output")
|
||||
MESSAGE(STATUS " This is probably NOT what you want!")
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
#define _AL_FILTER_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
ALfloat history[4];
|
||||
ALfloat history[OUTPUTCHANNELS*2];
|
||||
ALfloat coeff;
|
||||
} FILTER;
|
||||
|
||||
|
||||
@@ -129,6 +129,9 @@ extern char _alDebug[256];
|
||||
|
||||
#define LOWPASSFREQCUTOFF (5000)
|
||||
|
||||
#define QUADRANT_NUM 128
|
||||
#define LUT_NUM (4 * QUADRANT_NUM)
|
||||
|
||||
|
||||
typedef struct {
|
||||
ALCboolean (*OpenPlayback)(ALCdevice*, const ALCchar*);
|
||||
@@ -147,6 +150,7 @@ void alc_oss_init(BackendFuncs *func_list);
|
||||
void alc_solaris_init(BackendFuncs *func_list);
|
||||
void alcDSoundInit(BackendFuncs *func_list);
|
||||
void alcWinMMInit(BackendFuncs *FuncList);
|
||||
void alc_pa_init(BackendFuncs *func_list);
|
||||
void alc_wave_init(BackendFuncs *func_list);
|
||||
|
||||
|
||||
@@ -205,6 +209,11 @@ struct ALCcontext_struct
|
||||
ALint lNumMonoSources;
|
||||
ALint lNumStereoSources;
|
||||
|
||||
ALfloat PanningLUT[OUTPUTCHANNELS * LUT_NUM];
|
||||
ALint NumChan;
|
||||
|
||||
ALfloat ChannelMatrix[OUTPUTCHANNELS][OUTPUTCHANNELS];
|
||||
|
||||
ALCdevice *Device;
|
||||
const ALCchar *ExtensionList;
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ typedef struct ALsource
|
||||
ALfloat vOrientation[3];
|
||||
ALboolean bHeadRelative;
|
||||
ALboolean bLooping;
|
||||
ALenum DistanceModel;
|
||||
|
||||
ALuint ulBufferID;
|
||||
|
||||
|
||||
@@ -11,11 +11,12 @@ extern "C" {
|
||||
enum {
|
||||
FRONT_LEFT = 0,
|
||||
FRONT_RIGHT,
|
||||
FRONT_CENTER,
|
||||
SIDE_LEFT,
|
||||
SIDE_RIGHT,
|
||||
BACK_LEFT,
|
||||
BACK_RIGHT,
|
||||
CENTER,
|
||||
BACK_CENTER,
|
||||
LFE,
|
||||
|
||||
OUTPUTCHANNELS
|
||||
@@ -25,6 +26,7 @@ extern ALboolean DuplicateStereo;
|
||||
|
||||
__inline ALuint aluBytesFromFormat(ALenum format);
|
||||
__inline ALuint aluChannelsFromFormat(ALenum format);
|
||||
ALvoid aluInitPanning(ALCcontext *Context);
|
||||
ALvoid aluMixData(ALCcontext *context,ALvoid *buffer,ALsizei size,ALenum format);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
+25
-4
@@ -32,7 +32,7 @@
|
||||
#include "alThunk.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
|
||||
static ALvoid InitSourceParams(ALsource *pSource);
|
||||
static ALvoid InitSourceParams(ALCcontext *Context, ALsource *pSource);
|
||||
static ALboolean GetSourceOffset(ALsource *pSource, ALenum eName, ALfloat *pflOffset, ALuint updateSize);
|
||||
static ALvoid ApplyOffset(ALsource *pSource, ALboolean bUpdateContext);
|
||||
static ALint GetByteOffset(ALsource *pSource);
|
||||
@@ -78,7 +78,7 @@ ALAPI ALvoid ALAPIENTRY alGenSources(ALsizei n,ALuint *sources)
|
||||
sources[i] = (ALuint)ALTHUNK_ADDENTRY(*list);
|
||||
(*list)->source = sources[i];
|
||||
|
||||
InitSourceParams(*list);
|
||||
InitSourceParams(Context, *list);
|
||||
Context->SourceCount++;
|
||||
i++;
|
||||
|
||||
@@ -359,7 +359,7 @@ ALAPI ALvoid ALAPIENTRY alSourcef(ALuint source, ALenum eParam, ALfloat flValue)
|
||||
break;
|
||||
|
||||
case AL_ROOM_ROLLOFF_FACTOR:
|
||||
if (flValue >= 0.0f && flValue <= 1.0f)
|
||||
if (flValue >= 0.0f && flValue <= 10.0f)
|
||||
pSource->RoomRolloffFactor = flValue;
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
@@ -704,6 +704,19 @@ ALAPI ALvoid ALAPIENTRY alSourcei(ALuint source,ALenum eParam,ALint lValue)
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
break;
|
||||
|
||||
case AL_DISTANCE_MODEL:
|
||||
if(lValue == AL_NONE ||
|
||||
lValue == AL_INVERSE_DISTANCE ||
|
||||
lValue == AL_INVERSE_DISTANCE_CLAMPED ||
|
||||
lValue == AL_LINEAR_DISTANCE ||
|
||||
lValue == AL_LINEAR_DISTANCE_CLAMPED ||
|
||||
lValue == AL_EXPONENT_DISTANCE ||
|
||||
lValue == AL_EXPONENT_DISTANCE_CLAMPED)
|
||||
pSource->DistanceModel = lValue;
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
@@ -819,6 +832,7 @@ ALAPI void ALAPIENTRY alSourceiv(ALuint source, ALenum eParam, const ALint* plVa
|
||||
case AL_DIRECT_FILTER_GAINHF_AUTO:
|
||||
case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
|
||||
case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
|
||||
case AL_DISTANCE_MODEL:
|
||||
alSourcei(source, eParam, plValues[0]);
|
||||
break;
|
||||
|
||||
@@ -1212,6 +1226,10 @@ ALAPI ALvoid ALAPIENTRY alGetSourcei(ALuint source, ALenum eParam, ALint *plValu
|
||||
*plValue = (ALint)pSource->DopplerFactor;
|
||||
break;
|
||||
|
||||
case AL_DISTANCE_MODEL:
|
||||
*plValue = pSource->DistanceModel;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
@@ -1326,6 +1344,7 @@ ALAPI void ALAPIENTRY alGetSourceiv(ALuint source, ALenum eParam, ALint* plValue
|
||||
case AL_DIRECT_FILTER_GAINHF_AUTO:
|
||||
case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
|
||||
case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
|
||||
case AL_DISTANCE_MODEL:
|
||||
alGetSourcei(source, eParam, plValues);
|
||||
break;
|
||||
|
||||
@@ -1979,7 +1998,7 @@ ALAPI ALvoid ALAPIENTRY alSourceUnqueueBuffers( ALuint source, ALsizei n, ALuint
|
||||
}
|
||||
|
||||
|
||||
static ALvoid InitSourceParams(ALsource *pSource)
|
||||
static ALvoid InitSourceParams(ALCcontext *Context, ALsource *pSource)
|
||||
{
|
||||
pSource->flInnerAngle = 360.0f;
|
||||
pSource->flOuterAngle = 360.0f;
|
||||
@@ -2010,6 +2029,8 @@ static ALvoid InitSourceParams(ALsource *pSource)
|
||||
pSource->RoomRolloffFactor = 0.0f;
|
||||
pSource->DopplerFactor = 1.0f;
|
||||
|
||||
pSource->DistanceModel = Context->DistanceModel;
|
||||
|
||||
pSource->state = AL_INITIAL;
|
||||
pSource->lSourceType = AL_UNDETERMINED;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "alMain.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alError.h"
|
||||
#include "alSource.h"
|
||||
#include "alState.h"
|
||||
|
||||
static const ALchar alVendor[] = "OpenAL Community";
|
||||
@@ -644,6 +645,7 @@ ALAPI ALvoid ALAPIENTRY alSpeedOfSound(ALfloat flSpeedOfSound)
|
||||
ALAPI ALvoid ALAPIENTRY alDistanceModel(ALenum value)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALsource *Source;
|
||||
|
||||
Context=alcGetCurrentContext();
|
||||
if (Context)
|
||||
@@ -660,6 +662,8 @@ ALAPI ALvoid ALAPIENTRY alDistanceModel(ALenum value)
|
||||
case AL_EXPONENT_DISTANCE:
|
||||
case AL_EXPONENT_DISTANCE_CLAMPED:
|
||||
Context->DistanceModel = value;
|
||||
for(Source = Context->Source;Source != NULL;Source = Source->next)
|
||||
Source->DistanceModel = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Cross-compiling requires CMake 2.6 or newer. To cross-compile, first modify
|
||||
# this file to set the proper settings and paths. Then use it from CMakeConf/
|
||||
# like:
|
||||
# cmake .. -DCMAKE_TOOLCHAIN_FILE=../XCompile.txt \
|
||||
# -DCMAKE_INSTALL_PREFIX=/usr/mingw32/mingw
|
||||
# If you already have a toolchain file setup, you may use that instead of this
|
||||
# file.
|
||||
|
||||
# the name of the target operating system
|
||||
SET(CMAKE_SYSTEM_NAME Windows)
|
||||
|
||||
# which compilers to use for C and C++
|
||||
SET(CMAKE_C_COMPILER mingw32-gcc)
|
||||
SET(CMAKE_CXX_COMPILER mingw32-g++)
|
||||
|
||||
# here is the target environment located
|
||||
SET(CMAKE_FIND_ROOT_PATH /usr/mingw32/mingw)
|
||||
|
||||
# adjust the default behaviour of the FIND_XXX() commands:
|
||||
# search headers and libraries in the target environment, search
|
||||
# programs in the host environment
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
+49
-7
@@ -47,19 +47,19 @@ sources = 256 # Sets the maximum number of allocatable sources. Lower values
|
||||
# may help for systems with apps that try to play more sounds
|
||||
# than the CPU can handle. Default is 256
|
||||
|
||||
stereodup = # Sets whether to duplicate stereo sounds on the rear speakers for
|
||||
# 4+ channel output. This can make stereo sources substantially
|
||||
# louder than mono or even 4+ channel sources, but provides a
|
||||
# "fuller" playback quality. True, yes, on, and non-0 values will
|
||||
# duplicate stereo sources. 0 and anything else will cause stereo
|
||||
# sounds to only play out the front speakers.
|
||||
stereodup = # Sets whether to duplicate stereo sounds on the rear and side
|
||||
# speakers for 4+ channel output. This can make stereo sources
|
||||
# substantially louder than mono or even 4+ channel sources, but
|
||||
# provides a "fuller" playback quality. True, yes, on, and non-0
|
||||
# values will duplicate stereo sources. 0 and anything else will
|
||||
# cause stereo sounds to only play out the front speakers.
|
||||
# Default is false
|
||||
|
||||
drivers = # Sets the backend driver list order, comma-seperated. Unknown
|
||||
# backends and duplicated names are ignored, and unlisted backends
|
||||
# won't be considered for use. An empty list means the default.
|
||||
# Default is:
|
||||
# alsa,oss,solaris,dsound,winmm,wave
|
||||
# alsa,oss,solaris,dsound,winmm,port,wave
|
||||
|
||||
excludefx = # Sets which effects to exclude, preventing apps from using them.
|
||||
# This can help for apps that try to use effects which are too CPU
|
||||
@@ -67,6 +67,42 @@ excludefx = # Sets which effects to exclude, preventing apps from using them.
|
||||
# reverb
|
||||
# Default is empty (all available effects enabled)
|
||||
|
||||
layout_STEREO = # Sets the speaker layout when using stereo output. Values are
|
||||
specified in degrees, where 0 is straight in front, negative
|
||||
goes left, and positive goes right. The values must define a
|
||||
circular pattern, starting with the back-left at the most
|
||||
negative, around the front to back-center. Unspecified
|
||||
speakers will remain at their default position. Available
|
||||
speakers are front-left(fl) and front-right(fr).
|
||||
The default is:
|
||||
fl=-90, fr=90
|
||||
|
||||
layout_QUAD = # Sets the speaker layout when using quadriphonic output.
|
||||
Available speakers are back-left(bl), front-left(fl),
|
||||
front-right(fr), and back-right(br).
|
||||
The default is:
|
||||
bl=-135, fl=-45, fr=45, br=135
|
||||
|
||||
layout_51CHN = # Sets the speaker layout when using 5.1 output. Available
|
||||
speakers are back-left(bl), front-left(fl), front-center(fc),
|
||||
front-right(fr), and back-right(br).
|
||||
The default is:
|
||||
bl=-110, fl=-30, fc=0, fr=30, br=110
|
||||
|
||||
layout_61CHN = # Sets the speaker layout when using 6.1 output. Available
|
||||
speakers are side-left(sl), front-left(fl), front-center(fc),
|
||||
front-right(fr), side-right(sr), and back-center(bc).
|
||||
The default is:
|
||||
sl=-90, fl=-30, fc=0, fr=30, sr=90, bc=180
|
||||
|
||||
layout_71CHN = # Sets the speaker layout when using 7.1 output. Available
|
||||
speakers are back-left(bl), side-left(sl), front-left(fl),
|
||||
front-center(fc), front-right(fr), side-right(sr), and
|
||||
back-right(br).
|
||||
The default is:
|
||||
bl=-150, sl=-90, fl=-30, fc=0, fr=30, sr=90 br=150
|
||||
|
||||
|
||||
[alsa] # ALSA backend stuff
|
||||
device = default # Sets the device name for the default playback device.
|
||||
# Default is default
|
||||
@@ -100,6 +136,12 @@ periods = 4 # Sets the number of updates for the output buffer. Default is 4
|
||||
[winmm] # Windows Multimedia backend stuff
|
||||
# Nothing yet...
|
||||
|
||||
[port] # PortAudio backend stuff
|
||||
device = -1 # Sets the device index for output. Negative values will use the
|
||||
# default as given by PortAudio itself. Default is -1
|
||||
|
||||
periods = 4 # Sets the number of update buffers. Default is 4
|
||||
|
||||
[wave] # Wave File Writer stuff
|
||||
file = # Sets the filename of the wave file to write to. An empty name
|
||||
# prevents the backend from opening, even when explicitly requested.
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
/* Define if we have the Windows Multimedia backend */
|
||||
#cmakedefine HAVE_WINMM
|
||||
|
||||
/* Define if we have the PortAudio backend */
|
||||
#cmakedefine HAVE_PORTAUDIO
|
||||
|
||||
/* Define if we have dlfcn.h */
|
||||
#cmakedefine HAVE_DLFCN_H
|
||||
|
||||
@@ -28,6 +31,12 @@
|
||||
/* Define if we have the acosf function */
|
||||
#cmakedefine HAVE_ACOSF
|
||||
|
||||
/* Define if we have the atanf function */
|
||||
#cmakedefine HAVE_ATANF
|
||||
|
||||
/* Define if we have the fabsf function */
|
||||
#cmakedefine HAVE_FABSF
|
||||
|
||||
/* Define if we have the strtof function */
|
||||
#cmakedefine HAVE_STRTOF
|
||||
|
||||
|
||||
Reference in New Issue
Block a user