aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Alc/ALc.c3
-rw-r--r--Alc/ALu.c257
-rw-r--r--CMakeLists.txt4
-rw-r--r--OpenAL32/Include/alMain.h6
-rw-r--r--OpenAL32/Include/alu.h1
-rw-r--r--config.h.in6
6 files changed, 213 insertions, 64 deletions
diff --git a/Alc/ALc.c b/Alc/ALc.c
index 7f393c7c..72902292 100644
--- a/Alc/ALc.c
+++ b/Alc/ALc.c
@@ -37,6 +37,7 @@
#include "alExtension.h"
#include "alAuxEffectSlot.h"
#include "bs2b.h"
+#include "alu.h"
///////////////////////////////////////////////////////
// DEBUG INFORMATION
@@ -487,6 +488,8 @@ static ALvoid InitContext(ALCcontext *pContext)
bs2b_set_srate(pContext->bs2b, pContext->Frequency);
bs2b_set_level(pContext->bs2b, level);
}
+
+ aluInitPanning(pContext);
}
diff --git a/Alc/ALu.c b/Alc/ALu.c
index a6be6ae6..032ec958 100644
--- a/Alc/ALu.c
+++ b/Alc/ALu.c
@@ -23,6 +23,8 @@
#include "config.h"
#include <math.h>
+#include <stdlib.h>
+#include <string.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
@@ -39,6 +41,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 +69,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
@@ -240,17 +259,170 @@ static __inline ALvoid aluMatrixVector(ALfloat *vector,ALfloat matrix[3][3])
memcpy(vector, result, sizeof(result));
}
+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];
+
+ 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:
+ case AL_FORMAT_STEREO8:
+ case AL_FORMAT_STEREO16:
+ case AL_FORMAT_STEREO_FLOAT32:
+ 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;
+
+ case AL_FORMAT_QUAD8:
+ case AL_FORMAT_QUAD16:
+ case AL_FORMAT_QUAD32:
+ 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;
+ break;
+
+ case AL_FORMAT_51CHN8:
+ case AL_FORMAT_51CHN16:
+ case AL_FORMAT_51CHN32:
+ Context->NumChan = 5;
+ Speaker2Chan[0] = BACK_LEFT;
+ Speaker2Chan[1] = FRONT_LEFT;
+ Speaker2Chan[2] = 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;
+ break;
+
+ case AL_FORMAT_61CHN8:
+ case AL_FORMAT_61CHN16:
+ case AL_FORMAT_61CHN32:
+ Context->NumChan = 6;
+ Speaker2Chan[0] = BACK_LEFT;
+ Speaker2Chan[1] = SIDE_LEFT;
+ Speaker2Chan[2] = FRONT_LEFT;
+ Speaker2Chan[3] = FRONT_RIGHT;
+ Speaker2Chan[4] = SIDE_RIGHT;
+ Speaker2Chan[5] = 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] = 30.0f * M_PI/180.0f;
+ SpeakerAngle[4] = 90.0f * M_PI/180.0f;
+ SpeakerAngle[5] = 150.0f * M_PI/180.0f;
+ break;
+
+ case AL_FORMAT_71CHN8:
+ case AL_FORMAT_71CHN16:
+ case AL_FORMAT_71CHN32:
+ Context->NumChan = 7;
+ Speaker2Chan[0] = BACK_LEFT;
+ Speaker2Chan[1] = SIDE_LEFT;
+ Speaker2Chan[2] = FRONT_LEFT;
+ Speaker2Chan[3] = 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;
+ 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(ALCcontext *ALContext, ALsource *ALSource,
- ALenum isMono, ALenum OutputFormat,
- ALfloat *drysend, ALfloat *wetsend,
- ALfloat *pitch, ALfloat *drygainhf,
- ALfloat *wetgainhf)
+ ALenum isMono, ALfloat *drysend,
+ ALfloat *wetsend, ALfloat *pitch,
+ ALfloat *drygainhf, ALfloat *wetgainhf)
{
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];
@@ -260,6 +432,9 @@ static ALvoid CalcSourceParams(ALCcontext *ALContext, ALsource *ALSource,
ALfloat RoomRolloff;
ALfloat DryGainHF = 1.0f;
ALfloat WetGainHF = 1.0f;
+ ALfloat DirGain, AmbientGain;
+ const ALfloat *SpeakerGain;
+ ALint pos, s;
ALfloat cw, a, g;
//Get context properties
@@ -519,65 +694,20 @@ 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;
@@ -748,7 +878,7 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
CalcSourceParams(ALContext, ALSource,
(Channels==1) ? AL_TRUE : AL_FALSE,
- format, newDrySend, &newWetSend, &Pitch,
+ newDrySend, &newWetSend, &Pitch,
&DryGainHF, &WetGainHF);
Pitch = (Pitch*Frequency) / ALContext->Frequency;
@@ -847,6 +977,7 @@ ALvoid aluMixData(ALCcontext *ALContext,ALvoid *buffer,ALsizei size,ALenum forma
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][CENTER] += outsamp*DrySend[CENTER];
//Room path final mix buffer and panning
outsamp = lpFilter(WetFilter, sample);
WetBuffer[j] += outsamp*(*WetSend);
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 28622177..845442c3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -122,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)
diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h
index 042ab779..de53b072 100644
--- a/OpenAL32/Include/alMain.h
+++ b/OpenAL32/Include/alMain.h
@@ -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*);
@@ -206,6 +209,9 @@ struct ALCcontext_struct
ALint lNumMonoSources;
ALint lNumStereoSources;
+ ALfloat PanningLUT[OUTPUTCHANNELS * LUT_NUM];
+ ALint NumChan;
+
ALCdevice *Device;
const ALCchar *ExtensionList;
diff --git a/OpenAL32/Include/alu.h b/OpenAL32/Include/alu.h
index b14180b2..f307ea01 100644
--- a/OpenAL32/Include/alu.h
+++ b/OpenAL32/Include/alu.h
@@ -25,6 +25,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
diff --git a/config.h.in b/config.h.in
index 45df5157..0c8ef40d 100644
--- a/config.h.in
+++ b/config.h.in
@@ -31,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