aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2014-01-26 07:06:02 +0100
committerSven Gothel <[email protected]>2014-01-26 07:06:02 +0100
commite6f4251945c228a775649b5ccd7f11dd4519c28d (patch)
tree8454b34363358cf9bb502021a68c6985c97daac4 /Alc
parent389ae1f767bfad6116e21306fc3cdf89a4cbcc0a (diff)
parent49baa9128dd98e986639def4f24c7522d9ec6b56 (diff)
Merge branch 'UPSTREAM'
Diffstat (limited to 'Alc')
-rw-r--r--Alc/ALc.c589
-rw-r--r--Alc/ALu.c251
-rw-r--r--Alc/alcConfig.c305
-rw-r--r--Alc/alcDedicated.c100
-rw-r--r--Alc/alcEcho.c184
-rw-r--r--Alc/alcModulator.c204
-rw-r--r--Alc/alcRing.c1
-rw-r--r--Alc/alcThread.c144
-rw-r--r--Alc/atomic.h180
-rw-r--r--Alc/backends/alsa.c893
-rw-r--r--Alc/backends/base.c230
-rw-r--r--Alc/backends/base.h133
-rw-r--r--Alc/backends/coreaudio.c2
-rw-r--r--Alc/backends/dsound.c37
-rw-r--r--Alc/backends/loopback.c116
-rw-r--r--Alc/backends/mmdevapi.c15
-rw-r--r--Alc/backends/null.c184
-rw-r--r--Alc/backends/opensl.c40
-rw-r--r--Alc/backends/oss.c457
-rw-r--r--Alc/backends/portaudio.c28
-rw-r--r--Alc/backends/pulseaudio.c1516
-rw-r--r--Alc/backends/qsa.c1169
-rw-r--r--Alc/backends/sndio.c9
-rw-r--r--Alc/backends/solaris.c13
-rw-r--r--Alc/backends/wave.c16
-rw-r--r--Alc/backends/winmm.c17
-rw-r--r--Alc/compat.h65
-rw-r--r--Alc/effects/autowah.c275
-rw-r--r--Alc/effects/chorus.c381
-rw-r--r--Alc/effects/compressor.c223
-rw-r--r--Alc/effects/dedicated.c167
-rw-r--r--Alc/effects/distortion.c300
-rw-r--r--Alc/effects/echo.c296
-rw-r--r--Alc/effects/equalizer.c337
-rw-r--r--Alc/effects/flanger.c380
-rw-r--r--Alc/effects/modulator.c306
-rw-r--r--Alc/effects/null.c155
-rw-r--r--Alc/effects/reverb.c (renamed from Alc/alcReverb.c)931
-rw-r--r--Alc/evtqueue.h31
-rw-r--r--Alc/helpers.c130
-rw-r--r--Alc/hrtf.c26
-rw-r--r--Alc/midi/base.c278
-rw-r--r--Alc/midi/base.h128
-rw-r--r--Alc/midi/dummy.c95
-rw-r--r--Alc/midi/fluidsynth.c843
-rw-r--r--Alc/midi/sf2load.c1436
-rw-r--r--Alc/mixer.c22
-rw-r--r--Alc/mixer_c.c66
-rw-r--r--Alc/mixer_defs.h22
-rw-r--r--Alc/mixer_inc.c43
-rw-r--r--Alc/mixer_neon.c19
-rw-r--r--Alc/mixer_sse.c120
-rw-r--r--Alc/panning.c108
-rw-r--r--Alc/rwlock.h21
-rw-r--r--Alc/threads.c200
-rw-r--r--Alc/uintmap.h34
56 files changed, 11377 insertions, 2894 deletions
diff --git a/Alc/ALc.c b/Alc/ALc.c
index fd5b8894..fea0320d 100644
--- a/Alc/ALc.c
+++ b/Alc/ALc.c
@@ -35,64 +35,78 @@
#include "alBuffer.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
+#include "alMidi.h"
#include "bs2b.h"
#include "alu.h"
+#include "backends/base.h"
+#include "midi/base.h"
+
/************************************************
* Backends
************************************************/
-#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+struct BackendInfo {
+ const char *name;
+ ALCbackendFactory* (*getFactory)(void);
+ ALCboolean (*Init)(BackendFuncs*);
+ void (*Deinit)(void);
+ void (*Probe)(enum DevProbe);
+ BackendFuncs Funcs;
+};
+
+#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
static struct BackendInfo BackendList[] = {
#ifdef HAVE_PULSEAUDIO
- { "pulse", alc_pulse_init, alc_pulse_deinit, alc_pulse_probe, EmptyFuncs },
+ { "pulse", ALCpulseBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
#endif
#ifdef HAVE_ALSA
- { "alsa", alc_alsa_init, alc_alsa_deinit, alc_alsa_probe, EmptyFuncs },
+ { "alsa", ALCalsaBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
#endif
#ifdef HAVE_COREAUDIO
- { "core", alc_ca_init, alc_ca_deinit, alc_ca_probe, EmptyFuncs },
+ { "core", NULL, alc_ca_init, alc_ca_deinit, alc_ca_probe, EmptyFuncs },
#endif
#ifdef HAVE_OSS
- { "oss", alc_oss_init, alc_oss_deinit, alc_oss_probe, EmptyFuncs },
+ { "oss", ALCossBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
#endif
#ifdef HAVE_SOLARIS
- { "solaris", alc_solaris_init, alc_solaris_deinit, alc_solaris_probe, EmptyFuncs },
+ { "solaris", NULL, alc_solaris_init, alc_solaris_deinit, alc_solaris_probe, EmptyFuncs },
#endif
#ifdef HAVE_SNDIO
- { "sndio", alc_sndio_init, alc_sndio_deinit, alc_sndio_probe, EmptyFuncs },
+ { "sndio", NULL, alc_sndio_init, alc_sndio_deinit, alc_sndio_probe, EmptyFuncs },
+#endif
+#ifdef HAVE_QSA
+ { "qsa", NULL, alc_qsa_init, alc_qsa_deinit, alc_qsa_probe, EmptyFuncs },
#endif
#ifdef HAVE_MMDEVAPI
- { "mmdevapi", alcMMDevApiInit, alcMMDevApiDeinit, alcMMDevApiProbe, EmptyFuncs },
+ { "mmdevapi", NULL, alcMMDevApiInit, alcMMDevApiDeinit, alcMMDevApiProbe, EmptyFuncs },
#endif
#ifdef HAVE_DSOUND
- { "dsound", alcDSoundInit, alcDSoundDeinit, alcDSoundProbe, EmptyFuncs },
+ { "dsound", NULL, alcDSoundInit, alcDSoundDeinit, alcDSoundProbe, EmptyFuncs },
#endif
#ifdef HAVE_WINMM
- { "winmm", alcWinMMInit, alcWinMMDeinit, alcWinMMProbe, EmptyFuncs },
+ { "winmm", NULL, alcWinMMInit, alcWinMMDeinit, alcWinMMProbe, EmptyFuncs },
#endif
#ifdef HAVE_PORTAUDIO
- { "port", alc_pa_init, alc_pa_deinit, alc_pa_probe, EmptyFuncs },
+ { "port", NULL, alc_pa_init, alc_pa_deinit, alc_pa_probe, EmptyFuncs },
#endif
#ifdef HAVE_OPENSL
- { "opensl", alc_opensl_init, alc_opensl_deinit, alc_opensl_probe, EmptyFuncs },
+ { "opensl", NULL, alc_opensl_init, alc_opensl_deinit, alc_opensl_probe, EmptyFuncs },
#endif
- { "null", alc_null_init, alc_null_deinit, alc_null_probe, EmptyFuncs },
+ { "null", ALCnullBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
#ifdef HAVE_WAVE
- { "wave", alc_wave_init, alc_wave_deinit, alc_wave_probe, EmptyFuncs },
+ { "wave", NULL, alc_wave_init, alc_wave_deinit, alc_wave_probe, EmptyFuncs },
#endif
- { NULL, NULL, NULL, NULL, EmptyFuncs }
-};
-static struct BackendInfo BackendLoopback = {
- "loopback", alc_loopback_init, alc_loopback_deinit, alc_loopback_probe, EmptyFuncs
+ { NULL, NULL, NULL, NULL, NULL, EmptyFuncs }
};
#undef EmptyFuncs
static struct BackendInfo PlaybackBackend;
static struct BackendInfo CaptureBackend;
+
/************************************************
* Functions, enums, and errors
************************************************/
@@ -136,6 +150,9 @@ static const ALCfunction alcFunctions[] = {
DECL(alcIsRenderFormatSupportedSOFT),
DECL(alcRenderSamplesSOFT),
+ DECL(alcDevicePauseSOFT),
+ DECL(alcDeviceResumeSOFT),
+
DECL(alEnable),
DECL(alDisable),
DECL(alIsEnabled),
@@ -267,6 +284,44 @@ static const ALCfunction alcFunctions[] = {
DECL(alGetSource3i64SOFT),
DECL(alGetSourcei64vSOFT),
+ DECL(alGenSoundfontsSOFT),
+ DECL(alDeleteSoundfontsSOFT),
+ DECL(alIsSoundfontSOFT),
+ DECL(alSoundfontSamplesSOFT),
+ DECL(alGetSoundfontSamplesSOFT),
+ DECL(alSoundfontMapSamplesSOFT),
+ DECL(alSoundfontUnmapSamplesSOFT),
+ DECL(alGetSoundfontivSOFT),
+ DECL(alSoundfontPresetsSOFT),
+ DECL(alGenPresetsSOFT),
+ DECL(alDeletePresetsSOFT),
+ DECL(alIsPresetSOFT),
+ DECL(alPresetiSOFT),
+ DECL(alPresetivSOFT),
+ DECL(alGetPresetivSOFT),
+ DECL(alPresetFontsoundsSOFT),
+ DECL(alGenFontsoundsSOFT),
+ DECL(alDeleteFontsoundsSOFT),
+ DECL(alIsFontsoundSOFT),
+ DECL(alFontsoundiSOFT),
+ DECL(alFontsound2iSOFT),
+ DECL(alFontsoundivSOFT),
+ DECL(alGetFontsoundivSOFT),
+ DECL(alFontsoundModulatoriSOFT),
+ DECL(alGetFontsoundModulatorivSOFT),
+ DECL(alMidiSoundfontSOFT),
+ DECL(alMidiSoundfontvSOFT),
+ DECL(alMidiEventSOFT),
+ DECL(alMidiSysExSOFT),
+ DECL(alMidiPlaySOFT),
+ DECL(alMidiPauseSOFT),
+ DECL(alMidiStopSOFT),
+ DECL(alMidiResetSOFT),
+ DECL(alMidiGainSOFT),
+ DECL(alGetInteger64SOFT),
+ DECL(alGetInteger64vSOFT),
+ DECL(alLoadSoundfontSOFT),
+
{ NULL, NULL }
};
#undef DECL
@@ -512,23 +567,19 @@ static const ALCenums enumeration[] = {
DECL(AL_EFFECT_NULL),
DECL(AL_EFFECT_REVERB),
DECL(AL_EFFECT_EAXREVERB),
-#if 0
DECL(AL_EFFECT_CHORUS),
DECL(AL_EFFECT_DISTORTION),
-#endif
DECL(AL_EFFECT_ECHO),
-#if 0
DECL(AL_EFFECT_FLANGER),
+#if 0
DECL(AL_EFFECT_FREQUENCY_SHIFTER),
DECL(AL_EFFECT_VOCAL_MORPHER),
DECL(AL_EFFECT_PITCH_SHIFTER),
#endif
DECL(AL_EFFECT_RING_MODULATOR),
-#if 0
DECL(AL_EFFECT_AUTOWAH),
DECL(AL_EFFECT_COMPRESSOR),
DECL(AL_EFFECT_EQUALIZER),
-#endif
DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT),
DECL(AL_EFFECT_DEDICATED_DIALOGUE),
@@ -570,16 +621,54 @@ static const ALCenums enumeration[] = {
DECL(AL_REVERB_ROOM_ROLLOFF_FACTOR),
DECL(AL_REVERB_DECAY_HFLIMIT),
+ DECL(AL_CHORUS_WAVEFORM),
+ DECL(AL_CHORUS_PHASE),
+ DECL(AL_CHORUS_RATE),
+ DECL(AL_CHORUS_DEPTH),
+ DECL(AL_CHORUS_FEEDBACK),
+ DECL(AL_CHORUS_DELAY),
+
+ DECL(AL_DISTORTION_EDGE),
+ DECL(AL_DISTORTION_GAIN),
+ DECL(AL_DISTORTION_LOWPASS_CUTOFF),
+ DECL(AL_DISTORTION_EQCENTER),
+ DECL(AL_DISTORTION_EQBANDWIDTH),
+
DECL(AL_ECHO_DELAY),
DECL(AL_ECHO_LRDELAY),
DECL(AL_ECHO_DAMPING),
DECL(AL_ECHO_FEEDBACK),
DECL(AL_ECHO_SPREAD),
+ DECL(AL_FLANGER_WAVEFORM),
+ DECL(AL_FLANGER_PHASE),
+ DECL(AL_FLANGER_RATE),
+ DECL(AL_FLANGER_DEPTH),
+ DECL(AL_FLANGER_FEEDBACK),
+ DECL(AL_FLANGER_DELAY),
+
DECL(AL_RING_MODULATOR_FREQUENCY),
DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF),
DECL(AL_RING_MODULATOR_WAVEFORM),
+ DECL(AL_AUTOWAH_ATTACK_TIME),
+ DECL(AL_AUTOWAH_PEAK_GAIN),
+ DECL(AL_AUTOWAH_RELEASE_TIME),
+ DECL(AL_AUTOWAH_RESONANCE),
+
+ DECL(AL_COMPRESSOR_ONOFF),
+
+ DECL(AL_EQUALIZER_LOW_GAIN),
+ DECL(AL_EQUALIZER_LOW_CUTOFF),
+ DECL(AL_EQUALIZER_MID1_GAIN),
+ DECL(AL_EQUALIZER_MID1_CENTER),
+ DECL(AL_EQUALIZER_MID1_WIDTH),
+ DECL(AL_EQUALIZER_MID2_GAIN),
+ DECL(AL_EQUALIZER_MID2_CENTER),
+ DECL(AL_EQUALIZER_MID2_WIDTH),
+ DECL(AL_EQUALIZER_HIGH_GAIN),
+ DECL(AL_EQUALIZER_HIGH_CUTOFF),
+
DECL(AL_DEDICATED_GAIN),
{ NULL, (ALCenum)0 }
@@ -616,13 +705,13 @@ static const ALchar alExtList[] =
"AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_MULAW "
"AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET AL_EXT_source_distance_model "
"AL_LOKI_quadriphonic AL_SOFT_buffer_samples AL_SOFT_buffer_sub_data "
- "AL_SOFTX_deferred_updates AL_SOFT_direct_channels AL_SOFT_loop_points "
+ "AL_SOFT_deferred_updates AL_SOFT_direct_channels AL_SOFT_loop_points "
"AL_SOFT_source_latency";
static volatile ALCenum LastNullDeviceError = ALC_NO_ERROR;
/* Thread-local current context */
-static pthread_key_t LocalContext;
+static althread_key_t LocalContext;
/* Process-wide current context */
static ALCcontext *volatile GlobalContext = NULL;
@@ -640,7 +729,7 @@ enum LogLevel LogLevel = LogError;
static ALCboolean TrapALCError = ALC_FALSE;
/* One-time configuration init control */
-static pthread_once_t alc_config_once = PTHREAD_ONCE_INIT;
+static althread_once_t alc_config_once = ALTHREAD_ONCE_INIT;
/* Default effect that applies to sources that don't have an effect on send 0 */
static ALeffect DefaultEffect;
@@ -655,7 +744,8 @@ static const ALCchar alcNoDeviceExtList[] =
static const ALCchar alcExtensionList[] =
"ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
"ALC_EXT_DEDICATED ALC_EXT_disconnect ALC_EXT_EFX "
- "ALC_EXT_thread_local_context ALC_SOFT_loopback";
+ "ALC_EXT_thread_local_context ALC_SOFTX_HRTF ALC_SOFT_loopback "
+ "ALC_SOFTX_midi_interface ALC_SOFTX_pause_device";
static const ALCint alcMajorVersion = 1;
static const ALCint alcMinorVersion = 1;
@@ -709,7 +799,7 @@ BOOL APIENTRY DllMain(HINSTANCE hModule,DWORD ul_reason_for_call,LPVOID lpReserv
LockUIntMapRead(&TlsDestructor);
for(i = 0;i < TlsDestructor.size;i++)
{
- void *ptr = pthread_getspecific(TlsDestructor.array[i].key);
+ void *ptr = althread_getspecific(TlsDestructor.array[i].key);
void (*callback)(void*) = (void(*)(void*))TlsDestructor.array[i].value;
if(ptr && callback)
callback(ptr);
@@ -774,7 +864,7 @@ static void alc_init(void)
if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
ZScale *= -1.0f;
- pthread_key_create(&LocalContext, ReleaseThreadCtx);
+ althread_key_create(&LocalContext, ReleaseThreadCtx);
InitializeCriticalSection(&ListLock);
ThunkInit();
}
@@ -797,7 +887,7 @@ static void alc_initconfig(void)
str = getenv("ALSOFT_LOGFILE");
if(str && str[0])
{
- FILE *logfile = fopen(str, "wat");
+ FILE *logfile = fopen(str, "wt");
if(logfile) LogFile = logfile;
else ERR("Failed to open log file '%s'\n", str);
}
@@ -813,7 +903,7 @@ static void alc_initconfig(void)
capfilter = 0;
#ifdef HAVE_SSE
- capfilter |= CPU_CAP_SSE;
+ capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2;
#endif
#ifdef HAVE_NEON
capfilter |= CPU_CAP_NEON;
@@ -827,20 +917,23 @@ static void alc_initconfig(void)
size_t len;
const char *next = str;
- i = 0;
do {
str = next;
- next = strchr(str, ',');
-
while(isspace(str[0]))
str++;
+ next = strchr(str, ',');
+
if(!str[0] || str[0] == ',')
continue;
len = (next ? ((size_t)(next-str)) : strlen(str));
- if(strncasecmp(str, "sse", len) == 0)
+ while(len > 0 && isspace(str[len-1]))
+ len--;
+ if(len == 3 && strncasecmp(str, "sse", len) == 0)
capfilter &= ~CPU_CAP_SSE;
- else if(strncasecmp(str, "neon", len) == 0)
+ else if(len == 4 && strncasecmp(str, "sse2", len) == 0)
+ capfilter &= ~CPU_CAP_SSE2;
+ else if(len == 4 && strncasecmp(str, "neon", len) == 0)
capfilter &= ~CPU_CAP_NEON;
else
WARN("Invalid CPU extension \"%s\"\n", str);
@@ -911,6 +1004,8 @@ static void alc_initconfig(void)
i = 0;
do {
devs = next;
+ while(isspace(devs[0]))
+ devs++;
next = strchr(devs, ',');
delitem = (devs[0] == '-');
@@ -924,7 +1019,9 @@ static void alc_initconfig(void)
endlist = 1;
len = (next ? ((size_t)(next-devs)) : strlen(devs));
- for(n = i;BackendList[n].Init;n++)
+ while(len > 0 && isspace(devs[len-1]))
+ len--;
+ for(n = i;BackendList[n].name;n++)
{
if(len == strlen(BackendList[n].name) &&
strncmp(BackendList[n].name, devs, len) == 0)
@@ -934,7 +1031,7 @@ static void alc_initconfig(void)
do {
BackendList[n] = BackendList[n+1];
++n;
- } while(BackendList[n].Init);
+ } while(BackendList[n].name);
}
else
{
@@ -956,14 +1053,39 @@ static void alc_initconfig(void)
if(endlist)
{
BackendList[i].name = NULL;
+ BackendList[i].getFactory = NULL;
BackendList[i].Init = NULL;
BackendList[i].Deinit = NULL;
BackendList[i].Probe = NULL;
}
}
- for(i = 0;BackendList[i].Init && (!PlaybackBackend.name || !CaptureBackend.name);i++)
+ for(i = 0;(BackendList[i].Init || BackendList[i].getFactory) && (!PlaybackBackend.name || !CaptureBackend.name);i++)
{
+ if(BackendList[i].getFactory)
+ {
+ ALCbackendFactory *factory = BackendList[i].getFactory();
+ if(!V0(factory,init)())
+ {
+ WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name);
+ continue;
+ }
+
+ TRACE("Initialized backend \"%s\"\n", BackendList[i].name);
+ if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback))
+ {
+ PlaybackBackend = BackendList[i];
+ TRACE("Added \"%s\" for playback\n", PlaybackBackend.name);
+ }
+ if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture))
+ {
+ CaptureBackend = BackendList[i];
+ TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
+ }
+
+ continue;
+ }
+
if(!BackendList[i].Init(&BackendList[i].Funcs))
{
WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name);
@@ -982,7 +1104,10 @@ static void alc_initconfig(void)
TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
}
}
- BackendLoopback.Init(&BackendLoopback.Funcs);
+ {
+ ALCbackendFactory *factory = ALCloopbackFactory_getFactory();
+ V0(factory,init)();
+ }
if(ConfigValueStr(NULL, "excludefx", &str))
{
@@ -1006,12 +1131,14 @@ static void alc_initconfig(void)
} while(next++);
}
+ InitEffectFactoryMap();
+
InitEffect(&DefaultEffect);
str = getenv("ALSOFT_DEFAULT_REVERB");
if((str && str[0]) || ConfigValueStr(NULL, "default-reverb", &str))
LoadReverbPreset(str, &DefaultEffect);
}
-#define DO_INITCONFIG() pthread_once(&alc_config_once, alc_initconfig)
+#define DO_INITCONFIG() althread_once(&alc_config_once, alc_initconfig)
/************************************************
@@ -1039,6 +1166,8 @@ static void alc_cleanup(void)
} while((dev=dev->next) != NULL);
ERR("%u device%s not closed\n", num, (num>1)?"s":"");
}
+
+ DeinitEffectFactoryMap();
}
static void alc_deinit_safe(void)
@@ -1050,7 +1179,7 @@ static void alc_deinit_safe(void)
ThunkExit();
DeleteCriticalSection(&ListLock);
- pthread_key_delete(LocalContext);
+ althread_key_delete(LocalContext);
if(LogFile != stderr)
fclose(LogFile);
@@ -1066,9 +1195,20 @@ static void alc_deinit(void)
memset(&PlaybackBackend, 0, sizeof(PlaybackBackend));
memset(&CaptureBackend, 0, sizeof(CaptureBackend));
- for(i = 0;BackendList[i].Deinit;i++)
- BackendList[i].Deinit();
- BackendLoopback.Deinit();
+ for(i = 0;BackendList[i].Deinit || BackendList[i].getFactory;i++)
+ {
+ if(!BackendList[i].getFactory)
+ BackendList[i].Deinit();
+ else
+ {
+ ALCbackendFactory *factory = BackendList[i].getFactory();
+ V0(factory,deinit)();
+ }
+ }
+ {
+ ALCbackendFactory *factory = ALCloopbackFactory_getFactory();
+ V0(factory,deinit)();
+ }
alc_deinit_safe();
}
@@ -1086,10 +1226,26 @@ static void ProbeList(ALCchar **list, size_t *listsize, enum DevProbe type)
*list = NULL;
*listsize = 0;
- if(type == ALL_DEVICE_PROBE && PlaybackBackend.Probe)
- PlaybackBackend.Probe(type);
- else if(type == CAPTURE_DEVICE_PROBE && CaptureBackend.Probe)
- CaptureBackend.Probe(type);
+ if(type == ALL_DEVICE_PROBE && (PlaybackBackend.Probe || PlaybackBackend.getFactory))
+ {
+ if(!PlaybackBackend.getFactory)
+ PlaybackBackend.Probe(type);
+ else
+ {
+ ALCbackendFactory *factory = PlaybackBackend.getFactory();
+ V(factory,probe)(type);
+ }
+ }
+ else if(type == CAPTURE_DEVICE_PROBE && (CaptureBackend.Probe || CaptureBackend.getFactory))
+ {
+ if(!CaptureBackend.getFactory)
+ CaptureBackend.Probe(type);
+ else
+ {
+ ALCbackendFactory *factory = CaptureBackend.getFactory();
+ V(factory,probe)(type);
+ }
+ }
UnlockLists();
}
@@ -1162,6 +1318,7 @@ const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans)
return "(unknown channels)";
}
+extern inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type);
ALuint BytesFromDevFmt(enum DevFmtType type)
{
switch(type)
@@ -1273,21 +1430,30 @@ static ALCboolean IsValidALCChannels(ALCenum channels)
/************************************************
* Miscellaneous ALC helpers
************************************************/
+extern inline void LockContext(ALCcontext *context);
+extern inline void UnlockContext(ALCcontext *context);
-void ALCdevice_LockDefault(ALCdevice *device)
+ALint64 ALCdevice_GetLatencyDefault(ALCdevice *UNUSED(device))
{
- EnterCriticalSection(&device->Mutex);
+ return 0;
}
-void ALCdevice_UnlockDefault(ALCdevice *device)
+
+ALint64 ALCdevice_GetLatency(ALCdevice *device)
{
- LeaveCriticalSection(&device->Mutex);
+ return V0(device->Backend,getLatency)();
}
-ALint64 ALCdevice_GetLatencyDefault(ALCdevice *device)
+
+void ALCdevice_Lock(ALCdevice *device)
{
- (void)device;
- return 0;
+ V0(device->Backend,lock)();
+}
+
+void ALCdevice_Unlock(ALCdevice *device)
+{
+ V0(device->Backend,unlock)();
}
+
/* SetDefaultWFXChannelOrder
*
* Sets the default channel order used by WaveFormatEx.
@@ -1493,6 +1659,14 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS)
numSends = attrList[attrIdx + 1];
+ if(attrList[attrIdx] == ALC_HRTF_SOFT)
+ {
+ if(attrList[attrIdx + 1] != ALC_FALSE)
+ device->Flags |= DEVICE_HRTF_REQUEST;
+ else
+ device->Flags &= ~DEVICE_HRTF_REQUEST;
+ }
+
attrIdx += 2;
}
@@ -1506,7 +1680,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
numSends = minu(MAX_SENDS, numSends);
if((device->Flags&DEVICE_RUNNING))
- ALCdevice_StopPlayback(device);
+ V0(device->Backend,stop)();
device->Flags &= ~DEVICE_RUNNING;
device->Frequency = freq;
@@ -1524,7 +1698,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
/* If a context is already running on the device, stop playback so the
* device attributes can be updated. */
if((device->Flags&DEVICE_RUNNING))
- ALCdevice_StopPlayback(device);
+ V0(device->Backend,stop)();
device->Flags &= ~DEVICE_RUNNING;
freq = device->Frequency;
@@ -1552,6 +1726,14 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS)
numSends = attrList[attrIdx + 1];
+ if(attrList[attrIdx] == ALC_HRTF_SOFT)
+ {
+ if(attrList[attrIdx + 1] != ALC_FALSE)
+ device->Flags |= DEVICE_HRTF_REQUEST;
+ else
+ device->Flags &= ~DEVICE_HRTF_REQUEST;
+ }
+
attrIdx += 2;
}
@@ -1589,7 +1771,19 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
device->Frequency,
device->UpdateSize, device->NumUpdates);
- if(ALCdevice_ResetPlayback(device) == ALC_FALSE)
+ if((device->Flags&DEVICE_HRTF_REQUEST))
+ {
+ enum DevFmtChannels chans;
+ ALCuint freq;
+
+ FindHrtfFormat(device, &chans, &freq);
+ device->Frequency = freq;
+ device->FmtChans = chans;
+ device->Flags |= DEVICE_CHANNELS_REQUEST |
+ DEVICE_FREQUENCY_REQUEST;
+ }
+
+ if(V0(device->Backend,reset)() == ALC_FALSE)
return ALC_INVALID_DEVICE;
if(device->FmtChans != oldChans && (device->Flags&DEVICE_CHANNELS_REQUEST))
@@ -1623,9 +1817,22 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
device->PendingClicks[i] = 0.0f;
}
+ V(device->Synth,update)(device);
+
device->Hrtf = NULL;
- if(device->Type != Loopback && GetConfigValueBool(NULL, "hrtf", AL_FALSE))
+ if(device->Type != Loopback && ConfigValueExists(NULL, "hrtf"))
+ {
+ if(GetConfigValueBool(NULL, "hrtf", AL_FALSE))
+ device->Flags |= DEVICE_HRTF_REQUEST;
+ else
+ device->Flags &= ~DEVICE_HRTF_REQUEST;
+ }
+ if((device->Flags&DEVICE_HRTF_REQUEST))
+ {
device->Hrtf = GetHrtf(device);
+ if(!device->Hrtf)
+ device->Flags &= ~DEVICE_HRTF_REQUEST;
+ }
TRACE("HRTF %s\n", device->Hrtf?"enabled":"disabled");
if(!device->Hrtf && device->Bs2bLevel > 0 && device->Bs2bLevel <= 6)
@@ -1669,7 +1876,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
{
ALeffectslot *slot = context->EffectSlotMap.array[pos].value;
- if(ALeffectState_DeviceUpdate(slot->EffectState, device) == AL_FALSE)
+ if(V(slot->EffectState,deviceUpdate)(device) == AL_FALSE)
{
UnlockUIntMapRead(&context->EffectSlotMap);
ALCdevice_Unlock(device);
@@ -1677,7 +1884,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
return ALC_INVALID_DEVICE;
}
slot->NeedsUpdate = AL_FALSE;
- ALeffectState_Update(slot->EffectState, device, slot);
+ V(slot->EffectState,update)(device, slot);
}
UnlockUIntMapRead(&context->EffectSlotMap);
@@ -1706,21 +1913,24 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
{
ALeffectslot *slot = device->DefaultSlot;
- if(ALeffectState_DeviceUpdate(slot->EffectState, device) == AL_FALSE)
+ if(V(slot->EffectState,deviceUpdate)(device) == AL_FALSE)
{
ALCdevice_Unlock(device);
RestoreFPUMode(&oldMode);
return ALC_INVALID_DEVICE;
}
slot->NeedsUpdate = AL_FALSE;
- ALeffectState_Update(slot->EffectState, device, slot);
+ V(slot->EffectState,update)(device, slot);
}
ALCdevice_Unlock(device);
RestoreFPUMode(&oldMode);
- if(ALCdevice_StartPlayback(device) == ALC_FALSE)
- return ALC_INVALID_DEVICE;
- device->Flags |= DEVICE_RUNNING;
+ if(!(device->Flags&DEVICE_PAUSED))
+ {
+ if(V0(device->Backend,start)() == ALC_FALSE)
+ return ALC_INVALID_DEVICE;
+ device->Flags |= DEVICE_RUNNING;
+ }
return ALC_NO_ERROR;
}
@@ -1734,17 +1944,24 @@ static ALCvoid FreeDevice(ALCdevice *device)
{
TRACE("%p\n", device);
- if(device->Type != Capture)
- ALCdevice_ClosePlayback(device);
- else
- ALCdevice_CloseCapture(device);
+ V0(device->Backend,close)();
+ DELETE_OBJ(device->Backend);
+ device->Backend = NULL;
+
+ DELETE_OBJ(device->Synth);
+ device->Synth = NULL;
if(device->DefaultSlot)
{
- ALeffectState_Destroy(device->DefaultSlot->EffectState);
- device->DefaultSlot->EffectState = NULL;
+ ALeffectState *state = device->DefaultSlot->EffectState;
+ device->DefaultSlot = NULL;
+ DELETE_OBJ(state);
}
+ if(device->DefaultSfont)
+ ALsoundfont_deleteSoundfont(device->DefaultSfont, device);
+ device->DefaultSfont = NULL;
+
if(device->BufferMap.size > 0)
{
WARN("(%p) Deleting %d Buffer(s)\n", device, device->BufferMap.size);
@@ -1766,14 +1983,33 @@ static ALCvoid FreeDevice(ALCdevice *device)
}
ResetUIntMap(&device->FilterMap);
+ if(device->SfontMap.size > 0)
+ {
+ WARN("(%p) Deleting %d Soundfont(s)\n", device, device->SfontMap.size);
+ ReleaseALSoundfonts(device);
+ }
+ ResetUIntMap(&device->SfontMap);
+
+ if(device->PresetMap.size > 0)
+ {
+ WARN("(%p) Deleting %d Preset(s)\n", device, device->PresetMap.size);
+ ReleaseALPresets(device);
+ }
+ ResetUIntMap(&device->PresetMap);
+
+ if(device->FontsoundMap.size > 0)
+ {
+ WARN("(%p) Deleting %d Fontsound(s)\n", device, device->FontsoundMap.size);
+ ReleaseALFontsounds(device);
+ }
+ ResetUIntMap(&device->FontsoundMap);
+
free(device->Bs2b);
device->Bs2b = NULL;
free(device->DeviceName);
device->DeviceName = NULL;
- DeleteCriticalSection(&device->Mutex);
-
al_free(device);
}
@@ -1916,10 +2152,10 @@ static void ReleaseContext(ALCcontext *context, ALCdevice *device)
{
ALCcontext *volatile*tmp_ctx;
- if(pthread_getspecific(LocalContext) == context)
+ if(althread_getspecific(LocalContext) == context)
{
WARN("%p released while current on thread\n", context);
- pthread_setspecific(LocalContext, NULL);
+ althread_setspecific(LocalContext, NULL);
ALCcontext_DecRef(context);
}
@@ -2000,7 +2236,7 @@ ALCcontext *GetContextRef(void)
{
ALCcontext *context;
- context = pthread_getspecific(LocalContext);
+ context = althread_getspecific(LocalContext);
if(context)
ALCcontext_IncRef(context);
else
@@ -2044,18 +2280,16 @@ ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)
*
* Not functional
*/
-ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *Context)
+ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *UNUSED(context))
{
- (void)Context;
}
/* alcProcessContext
*
* Not functional
*/
-ALC_API ALCvoid ALC_APIENTRY alcProcessContext(ALCcontext *Context)
+ALC_API ALCvoid ALC_APIENTRY alcProcessContext(ALCcontext *UNUSED(context))
{
- (void)Context;
}
@@ -2231,7 +2465,7 @@ ALC_API ALCvoid ALC_APIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALsi
{
case ALC_CAPTURE_SAMPLES:
ALCdevice_Lock(device);
- *data = ALCdevice_AvailableSamples(device);
+ *data = V0(device->Backend,availableSamples)();
ALCdevice_Unlock(device);
break;
@@ -2265,11 +2499,11 @@ ALC_API ALCvoid ALC_APIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALsi
break;
case ALC_ATTRIBUTES_SIZE:
- *data = 13;
+ *data = 15;
break;
case ALC_ALL_ATTRIBUTES:
- if(size < 13)
+ if(size < 15)
alcSetError(device, ALC_INVALID_VALUE);
else
{
@@ -2304,6 +2538,9 @@ ALC_API ALCvoid ALC_APIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALsi
data[i++] = ALC_MAX_AUXILIARY_SENDS;
data[i++] = device->NumAuxSends;
+ data[i++] = ALC_HRTF_SOFT;
+ data[i++] = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
+
data[i++] = 0;
}
break;
@@ -2356,6 +2593,10 @@ ALC_API ALCvoid ALC_APIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALsi
*data = device->Connected;
break;
+ case ALC_HRTF_SOFT:
+ *data = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
+ break;
+
default:
alcSetError(device, ALC_INVALID_ENUM);
break;
@@ -2504,7 +2745,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
{
if(!device->ContextList)
{
- ALCdevice_StopPlayback(device);
+ V0(device->Backend,stop)();
device->Flags &= ~DEVICE_RUNNING;
}
UnlockLists();
@@ -2548,7 +2789,7 @@ ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context)
ReleaseContext(context, Device);
if(!Device->ContextList)
{
- ALCdevice_StopPlayback(Device);
+ V0(Device->Backend,stop)();
Device->Flags &= ~DEVICE_RUNNING;
}
}
@@ -2564,7 +2805,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void)
{
ALCcontext *Context;
- Context = pthread_getspecific(LocalContext);
+ Context = althread_getspecific(LocalContext);
if(!Context) Context = GlobalContext;
return Context;
@@ -2577,7 +2818,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void)
ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void)
{
ALCcontext *Context;
- Context = pthread_getspecific(LocalContext);
+ Context = althread_getspecific(LocalContext);
return Context;
}
@@ -2599,9 +2840,9 @@ ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context)
context = ExchangePtr((XchgPtr*)&GlobalContext, context);
if(context) ALCcontext_DecRef(context);
- if((context=pthread_getspecific(LocalContext)) != NULL)
+ if((context=althread_getspecific(LocalContext)) != NULL)
{
- pthread_setspecific(LocalContext, NULL);
+ althread_setspecific(LocalContext, NULL);
ALCcontext_DecRef(context);
}
@@ -2623,8 +2864,8 @@ ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context)
return ALC_FALSE;
}
/* context's reference count is already incremented */
- old = pthread_getspecific(LocalContext);
- pthread_setspecific(LocalContext, context);
+ old = althread_getspecific(LocalContext);
+ althread_setspecific(LocalContext, context);
if(old) ALCcontext_DecRef(old);
return ALC_TRUE;
@@ -2680,11 +2921,9 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
}
//Validate device
- device->Funcs = &PlaybackBackend.Funcs;
device->ref = 1;
device->Connected = ALC_TRUE;
device->Type = Playback;
- InitializeCriticalSection(&device->Mutex);
device->LastError = ALC_NO_ERROR;
device->Flags = 0;
@@ -2701,6 +2940,9 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
InitUIntMap(&device->BufferMap, ~0);
InitUIntMap(&device->EffectMap, ~0);
InitUIntMap(&device->FilterMap, ~0);
+ InitUIntMap(&device->SfontMap, ~0);
+ InitUIntMap(&device->PresetMap, ~0);
+ InitUIntMap(&device->FontsoundMap, ~0);
//Set output format
device->FmtChans = DevFmtChannelsDefault;
@@ -2709,6 +2951,24 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
device->NumUpdates = 4;
device->UpdateSize = 1024;
+ if(!PlaybackBackend.getFactory)
+ {
+ device->Funcs = &PlaybackBackend.Funcs;
+ device->Backend = create_backend_wrapper(device, ALCbackend_Playback);
+ }
+ else
+ {
+ ALCbackendFactory *factory = PlaybackBackend.getFactory();
+ device->Backend = V(factory,createBackend)(device, ALCbackend_Playback);
+ }
+ if(!device->Backend)
+ {
+ al_free(device);
+ alcSetError(NULL, ALC_OUT_OF_MEMORY);
+ return NULL;
+ }
+
+
if(ConfigValueStr(NULL, "channels", &fmt))
{
static const struct {
@@ -2844,10 +3104,20 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
device->NumStereoSources = 1;
device->NumMonoSources = device->MaxNoOfSources - device->NumStereoSources;
+ device->Synth = SynthCreate(device);
+ if(!device->Synth)
+ {
+ DELETE_OBJ(device->Backend);
+ al_free(device);
+ alcSetError(NULL, ALC_OUT_OF_MEMORY);
+ return NULL;
+ }
+
// Find a playback device to open
- if((err=ALCdevice_OpenPlayback(device, deviceName)) != ALC_NO_ERROR)
+ if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR)
{
- DeleteCriticalSection(&device->Mutex);
+ DELETE_OBJ(device->Synth);
+ DELETE_OBJ(device->Backend);
al_free(device);
alcSetError(NULL, err);
return NULL;
@@ -2863,8 +3133,9 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
}
else if(InitializeEffect(device, device->DefaultSlot, &DefaultEffect) != AL_NO_ERROR)
{
- ALeffectState_Destroy(device->DefaultSlot->EffectState);
+ ALeffectState *state = device->DefaultSlot->EffectState;
device->DefaultSlot = NULL;
+ DELETE_OBJ(state);
ERR("Failed to initialize the default effect\n");
}
}
@@ -2907,7 +3178,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *Device)
ReleaseContext(ctx, Device);
}
if((Device->Flags&DEVICE_RUNNING))
- ALCdevice_StopPlayback(Device);
+ V0(Device->Backend,stop)();
Device->Flags &= ~DEVICE_RUNNING;
ALCdevice_DecRef(Device);
@@ -2949,25 +3220,42 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName,
}
//Validate device
- device->Funcs = &CaptureBackend.Funcs;
device->ref = 1;
device->Connected = ALC_TRUE;
device->Type = Capture;
- InitializeCriticalSection(&device->Mutex);
InitUIntMap(&device->BufferMap, ~0);
InitUIntMap(&device->EffectMap, ~0);
InitUIntMap(&device->FilterMap, ~0);
+ InitUIntMap(&device->SfontMap, ~0);
+ InitUIntMap(&device->PresetMap, ~0);
+ InitUIntMap(&device->FontsoundMap, ~0);
device->DeviceName = NULL;
+ if(!CaptureBackend.getFactory)
+ {
+ device->Funcs = &CaptureBackend.Funcs;
+ device->Backend = create_backend_wrapper(device, ALCbackend_Capture);
+ }
+ else
+ {
+ ALCbackendFactory *factory = CaptureBackend.getFactory();
+ device->Backend = V(factory,createBackend)(device, ALCbackend_Capture);
+ }
+ if(!device->Backend)
+ {
+ al_free(device);
+ alcSetError(NULL, ALC_OUT_OF_MEMORY);
+ return NULL;
+ }
+
device->Flags |= DEVICE_FREQUENCY_REQUEST;
device->Frequency = frequency;
device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_SAMPLE_TYPE_REQUEST;
if(DecomposeDevFormat(format, &device->FmtChans, &device->FmtType) == AL_FALSE)
{
- DeleteCriticalSection(&device->Mutex);
al_free(device);
alcSetError(NULL, ALC_INVALID_ENUM);
return NULL;
@@ -2976,9 +3264,8 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName,
device->UpdateSize = samples;
device->NumUpdates = 1;
- if((err=ALCdevice_OpenCapture(device, deviceName)) != ALC_NO_ERROR)
+ if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR)
{
- DeleteCriticalSection(&device->Mutex);
al_free(device);
alcSetError(NULL, err);
return NULL;
@@ -3026,7 +3313,7 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
if(device->Connected)
{
if(!(device->Flags&DEVICE_RUNNING))
- ALCdevice_StartCapture(device);
+ V0(device->Backend,start)();
device->Flags |= DEVICE_RUNNING;
}
ALCdevice_Unlock(device);
@@ -3043,7 +3330,7 @@ ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
{
ALCdevice_Lock(device);
if((device->Flags&DEVICE_RUNNING))
- ALCdevice_StopCapture(device);
+ V0(device->Backend,stop)();
device->Flags &= ~DEVICE_RUNNING;
ALCdevice_Unlock(device);
}
@@ -3053,15 +3340,15 @@ ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
{
- if(!(device=VerifyDevice(device)) && device->Type != Capture)
+ if(!(device=VerifyDevice(device)) || device->Type != Capture)
alcSetError(device, ALC_INVALID_DEVICE);
else
{
ALCenum err = ALC_INVALID_VALUE;
ALCdevice_Lock(device);
- if(samples >= 0 && ALCdevice_AvailableSamples(device) >= (ALCuint)samples)
- err = ALCdevice_CaptureSamples(device, buffer, samples);
+ if(samples >= 0 && V0(device->Backend,availableSamples)() >= (ALCuint)samples)
+ err = V(device->Backend,captureSamples)(buffer, samples);
ALCdevice_Unlock(device);
if(err != ALC_NO_ERROR)
@@ -3081,6 +3368,7 @@ ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer,
*/
ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName)
{
+ ALCbackendFactory *factory;
ALCdevice *device;
DO_INITCONFIG();
@@ -3100,11 +3388,9 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
}
//Validate device
- device->Funcs = &BackendLoopback.Funcs;
device->ref = 1;
device->Connected = ALC_TRUE;
device->Type = Loopback;
- InitializeCriticalSection(&device->Mutex);
device->LastError = ALC_NO_ERROR;
device->Flags = 0;
@@ -3121,6 +3407,18 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
InitUIntMap(&device->BufferMap, ~0);
InitUIntMap(&device->EffectMap, ~0);
InitUIntMap(&device->FilterMap, ~0);
+ InitUIntMap(&device->SfontMap, ~0);
+ InitUIntMap(&device->PresetMap, ~0);
+ InitUIntMap(&device->FontsoundMap, ~0);
+
+ factory = ALCloopbackFactory_getFactory();
+ device->Backend = V(factory,createBackend)(device, ALCbackend_Loopback);
+ if(!device->Backend)
+ {
+ al_free(device);
+ alcSetError(NULL, ALC_OUT_OF_MEMORY);
+ return NULL;
+ }
//Set output format
device->NumUpdates = 0;
@@ -3142,8 +3440,17 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
device->NumStereoSources = 1;
device->NumMonoSources = device->MaxNoOfSources - device->NumStereoSources;
+ device->Synth = SynthCreate(device);
+ if(!device->Synth)
+ {
+ DELETE_OBJ(device->Backend);
+ al_free(device);
+ alcSetError(NULL, ALC_OUT_OF_MEMORY);
+ return NULL;
+ }
+
// Open the "backend"
- ALCdevice_OpenPlayback(device, "Loopback");
+ V(device->Backend,open)("Loopback");
do {
device->next = DeviceList;
} while(!CompExchangePtr((XchgPtr*)&DeviceList, device->next, device));
@@ -3181,7 +3488,7 @@ ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device
* Renders some samples into a buffer, using the format last set by the
* attributes given to alcCreateContext.
*/
-ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
+FORCE_ALIGN ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
{
if(!(device=VerifyDevice(device)) || device->Type != Loopback)
alcSetError(device, ALC_INVALID_DEVICE);
@@ -3191,3 +3498,59 @@ ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffe
aluMixData(device, buffer, samples);
if(device) ALCdevice_DecRef(device);
}
+
+
+/************************************************
+ * ALC DSP pause/resume functions
+ ************************************************/
+
+/* alcDevicePauseSOFT
+ *
+ * Pause the DSP to stop audio processing.
+ */
+ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device)
+{
+ if(!(device=VerifyDevice(device)) || device->Type != Playback)
+ alcSetError(device, ALC_INVALID_DEVICE);
+ else
+ {
+ LockLists();
+ if((device->Flags&DEVICE_RUNNING))
+ V0(device->Backend,stop)();
+ device->Flags &= ~DEVICE_RUNNING;
+ device->Flags |= DEVICE_PAUSED;
+ UnlockLists();
+ }
+ if(device) ALCdevice_DecRef(device);
+}
+
+/* alcDeviceResumeSOFT
+ *
+ * Resume the DSP to restart audio processing.
+ */
+ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device)
+{
+ if(!(device=VerifyDevice(device)) || device->Type != Playback)
+ alcSetError(device, ALC_INVALID_DEVICE);
+ else
+ {
+ LockLists();
+ if((device->Flags&DEVICE_PAUSED))
+ {
+ if(V0(device->Backend,start)() != ALC_FALSE)
+ {
+ device->Flags |= DEVICE_RUNNING;
+ device->Flags &= ~DEVICE_PAUSED;
+ }
+ else
+ {
+ alcSetError(device, ALC_INVALID_DEVICE);
+ ALCdevice_Lock(device);
+ aluHandleDisconnect(device);
+ ALCdevice_Unlock(device);
+ }
+ }
+ UnlockLists();
+ }
+ if(device) ALCdevice_DecRef(device);
+}
diff --git a/Alc/ALu.c b/Alc/ALu.c
index 282053c7..34ac6687 100644
--- a/Alc/ALu.c
+++ b/Alc/ALu.c
@@ -36,6 +36,8 @@
#include "mixer_defs.h"
+#include "midi/base.h"
+
struct ChanMap {
enum Channel channel;
@@ -48,6 +50,32 @@ ALfloat ConeScale = 1.0f;
/* Localized Z scalar for mono sources */
ALfloat ZScale = 1.0f;
+extern inline ALfloat minf(ALfloat a, ALfloat b);
+extern inline ALfloat maxf(ALfloat a, ALfloat b);
+extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
+
+extern inline ALdouble mind(ALdouble a, ALdouble b);
+extern inline ALdouble maxd(ALdouble a, ALdouble b);
+extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
+
+extern inline ALuint minu(ALuint a, ALuint b);
+extern inline ALuint maxu(ALuint a, ALuint b);
+extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
+
+extern inline ALint mini(ALint a, ALint b);
+extern inline ALint maxi(ALint a, ALint b);
+extern inline ALint clampi(ALint val, ALint min, ALint max);
+
+extern inline ALint64 mini64(ALint64 a, ALint64 b);
+extern inline ALint64 maxi64(ALint64 a, ALint64 b);
+extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
+
+extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
+extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
+extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
+
+extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
+extern inline ALfloat cubic(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat mu);
static ResamplerFunc SelectResampler(enum Resampler Resampler, ALuint increment)
{
@@ -105,20 +133,20 @@ static WetMixerFunc SelectSendMixer(void)
}
-static __inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
+static inline void 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(const ALfloat *inVector1, const 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];
}
-static __inline void aluNormalize(ALfloat *inVector)
+static inline void aluNormalize(ALfloat *inVector)
{
ALfloat lengthsqr = aluDotproduct(inVector, inVector);
if(lengthsqr > 0.0f)
@@ -130,7 +158,7 @@ static __inline void aluNormalize(ALfloat *inVector)
}
}
-static __inline ALvoid aluMatrixVector(ALfloat *vector, ALfloat w, ALfloat (*RESTRICT matrix)[4])
+static inline ALvoid aluMatrixVector(ALfloat *vector, ALfloat w, ALfloat (*restrict matrix)[4])
{
ALfloat temp[4] = {
vector[0], vector[1], vector[2], w
@@ -194,49 +222,49 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
{
static const struct ChanMap MonoMap[1] = { { FrontCenter, 0.0f } };
static const struct ChanMap StereoMap[2] = {
- { FrontLeft, -30.0f * F_PI/180.0f },
- { FrontRight, 30.0f * F_PI/180.0f }
+ { FrontLeft, DEG2RAD(-30.0f) },
+ { FrontRight, DEG2RAD( 30.0f) }
};
static const struct ChanMap StereoWideMap[2] = {
- { FrontLeft, -90.0f * F_PI/180.0f },
- { FrontRight, 90.0f * F_PI/180.0f }
+ { FrontLeft, DEG2RAD(-90.0f) },
+ { FrontRight, DEG2RAD( 90.0f) }
};
static const struct ChanMap RearMap[2] = {
- { BackLeft, -150.0f * F_PI/180.0f },
- { BackRight, 150.0f * F_PI/180.0f }
+ { BackLeft, DEG2RAD(-150.0f) },
+ { BackRight, DEG2RAD( 150.0f) }
};
static const struct ChanMap QuadMap[4] = {
- { FrontLeft, -45.0f * F_PI/180.0f },
- { FrontRight, 45.0f * F_PI/180.0f },
- { BackLeft, -135.0f * F_PI/180.0f },
- { BackRight, 135.0f * F_PI/180.0f }
+ { FrontLeft, DEG2RAD( -45.0f) },
+ { FrontRight, DEG2RAD( 45.0f) },
+ { BackLeft, DEG2RAD(-135.0f) },
+ { BackRight, DEG2RAD( 135.0f) }
};
static const struct ChanMap X51Map[6] = {
- { FrontLeft, -30.0f * F_PI/180.0f },
- { FrontRight, 30.0f * F_PI/180.0f },
- { FrontCenter, 0.0f * F_PI/180.0f },
+ { FrontLeft, DEG2RAD( -30.0f) },
+ { FrontRight, DEG2RAD( 30.0f) },
+ { FrontCenter, DEG2RAD( 0.0f) },
{ LFE, 0.0f },
- { BackLeft, -110.0f * F_PI/180.0f },
- { BackRight, 110.0f * F_PI/180.0f }
+ { BackLeft, DEG2RAD(-110.0f) },
+ { BackRight, DEG2RAD( 110.0f) }
};
static const struct ChanMap X61Map[7] = {
- { FrontLeft, -30.0f * F_PI/180.0f },
- { FrontRight, 30.0f * F_PI/180.0f },
- { FrontCenter, 0.0f * F_PI/180.0f },
+ { FrontLeft, DEG2RAD(-30.0f) },
+ { FrontRight, DEG2RAD( 30.0f) },
+ { FrontCenter, DEG2RAD( 0.0f) },
{ LFE, 0.0f },
- { BackCenter, 180.0f * F_PI/180.0f },
- { SideLeft, -90.0f * F_PI/180.0f },
- { SideRight, 90.0f * F_PI/180.0f }
+ { BackCenter, DEG2RAD(180.0f) },
+ { SideLeft, DEG2RAD(-90.0f) },
+ { SideRight, DEG2RAD( 90.0f) }
};
static const struct ChanMap X71Map[8] = {
- { FrontLeft, -30.0f * F_PI/180.0f },
- { FrontRight, 30.0f * F_PI/180.0f },
- { FrontCenter, 0.0f * F_PI/180.0f },
+ { FrontLeft, DEG2RAD( -30.0f) },
+ { FrontRight, DEG2RAD( 30.0f) },
+ { FrontCenter, DEG2RAD( 0.0f) },
{ LFE, 0.0f },
- { BackLeft, -150.0f * F_PI/180.0f },
- { BackRight, 150.0f * F_PI/180.0f },
- { SideLeft, -90.0f * F_PI/180.0f },
- { SideRight, 90.0f * F_PI/180.0f }
+ { BackLeft, DEG2RAD(-150.0f) },
+ { BackRight, DEG2RAD( 150.0f) },
+ { SideLeft, DEG2RAD( -90.0f) },
+ { SideRight, DEG2RAD( 90.0f) }
};
ALCdevice *Device = ALContext->Device;
@@ -254,7 +282,6 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
ALboolean DirectChannels;
ALfloat hwidth = 0.0f;
ALfloat Pitch;
- ALfloat cw;
ALint i, c;
/* Get device properties */
@@ -280,14 +307,9 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
ALbuffer *ALBuffer;
if((ALBuffer=BufferListItem->buffer) != NULL)
{
- ALsizei maxstep = BUFFERSIZE;
- maxstep -= ResamplerPadding[Resampler] +
- ResamplerPrePadding[Resampler] + 1;
- maxstep = mini(maxstep, INT_MAX>>FRACTIONBITS);
-
Pitch = Pitch * ALBuffer->Frequency / Frequency;
- if(Pitch > (ALfloat)maxstep)
- ALSource->Params.Step = maxstep<<FRACTIONBITS;
+ if(Pitch > 10.0f)
+ ALSource->Params.Step = 10<<FRACTIONBITS;
else
{
ALSource->Params.Step = fastf2i(Pitch*FRACTIONONE);
@@ -319,7 +341,7 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
}
SrcMatrix = ALSource->Params.Direct.Gains;
- for(i = 0;i < MaxChannels;i++)
+ for(i = 0;i < MAX_INPUT_CHANNELS;i++)
{
for(c = 0;c < MaxChannels;c++)
SrcMatrix[i][c] = 0.0f;
@@ -345,7 +367,7 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
else
{
chans = StereoWideMap;
- hwidth = 60.0f * F_PI/180.0f;
+ hwidth = DEG2RAD(60.0f);
}
num_channels = 2;
break;
@@ -443,27 +465,37 @@ ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
for(i = 0;i < NumSends;i++)
{
ALeffectslot *Slot = ALSource->Send[i].Slot;
-
if(!Slot && i == 0)
Slot = Device->DefaultSlot;
- if(Slot && Slot->effect.type == AL_EFFECT_NULL)
- Slot = NULL;
- ALSource->Params.Send[i].Slot = Slot;
+ if(!Slot || Slot->EffectType == AL_EFFECT_NULL)
+ {
+ ALSource->Params.Send[i].OutBuffer = NULL;
+ ALSource->Params.Send[i].ClickRemoval = NULL;
+ ALSource->Params.Send[i].PendingClicks = NULL;
+ }
+ else
+ {
+ ALSource->Params.Send[i].OutBuffer = Slot->WetBuffer;
+ ALSource->Params.Send[i].ClickRemoval = Slot->ClickRemoval;
+ ALSource->Params.Send[i].PendingClicks = Slot->PendingClicks;
+ }
ALSource->Params.Send[i].Gain = WetGain[i];
}
- /* Update filter coefficients. Calculations based on the I3DL2
- * spec. */
- cw = cosf(F_PI*2.0f * LOWPASSFREQREF / Frequency);
-
- /* We use two chained one-pole filters, so we need to take the
- * square root of the squared gain, which is the same as the base
- * gain. */
- ALSource->Params.Direct.iirFilter.coeff = lpCoeffCalc(DryGainHF, cw);
+ {
+ ALfloat gain = maxf(0.01f, DryGainHF);
+ for(c = 0;c < num_channels;c++)
+ ALfilterState_setParams(&ALSource->Params.Direct.LpFilter[c],
+ ALfilterType_HighShelf, gain,
+ (ALfloat)LOWPASSFREQREF/Frequency, 0.0f);
+ }
for(i = 0;i < NumSends;i++)
{
- ALfloat a = lpCoeffCalc(WetGainHF[i], cw);
- ALSource->Params.Send[i].iirFilter.coeff = a;
+ ALfloat gain = maxf(0.01f, WetGainHF[i]);
+ for(c = 0;c < num_channels;c++)
+ ALfilterState_setParams(&ALSource->Params.Send[i].LpFilter[c],
+ ALfilterType_HighShelf, gain,
+ (ALfloat)LOWPASSFREQREF/Frequency, 0.0f);
}
}
@@ -495,7 +527,6 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
ALfloat Pitch;
ALuint Frequency;
ALint NumSends;
- ALfloat cw;
ALint i, j;
DryGainHF = 1.0f;
@@ -547,7 +578,7 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
if(!Slot && i == 0)
Slot = Device->DefaultSlot;
- if(!Slot || Slot->effect.type == AL_EFFECT_NULL)
+ if(!Slot || Slot->EffectType == AL_EFFECT_NULL)
{
Slot = NULL;
RoomRolloff[i] = 0.0f;
@@ -557,12 +588,12 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
else if(Slot->AuxSendAuto)
{
RoomRolloff[i] = RoomRolloffBase;
- if(IsReverbEffect(Slot->effect.type))
+ if(IsReverbEffect(Slot->EffectType))
{
- RoomRolloff[i] += Slot->effect.Reverb.RoomRolloffFactor;
- DecayDistance[i] = Slot->effect.Reverb.DecayTime *
+ RoomRolloff[i] += Slot->EffectProps.Reverb.RoomRolloffFactor;
+ DecayDistance[i] = Slot->EffectProps.Reverb.DecayTime *
SPEEDOFSOUNDMETRESPERSEC;
- RoomAirAbsorption[i] = Slot->effect.Reverb.AirAbsorptionGainHF;
+ RoomAirAbsorption[i] = Slot->EffectProps.Reverb.AirAbsorptionGainHF;
}
else
{
@@ -579,13 +610,24 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
RoomAirAbsorption[i] = AIRABSORBGAINHF;
}
- ALSource->Params.Send[i].Slot = Slot;
+ if(!Slot || Slot->EffectType == AL_EFFECT_NULL)
+ {
+ ALSource->Params.Send[i].OutBuffer = NULL;
+ ALSource->Params.Send[i].ClickRemoval = NULL;
+ ALSource->Params.Send[i].PendingClicks = NULL;
+ }
+ else
+ {
+ ALSource->Params.Send[i].OutBuffer = Slot->WetBuffer;
+ ALSource->Params.Send[i].ClickRemoval = Slot->ClickRemoval;
+ ALSource->Params.Send[i].PendingClicks = Slot->PendingClicks;
+ }
}
/* Transform source to listener space (convert to head relative) */
if(ALSource->HeadRelative == AL_FALSE)
{
- ALfloat (*RESTRICT Matrix)[4] = ALContext->Listener->Params.Matrix;
+ ALfloat (*restrict Matrix)[4] = ALContext->Listener->Params.Matrix;
/* Transform source vectors */
aluMatrixVector(Position, 1.0f, Matrix);
aluMatrixVector(Direction, 0.0f, Matrix);
@@ -704,7 +746,7 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
}
/* Calculate directional soundcones */
- Angle = acosf(aluDotproduct(Direction,SourceToListener)) * ConeScale * (360.0f/F_PI);
+ Angle = RAD2DEG(acosf(aluDotproduct(Direction,SourceToListener)) * ConeScale) * 2.0f;
if(Angle > InnerAngle && Angle <= OuterAngle)
{
ALfloat scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
@@ -777,14 +819,9 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
{
/* Calculate fixed-point stepping value, based on the pitch, buffer
* frequency, and output frequency. */
- ALsizei maxstep = BUFFERSIZE;
- maxstep -= ResamplerPadding[Resampler] +
- ResamplerPrePadding[Resampler] + 1;
- maxstep = mini(maxstep, INT_MAX>>FRACTIONBITS);
-
Pitch = Pitch * ALBuffer->Frequency / Frequency;
- if(Pitch > (ALfloat)maxstep)
- ALSource->Params.Step = maxstep<<FRACTIONBITS;
+ if(Pitch > 10.0f)
+ ALSource->Params.Step = 10<<FRACTIONBITS;
else
{
ALSource->Params.Step = fastf2i(Pitch*FRACTIONONE);
@@ -869,7 +906,7 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
ALfloat DirGain = 0.0f;
ALfloat AmbientGain;
- for(i = 0;i < MaxChannels;i++)
+ for(i = 0;i < MAX_INPUT_CHANNELS;i++)
{
for(j = 0;j < MaxChannels;j++)
Matrix[i][j] = 0.0f;
@@ -900,51 +937,59 @@ ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext)
for(i = 0;i < NumSends;i++)
ALSource->Params.Send[i].Gain = WetGain[i];
- /* Update filter coefficients. */
- cw = cosf(F_PI*2.0f * LOWPASSFREQREF / Frequency);
- ALSource->Params.Direct.iirFilter.coeff = lpCoeffCalc(DryGainHF, cw);
+ {
+ ALfloat gain = maxf(0.01f, DryGainHF);
+ ALfilterState_setParams(&ALSource->Params.Direct.LpFilter[0],
+ ALfilterType_HighShelf, gain,
+ (ALfloat)LOWPASSFREQREF/Frequency, 0.0f);
+ }
for(i = 0;i < NumSends;i++)
{
- ALfloat a = lpCoeffCalc(WetGainHF[i], cw);
- ALSource->Params.Send[i].iirFilter.coeff = a;
+ ALfloat gain = maxf(0.01f, WetGainHF[i]);
+ ALfilterState_setParams(&ALSource->Params.Send[i].LpFilter[0],
+ ALfilterType_HighShelf, gain,
+ (ALfloat)LOWPASSFREQREF/Frequency, 0.0f);
}
}
-static __inline ALfloat aluF2F(ALfloat val)
-{ return val; }
-static __inline ALint aluF2I(ALfloat val)
+static inline ALint aluF2I25(ALfloat val)
{
- /* Clamp the value between -1 and +1. This handles that without branching. */
- val = val+1.0f - fabsf(val-1.0f);
- val = (val-2.0f + fabsf(val+2.0f)) * 0.25f;
- /* Convert to a signed integer, between -2147483647 and +2147483647. */
- return fastf2i((ALfloat)(val*2147483647.0));
+ /* Clamp the value between -1 and +1. This handles that with only a single branch. */
+ if(fabsf(val) > 1.0f)
+ val = (ALfloat)((0.0f < val) - (val < 0.0f));
+ /* Convert to a signed integer, between -16777215 and +16777215. */
+ return fastf2i(val*16777215.0f);
}
-static __inline ALuint aluF2UI(ALfloat val)
+
+static inline ALfloat aluF2F(ALfloat val)
+{ return val; }
+static inline ALint aluF2I(ALfloat val)
+{ return aluF2I25(val)<<7; }
+static inline ALuint aluF2UI(ALfloat val)
{ return aluF2I(val)+2147483648u; }
-static __inline ALshort aluF2S(ALfloat val)
-{ return aluF2I(val)>>16; }
-static __inline ALushort aluF2US(ALfloat val)
+static inline ALshort aluF2S(ALfloat val)
+{ return aluF2I25(val)>>9; }
+static inline ALushort aluF2US(ALfloat val)
{ return aluF2S(val)+32768; }
-static __inline ALbyte aluF2B(ALfloat val)
-{ return aluF2I(val)>>24; }
-static __inline ALubyte aluF2UB(ALfloat val)
+static inline ALbyte aluF2B(ALfloat val)
+{ return aluF2I25(val)>>17; }
+static inline ALubyte aluF2UB(ALfloat val)
{ return aluF2B(val)+128; }
#define DECL_TEMPLATE(T, func) \
-static int Write_##T(ALCdevice *device, T *RESTRICT buffer, \
+static int Write_##T(ALCdevice *device, T *restrict buffer, \
ALuint SamplesToDo) \
{ \
- ALfloat (*RESTRICT DryBuffer)[BUFFERSIZE] = device->DryBuffer; \
+ ALfloat (*restrict DryBuffer)[BUFFERSIZE] = device->DryBuffer; \
ALuint numchans = ChannelsFromDevFmt(device->FmtChans); \
const ALuint *offsets = device->ChannelOffsets; \
ALuint i, j; \
\
for(j = 0;j < MaxChannels;j++) \
{ \
- T *RESTRICT out; \
+ T *restrict out; \
\
if(offsets[j] == INVALID_OFFSET) \
continue; \
@@ -985,6 +1030,8 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
memset(device->DryBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
ALCdevice_Lock(device);
+ V(device->Synth,process)(SamplesToDo, device->DryBuffer);
+
ctx = device->ContextList;
while(ctx)
{
@@ -1034,10 +1081,10 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
(*slot)->PendingClicks[0] = 0.0f;
if(!DeferUpdates && ExchangeInt(&(*slot)->NeedsUpdate, AL_FALSE))
- ALeffectState_Update((*slot)->EffectState, device, *slot);
+ V((*slot)->EffectState,update)(device, *slot);
- ALeffectState_Process((*slot)->EffectState, SamplesToDo,
- (*slot)->WetBuffer[0], device->DryBuffer);
+ V((*slot)->EffectState,process)(SamplesToDo, (*slot)->WetBuffer[0],
+ device->DryBuffer);
for(i = 0;i < SamplesToDo;i++)
(*slot)->WetBuffer[0][i] = 0.0f;
@@ -1063,10 +1110,10 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
(*slot)->PendingClicks[0] = 0.0f;
if(ExchangeInt(&(*slot)->NeedsUpdate, AL_FALSE))
- ALeffectState_Update((*slot)->EffectState, device, *slot);
+ V((*slot)->EffectState,update)(device, *slot);
- ALeffectState_Process((*slot)->EffectState, SamplesToDo,
- (*slot)->WetBuffer[0], device->DryBuffer);
+ V((*slot)->EffectState,process)(SamplesToDo, (*slot)->WetBuffer[0],
+ device->DryBuffer);
for(i = 0;i < SamplesToDo;i++)
(*slot)->WetBuffer[0][i] = 0.0f;
diff --git a/Alc/alcConfig.c b/Alc/alcConfig.c
index 49a02b23..98c0df32 100644
--- a/Alc/alcConfig.c
+++ b/Alc/alcConfig.c
@@ -45,161 +45,133 @@ typedef struct ConfigEntry {
} ConfigEntry;
typedef struct ConfigBlock {
- char *name;
ConfigEntry *entries;
unsigned int entryCount;
} ConfigBlock;
-static ConfigBlock *cfgBlocks;
-static unsigned int cfgCount;
+static ConfigBlock cfgBlock;
static char buffer[1024];
+static char *lstrip(char *line)
+{
+ while(isspace(line[0]))
+ line++;
+ return line;
+}
+
+static char *rstrip(char *line)
+{
+ size_t len = strlen(line);
+ while(len > 0 && isspace(line[len-1]))
+ len--;
+ line[len] = 0;
+ return line;
+}
+
static void LoadConfigFromFile(FILE *f)
{
- ConfigBlock *curBlock = cfgBlocks;
+ char curSection[128] = "";
ConfigEntry *ent;
while(fgets(buffer, sizeof(buffer), f))
{
- int i = 0;
+ char *line, *comment;
+ char key[256] = "";
+ char value[256] = "";
- while(isspace(buffer[i]))
- i++;
- if(!buffer[i] || buffer[i] == '#')
- continue;
+ comment = strchr(buffer, '#');
+ if(comment)
+ {
+ *(comment++) = 0;
+ comment = rstrip(lstrip(comment));
+ }
- memmove(buffer, buffer+i, strlen(buffer+i)+1);
+ line = rstrip(lstrip(buffer));
+ if(!line[0])
+ continue;
- if(buffer[0] == '[')
+ if(line[0] == '[')
{
- ConfigBlock *nextBlock;
- unsigned int i;
+ char *section = line+1;
+ char *endsection;
- i = 1;
- while(buffer[i] && buffer[i] != ']')
- i++;
-
- if(!buffer[i])
+ endsection = strchr(section, ']');
+ if(!endsection || section == endsection || endsection[1] != 0)
{
- ERR("config parse error: bad line \"%s\"\n", buffer);
+ ERR("config parse error: bad line \"%s\"\n", line);
continue;
}
- buffer[i] = 0;
-
- do {
- i++;
- if(buffer[i] && !isspace(buffer[i]))
- {
- if(buffer[i] != '#')
- WARN("config warning: extra data after block: \"%s\"\n", buffer+i);
- break;
- }
- } while(buffer[i]);
-
- nextBlock = NULL;
- for(i = 0;i < cfgCount;i++)
- {
- if(strcasecmp(cfgBlocks[i].name, buffer+1) == 0)
- {
- nextBlock = cfgBlocks+i;
- TRACE("found block '%s'\n", nextBlock->name);
- break;
- }
- }
+ *endsection = 0;
- if(!nextBlock)
+ if(strcasecmp(section, "general") == 0)
+ curSection[0] = 0;
+ else
{
- nextBlock = realloc(cfgBlocks, (cfgCount+1)*sizeof(ConfigBlock));
- if(!nextBlock)
- {
- ERR("config parse error: error reallocating config blocks\n");
- continue;
- }
- cfgBlocks = nextBlock;
- nextBlock = cfgBlocks+cfgCount;
- cfgCount++;
-
- nextBlock->name = strdup(buffer+1);
- nextBlock->entries = NULL;
- nextBlock->entryCount = 0;
-
- TRACE("found new block '%s'\n", nextBlock->name);
+ strncpy(curSection, section, sizeof(curSection)-1);
+ curSection[sizeof(curSection)-1] = 0;
}
- curBlock = nextBlock;
- continue;
- }
- /* Look for the option name */
- i = 0;
- while(buffer[i] && buffer[i] != '#' && buffer[i] != '=' &&
- !isspace(buffer[i]))
- i++;
-
- if(!buffer[i] || buffer[i] == '#' || i == 0)
- {
- ERR("config parse error: malformed option line: \"%s\"\n", buffer);
continue;
}
- /* Seperate the option */
- if(buffer[i] != '=')
+ if(sscanf(line, "%255[^=] = \"%255[^\"]\"", key, value) == 2 ||
+ sscanf(line, "%255[^=] = '%255[^\']'", key, value) == 2 ||
+ sscanf(line, "%255[^=] = %255[^\n]", key, value) == 2)
{
- buffer[i++] = 0;
-
- while(isspace(buffer[i]))
- i++;
- if(buffer[i] != '=')
- {
- ERR("config parse error: option without a value: \"%s\"\n", buffer);
- continue;
- }
- }
- /* Find the start of the value */
- buffer[i++] = 0;
- while(isspace(buffer[i]))
- i++;
+ /* sscanf doesn't handle '' or "" as empty values, so clip it
+ * manually. */
+ if(strcmp(value, "\"\"") == 0 || strcmp(value, "''") == 0)
+ value[0] = 0;
+ }
+ else if(sscanf(line, "%255[^=] %255[=]", key, value) == 2)
+ {
+ /* Special case for 'key =' */
+ value[0] = 0;
+ }
+ else
+ {
+ ERR("config parse error: malformed option line: \"%s\"\n\n", line);
+ continue;
+ }
+ rstrip(key);
+
+ if(curSection[0] != 0)
+ {
+ size_t len = strlen(curSection);
+ memmove(&key[len+1], key, sizeof(key)-1-len);
+ key[len] = '/';
+ memcpy(key, curSection, len);
+ }
/* Check if we already have this option set */
- ent = curBlock->entries;
- while((unsigned int)(ent-curBlock->entries) < curBlock->entryCount)
+ ent = cfgBlock.entries;
+ while((unsigned int)(ent-cfgBlock.entries) < cfgBlock.entryCount)
{
- if(strcasecmp(ent->key, buffer) == 0)
+ if(strcasecmp(ent->key, key) == 0)
break;
ent++;
}
- if((unsigned int)(ent-curBlock->entries) >= curBlock->entryCount)
+ if((unsigned int)(ent-cfgBlock.entries) >= cfgBlock.entryCount)
{
/* Allocate a new option entry */
- ent = realloc(curBlock->entries, (curBlock->entryCount+1)*sizeof(ConfigEntry));
+ ent = realloc(cfgBlock.entries, (cfgBlock.entryCount+1)*sizeof(ConfigEntry));
if(!ent)
{
ERR("config parse error: error reallocating config entries\n");
continue;
}
- curBlock->entries = ent;
- ent = curBlock->entries + curBlock->entryCount;
- curBlock->entryCount++;
+ cfgBlock.entries = ent;
+ ent = cfgBlock.entries + cfgBlock.entryCount;
+ cfgBlock.entryCount++;
- ent->key = strdup(buffer);
+ ent->key = strdup(key);
ent->value = NULL;
}
- /* Look for the end of the line (Null term, new-line, or #-symbol) and
- eat up the trailing whitespace */
- memmove(buffer, buffer+i, strlen(buffer+i)+1);
-
- i = 0;
- while(buffer[i] && buffer[i] != '#' && buffer[i] != '\n')
- i++;
- do {
- i--;
- } while(i >= 0 && isspace(buffer[i]));
- buffer[++i] = 0;
-
free(ent->value);
- ent->value = strdup(buffer);
+ ent->value = strdup(value);
TRACE("found '%s' = '%s'\n", ent->key, ent->value);
}
@@ -210,15 +182,13 @@ void ReadALConfig(void)
const char *str;
FILE *f;
- cfgBlocks = calloc(1, sizeof(ConfigBlock));
- cfgBlocks->name = strdup("general");
- cfgCount = 1;
-
#ifdef _WIN32
if(SHGetSpecialFolderPathA(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
{
size_t p = strlen(buffer);
snprintf(buffer+p, sizeof(buffer)-p, "\\alsoft.ini");
+
+ TRACE("Loading config %s...\n", buffer);
f = fopen(buffer, "rt");
if(f)
{
@@ -227,15 +197,75 @@ void ReadALConfig(void)
}
}
#else
- f = fopen("/etc/openal/alsoft.conf", "r");
+ str = "/etc/openal/alsoft.conf";
+
+ TRACE("Loading config %s...\n", str);
+ f = fopen(str, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
+
+ if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0)
+ str = "/etc/xdg";
+ strncpy(buffer, str, sizeof(buffer)-1);
+ buffer[sizeof(buffer)-1] = 0;
+ /* Go through the list in reverse, since "the order of base directories
+ * denotes their importance; the first directory listed is the most
+ * important". Ergo, we need to load the settings from the later dirs
+ * first so that the settings in the earlier dirs override them.
+ */
+ while(1)
+ {
+ char *next = strrchr(buffer, ':');
+ if(next) *(next++) = 0;
+ else next = buffer;
+
+ if(next[0] != '/')
+ WARN("Ignoring XDG config dir: %s\n", next);
+ else
+ {
+ size_t len = strlen(next);
+ strncpy(next+len, "/alsoft.conf", buffer+sizeof(buffer)-next-len);
+ buffer[sizeof(buffer)-1] = 0;
+
+ TRACE("Loading config %s...\n", next);
+ f = fopen(next, "r");
+ if(f)
+ {
+ LoadConfigFromFile(f);
+ fclose(f);
+ }
+ }
+ if(next == buffer)
+ break;
+ }
+
if((str=getenv("HOME")) != NULL && *str)
{
snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", str);
+
+ TRACE("Loading config %s...\n", buffer);
+ f = fopen(buffer, "r");
+ if(f)
+ {
+ LoadConfigFromFile(f);
+ fclose(f);
+ }
+ }
+
+ if((str=getenv("XDG_CONFIG_HOME")) != NULL && str[0] != 0)
+ snprintf(buffer, sizeof(buffer), "%s/%s", str, "alsoft.conf");
+ else
+ {
+ buffer[0] = 0;
+ if((str=getenv("HOME")) != NULL && str[0] != 0)
+ snprintf(buffer, sizeof(buffer), "%s/.config/%s", str, "alsoft.conf");
+ }
+ if(buffer[0] != 0)
+ {
+ TRACE("Loading config %s...\n", buffer);
f = fopen(buffer, "r");
if(f)
{
@@ -244,8 +274,10 @@ void ReadALConfig(void)
}
}
#endif
+
if((str=getenv("ALSOFT_CONF")) != NULL && *str)
{
+ TRACE("Loading config %s...\n", str);
f = fopen(str, "r");
if(f)
{
@@ -259,51 +291,42 @@ void FreeALConfig(void)
{
unsigned int i;
- for(i = 0;i < cfgCount;i++)
+ for(i = 0;i < cfgBlock.entryCount;i++)
{
- unsigned int j;
- for(j = 0;j < cfgBlocks[i].entryCount;j++)
- {
- free(cfgBlocks[i].entries[j].key);
- free(cfgBlocks[i].entries[j].value);
- }
- free(cfgBlocks[i].entries);
- free(cfgBlocks[i].name);
+ free(cfgBlock.entries[i].key);
+ free(cfgBlock.entries[i].value);
}
- free(cfgBlocks);
- cfgBlocks = NULL;
- cfgCount = 0;
+ free(cfgBlock.entries);
}
const char *GetConfigValue(const char *blockName, const char *keyName, const char *def)
{
- unsigned int i, j;
+ unsigned int i;
+ char key[256];
if(!keyName)
return def;
- if(!blockName)
- blockName = "general";
-
- for(i = 0;i < cfgCount;i++)
+ if(blockName && strcasecmp(blockName, "general") != 0)
+ snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
+ else
{
- if(strcasecmp(cfgBlocks[i].name, blockName) != 0)
- continue;
+ strncpy(key, keyName, sizeof(key)-1);
+ key[sizeof(key)-1] = 0;
+ }
- for(j = 0;j < cfgBlocks[i].entryCount;j++)
+ for(i = 0;i < cfgBlock.entryCount;i++)
+ {
+ if(strcasecmp(cfgBlock.entries[i].key, key) == 0)
{
- if(strcasecmp(cfgBlocks[i].entries[j].key, keyName) == 0)
- {
- TRACE("Found %s:%s = \"%s\"\n", blockName, keyName,
- cfgBlocks[i].entries[j].value);
- if(cfgBlocks[i].entries[j].value[0])
- return cfgBlocks[i].entries[j].value;
- return def;
- }
+ TRACE("Found %s = \"%s\"\n", key, cfgBlock.entries[i].value);
+ if(cfgBlock.entries[i].value[0])
+ return cfgBlock.entries[i].value;
+ return def;
}
}
- TRACE("Key %s:%s not found\n", blockName, keyName);
+ TRACE("Key %s not found\n", key);
return def;
}
diff --git a/Alc/alcDedicated.c b/Alc/alcDedicated.c
deleted file mode 100644
index 1d2cbc42..00000000
--- a/Alc/alcDedicated.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2011 by Chris Robinson.
- * 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 <stdlib.h>
-
-#include "alMain.h"
-#include "alFilter.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-
-
-typedef struct ALdedicatedState {
- // Must be first in all effects!
- ALeffectState state;
-
- ALfloat gains[MaxChannels];
-} ALdedicatedState;
-
-
-static ALvoid DedicatedDestroy(ALeffectState *effect)
-{
- ALdedicatedState *state = (ALdedicatedState*)effect;
- free(state);
-}
-
-static ALboolean DedicatedDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
-{
- (void)effect;
- (void)Device;
- return AL_TRUE;
-}
-
-static ALvoid DedicatedUpdate(ALeffectState *effect, ALCdevice *device, const ALeffectslot *Slot)
-{
- ALdedicatedState *state = (ALdedicatedState*)effect;
- ALfloat Gain;
- ALsizei s;
-
- Gain = Slot->Gain * Slot->effect.Dedicated.Gain;
- for(s = 0;s < MaxChannels;s++)
- state->gains[s] = 0.0f;
-
- if(Slot->effect.type == AL_EFFECT_DEDICATED_DIALOGUE)
- ComputeAngleGains(device, atan2f(0.0f, 1.0f), 0.0f, Gain, state->gains);
- else if(Slot->effect.type == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
- state->gains[LFE] = Gain;
-}
-
-static ALvoid DedicatedProcess(ALeffectState *effect, ALuint SamplesToDo, const ALfloat *RESTRICT SamplesIn, ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE])
-{
- ALdedicatedState *state = (ALdedicatedState*)effect;
- const ALfloat *gains = state->gains;
- ALuint i, c;
-
- for(c = 0;c < MaxChannels;c++)
- {
- for(i = 0;i < SamplesToDo;i++)
- SamplesOut[c][i] = SamplesIn[i] * gains[c];
- }
-}
-
-ALeffectState *DedicatedCreate(void)
-{
- ALdedicatedState *state;
- ALsizei s;
-
- state = malloc(sizeof(*state));
- if(!state)
- return NULL;
-
- state->state.Destroy = DedicatedDestroy;
- state->state.DeviceUpdate = DedicatedDeviceUpdate;
- state->state.Update = DedicatedUpdate;
- state->state.Process = DedicatedProcess;
-
- for(s = 0;s < MaxChannels;s++)
- state->gains[s] = 0.0f;
-
- return &state->state;
-}
diff --git a/Alc/alcEcho.c b/Alc/alcEcho.c
deleted file mode 100644
index 9ca91747..00000000
--- a/Alc/alcEcho.c
+++ /dev/null
@@ -1,184 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2009 by Chris Robinson.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include "alMain.h"
-#include "alFilter.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-
-
-typedef struct ALechoState {
- // Must be first in all effects!
- ALeffectState state;
-
- ALfloat *SampleBuffer;
- ALuint BufferLength;
-
- // The echo is two tap. The delay is the number of samples from before the
- // current offset
- struct {
- ALuint delay;
- } Tap[2];
- ALuint Offset;
- /* The panning gains for the two taps */
- ALfloat Gain[2][MaxChannels];
-
- ALfloat FeedGain;
-
- FILTER iirFilter;
- ALfloat history[2];
-} ALechoState;
-
-static ALvoid EchoDestroy(ALeffectState *effect)
-{
- ALechoState *state = (ALechoState*)effect;
- if(state)
- {
- free(state->SampleBuffer);
- state->SampleBuffer = NULL;
- free(state);
- }
-}
-
-static ALboolean EchoDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
-{
- ALechoState *state = (ALechoState*)effect;
- ALuint maxlen, i;
-
- // Use the next power of 2 for the buffer length, so the tap offsets can be
- // wrapped using a mask instead of a modulo
- maxlen = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
- maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
- maxlen = NextPowerOf2(maxlen);
-
- if(maxlen != state->BufferLength)
- {
- void *temp;
-
- temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
- if(!temp)
- return AL_FALSE;
- state->SampleBuffer = temp;
- state->BufferLength = maxlen;
- }
- for(i = 0;i < state->BufferLength;i++)
- state->SampleBuffer[i] = 0.0f;
-
- return AL_TRUE;
-}
-
-static ALvoid EchoUpdate(ALeffectState *effect, ALCdevice *Device, const ALeffectslot *Slot)
-{
- ALechoState *state = (ALechoState*)effect;
- ALuint frequency = Device->Frequency;
- ALfloat lrpan, cw, g, gain;
- ALfloat dirGain;
- ALuint i;
-
- state->Tap[0].delay = fastf2u(Slot->effect.Echo.Delay * frequency) + 1;
- state->Tap[1].delay = fastf2u(Slot->effect.Echo.LRDelay * frequency);
- state->Tap[1].delay += state->Tap[0].delay;
-
- lrpan = Slot->effect.Echo.Spread;
-
- state->FeedGain = Slot->effect.Echo.Feedback;
-
- cw = cosf(F_PI*2.0f * LOWPASSFREQREF / frequency);
- g = 1.0f - Slot->effect.Echo.Damping;
- state->iirFilter.coeff = lpCoeffCalc(g, cw);
-
- gain = Slot->Gain;
- for(i = 0;i < MaxChannels;i++)
- {
- state->Gain[0][i] = 0.0f;
- state->Gain[1][i] = 0.0f;
- }
-
- dirGain = fabsf(lrpan);
-
- /* First tap panning */
- ComputeAngleGains(Device, atan2f(-lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[0]);
-
- /* Second tap panning */
- ComputeAngleGains(Device, atan2f(+lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[1]);
-}
-
-static ALvoid EchoProcess(ALeffectState *effect, ALuint SamplesToDo, const ALfloat *RESTRICT SamplesIn, ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE])
-{
- ALechoState *state = (ALechoState*)effect;
- const ALuint mask = state->BufferLength-1;
- const ALuint tap1 = state->Tap[0].delay;
- const ALuint tap2 = state->Tap[1].delay;
- ALuint offset = state->Offset;
- ALfloat smp;
- ALuint i, k;
-
- for(i = 0;i < SamplesToDo;i++,offset++)
- {
- /* First tap */
- smp = state->SampleBuffer[(offset-tap1) & mask];
- for(k = 0;k < MaxChannels;k++)
- SamplesOut[k][i] += smp * state->Gain[0][k];
-
- /* Second tap */
- smp = state->SampleBuffer[(offset-tap2) & mask];
- for(k = 0;k < MaxChannels;k++)
- SamplesOut[k][i] += smp * state->Gain[1][k];
-
- // Apply damping and feedback gain to the second tap, and mix in the
- // new sample
- smp = lpFilter2P(&state->iirFilter, 0, smp+SamplesIn[i]);
- state->SampleBuffer[offset&mask] = smp * state->FeedGain;
- }
- state->Offset = offset;
-}
-
-ALeffectState *EchoCreate(void)
-{
- ALechoState *state;
-
- state = malloc(sizeof(*state));
- if(!state)
- return NULL;
-
- state->state.Destroy = EchoDestroy;
- state->state.DeviceUpdate = EchoDeviceUpdate;
- state->state.Update = EchoUpdate;
- state->state.Process = EchoProcess;
-
- state->BufferLength = 0;
- state->SampleBuffer = NULL;
-
- state->Tap[0].delay = 0;
- state->Tap[1].delay = 0;
- state->Offset = 0;
-
- state->iirFilter.coeff = 0.0f;
- state->iirFilter.history[0] = 0.0f;
- state->iirFilter.history[1] = 0.0f;
-
- return &state->state;
-}
diff --git a/Alc/alcModulator.c b/Alc/alcModulator.c
deleted file mode 100644
index de062151..00000000
--- a/Alc/alcModulator.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2009 by Chris Robinson.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include "alMain.h"
-#include "alFilter.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-
-
-typedef struct ALmodulatorState {
- // Must be first in all effects!
- ALeffectState state;
-
- enum {
- SINUSOID,
- SAWTOOTH,
- SQUARE
- } Waveform;
-
- ALuint index;
- ALuint step;
-
- ALfloat Gain[MaxChannels];
-
- FILTER iirFilter;
- ALfloat history[1];
-} ALmodulatorState;
-
-#define WAVEFORM_FRACBITS 16
-#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
-#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
-
-static __inline ALfloat Sin(ALuint index)
-{
- return sinf(index * (F_PI*2.0f / WAVEFORM_FRACONE));
-}
-
-static __inline ALfloat Saw(ALuint index)
-{
- return index*(2.0f/WAVEFORM_FRACONE) - 1.0f;
-}
-
-static __inline ALfloat Square(ALuint index)
-{
- return ((index>>(WAVEFORM_FRACBITS-1))&1)*2.0f - 1.0f;
-}
-
-
-static __inline ALfloat hpFilter1P(FILTER *iir, ALuint offset, ALfloat input)
-{
- ALfloat *history = &iir->history[offset];
- ALfloat a = iir->coeff;
- ALfloat output = input;
-
- output = output + (history[0]-output)*a;
- history[0] = output;
-
- return input - output;
-}
-
-
-#define DECL_TEMPLATE(func) \
-static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \
- const ALfloat *RESTRICT SamplesIn, \
- ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE]) \
-{ \
- const ALuint step = state->step; \
- ALuint index = state->index; \
- ALfloat samp; \
- ALuint i, k; \
- \
- for(i = 0;i < SamplesToDo;i++) \
- { \
- samp = SamplesIn[i]; \
- \
- index += step; \
- index &= WAVEFORM_FRACMASK; \
- samp *= func(index); \
- \
- samp = hpFilter1P(&state->iirFilter, 0, samp); \
- \
- for(k = 0;k < MaxChannels;k++) \
- SamplesOut[k][i] += state->Gain[k] * samp; \
- } \
- state->index = index; \
-}
-
-DECL_TEMPLATE(Sin)
-DECL_TEMPLATE(Saw)
-DECL_TEMPLATE(Square)
-
-#undef DECL_TEMPLATE
-
-
-static ALvoid ModulatorDestroy(ALeffectState *effect)
-{
- ALmodulatorState *state = (ALmodulatorState*)effect;
- free(state);
-}
-
-static ALboolean ModulatorDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
-{
- return AL_TRUE;
- (void)effect;
- (void)Device;
-}
-
-static ALvoid ModulatorUpdate(ALeffectState *effect, ALCdevice *Device, const ALeffectslot *Slot)
-{
- ALmodulatorState *state = (ALmodulatorState*)effect;
- ALfloat gain, cw, a = 0.0f;
- ALuint index;
-
- if(Slot->effect.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
- state->Waveform = SINUSOID;
- else if(Slot->effect.Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
- state->Waveform = SAWTOOTH;
- else if(Slot->effect.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)
- state->Waveform = SQUARE;
-
- state->step = fastf2u(Slot->effect.Modulator.Frequency*WAVEFORM_FRACONE /
- Device->Frequency);
- if(state->step == 0) state->step = 1;
-
- cw = cosf(F_PI*2.0f * Slot->effect.Modulator.HighPassCutoff /
- Device->Frequency);
- a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
- state->iirFilter.coeff = a;
-
- gain = sqrtf(1.0f/Device->NumChan);
- gain *= Slot->Gain;
- for(index = 0;index < MaxChannels;index++)
- state->Gain[index] = 0.0f;
- for(index = 0;index < Device->NumChan;index++)
- {
- enum Channel chan = Device->Speaker2Chan[index];
- state->Gain[chan] = gain;
- }
-}
-
-static ALvoid ModulatorProcess(ALeffectState *effect, ALuint SamplesToDo, const ALfloat *RESTRICT SamplesIn, ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE])
-{
- ALmodulatorState *state = (ALmodulatorState*)effect;
-
- switch(state->Waveform)
- {
- case SINUSOID:
- ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut);
- break;
-
- case SAWTOOTH:
- ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut);
- break;
-
- case SQUARE:
- ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut);
- break;
- }
-}
-
-ALeffectState *ModulatorCreate(void)
-{
- ALmodulatorState *state;
-
- state = malloc(sizeof(*state));
- if(!state)
- return NULL;
-
- state->state.Destroy = ModulatorDestroy;
- state->state.DeviceUpdate = ModulatorDeviceUpdate;
- state->state.Update = ModulatorUpdate;
- state->state.Process = ModulatorProcess;
-
- state->index = 0;
- state->step = 1;
-
- state->iirFilter.coeff = 0.0f;
- state->iirFilter.history[0] = 0.0f;
-
- return &state->state;
-}
diff --git a/Alc/alcRing.c b/Alc/alcRing.c
index 2d9d5069..f831860f 100644
--- a/Alc/alcRing.c
+++ b/Alc/alcRing.c
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include "alMain.h"
+#include "compat.h"
struct RingBuffer {
diff --git a/Alc/alcThread.c b/Alc/alcThread.c
deleted file mode 100644
index c2f38031..00000000
--- a/Alc/alcThread.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/**
- * 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 <stdlib.h>
-
-#include "alMain.h"
-#include "alThunk.h"
-
-#define THREAD_STACK_SIZE (1*1024*1024) /* 1MB */
-
-#ifdef _WIN32
-
-typedef struct {
- ALuint (*func)(ALvoid*);
- ALvoid *ptr;
- HANDLE thread;
-} ThreadInfo;
-
-static DWORD CALLBACK StarterFunc(void *ptr)
-{
- ThreadInfo *inf = (ThreadInfo*)ptr;
- ALint ret;
-
- ret = inf->func(inf->ptr);
- ExitThread((DWORD)ret);
-
- return (DWORD)ret;
-}
-
-ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr)
-{
- DWORD dummy;
- ThreadInfo *inf = malloc(sizeof(ThreadInfo));
- if(!inf) return 0;
-
- inf->func = func;
- inf->ptr = ptr;
-
- inf->thread = CreateThread(NULL, THREAD_STACK_SIZE, StarterFunc, inf, 0, &dummy);
- if(!inf->thread)
- {
- free(inf);
- return NULL;
- }
-
- return inf;
-}
-
-ALuint StopThread(ALvoid *thread)
-{
- ThreadInfo *inf = thread;
- DWORD ret = 0;
-
- WaitForSingleObject(inf->thread, INFINITE);
- GetExitCodeThread(inf->thread, &ret);
- CloseHandle(inf->thread);
-
- free(inf);
-
- return (ALuint)ret;
-}
-
-#else
-
-#include <pthread.h>
-
-typedef struct {
- ALuint (*func)(ALvoid*);
- ALvoid *ptr;
- ALuint ret;
- pthread_t thread;
-} ThreadInfo;
-
-static void *StarterFunc(void *ptr)
-{
- ThreadInfo *inf = (ThreadInfo*)ptr;
- inf->ret = inf->func(inf->ptr);
- return NULL;
-}
-
-ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr)
-{
- pthread_attr_t attr;
- ThreadInfo *inf = malloc(sizeof(ThreadInfo));
- if(!inf) return NULL;
-
- if(pthread_attr_init(&attr) != 0)
- {
- free(inf);
- return NULL;
- }
- if(pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE) != 0)
- {
- pthread_attr_destroy(&attr);
- free(inf);
- return NULL;
- }
-
- inf->func = func;
- inf->ptr = ptr;
- if(pthread_create(&inf->thread, &attr, StarterFunc, inf) != 0)
- {
- pthread_attr_destroy(&attr);
- free(inf);
- return NULL;
- }
- pthread_attr_destroy(&attr);
-
- return inf;
-}
-
-ALuint StopThread(ALvoid *thread)
-{
- ThreadInfo *inf = thread;
- ALuint ret;
-
- pthread_join(inf->thread, NULL);
- ret = inf->ret;
-
- free(inf);
-
- return ret;
-}
-
-#endif
diff --git a/Alc/atomic.h b/Alc/atomic.h
new file mode 100644
index 00000000..1dd8f9dc
--- /dev/null
+++ b/Alc/atomic.h
@@ -0,0 +1,180 @@
+#ifndef AL_ATOMIC_H
+#define AL_ATOMIC_H
+
+
+typedef void *volatile XchgPtr;
+
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && !defined(__QNXNTO__)
+typedef unsigned int RefCount;
+inline RefCount IncrementRef(volatile RefCount *ptr)
+{ return __sync_add_and_fetch(ptr, 1); }
+inline RefCount DecrementRef(volatile RefCount *ptr)
+{ return __sync_sub_and_fetch(ptr, 1); }
+
+inline int ExchangeInt(volatile int *ptr, int newval)
+{
+ return __sync_lock_test_and_set(ptr, newval);
+}
+inline void *ExchangePtr(XchgPtr *ptr, void *newval)
+{
+ return __sync_lock_test_and_set(ptr, newval);
+}
+inline ALboolean CompExchangeInt(volatile int *ptr, int oldval, int newval)
+{
+ return __sync_bool_compare_and_swap(ptr, oldval, newval);
+}
+inline ALboolean CompExchangePtr(XchgPtr *ptr, void *oldval, void *newval)
+{
+ return __sync_bool_compare_and_swap(ptr, oldval, newval);
+}
+
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+inline unsigned int xaddl(volatile unsigned int *dest, int incr)
+{
+ unsigned int ret;
+ __asm__ __volatile__("lock; xaddl %0,(%1)"
+ : "=r" (ret)
+ : "r" (dest), "0" (incr)
+ : "memory");
+ return ret;
+}
+
+typedef unsigned int RefCount;
+inline RefCount IncrementRef(volatile RefCount *ptr)
+{ return xaddl(ptr, 1)+1; }
+inline RefCount DecrementRef(volatile RefCount *ptr)
+{ return xaddl(ptr, -1)-1; }
+
+inline int ExchangeInt(volatile int *dest, int newval)
+{
+ int ret;
+ __asm__ __volatile__("lock; xchgl %0,(%1)"
+ : "=r" (ret)
+ : "r" (dest), "0" (newval)
+ : "memory");
+ return ret;
+}
+
+inline ALboolean CompExchangeInt(volatile int *dest, int oldval, int newval)
+{
+ int ret;
+ __asm__ __volatile__("lock; cmpxchgl %2,(%1)"
+ : "=a" (ret)
+ : "r" (dest), "r" (newval), "0" (oldval)
+ : "memory");
+ return ret == oldval;
+}
+
+inline void *ExchangePtr(XchgPtr *dest, void *newval)
+{
+ void *ret;
+ __asm__ __volatile__(
+#ifdef __i386__
+ "lock; xchgl %0,(%1)"
+#else
+ "lock; xchgq %0,(%1)"
+#endif
+ : "=r" (ret)
+ : "r" (dest), "0" (newval)
+ : "memory"
+ );
+ return ret;
+}
+
+inline ALboolean CompExchangePtr(XchgPtr *dest, void *oldval, void *newval)
+{
+ void *ret;
+ __asm__ __volatile__(
+#ifdef __i386__
+ "lock; cmpxchgl %2,(%1)"
+#else
+ "lock; cmpxchgq %2,(%1)"
+#endif
+ : "=a" (ret)
+ : "r" (dest), "r" (newval), "0" (oldval)
+ : "memory"
+ );
+ return ret == oldval;
+}
+
+#elif defined(_WIN32)
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+typedef LONG RefCount;
+inline RefCount IncrementRef(volatile RefCount *ptr)
+{ return InterlockedIncrement(ptr); }
+inline RefCount DecrementRef(volatile RefCount *ptr)
+{ return InterlockedDecrement(ptr); }
+
+extern ALbyte LONG_size_does_not_match_int[(sizeof(LONG)==sizeof(int))?1:-1];
+
+inline int ExchangeInt(volatile int *ptr, int newval)
+{
+ union {
+ volatile int *i;
+ volatile LONG *l;
+ } u = { ptr };
+ return InterlockedExchange(u.l, newval);
+}
+inline void *ExchangePtr(XchgPtr *ptr, void *newval)
+{
+ return InterlockedExchangePointer(ptr, newval);
+}
+inline ALboolean CompExchangeInt(volatile int *ptr, int oldval, int newval)
+{
+ union {
+ volatile int *i;
+ volatile LONG *l;
+ } u = { ptr };
+ return InterlockedCompareExchange(u.l, newval, oldval) == oldval;
+}
+inline ALboolean CompExchangePtr(XchgPtr *ptr, void *oldval, void *newval)
+{
+ return InterlockedCompareExchangePointer(ptr, newval, oldval) == oldval;
+}
+
+#elif defined(__APPLE__)
+
+#include <libkern/OSAtomic.h>
+
+typedef int32_t RefCount;
+inline RefCount IncrementRef(volatile RefCount *ptr)
+{ return OSAtomicIncrement32Barrier(ptr); }
+inline RefCount DecrementRef(volatile RefCount *ptr)
+{ return OSAtomicDecrement32Barrier(ptr); }
+
+inline int ExchangeInt(volatile int *ptr, int newval)
+{
+ /* Really? No regular old atomic swap? */
+ int oldval;
+ do {
+ oldval = *ptr;
+ } while(!OSAtomicCompareAndSwap32Barrier(oldval, newval, ptr));
+ return oldval;
+}
+inline void *ExchangePtr(XchgPtr *ptr, void *newval)
+{
+ void *oldval;
+ do {
+ oldval = *ptr;
+ } while(!OSAtomicCompareAndSwapPtrBarrier(oldval, newval, ptr));
+ return oldval;
+}
+inline ALboolean CompExchangeInt(volatile int *ptr, int oldval, int newval)
+{
+ return OSAtomicCompareAndSwap32Barrier(oldval, newval, ptr);
+}
+inline ALboolean CompExchangePtr(XchgPtr *ptr, void *oldval, void *newval)
+{
+ return OSAtomicCompareAndSwapPtrBarrier(oldval, newval, ptr);
+}
+
+#else
+#error "No atomic functions available on this platform!"
+typedef ALuint RefCount;
+#endif
+
+#endif /* AL_ATOMIC_H */
diff --git a/Alc/backends/alsa.c b/Alc/backends/alsa.c
index 5ff6307f..874b31c3 100644
--- a/Alc/backends/alsa.c
+++ b/Alc/backends/alsa.c
@@ -26,6 +26,10 @@
#include "alMain.h"
#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
#include <alsa/asoundlib.h>
@@ -34,80 +38,83 @@ static const ALCchar alsaDevice[] = "ALSA Default";
#ifdef HAVE_DYNLOAD
+#define ALSA_FUNCS(MAGIC) \
+ MAGIC(snd_strerror); \
+ MAGIC(snd_pcm_open); \
+ MAGIC(snd_pcm_close); \
+ MAGIC(snd_pcm_nonblock); \
+ MAGIC(snd_pcm_frames_to_bytes); \
+ MAGIC(snd_pcm_bytes_to_frames); \
+ MAGIC(snd_pcm_hw_params_malloc); \
+ MAGIC(snd_pcm_hw_params_free); \
+ MAGIC(snd_pcm_hw_params_any); \
+ MAGIC(snd_pcm_hw_params_current); \
+ MAGIC(snd_pcm_hw_params_set_access); \
+ MAGIC(snd_pcm_hw_params_set_format); \
+ MAGIC(snd_pcm_hw_params_set_channels); \
+ MAGIC(snd_pcm_hw_params_set_periods_near); \
+ MAGIC(snd_pcm_hw_params_set_rate_near); \
+ MAGIC(snd_pcm_hw_params_set_rate); \
+ MAGIC(snd_pcm_hw_params_set_rate_resample); \
+ MAGIC(snd_pcm_hw_params_set_buffer_time_near); \
+ MAGIC(snd_pcm_hw_params_set_period_time_near); \
+ MAGIC(snd_pcm_hw_params_set_buffer_size_near); \
+ MAGIC(snd_pcm_hw_params_set_period_size_near); \
+ MAGIC(snd_pcm_hw_params_set_buffer_size_min); \
+ MAGIC(snd_pcm_hw_params_get_buffer_time_min); \
+ MAGIC(snd_pcm_hw_params_get_buffer_time_max); \
+ MAGIC(snd_pcm_hw_params_get_period_time_min); \
+ MAGIC(snd_pcm_hw_params_get_period_time_max); \
+ MAGIC(snd_pcm_hw_params_get_buffer_size); \
+ MAGIC(snd_pcm_hw_params_get_period_size); \
+ MAGIC(snd_pcm_hw_params_get_access); \
+ MAGIC(snd_pcm_hw_params_get_periods); \
+ MAGIC(snd_pcm_hw_params_test_format); \
+ MAGIC(snd_pcm_hw_params_test_channels); \
+ MAGIC(snd_pcm_hw_params); \
+ MAGIC(snd_pcm_sw_params_malloc); \
+ MAGIC(snd_pcm_sw_params_current); \
+ MAGIC(snd_pcm_sw_params_set_avail_min); \
+ MAGIC(snd_pcm_sw_params_set_stop_threshold); \
+ MAGIC(snd_pcm_sw_params); \
+ MAGIC(snd_pcm_sw_params_free); \
+ MAGIC(snd_pcm_prepare); \
+ MAGIC(snd_pcm_start); \
+ MAGIC(snd_pcm_resume); \
+ MAGIC(snd_pcm_reset); \
+ MAGIC(snd_pcm_wait); \
+ MAGIC(snd_pcm_delay); \
+ MAGIC(snd_pcm_state); \
+ MAGIC(snd_pcm_avail_update); \
+ MAGIC(snd_pcm_areas_silence); \
+ MAGIC(snd_pcm_mmap_begin); \
+ MAGIC(snd_pcm_mmap_commit); \
+ MAGIC(snd_pcm_readi); \
+ MAGIC(snd_pcm_writei); \
+ MAGIC(snd_pcm_drain); \
+ MAGIC(snd_pcm_drop); \
+ MAGIC(snd_pcm_recover); \
+ MAGIC(snd_pcm_info_malloc); \
+ MAGIC(snd_pcm_info_free); \
+ MAGIC(snd_pcm_info_set_device); \
+ MAGIC(snd_pcm_info_set_subdevice); \
+ MAGIC(snd_pcm_info_set_stream); \
+ MAGIC(snd_pcm_info_get_name); \
+ MAGIC(snd_ctl_pcm_next_device); \
+ MAGIC(snd_ctl_pcm_info); \
+ MAGIC(snd_ctl_open); \
+ MAGIC(snd_ctl_close); \
+ MAGIC(snd_ctl_card_info_malloc); \
+ MAGIC(snd_ctl_card_info_free); \
+ MAGIC(snd_ctl_card_info); \
+ MAGIC(snd_ctl_card_info_get_name); \
+ MAGIC(snd_ctl_card_info_get_id); \
+ MAGIC(snd_card_next); \
+ MAGIC(snd_config_update_free_global)
+
static void *alsa_handle;
-#define MAKE_FUNC(f) static typeof(f) * p##f
-MAKE_FUNC(snd_strerror);
-MAKE_FUNC(snd_pcm_open);
-MAKE_FUNC(snd_pcm_close);
-MAKE_FUNC(snd_pcm_nonblock);
-MAKE_FUNC(snd_pcm_frames_to_bytes);
-MAKE_FUNC(snd_pcm_bytes_to_frames);
-MAKE_FUNC(snd_pcm_hw_params_malloc);
-MAKE_FUNC(snd_pcm_hw_params_free);
-MAKE_FUNC(snd_pcm_hw_params_any);
-MAKE_FUNC(snd_pcm_hw_params_current);
-MAKE_FUNC(snd_pcm_hw_params_set_access);
-MAKE_FUNC(snd_pcm_hw_params_set_format);
-MAKE_FUNC(snd_pcm_hw_params_set_channels);
-MAKE_FUNC(snd_pcm_hw_params_set_periods_near);
-MAKE_FUNC(snd_pcm_hw_params_set_rate_near);
-MAKE_FUNC(snd_pcm_hw_params_set_rate);
-MAKE_FUNC(snd_pcm_hw_params_set_rate_resample);
-MAKE_FUNC(snd_pcm_hw_params_set_buffer_time_near);
-MAKE_FUNC(snd_pcm_hw_params_set_period_time_near);
-MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_near);
-MAKE_FUNC(snd_pcm_hw_params_set_period_size_near);
-MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_min);
-MAKE_FUNC(snd_pcm_hw_params_get_buffer_time_min);
-MAKE_FUNC(snd_pcm_hw_params_get_buffer_time_max);
-MAKE_FUNC(snd_pcm_hw_params_get_period_time_min);
-MAKE_FUNC(snd_pcm_hw_params_get_period_time_max);
-MAKE_FUNC(snd_pcm_hw_params_get_buffer_size);
-MAKE_FUNC(snd_pcm_hw_params_get_period_size);
-MAKE_FUNC(snd_pcm_hw_params_get_access);
-MAKE_FUNC(snd_pcm_hw_params_get_periods);
-MAKE_FUNC(snd_pcm_hw_params_test_format);
-MAKE_FUNC(snd_pcm_hw_params_test_channels);
-MAKE_FUNC(snd_pcm_hw_params);
-MAKE_FUNC(snd_pcm_sw_params_malloc);
-MAKE_FUNC(snd_pcm_sw_params_current);
-MAKE_FUNC(snd_pcm_sw_params_set_avail_min);
-MAKE_FUNC(snd_pcm_sw_params_set_stop_threshold);
-MAKE_FUNC(snd_pcm_sw_params);
-MAKE_FUNC(snd_pcm_sw_params_free);
-MAKE_FUNC(snd_pcm_prepare);
-MAKE_FUNC(snd_pcm_start);
-MAKE_FUNC(snd_pcm_resume);
-MAKE_FUNC(snd_pcm_reset);
-MAKE_FUNC(snd_pcm_wait);
-MAKE_FUNC(snd_pcm_delay);
-MAKE_FUNC(snd_pcm_state);
-MAKE_FUNC(snd_pcm_avail_update);
-MAKE_FUNC(snd_pcm_areas_silence);
-MAKE_FUNC(snd_pcm_mmap_begin);
-MAKE_FUNC(snd_pcm_mmap_commit);
-MAKE_FUNC(snd_pcm_readi);
-MAKE_FUNC(snd_pcm_writei);
-MAKE_FUNC(snd_pcm_drain);
-MAKE_FUNC(snd_pcm_drop);
-MAKE_FUNC(snd_pcm_recover);
-MAKE_FUNC(snd_pcm_info_malloc);
-MAKE_FUNC(snd_pcm_info_free);
-MAKE_FUNC(snd_pcm_info_set_device);
-MAKE_FUNC(snd_pcm_info_set_subdevice);
-MAKE_FUNC(snd_pcm_info_set_stream);
-MAKE_FUNC(snd_pcm_info_get_name);
-MAKE_FUNC(snd_ctl_pcm_next_device);
-MAKE_FUNC(snd_ctl_pcm_info);
-MAKE_FUNC(snd_ctl_open);
-MAKE_FUNC(snd_ctl_close);
-MAKE_FUNC(snd_ctl_card_info_malloc);
-MAKE_FUNC(snd_ctl_card_info_free);
-MAKE_FUNC(snd_ctl_card_info);
-MAKE_FUNC(snd_ctl_card_info_get_name);
-MAKE_FUNC(snd_ctl_card_info_get_id);
-MAKE_FUNC(snd_card_next);
-MAKE_FUNC(snd_config_update_free_global);
+#define MAKE_FUNC(f) static __typeof(f) * p##f
+ALSA_FUNCS(MAKE_FUNC);
#undef MAKE_FUNC
#define snd_strerror psnd_strerror
@@ -187,6 +194,8 @@ MAKE_FUNC(snd_config_update_free_global);
static ALCboolean alsa_load(void)
{
+ ALCboolean error = ALC_FALSE;
+
#ifdef HAVE_DYNLOAD
if(!alsa_handle)
{
@@ -194,107 +203,28 @@ static ALCboolean alsa_load(void)
if(!alsa_handle)
return ALC_FALSE;
+ error = ALC_FALSE;
#define LOAD_FUNC(f) do { \
p##f = GetSymbol(alsa_handle, #f); \
if(p##f == NULL) { \
- CloseLib(alsa_handle); \
- alsa_handle = NULL; \
- return ALC_FALSE; \
+ error = ALC_TRUE; \
} \
} while(0)
- LOAD_FUNC(snd_strerror);
- LOAD_FUNC(snd_pcm_open);
- LOAD_FUNC(snd_pcm_close);
- LOAD_FUNC(snd_pcm_nonblock);
- LOAD_FUNC(snd_pcm_frames_to_bytes);
- LOAD_FUNC(snd_pcm_bytes_to_frames);
- LOAD_FUNC(snd_pcm_hw_params_malloc);
- LOAD_FUNC(snd_pcm_hw_params_free);
- LOAD_FUNC(snd_pcm_hw_params_any);
- LOAD_FUNC(snd_pcm_hw_params_current);
- LOAD_FUNC(snd_pcm_hw_params_set_access);
- LOAD_FUNC(snd_pcm_hw_params_set_format);
- LOAD_FUNC(snd_pcm_hw_params_set_channels);
- LOAD_FUNC(snd_pcm_hw_params_set_periods_near);
- LOAD_FUNC(snd_pcm_hw_params_set_rate_near);
- LOAD_FUNC(snd_pcm_hw_params_set_rate);
- LOAD_FUNC(snd_pcm_hw_params_set_rate_resample);
- LOAD_FUNC(snd_pcm_hw_params_set_buffer_time_near);
- LOAD_FUNC(snd_pcm_hw_params_set_period_time_near);
- LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near);
- LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min);
- LOAD_FUNC(snd_pcm_hw_params_set_period_size_near);
- LOAD_FUNC(snd_pcm_hw_params_get_buffer_time_min);
- LOAD_FUNC(snd_pcm_hw_params_get_buffer_time_max);
- LOAD_FUNC(snd_pcm_hw_params_get_period_time_min);
- LOAD_FUNC(snd_pcm_hw_params_get_period_time_max);
- LOAD_FUNC(snd_pcm_hw_params_get_buffer_size);
- LOAD_FUNC(snd_pcm_hw_params_get_period_size);
- LOAD_FUNC(snd_pcm_hw_params_get_access);
- LOAD_FUNC(snd_pcm_hw_params_get_periods);
- LOAD_FUNC(snd_pcm_hw_params_test_format);
- LOAD_FUNC(snd_pcm_hw_params_test_channels);
- LOAD_FUNC(snd_pcm_hw_params);
- LOAD_FUNC(snd_pcm_sw_params_malloc);
- LOAD_FUNC(snd_pcm_sw_params_current);
- LOAD_FUNC(snd_pcm_sw_params_set_avail_min);
- LOAD_FUNC(snd_pcm_sw_params_set_stop_threshold);
- LOAD_FUNC(snd_pcm_sw_params);
- LOAD_FUNC(snd_pcm_sw_params_free);
- LOAD_FUNC(snd_pcm_prepare);
- LOAD_FUNC(snd_pcm_start);
- LOAD_FUNC(snd_pcm_resume);
- LOAD_FUNC(snd_pcm_reset);
- LOAD_FUNC(snd_pcm_wait);
- LOAD_FUNC(snd_pcm_delay);
- LOAD_FUNC(snd_pcm_state);
- LOAD_FUNC(snd_pcm_avail_update);
- LOAD_FUNC(snd_pcm_areas_silence);
- LOAD_FUNC(snd_pcm_mmap_begin);
- LOAD_FUNC(snd_pcm_mmap_commit);
- LOAD_FUNC(snd_pcm_readi);
- LOAD_FUNC(snd_pcm_writei);
- LOAD_FUNC(snd_pcm_drain);
- LOAD_FUNC(snd_pcm_drop);
- LOAD_FUNC(snd_pcm_recover);
- LOAD_FUNC(snd_pcm_info_malloc);
- LOAD_FUNC(snd_pcm_info_free);
- LOAD_FUNC(snd_pcm_info_set_device);
- LOAD_FUNC(snd_pcm_info_set_subdevice);
- LOAD_FUNC(snd_pcm_info_set_stream);
- LOAD_FUNC(snd_pcm_info_get_name);
- LOAD_FUNC(snd_ctl_pcm_next_device);
- LOAD_FUNC(snd_ctl_pcm_info);
- LOAD_FUNC(snd_ctl_open);
- LOAD_FUNC(snd_ctl_close);
- LOAD_FUNC(snd_ctl_card_info_malloc);
- LOAD_FUNC(snd_ctl_card_info_free);
- LOAD_FUNC(snd_ctl_card_info);
- LOAD_FUNC(snd_ctl_card_info_get_name);
- LOAD_FUNC(snd_ctl_card_info_get_id);
- LOAD_FUNC(snd_card_next);
- LOAD_FUNC(snd_config_update_free_global);
+ ALSA_FUNCS(LOAD_FUNC);
#undef LOAD_FUNC
+
+ if(error)
+ {
+ CloseLib(alsa_handle);
+ alsa_handle = NULL;
+ return ALC_FALSE;
+ }
}
#endif
- return ALC_TRUE;
-}
-
-
-typedef struct {
- snd_pcm_t *pcmHandle;
-
- ALvoid *buffer;
- ALsizei size;
-
- ALboolean doCapture;
- RingBuffer *ring;
- snd_pcm_sframes_t last_avail;
+ return !error;
+}
- volatile int killNow;
- ALvoid *thread;
-} alsa_data;
typedef struct {
ALCchar *name;
@@ -451,10 +381,46 @@ static int verify_state(snd_pcm_t *handle)
}
-static ALuint ALSAProc(ALvoid *ptr)
+typedef struct ALCplaybackAlsa {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ snd_pcm_t *pcmHandle;
+
+ ALvoid *buffer;
+ ALsizei size;
+
+ volatile int killNow;
+ althread_t thread;
+} ALCplaybackAlsa;
+DECLARE_ALCBACKEND_VTABLE(ALCplaybackAlsa);
+
+static ALuint ALCplaybackAlsa_mixerProc(ALvoid *ptr);
+static ALuint ALCplaybackAlsa_mixerNoMMapProc(ALvoid *ptr);
+
+static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, Destruct)
+static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name);
+static void ALCplaybackAlsa_close(ALCplaybackAlsa *self);
+static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self);
+static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self);
+static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self);
+static DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock)
+
+
+static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCplaybackAlsa, ALCbackend, self);
+}
+
+
+static ALuint ALCplaybackAlsa_mixerProc(ALvoid *ptr)
{
- ALCdevice *Device = (ALCdevice*)ptr;
- alsa_data *data = (alsa_data*)Device->ExtraData;
+ ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
const snd_pcm_channel_area_t *areas = NULL;
snd_pcm_uframes_t update_size, num_updates;
snd_pcm_sframes_t avail, commitres;
@@ -463,22 +429,23 @@ static ALuint ALSAProc(ALvoid *ptr)
int err;
SetRTPriority();
+ SetThreadName(MIXER_THREAD_NAME);
- update_size = Device->UpdateSize;
- num_updates = Device->NumUpdates;
- while(!data->killNow)
+ update_size = device->UpdateSize;
+ num_updates = device->NumUpdates;
+ while(!self->killNow)
{
- int state = verify_state(data->pcmHandle);
+ int state = verify_state(self->pcmHandle);
if(state < 0)
{
ERR("Invalid state detected: %s\n", snd_strerror(state));
- ALCdevice_Lock(Device);
- aluHandleDisconnect(Device);
- ALCdevice_Unlock(Device);
+ ALCplaybackAlsa_lock(self);
+ aluHandleDisconnect(device);
+ ALCplaybackAlsa_unlock(self);
break;
}
- avail = snd_pcm_avail_update(data->pcmHandle);
+ avail = snd_pcm_avail_update(self->pcmHandle);
if(avail < 0)
{
ERR("available update failed: %s\n", snd_strerror(avail));
@@ -488,7 +455,7 @@ static ALuint ALSAProc(ALvoid *ptr)
if((snd_pcm_uframes_t)avail > update_size*(num_updates+1))
{
WARN("available samples exceeds the buffer size\n");
- snd_pcm_reset(data->pcmHandle);
+ snd_pcm_reset(self->pcmHandle);
continue;
}
@@ -497,26 +464,26 @@ static ALuint ALSAProc(ALvoid *ptr)
{
if(state != SND_PCM_STATE_RUNNING)
{
- err = snd_pcm_start(data->pcmHandle);
+ err = snd_pcm_start(self->pcmHandle);
if(err < 0)
{
ERR("start failed: %s\n", snd_strerror(err));
continue;
}
}
- if(snd_pcm_wait(data->pcmHandle, 1000) == 0)
+ if(snd_pcm_wait(self->pcmHandle, 1000) == 0)
ERR("Wait timeout... buffer size too low?\n");
continue;
}
avail -= avail%update_size;
// it is possible that contiguous areas are smaller, thus we use a loop
- ALCdevice_Lock(Device);
+ ALCplaybackAlsa_lock(self);
while(avail > 0)
{
frames = avail;
- err = snd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames);
+ err = snd_pcm_mmap_begin(self->pcmHandle, &areas, &offset, &frames);
if(err < 0)
{
ERR("mmap begin error: %s\n", snd_strerror(err));
@@ -524,9 +491,9 @@ static ALuint ALSAProc(ALvoid *ptr)
}
WritePtr = (char*)areas->addr + (offset * areas->step / 8);
- aluMixData(Device, WritePtr, frames);
+ aluMixData(device, WritePtr, frames);
- commitres = snd_pcm_mmap_commit(data->pcmHandle, offset, frames);
+ commitres = snd_pcm_mmap_commit(self->pcmHandle, offset, frames);
if(commitres < 0 || (commitres-frames) != 0)
{
ERR("mmap commit error: %s\n",
@@ -536,38 +503,39 @@ static ALuint ALSAProc(ALvoid *ptr)
avail -= frames;
}
- ALCdevice_Unlock(Device);
+ ALCplaybackAlsa_unlock(self);
}
return 0;
}
-static ALuint ALSANoMMapProc(ALvoid *ptr)
+static ALuint ALCplaybackAlsa_mixerNoMMapProc(ALvoid *ptr)
{
- ALCdevice *Device = (ALCdevice*)ptr;
- alsa_data *data = (alsa_data*)Device->ExtraData;
+ ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_uframes_t update_size, num_updates;
snd_pcm_sframes_t avail;
char *WritePtr;
int err;
SetRTPriority();
+ SetThreadName(MIXER_THREAD_NAME);
- update_size = Device->UpdateSize;
- num_updates = Device->NumUpdates;
- while(!data->killNow)
+ update_size = device->UpdateSize;
+ num_updates = device->NumUpdates;
+ while(!self->killNow)
{
- int state = verify_state(data->pcmHandle);
+ int state = verify_state(self->pcmHandle);
if(state < 0)
{
ERR("Invalid state detected: %s\n", snd_strerror(state));
- ALCdevice_Lock(Device);
- aluHandleDisconnect(Device);
- ALCdevice_Unlock(Device);
+ ALCplaybackAlsa_lock(self);
+ aluHandleDisconnect(device);
+ ALCplaybackAlsa_unlock(self);
break;
}
- avail = snd_pcm_avail_update(data->pcmHandle);
+ avail = snd_pcm_avail_update(self->pcmHandle);
if(avail < 0)
{
ERR("available update failed: %s\n", snd_strerror(avail));
@@ -577,7 +545,7 @@ static ALuint ALSANoMMapProc(ALvoid *ptr)
if((snd_pcm_uframes_t)avail > update_size*num_updates)
{
WARN("available samples exceeds the buffer size\n");
- snd_pcm_reset(data->pcmHandle);
+ snd_pcm_reset(self->pcmHandle);
continue;
}
@@ -585,26 +553,26 @@ static ALuint ALSANoMMapProc(ALvoid *ptr)
{
if(state != SND_PCM_STATE_RUNNING)
{
- err = snd_pcm_start(data->pcmHandle);
+ err = snd_pcm_start(self->pcmHandle);
if(err < 0)
{
ERR("start failed: %s\n", snd_strerror(err));
continue;
}
}
- if(snd_pcm_wait(data->pcmHandle, 1000) == 0)
+ if(snd_pcm_wait(self->pcmHandle, 1000) == 0)
ERR("Wait timeout... buffer size too low?\n");
continue;
}
- ALCdevice_Lock(Device);
- WritePtr = data->buffer;
- avail = snd_pcm_bytes_to_frames(data->pcmHandle, data->size);
- aluMixData(Device, WritePtr, avail);
+ ALCplaybackAlsa_lock(self);
+ WritePtr = self->buffer;
+ avail = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
+ aluMixData(device, WritePtr, avail);
while(avail > 0)
{
- int ret = snd_pcm_writei(data->pcmHandle, WritePtr, avail);
+ int ret = snd_pcm_writei(self->pcmHandle, WritePtr, avail);
switch (ret)
{
case -EAGAIN:
@@ -612,38 +580,39 @@ static ALuint ALSANoMMapProc(ALvoid *ptr)
case -ESTRPIPE:
case -EPIPE:
case -EINTR:
- ret = snd_pcm_recover(data->pcmHandle, ret, 1);
+ ret = snd_pcm_recover(self->pcmHandle, ret, 1);
if(ret < 0)
avail = 0;
break;
default:
if (ret >= 0)
{
- WritePtr += snd_pcm_frames_to_bytes(data->pcmHandle, ret);
+ WritePtr += snd_pcm_frames_to_bytes(self->pcmHandle, ret);
avail -= ret;
}
break;
}
if (ret < 0)
{
- ret = snd_pcm_prepare(data->pcmHandle);
+ ret = snd_pcm_prepare(self->pcmHandle);
if(ret < 0)
break;
}
}
- ALCdevice_Unlock(Device);
+ ALCplaybackAlsa_unlock(self);
}
return 0;
}
-static ALCenum alsa_open_playback(ALCdevice *device, const ALCchar *deviceName)
+
+static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
const char *driver = NULL;
- alsa_data *data;
int err;
- if(deviceName)
+ if(name)
{
size_t idx;
@@ -652,7 +621,7 @@ static ALCenum alsa_open_playback(ALCdevice *device, const ALCchar *deviceName)
for(idx = 0;idx < numDevNames;idx++)
{
- if(strcmp(deviceName, allDevNameMap[idx].name) == 0)
+ if(strcmp(name, allDevNameMap[idx].name) == 0)
{
driver = allDevNameMap[idx].device;
break;
@@ -663,16 +632,13 @@ static ALCenum alsa_open_playback(ALCdevice *device, const ALCchar *deviceName)
}
else
{
- deviceName = alsaDevice;
+ name = alsaDevice;
driver = GetConfigValue("alsa", "device", "default");
}
- data = (alsa_data*)calloc(1, sizeof(alsa_data));
-
- err = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if(err < 0)
{
- free(data);
ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(err));
return ALC_OUT_OF_MEMORY;
}
@@ -680,23 +646,19 @@ static ALCenum alsa_open_playback(ALCdevice *device, const ALCchar *deviceName)
/* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
snd_config_update_free_global();
- device->DeviceName = strdup(deviceName);
- device->ExtraData = data;
+ device->DeviceName = strdup(name);
+
return ALC_NO_ERROR;
}
-static void alsa_close_playback(ALCdevice *device)
+static void ALCplaybackAlsa_close(ALCplaybackAlsa *self)
{
- alsa_data *data = (alsa_data*)device->ExtraData;
-
- snd_pcm_close(data->pcmHandle);
- free(data);
- device->ExtraData = NULL;
+ snd_pcm_close(self->pcmHandle);
}
-static ALCboolean alsa_reset_playback(ALCdevice *device)
+static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
{
- alsa_data *data = (alsa_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_uframes_t periodSizeInFrames;
unsigned int periodLen, bufferLen;
snd_pcm_sw_params_t *sp = NULL;
@@ -743,15 +705,15 @@ static ALCboolean alsa_reset_playback(ALCdevice *device)
snd_pcm_hw_params_malloc(&hp);
#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
- CHECK(snd_pcm_hw_params_any(data->pcmHandle, hp));
+ CHECK(snd_pcm_hw_params_any(self->pcmHandle, hp));
/* set interleaved access */
- if(!allowmmap || snd_pcm_hw_params_set_access(data->pcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
+ if(!allowmmap || snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
{
/* No mmap */
- CHECK(snd_pcm_hw_params_set_access(data->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
+ CHECK(snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
}
/* test and set format (implicitly sets sample bits) */
- if(snd_pcm_hw_params_test_format(data->pcmHandle, hp, format) < 0)
+ if(snd_pcm_hw_params_test_format(self->pcmHandle, hp, format) < 0)
{
static const struct {
snd_pcm_format_t format;
@@ -770,16 +732,16 @@ static ALCboolean alsa_reset_playback(ALCdevice *device)
for(k = 0;k < COUNTOF(formatlist);k++)
{
format = formatlist[k].format;
- if(snd_pcm_hw_params_test_format(data->pcmHandle, hp, format) >= 0)
+ if(snd_pcm_hw_params_test_format(self->pcmHandle, hp, format) >= 0)
{
device->FmtType = formatlist[k].fmttype;
break;
}
}
}
- CHECK(snd_pcm_hw_params_set_format(data->pcmHandle, hp, format));
+ CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
/* test and set channels (implicitly sets frame bits) */
- if(snd_pcm_hw_params_test_channels(data->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0)
+ if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0)
{
static const enum DevFmtChannels channellist[] = {
DevFmtStereo,
@@ -792,26 +754,26 @@ static ALCboolean alsa_reset_playback(ALCdevice *device)
for(k = 0;k < COUNTOF(channellist);k++)
{
- if(snd_pcm_hw_params_test_channels(data->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0)
+ if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0)
{
device->FmtChans = channellist[k];
break;
}
}
}
- CHECK(snd_pcm_hw_params_set_channels(data->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
+ CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
/* set rate (implicitly constrains period/buffer parameters) */
- if(snd_pcm_hw_params_set_rate_resample(data->pcmHandle, hp, 0) < 0)
+ if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
ERR("Failed to disable ALSA resampler\n");
- CHECK(snd_pcm_hw_params_set_rate_near(data->pcmHandle, hp, &rate, NULL));
+ CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL));
/* set buffer time (implicitly constrains period/buffer parameters) */
- if(snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, hp, &bufferLen, NULL) < 0)
+ if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0)
ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err));
/* set period time (implicitly sets buffer size/bytes/time and period size/bytes) */
- if(snd_pcm_hw_params_set_period_time_near(data->pcmHandle, hp, &periodLen, NULL) < 0)
+ if((err=snd_pcm_hw_params_set_period_time_near(self->pcmHandle, hp, &periodLen, NULL)) < 0)
ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
/* install and prepare hardware configuration */
- CHECK(snd_pcm_hw_params(data->pcmHandle, hp));
+ CHECK(snd_pcm_hw_params(self->pcmHandle, hp));
/* retrieve configuration info */
CHECK(snd_pcm_hw_params_get_access(hp, &access));
CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
@@ -821,10 +783,10 @@ static ALCboolean alsa_reset_playback(ALCdevice *device)
hp = NULL;
snd_pcm_sw_params_malloc(&sp);
- CHECK(snd_pcm_sw_params_current(data->pcmHandle, sp));
- CHECK(snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames));
- CHECK(snd_pcm_sw_params_set_stop_threshold(data->pcmHandle, sp, periodSizeInFrames*periods));
- CHECK(snd_pcm_sw_params(data->pcmHandle, sp));
+ CHECK(snd_pcm_sw_params_current(self->pcmHandle, sp));
+ CHECK(snd_pcm_sw_params_set_avail_min(self->pcmHandle, sp, periodSizeInFrames));
+ CHECK(snd_pcm_sw_params_set_stop_threshold(self->pcmHandle, sp, periodSizeInFrames*periods));
+ CHECK(snd_pcm_sw_params(self->pcmHandle, sp));
#undef CHECK
snd_pcm_sw_params_free(sp);
sp = NULL;
@@ -844,9 +806,10 @@ error:
return ALC_FALSE;
}
-static ALCboolean alsa_start_playback(ALCdevice *device)
+static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
{
- alsa_data *data = (alsa_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ ALuint (*thread_func)(ALvoid*) = NULL;
snd_pcm_hw_params_t *hp = NULL;
snd_pcm_access_t access;
const char *funcerr;
@@ -854,39 +817,39 @@ static ALCboolean alsa_start_playback(ALCdevice *device)
snd_pcm_hw_params_malloc(&hp);
#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
- CHECK(snd_pcm_hw_params_current(data->pcmHandle, hp));
+ CHECK(snd_pcm_hw_params_current(self->pcmHandle, hp));
/* retrieve configuration info */
CHECK(snd_pcm_hw_params_get_access(hp, &access));
#undef CHECK
snd_pcm_hw_params_free(hp);
hp = NULL;
- data->size = snd_pcm_frames_to_bytes(data->pcmHandle, device->UpdateSize);
+ self->size = snd_pcm_frames_to_bytes(self->pcmHandle, device->UpdateSize);
if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
{
- data->buffer = malloc(data->size);
- if(!data->buffer)
+ self->buffer = malloc(self->size);
+ if(!self->buffer)
{
ERR("buffer malloc failed\n");
return ALC_FALSE;
}
- data->thread = StartThread(ALSANoMMapProc, device);
+ thread_func = ALCplaybackAlsa_mixerNoMMapProc;
}
else
{
- err = snd_pcm_prepare(data->pcmHandle);
+ err = snd_pcm_prepare(self->pcmHandle);
if(err < 0)
{
ERR("snd_pcm_prepare(data->pcmHandle) failed: %s\n", snd_strerror(err));
return ALC_FALSE;
}
- data->thread = StartThread(ALSAProc, device);
+ thread_func = ALCplaybackAlsa_mixerProc;
}
- if(data->thread == NULL)
+ if(!StartThread(&self->thread, thread_func, self))
{
ERR("Could not create playback thread\n");
- free(data->buffer);
- data->buffer = NULL;
+ free(self->buffer);
+ self->buffer = NULL;
return ALC_FALSE;
}
@@ -898,24 +861,80 @@ error:
return ALC_FALSE;
}
-static void alsa_stop_playback(ALCdevice *device)
+static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self)
{
- alsa_data *data = (alsa_data*)device->ExtraData;
+ if(self->thread)
+ {
+ self->killNow = 1;
+ StopThread(self->thread);
+ self->thread = NULL;
+ }
+ self->killNow = 0;
+ free(self->buffer);
+ self->buffer = NULL;
+}
- if(data->thread)
+static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ snd_pcm_sframes_t delay = 0;
+ int err;
+
+ if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
{
- data->killNow = 1;
- StopThread(data->thread);
- data->thread = NULL;
+ ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
+ return 0;
}
- data->killNow = 0;
- free(data->buffer);
- data->buffer = NULL;
+ return maxi64((ALint64)delay*1000000000/device->Frequency, 0);
+}
+
+
+static void ALCplaybackAlsa_Delete(ALCplaybackAlsa *self)
+{
+ free(self);
+}
+
+DEFINE_ALCBACKEND_VTABLE(ALCplaybackAlsa);
+
+
+typedef struct ALCcaptureAlsa {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ snd_pcm_t *pcmHandle;
+
+ ALvoid *buffer;
+ ALsizei size;
+
+ ALboolean doCapture;
+ RingBuffer *ring;
+
+ snd_pcm_sframes_t last_avail;
+} ALCcaptureAlsa;
+DECLARE_ALCBACKEND_VTABLE(ALCcaptureAlsa);
+
+static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, Destruct)
+static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name);
+static void ALCcaptureAlsa_close(ALCcaptureAlsa *self);
+static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self);
+static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self);
+static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self);
+static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock)
+
+
+static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCcaptureAlsa, ALCbackend, self);
}
-static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName)
+static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
const char *driver = NULL;
snd_pcm_hw_params_t *hp;
snd_pcm_uframes_t bufferSizeInFrames;
@@ -923,10 +942,9 @@ static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName)
ALboolean needring = AL_FALSE;
snd_pcm_format_t format;
const char *funcerr;
- alsa_data *data;
int err;
- if(deviceName)
+ if(name)
{
size_t idx;
@@ -935,7 +953,7 @@ static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName)
for(idx = 0;idx < numCaptureDevNames;idx++)
{
- if(strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0)
+ if(strcmp(name, allCaptureDevNameMap[idx].name) == 0)
{
driver = allCaptureDevNameMap[idx].device;
break;
@@ -946,17 +964,14 @@ static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName)
}
else
{
- deviceName = alsaDevice;
+ name = alsaDevice;
driver = GetConfigValue("alsa", "capture", "default");
}
- data = (alsa_data*)calloc(1, sizeof(alsa_data));
-
- err = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
+ err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
if(err < 0)
{
ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(err));
- free(data);
return ALC_INVALID_VALUE;
}
@@ -964,7 +979,7 @@ static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName)
snd_config_update_free_global();
format = -1;
- switch(Device->FmtType)
+ switch(device->FmtType)
{
case DevFmtByte:
format = SND_PCM_FORMAT_S8;
@@ -990,32 +1005,32 @@ static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName)
}
funcerr = NULL;
- bufferSizeInFrames = maxu(Device->UpdateSize*Device->NumUpdates,
- 100*Device->Frequency/1000);
- periodSizeInFrames = minu(bufferSizeInFrames, 25*Device->Frequency/1000);
+ bufferSizeInFrames = maxu(device->UpdateSize*device->NumUpdates,
+ 100*device->Frequency/1000);
+ periodSizeInFrames = minu(bufferSizeInFrames, 25*device->Frequency/1000);
snd_pcm_hw_params_malloc(&hp);
#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
- CHECK(snd_pcm_hw_params_any(data->pcmHandle, hp));
+ CHECK(snd_pcm_hw_params_any(self->pcmHandle, hp));
/* set interleaved access */
- CHECK(snd_pcm_hw_params_set_access(data->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
+ CHECK(snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
/* set format (implicitly sets sample bits) */
- CHECK(snd_pcm_hw_params_set_format(data->pcmHandle, hp, format));
+ CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
/* set channels (implicitly sets frame bits) */
- CHECK(snd_pcm_hw_params_set_channels(data->pcmHandle, hp, ChannelsFromDevFmt(Device->FmtChans)));
+ CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
/* set rate (implicitly constrains period/buffer parameters) */
- CHECK(snd_pcm_hw_params_set_rate(data->pcmHandle, hp, Device->Frequency, 0));
+ CHECK(snd_pcm_hw_params_set_rate(self->pcmHandle, hp, device->Frequency, 0));
/* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
- if(snd_pcm_hw_params_set_buffer_size_min(data->pcmHandle, hp, &bufferSizeInFrames) < 0)
+ if(snd_pcm_hw_params_set_buffer_size_min(self->pcmHandle, hp, &bufferSizeInFrames) < 0)
{
TRACE("Buffer too large, using intermediate ring buffer\n");
needring = AL_TRUE;
- CHECK(snd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, hp, &bufferSizeInFrames));
+ CHECK(snd_pcm_hw_params_set_buffer_size_near(self->pcmHandle, hp, &bufferSizeInFrames));
}
/* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
- CHECK(snd_pcm_hw_params_set_period_size_near(data->pcmHandle, hp, &periodSizeInFrames, NULL));
+ CHECK(snd_pcm_hw_params_set_period_size_near(self->pcmHandle, hp, &periodSizeInFrames, NULL));
/* install and prepare hardware configuration */
- CHECK(snd_pcm_hw_params(data->pcmHandle, hp));
+ CHECK(snd_pcm_hw_params(self->pcmHandle, hp));
/* retrieve configuration info */
CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
#undef CHECK
@@ -1024,26 +1039,25 @@ static ALCenum alsa_open_capture(ALCdevice *Device, const ALCchar *deviceName)
if(needring)
{
- data->ring = CreateRingBuffer(FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType),
- Device->UpdateSize*Device->NumUpdates);
- if(!data->ring)
+ self->ring = CreateRingBuffer(FrameSizeFromDevFmt(device->FmtChans, device->FmtType),
+ device->UpdateSize*device->NumUpdates);
+ if(!self->ring)
{
ERR("ring buffer create failed\n");
goto error2;
}
- data->size = snd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames);
- data->buffer = malloc(data->size);
- if(!data->buffer)
+ self->size = snd_pcm_frames_to_bytes(self->pcmHandle, periodSizeInFrames);
+ self->buffer = malloc(self->size);
+ if(!self->buffer)
{
ERR("buffer malloc failed\n");
goto error2;
}
}
- Device->DeviceName = strdup(deviceName);
+ device->DeviceName = strdup(name);
- Device->ExtraData = data;
return ALC_NO_ERROR;
error:
@@ -1051,227 +1065,221 @@ error:
if(hp) snd_pcm_hw_params_free(hp);
error2:
- free(data->buffer);
- DestroyRingBuffer(data->ring);
- snd_pcm_close(data->pcmHandle);
- free(data);
+ free(self->buffer);
+ self->buffer = NULL;
+ DestroyRingBuffer(self->ring);
+ self->ring = NULL;
+ snd_pcm_close(self->pcmHandle);
- Device->ExtraData = NULL;
return ALC_INVALID_VALUE;
}
-static void alsa_close_capture(ALCdevice *Device)
+static void ALCcaptureAlsa_close(ALCcaptureAlsa *self)
{
- alsa_data *data = (alsa_data*)Device->ExtraData;
+ snd_pcm_close(self->pcmHandle);
+ DestroyRingBuffer(self->ring);
- snd_pcm_close(data->pcmHandle);
- DestroyRingBuffer(data->ring);
+ free(self->buffer);
+ self->buffer = NULL;
+}
- free(data->buffer);
- free(data);
- Device->ExtraData = NULL;
+static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self)
+{
+ int err = snd_pcm_start(self->pcmHandle);
+ if(err < 0)
+ {
+ ERR("start failed: %s\n", snd_strerror(err));
+ aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
+ return ALC_FALSE;
+ }
+
+ self->doCapture = AL_TRUE;
+ return ALC_TRUE;
}
-static void alsa_start_capture(ALCdevice *Device)
+static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self)
{
- alsa_data *data = (alsa_data*)Device->ExtraData;
+ ALCuint avail;
int err;
- err = snd_pcm_start(data->pcmHandle);
- if(err < 0)
+ /* OpenAL requires access to unread audio after stopping, but ALSA's
+ * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's
+ * available now so it'll be available later after the drop. */
+ avail = ALCcaptureAlsa_availableSamples(self);
+ if(!self->ring && avail > 0)
{
- ERR("start failed: %s\n", snd_strerror(err));
- aluHandleDisconnect(Device);
+ /* The ring buffer implicitly captures when checking availability.
+ * Direct access needs to explicitly capture it into temp storage. */
+ ALsizei size;
+ void *ptr;
+
+ size = snd_pcm_frames_to_bytes(self->pcmHandle, avail);
+ ptr = realloc(self->buffer, size);
+ if(ptr)
+ {
+ self->buffer = ptr;
+ ALCcaptureAlsa_captureSamples(self, self->buffer, avail);
+ self->size = size;
+ }
}
- else
- data->doCapture = AL_TRUE;
+ err = snd_pcm_drop(self->pcmHandle);
+ if(err < 0)
+ ERR("drop failed: %s\n", snd_strerror(err));
+ self->doCapture = AL_FALSE;
}
-static ALCenum alsa_capture_samples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples)
+static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples)
{
- alsa_data *data = (alsa_data*)Device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- if(data->ring)
+ if(self->ring)
{
- ReadRingBuffer(data->ring, Buffer, Samples);
+ ReadRingBuffer(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
- data->last_avail -= Samples;
- while(Device->Connected && Samples > 0)
+ self->last_avail -= samples;
+ while(device->Connected && samples > 0)
{
snd_pcm_sframes_t amt = 0;
- if(data->size > 0)
+ if(self->size > 0)
{
/* First get any data stored from the last stop */
- amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size);
- if((snd_pcm_uframes_t)amt > Samples) amt = Samples;
+ amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
+ if((snd_pcm_uframes_t)amt > samples) amt = samples;
- amt = snd_pcm_frames_to_bytes(data->pcmHandle, amt);
- memmove(Buffer, data->buffer, amt);
+ amt = snd_pcm_frames_to_bytes(self->pcmHandle, amt);
+ memmove(buffer, self->buffer, amt);
- if(data->size > amt)
+ if(self->size > amt)
{
- memmove(data->buffer, data->buffer+amt, data->size - amt);
- data->size -= amt;
+ memmove(self->buffer, self->buffer+amt, self->size - amt);
+ self->size -= amt;
}
else
{
- free(data->buffer);
- data->buffer = NULL;
- data->size = 0;
+ free(self->buffer);
+ self->buffer = NULL;
+ self->size = 0;
}
- amt = snd_pcm_bytes_to_frames(data->pcmHandle, amt);
+ amt = snd_pcm_bytes_to_frames(self->pcmHandle, amt);
}
- else if(data->doCapture)
- amt = snd_pcm_readi(data->pcmHandle, Buffer, Samples);
+ else if(self->doCapture)
+ amt = snd_pcm_readi(self->pcmHandle, buffer, samples);
if(amt < 0)
{
ERR("read error: %s\n", snd_strerror(amt));
if(amt == -EAGAIN)
continue;
- if((amt=snd_pcm_recover(data->pcmHandle, amt, 1)) >= 0)
+ if((amt=snd_pcm_recover(self->pcmHandle, amt, 1)) >= 0)
{
- amt = snd_pcm_start(data->pcmHandle);
+ amt = snd_pcm_start(self->pcmHandle);
if(amt >= 0)
- amt = snd_pcm_avail_update(data->pcmHandle);
+ amt = snd_pcm_avail_update(self->pcmHandle);
}
if(amt < 0)
{
ERR("restore error: %s\n", snd_strerror(amt));
- aluHandleDisconnect(Device);
+ aluHandleDisconnect(device);
break;
}
/* If the amount available is less than what's asked, we lost it
* during recovery. So just give silence instead. */
- if((snd_pcm_uframes_t)amt < Samples)
+ if((snd_pcm_uframes_t)amt < samples)
break;
continue;
}
- Buffer = (ALbyte*)Buffer + amt;
- Samples -= amt;
+ buffer = (ALbyte*)buffer + amt;
+ samples -= amt;
}
- if(Samples > 0)
- memset(Buffer, ((Device->FmtType == DevFmtUByte) ? 0x80 : 0),
- snd_pcm_frames_to_bytes(data->pcmHandle, Samples));
+ if(samples > 0)
+ memset(buffer, ((device->FmtType == DevFmtUByte) ? 0x80 : 0),
+ snd_pcm_frames_to_bytes(self->pcmHandle, samples));
return ALC_NO_ERROR;
}
-static ALCuint alsa_available_samples(ALCdevice *Device)
+static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
{
- alsa_data *data = (alsa_data*)Device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_sframes_t avail = 0;
- if(Device->Connected && data->doCapture)
- avail = snd_pcm_avail_update(data->pcmHandle);
+ if(device->Connected && self->doCapture)
+ avail = snd_pcm_avail_update(self->pcmHandle);
if(avail < 0)
{
ERR("avail update failed: %s\n", snd_strerror(avail));
- if((avail=snd_pcm_recover(data->pcmHandle, avail, 1)) >= 0)
+ if((avail=snd_pcm_recover(self->pcmHandle, avail, 1)) >= 0)
{
- if(data->doCapture)
- avail = snd_pcm_start(data->pcmHandle);
+ if(self->doCapture)
+ avail = snd_pcm_start(self->pcmHandle);
if(avail >= 0)
- avail = snd_pcm_avail_update(data->pcmHandle);
+ avail = snd_pcm_avail_update(self->pcmHandle);
}
if(avail < 0)
{
ERR("restore error: %s\n", snd_strerror(avail));
- aluHandleDisconnect(Device);
+ aluHandleDisconnect(device);
}
}
- if(!data->ring)
+ if(!self->ring)
{
if(avail < 0) avail = 0;
- avail += snd_pcm_bytes_to_frames(data->pcmHandle, data->size);
- if(avail > data->last_avail) data->last_avail = avail;
- return data->last_avail;
+ avail += snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
+ if(avail > self->last_avail) self->last_avail = avail;
+ return self->last_avail;
}
while(avail > 0)
{
snd_pcm_sframes_t amt;
- amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size);
+ amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
if(avail < amt) amt = avail;
- amt = snd_pcm_readi(data->pcmHandle, data->buffer, amt);
+ amt = snd_pcm_readi(self->pcmHandle, self->buffer, amt);
if(amt < 0)
{
ERR("read error: %s\n", snd_strerror(amt));
if(amt == -EAGAIN)
continue;
- if((amt=snd_pcm_recover(data->pcmHandle, amt, 1)) >= 0)
+ if((amt=snd_pcm_recover(self->pcmHandle, amt, 1)) >= 0)
{
- if(data->doCapture)
- amt = snd_pcm_start(data->pcmHandle);
+ if(self->doCapture)
+ amt = snd_pcm_start(self->pcmHandle);
if(amt >= 0)
- amt = snd_pcm_avail_update(data->pcmHandle);
+ amt = snd_pcm_avail_update(self->pcmHandle);
}
if(amt < 0)
{
ERR("restore error: %s\n", snd_strerror(amt));
- aluHandleDisconnect(Device);
+ aluHandleDisconnect(device);
break;
}
avail = amt;
continue;
}
- WriteRingBuffer(data->ring, data->buffer, amt);
+ WriteRingBuffer(self->ring, self->buffer, amt);
avail -= amt;
}
- return RingBufferSize(data->ring);
-}
-
-static void alsa_stop_capture(ALCdevice *Device)
-{
- alsa_data *data = (alsa_data*)Device->ExtraData;
- ALCuint avail;
- int err;
-
- /* OpenAL requires access to unread audio after stopping, but ALSA's
- * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's
- * available now so it'll be available later after the drop. */
- avail = alsa_available_samples(Device);
- if(!data->ring && avail > 0)
- {
- /* The ring buffer implicitly captures when checking availability.
- * Direct access needs to explicitly capture it into temp storage. */
- ALsizei size;
- void *ptr;
-
- size = snd_pcm_frames_to_bytes(data->pcmHandle, avail);
- ptr = realloc(data->buffer, size);
- if(ptr)
- {
- data->buffer = ptr;
- alsa_capture_samples(Device, data->buffer, avail);
- data->size = size;
- }
- }
- err = snd_pcm_drop(data->pcmHandle);
- if(err < 0)
- ERR("drop failed: %s\n", snd_strerror(err));
- data->doCapture = AL_FALSE;
+ return RingBufferSize(self->ring);
}
-
-static ALint64 alsa_get_latency(ALCdevice *device)
+static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self)
{
- alsa_data *data = (alsa_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_sframes_t delay = 0;
int err;
- if((err=snd_pcm_delay(data->pcmHandle, &delay)) < 0)
+ if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
{
ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
return 0;
@@ -1279,33 +1287,28 @@ static ALint64 alsa_get_latency(ALCdevice *device)
return maxi64((ALint64)delay*1000000000/device->Frequency, 0);
}
+static void ALCcaptureAlsa_Delete(ALCcaptureAlsa *self)
+{
+ free(self);
+}
+
+DEFINE_ALCBACKEND_VTABLE(ALCcaptureAlsa);
+
+
-static const BackendFuncs alsa_funcs = {
- alsa_open_playback,
- alsa_close_playback,
- alsa_reset_playback,
- alsa_start_playback,
- alsa_stop_playback,
- alsa_open_capture,
- alsa_close_capture,
- alsa_start_capture,
- alsa_stop_capture,
- alsa_capture_samples,
- alsa_available_samples,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
- alsa_get_latency
-};
-
-ALCboolean alc_alsa_init(BackendFuncs *func_list)
+typedef struct ALCalsaBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCalsaBackendFactory;
+#define ALCALSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCalsaBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCalsaBackendFactory_init(ALCalsaBackendFactory* UNUSED(self))
{
if(!alsa_load())
return ALC_FALSE;
- *func_list = alsa_funcs;
return ALC_TRUE;
}
-void alc_alsa_deinit(void)
+static void ALCalsaBackendFactory_deinit(ALCalsaBackendFactory* UNUSED(self))
{
ALuint i;
@@ -1334,7 +1337,14 @@ void alc_alsa_deinit(void)
#endif
}
-void alc_alsa_probe(enum DevProbe type)
+static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+ return ALC_TRUE;
+ return ALC_FALSE;
+}
+
+static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type)
{
ALuint i;
@@ -1369,3 +1379,40 @@ void alc_alsa_probe(enum DevProbe type)
break;
}
}
+
+static ALCbackend* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ ALCplaybackAlsa *backend;
+
+ backend = calloc(1, sizeof(*backend));
+ if(!backend) return NULL;
+
+ ALCplaybackAlsa_Construct(backend, device);
+
+ return STATIC_CAST(ALCbackend, backend);
+ }
+ if(type == ALCbackend_Capture)
+ {
+ ALCcaptureAlsa *backend;
+
+ backend = calloc(1, sizeof(*backend));
+ if(!backend) return NULL;
+
+ ALCcaptureAlsa_Construct(backend, device);
+
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCalsaBackendFactory);
+
+
+ALCbackendFactory *ALCalsaBackendFactory_getFactory(void)
+{
+ static ALCalsaBackendFactory factory = ALCALSABACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
+}
diff --git a/Alc/backends/base.c b/Alc/backends/base.c
new file mode 100644
index 00000000..fe797562
--- /dev/null
+++ b/Alc/backends/base.c
@@ -0,0 +1,230 @@
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "alMain.h"
+
+#include "backends/base.h"
+
+
+/* Base ALCbackend method implementations. */
+void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
+{
+ self->mDevice = device;
+ InitializeCriticalSection(&self->mMutex);
+}
+
+void ALCbackend_Destruct(ALCbackend *self)
+{
+ DeleteCriticalSection(&self->mMutex);
+}
+
+ALCboolean ALCbackend_reset(ALCbackend* UNUSED(self))
+{
+ return ALC_FALSE;
+}
+
+ALCenum ALCbackend_captureSamples(ALCbackend* UNUSED(self), void* UNUSED(buffer), ALCuint UNUSED(samples))
+{
+ return ALC_INVALID_DEVICE;
+}
+
+ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
+{
+ return 0;
+}
+
+ALint64 ALCbackend_getLatency(ALCbackend* UNUSED(self))
+{
+ return 0;
+}
+
+void ALCbackend_lock(ALCbackend *self)
+{
+ EnterCriticalSection(&self->mMutex);
+}
+
+void ALCbackend_unlock(ALCbackend *self)
+{
+ LeaveCriticalSection(&self->mMutex);
+}
+
+
+/* Base ALCbackendFactory method implementations. */
+void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self))
+{
+}
+
+
+/* Wrappers to use an old-style backend with the new interface. */
+typedef struct PlaybackWrapper {
+ DERIVE_FROM_TYPE(ALCbackend);
+} PlaybackWrapper;
+
+static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device);
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct)
+static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
+static void PlaybackWrapper_close(PlaybackWrapper *self);
+static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
+static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
+static void PlaybackWrapper_stop(PlaybackWrapper *self);
+static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
+static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self);
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
+static void PlaybackWrapper_Delete(PlaybackWrapper *self);
+DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
+
+static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
+}
+
+static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ return device->Funcs->OpenPlayback(device, name);
+}
+
+static void PlaybackWrapper_close(PlaybackWrapper *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ device->Funcs->ClosePlayback(device);
+}
+
+static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ return device->Funcs->ResetPlayback(device);
+}
+
+static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ return device->Funcs->StartPlayback(device);
+}
+
+static void PlaybackWrapper_stop(PlaybackWrapper *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ device->Funcs->StopPlayback(device);
+}
+
+static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ return device->Funcs->GetLatency(device);
+}
+
+static void PlaybackWrapper_Delete(PlaybackWrapper *self)
+{
+ free(self);
+}
+
+
+typedef struct CaptureWrapper {
+ DERIVE_FROM_TYPE(ALCbackend);
+} CaptureWrapper;
+
+static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct)
+static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
+static void CaptureWrapper_close(CaptureWrapper *self);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
+static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
+static void CaptureWrapper_stop(CaptureWrapper *self);
+static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
+static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
+static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
+static void CaptureWrapper_Delete(CaptureWrapper *self);
+DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
+
+
+static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(CaptureWrapper, ALCbackend, self);
+}
+
+static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ return device->Funcs->OpenCapture(device, name);
+}
+
+static void CaptureWrapper_close(CaptureWrapper *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ device->Funcs->CloseCapture(device);
+}
+
+static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ device->Funcs->StartCapture(device);
+ return ALC_TRUE;
+}
+
+static void CaptureWrapper_stop(CaptureWrapper *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ device->Funcs->StopCapture(device);
+}
+
+static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ return device->Funcs->CaptureSamples(device, buffer, samples);
+}
+
+static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ return device->Funcs->AvailableSamples(device);
+}
+
+static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ return device->Funcs->GetLatency(device);
+}
+
+static void CaptureWrapper_Delete(CaptureWrapper *self)
+{
+ free(self);
+}
+
+
+ALCbackend *create_backend_wrapper(ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ PlaybackWrapper *backend;
+
+ backend = malloc(sizeof(*backend));
+ if(!backend) return NULL;
+
+ PlaybackWrapper_Construct(backend, device);
+
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ if(type == ALCbackend_Capture)
+ {
+ CaptureWrapper *backend;
+
+ backend = malloc(sizeof(*backend));
+ if(!backend) return NULL;
+
+ CaptureWrapper_Construct(backend, device);
+
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
diff --git a/Alc/backends/base.h b/Alc/backends/base.h
new file mode 100644
index 00000000..1bbc1fb0
--- /dev/null
+++ b/Alc/backends/base.h
@@ -0,0 +1,133 @@
+#ifndef AL_BACKENDS_BASE_H
+#define AL_BACKENDS_BASE_H
+
+#include "alMain.h"
+#include "compat.h"
+
+
+struct ALCbackendVtable;
+
+typedef struct ALCbackend {
+ const struct ALCbackendVtable *vtbl;
+
+ ALCdevice *mDevice;
+
+ CRITICAL_SECTION mMutex;
+} ALCbackend;
+
+void ALCbackend_Construct(ALCbackend *self, ALCdevice *device);
+void ALCbackend_Destruct(ALCbackend *self);
+ALCboolean ALCbackend_reset(ALCbackend *self);
+ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples);
+ALCuint ALCbackend_availableSamples(ALCbackend *self);
+ALint64 ALCbackend_getLatency(ALCbackend *self);
+void ALCbackend_lock(ALCbackend *self);
+void ALCbackend_unlock(ALCbackend *self);
+
+struct ALCbackendVtable {
+ void (*const Destruct)(ALCbackend*);
+
+ ALCenum (*const open)(ALCbackend*, const ALCchar*);
+ void (*const close)(ALCbackend*);
+
+ ALCboolean (*const reset)(ALCbackend*);
+ ALCboolean (*const start)(ALCbackend*);
+ void (*const stop)(ALCbackend*);
+
+ ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
+ ALCuint (*const availableSamples)(ALCbackend*);
+
+ ALint64 (*const getLatency)(ALCbackend*);
+
+ void (*const lock)(ALCbackend*);
+ void (*const unlock)(ALCbackend*);
+
+ void (*const Delete)(ALCbackend*);
+};
+
+#define DECLARE_ALCBACKEND_VTABLE(T) \
+static const struct ALCbackendVtable T##_ALCbackend_vtable
+
+#define DEFINE_ALCBACKEND_VTABLE(T) \
+DECLARE_THUNK(T, ALCbackend, void, Destruct) \
+DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \
+DECLARE_THUNK(T, ALCbackend, void, close) \
+DECLARE_THUNK(T, ALCbackend, ALCboolean, reset) \
+DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \
+DECLARE_THUNK(T, ALCbackend, void, stop) \
+DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint) \
+DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \
+DECLARE_THUNK(T, ALCbackend, ALint64, getLatency) \
+DECLARE_THUNK(T, ALCbackend, void, lock) \
+DECLARE_THUNK(T, ALCbackend, void, unlock) \
+DECLARE_THUNK(T, ALCbackend, void, Delete) \
+ \
+DECLARE_ALCBACKEND_VTABLE(T) = { \
+ T##_ALCbackend_Destruct, \
+ \
+ T##_ALCbackend_open, \
+ T##_ALCbackend_close, \
+ T##_ALCbackend_reset, \
+ T##_ALCbackend_start, \
+ T##_ALCbackend_stop, \
+ T##_ALCbackend_captureSamples, \
+ T##_ALCbackend_availableSamples, \
+ T##_ALCbackend_getLatency, \
+ T##_ALCbackend_lock, \
+ T##_ALCbackend_unlock, \
+ \
+ T##_ALCbackend_Delete, \
+}
+
+
+typedef enum ALCbackend_Type {
+ ALCbackend_Playback,
+ ALCbackend_Capture,
+ ALCbackend_Loopback
+} ALCbackend_Type;
+
+
+struct ALCbackendFactoryVtable;
+
+typedef struct ALCbackendFactory {
+ const struct ALCbackendFactoryVtable *vtbl;
+} ALCbackendFactory;
+
+void ALCbackendFactory_deinit(ALCbackendFactory *self);
+
+struct ALCbackendFactoryVtable {
+ ALCboolean (*const init)(ALCbackendFactory *self);
+ void (*const deinit)(ALCbackendFactory *self);
+
+ ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
+
+ void (*const probe)(ALCbackendFactory *self, enum DevProbe type);
+
+ ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+};
+
+#define DEFINE_ALCBACKENDFACTORY_VTABLE(T) \
+DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init) \
+DECLARE_THUNK(T, ALCbackendFactory, void, deinit) \
+DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
+DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe) \
+DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
+ \
+static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
+ T##_ALCbackendFactory_init, \
+ T##_ALCbackendFactory_deinit, \
+ T##_ALCbackendFactory_querySupport, \
+ T##_ALCbackendFactory_probe, \
+ T##_ALCbackendFactory_createBackend, \
+}
+
+
+ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
+ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
+ALCbackendFactory *ALCossBackendFactory_getFactory(void);
+ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
+ALCbackendFactory *ALCloopbackFactory_getFactory(void);
+
+ALCbackend *create_backend_wrapper(ALCdevice *device, ALCbackend_Type type);
+
+#endif /* AL_BACKENDS_BASE_H */
diff --git a/Alc/backends/coreaudio.c b/Alc/backends/coreaudio.c
index 150f37ac..5c9b69c8 100644
--- a/Alc/backends/coreaudio.c
+++ b/Alc/backends/coreaudio.c
@@ -677,8 +677,6 @@ static const BackendFuncs ca_funcs = {
ca_stop_capture,
ca_capture_samples,
ca_available_samples,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
ALCdevice_GetLatencyDefault
};
diff --git a/Alc/backends/dsound.c b/Alc/backends/dsound.c
index c6f666e1..6b108fba 100644
--- a/Alc/backends/dsound.c
+++ b/Alc/backends/dsound.c
@@ -34,13 +34,22 @@
#include "alMain.h"
#include "alu.h"
+#include "threads.h"
+#include "compat.h"
#ifndef DSSPEAKER_5POINT1
-#define DSSPEAKER_5POINT1 6
+# define DSSPEAKER_5POINT1 0x00000006
#endif
#ifndef DSSPEAKER_7POINT1
-#define DSSPEAKER_7POINT1 7
+# define DSSPEAKER_7POINT1 0x00000007
#endif
+#ifndef DSSPEAKER_7POINT1_SURROUND
+# define DSSPEAKER_7POINT1_SURROUND 0x00000008
+#endif
+#ifndef DSSPEAKER_5POINT1_SURROUND
+# define DSSPEAKER_5POINT1_SURROUND 0x00000009
+#endif
+
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
@@ -67,7 +76,7 @@ typedef struct {
HANDLE NotifyEvent;
volatile int killNow;
- ALvoid *thread;
+ althread_t thread;
} DSoundPlaybackData;
typedef struct {
@@ -121,7 +130,7 @@ static ALCboolean DSoundLoad(void)
}
-static BOOL CALLBACK DSoundEnumPlaybackDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
+static BOOL CALLBACK DSoundEnumPlaybackDevices(LPGUID guid, LPCSTR desc, LPCSTR UNUSED(drvname), LPVOID UNUSED(data))
{
LPOLESTR guidstr = NULL;
char str[1024];
@@ -130,9 +139,6 @@ static BOOL CALLBACK DSoundEnumPlaybackDevices(LPGUID guid, LPCSTR desc, LPCSTR
int count;
ALuint i;
- (void)data;
- (void)drvname;
-
if(!guid)
return TRUE;
@@ -171,7 +177,7 @@ static BOOL CALLBACK DSoundEnumPlaybackDevices(LPGUID guid, LPCSTR desc, LPCSTR
}
-static BOOL CALLBACK DSoundEnumCaptureDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
+static BOOL CALLBACK DSoundEnumCaptureDevices(LPGUID guid, LPCSTR desc, LPCSTR UNUSED(drvname), LPVOID UNUSED(data))
{
LPOLESTR guidstr = NULL;
char str[1024];
@@ -180,9 +186,6 @@ static BOOL CALLBACK DSoundEnumCaptureDevices(LPGUID guid, LPCSTR desc, LPCSTR d
int count;
ALuint i;
- (void)data;
- (void)drvname;
-
if(!guid)
return TRUE;
@@ -221,7 +224,7 @@ static BOOL CALLBACK DSoundEnumCaptureDevices(LPGUID guid, LPCSTR desc, LPCSTR d
}
-static ALuint DSoundPlaybackProc(ALvoid *ptr)
+FORCE_ALIGN static ALuint DSoundPlaybackProc(ALvoid *ptr)
{
ALCdevice *Device = (ALCdevice*)ptr;
DSoundPlaybackData *data = (DSoundPlaybackData*)Device->ExtraData;
@@ -237,6 +240,7 @@ static ALuint DSoundPlaybackProc(ALvoid *ptr)
HRESULT err;
SetRTPriority();
+ SetThreadName(MIXER_THREAD_NAME);
memset(&DSBCaps, 0, sizeof(DSBCaps));
DSBCaps.dwSize = sizeof(DSBCaps);
@@ -466,9 +470,9 @@ static ALCboolean DSoundResetPlayback(ALCdevice *device)
device->FmtChans = DevFmtStereo;
else if(speakers == DSSPEAKER_QUAD)
device->FmtChans = DevFmtQuad;
- else if(speakers == DSSPEAKER_5POINT1)
+ else if(speakers == DSSPEAKER_5POINT1 || speakers == DSSPEAKER_5POINT1_SURROUND)
device->FmtChans = DevFmtX51;
- else if(speakers == DSSPEAKER_7POINT1)
+ else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
device->FmtChans = DevFmtX71;
else
ERR("Unknown system speaker config: 0x%lx\n", speakers);
@@ -630,8 +634,7 @@ static ALCboolean DSoundStartPlayback(ALCdevice *device)
{
DSoundPlaybackData *data = (DSoundPlaybackData*)device->ExtraData;
- data->thread = StartThread(DSoundPlaybackProc, device);
- if(data->thread == NULL)
+ if(!StartThread(&data->thread, DSoundPlaybackProc, device))
return ALC_FALSE;
return ALC_TRUE;
@@ -952,8 +955,6 @@ static const BackendFuncs DSoundFuncs = {
DSoundStopCapture,
DSoundCaptureSamples,
DSoundAvailableSamples,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
ALCdevice_GetLatencyDefault
};
diff --git a/Alc/backends/loopback.c b/Alc/backends/loopback.c
index e1bdd3c1..cd5b1a1e 100644
--- a/Alc/backends/loopback.c
+++ b/Alc/backends/loopback.c
@@ -25,64 +25,116 @@
#include "alMain.h"
#include "alu.h"
+#include "backends/base.h"
-static ALCenum loopback_open_playback(ALCdevice *device, const ALCchar *deviceName)
+
+typedef struct ALCloopback {
+ DERIVE_FROM_TYPE(ALCbackend);
+} ALCloopback;
+
+static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCloopback, ALCbackend, void, Destruct)
+static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name);
+static void ALCloopback_close(ALCloopback *self);
+static ALCboolean ALCloopback_reset(ALCloopback *self);
+static ALCboolean ALCloopback_start(ALCloopback *self);
+static void ALCloopback_stop(ALCloopback *self);
+static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCloopback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
+static void ALCloopback_Delete(ALCloopback *self);
+DEFINE_ALCBACKEND_VTABLE(ALCloopback);
+
+
+static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device)
{
- device->DeviceName = strdup(deviceName);
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCloopback, ALCbackend, self);
+}
+
+
+static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+
+ device->DeviceName = strdup(name);
return ALC_NO_ERROR;
}
-static void loopback_close_playback(ALCdevice *device)
+static void ALCloopback_close(ALCloopback* UNUSED(self))
{
- (void)device;
}
-static ALCboolean loopback_reset_playback(ALCdevice *device)
+static ALCboolean ALCloopback_reset(ALCloopback *self)
{
- SetDefaultWFXChannelOrder(device);
+ SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
return ALC_TRUE;
}
-static ALCboolean loopback_start_playback(ALCdevice *device)
+static ALCboolean ALCloopback_start(ALCloopback* UNUSED(self))
{
return ALC_TRUE;
- (void)device;
}
-static void loopback_stop_playback(ALCdevice *device)
+static void ALCloopback_stop(ALCloopback* UNUSED(self))
+{
+}
+
+
+static void ALCloopback_Delete(ALCloopback *self)
{
- (void)device;
+ free(self);
}
-static const BackendFuncs loopback_funcs = {
- loopback_open_playback,
- loopback_close_playback,
- loopback_reset_playback,
- loopback_start_playback,
- loopback_stop_playback,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
- ALCdevice_GetLatencyDefault
-};
-
-ALCboolean alc_loopback_init(BackendFuncs *func_list)
+typedef struct ALCloopbackFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCloopbackFactory;
+#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCloopbackFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCloopbackFactory_getFactory(void);
+static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self);
+static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit)
+static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type);
+static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type);
+static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory);
+
+
+ALCbackendFactory *ALCloopbackFactory_getFactory(void)
+{
+ static ALCloopbackFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory* UNUSED(self))
{
- *func_list = loopback_funcs;
return ALC_TRUE;
}
-void alc_loopback_deinit(void)
+static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(self), ALCbackend_Type type)
{
+ if(type == ALCbackend_Loopback)
+ return ALC_TRUE;
+ return ALC_FALSE;
}
-void alc_loopback_probe(enum DevProbe type)
+static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type))
{
- (void)type;
+}
+
+static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ ALCloopback *backend;
+
+ assert(type == ALCbackend_Loopback);
+
+ backend = calloc(1, sizeof(*backend));
+ if(!backend) return NULL;
+
+ ALCloopback_Construct(backend, device);
+
+ return STATIC_CAST(ALCbackend, backend);
}
diff --git a/Alc/backends/mmdevapi.c b/Alc/backends/mmdevapi.c
index 2555c7f3..fa7c54f9 100644
--- a/Alc/backends/mmdevapi.c
+++ b/Alc/backends/mmdevapi.c
@@ -40,6 +40,8 @@
#include "alMain.h"
#include "alu.h"
+#include "threads.h"
+#include "compat.h"
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
@@ -69,7 +71,7 @@ typedef struct {
volatile UINT32 Padding;
volatile int killNow;
- ALvoid *thread;
+ althread_t thread;
} MMDevApiData;
@@ -218,7 +220,7 @@ static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALu
}
-static ALuint MMDevApiProc(ALvoid *ptr)
+FORCE_ALIGN static ALuint MMDevApiProc(ALvoid *ptr)
{
ALCdevice *device = ptr;
MMDevApiData *data = device->ExtraData;
@@ -238,6 +240,7 @@ static ALuint MMDevApiProc(ALvoid *ptr)
}
SetRTPriority();
+ SetThreadName(MIXER_THREAD_NAME);
update_size = device->UpdateSize;
buffer_len = update_size * device->NumUpdates;
@@ -676,8 +679,7 @@ static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
if(SUCCEEDED(hr))
{
data->render = ptr;
- data->thread = StartThread(MMDevApiProc, device);
- if(!data->thread)
+ if(!StartThread(&data->thread, MMDevApiProc, device))
{
if(data->render)
IAudioRenderClient_Release(data->render);
@@ -878,6 +880,9 @@ static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName
CloseHandle(data->MsgEvent);
data->MsgEvent = NULL;
+ free(data->devid);
+ data->devid = NULL;
+
free(data);
device->ExtraData = NULL;
@@ -963,8 +968,6 @@ static const BackendFuncs MMDevApiFuncs = {
NULL,
NULL,
NULL,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
MMDevApiGetLatency
};
diff --git a/Alc/backends/null.c b/Alc/backends/null.c
index 93b38063..a7056369 100644
--- a/Alc/backends/null.c
+++ b/Alc/backends/null.c
@@ -27,139 +27,175 @@
#include "alMain.h"
#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+#include "backends/base.h"
-typedef struct {
- volatile int killNow;
- ALvoid *thread;
-} null_data;
+typedef struct ALCnullBackend {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ volatile int killNow;
+ althread_t thread;
+} ALCnullBackend;
+DECLARE_ALCBACKEND_VTABLE(ALCnullBackend);
+
+static ALuint ALCnullBackend_mixerProc(ALvoid *ptr);
+
+static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct)
+static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name);
+static void ALCnullBackend_close(ALCnullBackend *self);
+static ALCboolean ALCnullBackend_reset(ALCnullBackend *self);
+static ALCboolean ALCnullBackend_start(ALCnullBackend *self);
+static void ALCnullBackend_stop(ALCnullBackend *self);
+static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
static const ALCchar nullDevice[] = "No Output";
-static ALuint NullProc(ALvoid *ptr)
+
+static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device)
{
- ALCdevice *Device = (ALCdevice*)ptr;
- null_data *data = (null_data*)Device->ExtraData;
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCnullBackend, ALCbackend, self);
+}
+
+
+static ALuint ALCnullBackend_mixerProc(ALvoid *ptr)
+{
+ ALCnullBackend *self = (ALCnullBackend*)ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALuint now, start;
ALuint64 avail, done;
- const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 /
- Device->Frequency / 2;
+
+ SetRTPriority();
+ SetThreadName(MIXER_THREAD_NAME);
done = 0;
start = timeGetTime();
- while(!data->killNow && Device->Connected)
+ while(!self->killNow && device->Connected)
{
now = timeGetTime();
- avail = (ALuint64)(now-start) * Device->Frequency / 1000;
+ avail = (ALuint64)(now-start) * device->Frequency / 1000;
if(avail < done)
{
/* Timer wrapped (50 days???). Add the remainder of the cycle to
* the available count and reset the number of samples done */
- avail += ((ALuint64)1<<32)*Device->Frequency/1000 - done;
+ avail += (U64(1)<<32)*device->Frequency/1000 - done;
done = 0;
}
- if(avail-done < Device->UpdateSize)
+ if(avail-done < device->UpdateSize)
{
+ ALuint restTime = (ALuint)((device->UpdateSize - (avail-done)) * 1000 /
+ device->Frequency);
Sleep(restTime);
continue;
}
- while(avail-done >= Device->UpdateSize)
- {
- aluMixData(Device, NULL, Device->UpdateSize);
- done += Device->UpdateSize;
- }
+ do {
+ aluMixData(device, NULL, device->UpdateSize);
+ done += device->UpdateSize;
+ } while(avail-done >= device->UpdateSize);
}
return 0;
}
-static ALCenum null_open_playback(ALCdevice *device, const ALCchar *deviceName)
+
+static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
{
- null_data *data;
+ ALCdevice *device;
- if(!deviceName)
- deviceName = nullDevice;
- else if(strcmp(deviceName, nullDevice) != 0)
+ if(!name)
+ name = nullDevice;
+ else if(strcmp(name, nullDevice) != 0)
return ALC_INVALID_VALUE;
- data = (null_data*)calloc(1, sizeof(*data));
+ device = STATIC_CAST(ALCbackend, self)->mDevice;
+ device->DeviceName = strdup(name);
- device->DeviceName = strdup(deviceName);
- device->ExtraData = data;
return ALC_NO_ERROR;
}
-static void null_close_playback(ALCdevice *device)
+static void ALCnullBackend_close(ALCnullBackend* UNUSED(self))
{
- null_data *data = (null_data*)device->ExtraData;
+}
- free(data);
- device->ExtraData = NULL;
+static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
+{
+ SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
+ return ALC_TRUE;
}
-static ALCboolean null_reset_playback(ALCdevice *device)
+static ALCboolean ALCnullBackend_start(ALCnullBackend *self)
{
- SetDefaultWFXChannelOrder(device);
+ if(!StartThread(&self->thread, ALCnullBackend_mixerProc, self))
+ return ALC_FALSE;
return ALC_TRUE;
}
-static ALCboolean null_start_playback(ALCdevice *device)
+static void ALCnullBackend_stop(ALCnullBackend *self)
{
- null_data *data = (null_data*)device->ExtraData;
+ if(!self->thread)
+ return;
- data->thread = StartThread(NullProc, device);
- if(data->thread == NULL)
- return ALC_FALSE;
+ self->killNow = 1;
+ StopThread(self->thread);
+ self->thread = NULL;
- return ALC_TRUE;
+ self->killNow = 0;
}
-static void null_stop_playback(ALCdevice *device)
+
+static void ALCnullBackend_Delete(ALCnullBackend *self)
{
- null_data *data = (null_data*)device->ExtraData;
+ free(self);
+}
- if(!data->thread)
- return;
+DEFINE_ALCBACKEND_VTABLE(ALCnullBackend);
+
+
+typedef struct ALCnullBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCnullBackendFactory;
+#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCnullBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
+
+static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self);
+static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit)
+static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type);
+static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
- data->killNow = 1;
- StopThread(data->thread);
- data->thread = NULL;
- data->killNow = 0;
+ALCbackendFactory *ALCnullBackendFactory_getFactory(void)
+{
+ static ALCnullBackendFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
}
-static const BackendFuncs null_funcs = {
- null_open_playback,
- null_close_playback,
- null_reset_playback,
- null_start_playback,
- null_stop_playback,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
- ALCdevice_GetLatencyDefault
-};
-
-ALCboolean alc_null_init(BackendFuncs *func_list)
+static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory* UNUSED(self))
{
- *func_list = null_funcs;
return ALC_TRUE;
}
-void alc_null_deinit(void)
+static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUSED(self), ALCbackend_Type type)
{
+ if(type == ALCbackend_Playback)
+ return ALC_TRUE;
+ return ALC_FALSE;
}
-void alc_null_probe(enum DevProbe type)
+static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
@@ -170,3 +206,17 @@ void alc_null_probe(enum DevProbe type)
break;
}
}
+
+static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ ALCnullBackend *backend;
+
+ assert(type == ALCbackend_Playback);
+
+ backend = calloc(1, sizeof(*backend));
+ if(!backend) return NULL;
+
+ ALCnullBackend_Construct(backend, device);
+
+ return STATIC_CAST(ALCbackend, backend);
+}
diff --git a/Alc/backends/opensl.c b/Alc/backends/opensl.c
index 1a104029..76cbaf1a 100644
--- a/Alc/backends/opensl.c
+++ b/Alc/backends/opensl.c
@@ -83,6 +83,10 @@ typedef struct SLDataLocator_AndroidSimpleBufferQueue {
#define SLPlayItf_SetPlayState(a,b) ((*(a))->SetPlayState((a),(b)))
+/* Should start using these generic callers instead of the name-specific ones above. */
+#define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
+#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
+
typedef struct {
/* engine interfaces */
@@ -97,6 +101,7 @@ typedef struct {
void *buffer;
ALuint bufferSize;
+ ALuint curBuffer;
ALuint frameSize;
} osl_data;
@@ -175,12 +180,16 @@ static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
ALCdevice *Device = context;
osl_data *data = Device->ExtraData;
+ ALvoid *buf;
SLresult result;
- aluMixData(Device, data->buffer, data->bufferSize/data->frameSize);
+ buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize;
+ aluMixData(Device, buf, data->bufferSize/data->frameSize);
- result = (*bq)->Enqueue(bq, data->buffer, data->bufferSize);
+ result = (*bq)->Enqueue(bq, buf, data->bufferSize);
PRINTERR(result, "bq->Enqueue");
+
+ data->curBuffer = (data->curBuffer+1) % Device->NumUpdates;
}
@@ -354,7 +363,7 @@ static ALCboolean opensl_start_playback(ALCdevice *Device)
{
data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
data->bufferSize = Device->UpdateSize * data->frameSize;
- data->buffer = calloc(1, data->bufferSize);
+ data->buffer = calloc(Device->NumUpdates, data->bufferSize);
if(!data->buffer)
{
result = SL_RESULT_MEMORY_FAILURE;
@@ -366,10 +375,12 @@ static ALCboolean opensl_start_playback(ALCdevice *Device)
{
if(SL_RESULT_SUCCESS == result)
{
- result = (*bufferQueue)->Enqueue(bufferQueue, data->buffer, data->bufferSize);
+ ALvoid *buf = (ALbyte*)data->buffer + i*data->bufferSize;
+ result = (*bufferQueue)->Enqueue(bufferQueue, buf, data->bufferSize);
PRINTERR(result, "bufferQueue->Enqueue");
}
}
+ data->curBuffer = 0;
if(SL_RESULT_SUCCESS == result)
{
result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_PLAY, &player);
@@ -401,6 +412,25 @@ static ALCboolean opensl_start_playback(ALCdevice *Device)
static void opensl_stop_playback(ALCdevice *Device)
{
osl_data *data = Device->ExtraData;
+ SLPlayItf player;
+ SLAndroidSimpleBufferQueueItf bufferQueue;
+ SLresult result;
+
+ result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
+ PRINTERR(result, "bufferQueue->GetInterface");
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
+ PRINTERR(result, "player->SetPlayState");
+ }
+
+ result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
+ PRINTERR(result, "bufferQueue->GetInterface");
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL0(bufferQueue,Clear)();
+ PRINTERR(result, "bufferQueue->Clear");
+ }
free(data->buffer);
data->buffer = NULL;
@@ -420,8 +450,6 @@ static const BackendFuncs opensl_funcs = {
NULL,
NULL,
NULL,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
ALCdevice_GetLatencyDefault
};
diff --git a/Alc/backends/oss.c b/Alc/backends/oss.c
index 0ed49517..c79793c2 100644
--- a/Alc/backends/oss.c
+++ b/Alc/backends/oss.c
@@ -33,6 +33,10 @@
#include "alMain.h"
#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
#include <sys/soundcard.h>
@@ -47,24 +51,12 @@
#define SOUND_MIXER_WRITE MIXER_WRITE
#endif
+
static const ALCchar oss_device[] = "OSS Default";
static const char *oss_driver = "/dev/dsp";
static const char *oss_capture = "/dev/dsp";
-typedef struct {
- int fd;
- volatile int killNow;
- ALvoid *thread;
-
- ALubyte *mix_data;
- int data_size;
-
- RingBuffer *ring;
- int doCapture;
-} oss_data;
-
-
static int log2i(ALCuint x)
{
int y = 0;
@@ -77,34 +69,65 @@ static int log2i(ALCuint x)
}
-static ALuint OSSProc(ALvoid *ptr)
+typedef struct ALCplaybackOSS {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ int fd;
+
+ ALubyte *mix_data;
+ int data_size;
+
+ volatile int killNow;
+ althread_t thread;
+} ALCplaybackOSS;
+
+static ALuint ALCplaybackOSS_mixerProc(ALvoid *ptr);
+
+static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct)
+static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name);
+static void ALCplaybackOSS_close(ALCplaybackOSS *self);
+static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self);
+static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self);
+static void ALCplaybackOSS_stop(ALCplaybackOSS *self);
+static DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
+static void ALCplaybackOSS_Delete(ALCplaybackOSS *self);
+DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS);
+
+
+static ALuint ALCplaybackOSS_mixerProc(ALvoid *ptr)
{
- ALCdevice *Device = (ALCdevice*)ptr;
- oss_data *data = (oss_data*)Device->ExtraData;
+ ALCplaybackOSS *self = (ALCplaybackOSS*)ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALint frameSize;
ssize_t wrote;
SetRTPriority();
+ SetThreadName(MIXER_THREAD_NAME);
- frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
+ frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
- while(!data->killNow && Device->Connected)
+ while(!self->killNow && device->Connected)
{
- ALint len = data->data_size;
- ALubyte *WritePtr = data->mix_data;
+ ALint len = self->data_size;
+ ALubyte *WritePtr = self->mix_data;
- aluMixData(Device, WritePtr, len/frameSize);
- while(len > 0 && !data->killNow)
+ aluMixData(device, WritePtr, len/frameSize);
+ while(len > 0 && !self->killNow)
{
- wrote = write(data->fd, WritePtr, len);
+ wrote = write(self->fd, WritePtr, len);
if(wrote < 0)
{
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
{
ERR("write failed: %s\n", strerror(errno));
- ALCdevice_Lock(Device);
- aluHandleDisconnect(Device);
- ALCdevice_Unlock(Device);
+ ALCplaybackOSS_lock(self);
+ aluHandleDisconnect(device);
+ ALCplaybackOSS_unlock(self);
break;
}
@@ -120,77 +143,45 @@ static ALuint OSSProc(ALvoid *ptr)
return 0;
}
-static ALuint OSSCaptureProc(ALvoid *ptr)
-{
- ALCdevice *Device = (ALCdevice*)ptr;
- oss_data *data = (oss_data*)Device->ExtraData;
- int frameSize;
- int amt;
-
- SetRTPriority();
-
- frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
-
- while(!data->killNow)
- {
- amt = read(data->fd, data->mix_data, data->data_size);
- if(amt < 0)
- {
- ERR("read failed: %s\n", strerror(errno));
- ALCdevice_Lock(Device);
- aluHandleDisconnect(Device);
- ALCdevice_Unlock(Device);
- break;
- }
- if(amt == 0)
- {
- Sleep(1);
- continue;
- }
- if(data->doCapture)
- WriteRingBuffer(data->ring, data->mix_data, amt/frameSize);
- }
- return 0;
+static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
}
-static ALCenum oss_open_playback(ALCdevice *device, const ALCchar *deviceName)
+static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
{
- oss_data *data;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- if(!deviceName)
- deviceName = oss_device;
- else if(strcmp(deviceName, oss_device) != 0)
+ if(!name)
+ name = oss_device;
+ else if(strcmp(name, oss_device) != 0)
return ALC_INVALID_VALUE;
- data = (oss_data*)calloc(1, sizeof(oss_data));
- data->killNow = 0;
+ self->killNow = 0;
- data->fd = open(oss_driver, O_WRONLY);
- if(data->fd == -1)
+ self->fd = open(oss_driver, O_WRONLY);
+ if(self->fd == -1)
{
- free(data);
ERR("Could not open %s: %s\n", oss_driver, strerror(errno));
return ALC_INVALID_VALUE;
}
- device->DeviceName = strdup(deviceName);
- device->ExtraData = data;
+ device->DeviceName = strdup(name);
+
return ALC_NO_ERROR;
}
-static void oss_close_playback(ALCdevice *device)
+static void ALCplaybackOSS_close(ALCplaybackOSS *self)
{
- oss_data *data = (oss_data*)device->ExtraData;
-
- close(data->fd);
- free(data);
- device->ExtraData = NULL;
+ close(self->fd);
+ self->fd = -1;
}
-static ALCboolean oss_reset_playback(ALCdevice *device)
+static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
{
- oss_data *data = (oss_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
@@ -241,11 +232,11 @@ static ALCboolean oss_reset_playback(ALCdevice *device)
}
/* Don't fail if SETFRAGMENT fails. We can handle just about anything
* that's reported back via GETOSPACE */
- ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
- CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat));
- CHECKERR(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels));
- CHECKERR(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed));
- CHECKERR(ioctl(data->fd, SNDCTL_DSP_GETOSPACE, &info));
+ ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
+ CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
+ CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
+ CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
+ CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &info));
if(0)
{
err:
@@ -277,69 +268,142 @@ static ALCboolean oss_reset_playback(ALCdevice *device)
return ALC_TRUE;
}
-static ALCboolean oss_start_playback(ALCdevice *device)
+static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
{
- oss_data *data = (oss_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
- data->mix_data = calloc(1, data->data_size);
+ self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ self->mix_data = calloc(1, self->data_size);
- data->thread = StartThread(OSSProc, device);
- if(data->thread == NULL)
+ if(!StartThread(&self->thread, ALCplaybackOSS_mixerProc, self))
{
- free(data->mix_data);
- data->mix_data = NULL;
+ free(self->mix_data);
+ self->mix_data = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
-static void oss_stop_playback(ALCdevice *device)
+static void ALCplaybackOSS_stop(ALCplaybackOSS *self)
{
- oss_data *data = (oss_data*)device->ExtraData;
-
- if(!data->thread)
+ if(!self->thread)
return;
- data->killNow = 1;
- StopThread(data->thread);
- data->thread = NULL;
+ self->killNow = 1;
+ StopThread(self->thread);
+ self->thread = NULL;
- data->killNow = 0;
- if(ioctl(data->fd, SNDCTL_DSP_RESET) != 0)
+ self->killNow = 0;
+ if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
ERR("Error resetting device: %s\n", strerror(errno));
- free(data->mix_data);
- data->mix_data = NULL;
+ free(self->mix_data);
+ self->mix_data = NULL;
+}
+
+static void ALCplaybackOSS_Delete(ALCplaybackOSS *self)
+{
+ free(self);
}
-static ALCenum oss_open_capture(ALCdevice *device, const ALCchar *deviceName)
+typedef struct ALCcaptureOSS {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ int fd;
+
+ ALubyte *read_data;
+ int data_size;
+
+ RingBuffer *ring;
+ int doCapture;
+
+ volatile int killNow;
+ althread_t thread;
+} ALCcaptureOSS;
+
+static ALuint ALCcaptureOSS_recordProc(ALvoid *ptr);
+
+static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct)
+static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name);
+static void ALCcaptureOSS_close(ALCcaptureOSS *self);
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self);
+static void ALCcaptureOSS_stop(ALCcaptureOSS *self);
+static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self);
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
+static void ALCcaptureOSS_Delete(ALCcaptureOSS *self);
+DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS);
+
+
+static ALuint ALCcaptureOSS_recordProc(ALvoid *ptr)
{
+ ALCcaptureOSS *self = (ALCcaptureOSS*)ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ int frameSize;
+ int amt;
+
+ SetRTPriority();
+ SetThreadName("alsoft-record");
+
+ frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+
+ while(!self->killNow)
+ {
+ amt = read(self->fd, self->read_data, self->data_size);
+ if(amt < 0)
+ {
+ ERR("read failed: %s\n", strerror(errno));
+ ALCcaptureOSS_lock(self);
+ aluHandleDisconnect(device);
+ ALCcaptureOSS_unlock(self);
+ break;
+ }
+ if(amt == 0)
+ {
+ Sleep(1);
+ continue;
+ }
+ if(self->doCapture)
+ WriteRingBuffer(self->ring, self->read_data, amt/frameSize);
+ }
+
+ return 0;
+}
+
+
+static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
+}
+
+static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
audio_buf_info info;
ALuint frameSize;
int numChannels;
- oss_data *data;
int ossFormat;
int ossSpeed;
char *err;
- if(!deviceName)
- deviceName = oss_device;
- else if(strcmp(deviceName, oss_device) != 0)
+ if(!name)
+ name = oss_device;
+ else if(strcmp(name, oss_device) != 0)
return ALC_INVALID_VALUE;
- data = (oss_data*)calloc(1, sizeof(oss_data));
- data->killNow = 0;
-
- data->fd = open(oss_capture, O_RDONLY);
- if(data->fd == -1)
+ self->fd = open(oss_capture, O_RDONLY);
+ if(self->fd == -1)
{
- free(data);
ERR("Could not open %s: %s\n", oss_capture, strerror(errno));
return ALC_INVALID_VALUE;
}
@@ -359,7 +423,6 @@ static ALCenum oss_open_capture(ALCdevice *device, const ALCchar *deviceName)
case DevFmtInt:
case DevFmtUInt:
case DevFmtFloat:
- free(data);
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
return ALC_INVALID_VALUE;
}
@@ -380,17 +443,17 @@ static ALCenum oss_open_capture(ALCdevice *device, const ALCchar *deviceName)
err = #func; \
goto err; \
}
- CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
- CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat));
- CHECKERR(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels));
- CHECKERR(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed));
- CHECKERR(ioctl(data->fd, SNDCTL_DSP_GETISPACE, &info));
+ CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
+ CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
+ CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
+ CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
+ CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETISPACE, &info));
if(0)
{
err:
ERR("%s failed: %s\n", err, strerror(errno));
- close(data->fd);
- free(data);
+ close(self->fd);
+ self->fd = -1;
return ALC_INVALID_VALUE;
}
#undef CHECKERR
@@ -398,8 +461,8 @@ static ALCenum oss_open_capture(ALCdevice *device, const ALCchar *deviceName)
if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
- close(data->fd);
- free(data);
+ close(self->fd);
+ self->fd = -1;
return ALC_INVALID_VALUE;
}
@@ -408,109 +471,119 @@ static ALCenum oss_open_capture(ALCdevice *device, const ALCchar *deviceName)
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
{
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
- close(data->fd);
- free(data);
+ close(self->fd);
+ self->fd = -1;
return ALC_INVALID_VALUE;
}
- data->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
- if(!data->ring)
+ self->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
+ if(!self->ring)
{
ERR("Ring buffer create failed\n");
- close(data->fd);
- free(data);
+ close(self->fd);
+ self->fd = -1;
return ALC_OUT_OF_MEMORY;
}
- data->data_size = info.fragsize;
- data->mix_data = calloc(1, data->data_size);
+ self->data_size = info.fragsize;
+ self->read_data = calloc(1, self->data_size);
- device->ExtraData = data;
- data->thread = StartThread(OSSCaptureProc, device);
- if(data->thread == NULL)
+ if(!StartThread(&self->thread, ALCcaptureOSS_recordProc, self))
{
device->ExtraData = NULL;
- free(data->mix_data);
- free(data);
+ close(self->fd);
+ self->fd = -1;
return ALC_OUT_OF_MEMORY;
}
- device->DeviceName = strdup(deviceName);
+ device->DeviceName = strdup(name);
+
return ALC_NO_ERROR;
}
-static void oss_close_capture(ALCdevice *device)
+static void ALCcaptureOSS_close(ALCcaptureOSS *self)
{
- oss_data *data = (oss_data*)device->ExtraData;
- data->killNow = 1;
- StopThread(data->thread);
+ self->killNow = 1;
+ StopThread(self->thread);
+ self->killNow = 0;
- close(data->fd);
+ close(self->fd);
+ self->fd = -1;
- DestroyRingBuffer(data->ring);
+ DestroyRingBuffer(self->ring);
+ self->ring = NULL;
- free(data->mix_data);
- free(data);
- device->ExtraData = NULL;
+ free(self->read_data);
+ self->read_data = NULL;
}
-static void oss_start_capture(ALCdevice *Device)
+static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
{
- oss_data *data = (oss_data*)Device->ExtraData;
- data->doCapture = 1;
+ self->doCapture = 1;
+ return ALC_TRUE;
}
-static void oss_stop_capture(ALCdevice *Device)
+static void ALCcaptureOSS_stop(ALCcaptureOSS *self)
{
- oss_data *data = (oss_data*)Device->ExtraData;
- data->doCapture = 0;
+ self->doCapture = 0;
}
-static ALCenum oss_capture_samples(ALCdevice *Device, ALCvoid *pBuffer, ALCuint lSamples)
+static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
{
- oss_data *data = (oss_data*)Device->ExtraData;
- ReadRingBuffer(data->ring, pBuffer, lSamples);
+ ReadRingBuffer(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
-static ALCuint oss_available_samples(ALCdevice *Device)
+static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
+{
+ return RingBufferSize(self->ring);
+}
+
+void ALCcaptureOSS_Delete(ALCcaptureOSS *self)
+{
+ free(self);
+}
+
+
+
+typedef struct ALCossBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCossBackendFactory;
+#define ALCOSSBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCossBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCossBackendFactory_getFactory(void);
+
+static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
+static DECLARE_FORWARD(ALCossBackendFactory, ALCbackendFactory, void, deinit)
+static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type);
+static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory);
+
+
+ALCbackendFactory *ALCossBackendFactory_getFactory(void)
{
- oss_data *data = (oss_data*)Device->ExtraData;
- return RingBufferSize(data->ring);
+ static ALCossBackendFactory factory = ALCOSSBACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
}
-static const BackendFuncs oss_funcs = {
- oss_open_playback,
- oss_close_playback,
- oss_reset_playback,
- oss_start_playback,
- oss_stop_playback,
- oss_open_capture,
- oss_close_capture,
- oss_start_capture,
- oss_stop_capture,
- oss_capture_samples,
- oss_available_samples,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
- ALCdevice_GetLatencyDefault
-};
-
-ALCboolean alc_oss_init(BackendFuncs *func_list)
+ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self))
{
ConfigValueStr("oss", "device", &oss_driver);
ConfigValueStr("oss", "capture", &oss_capture);
- *func_list = oss_funcs;
return ALC_TRUE;
}
-void alc_oss_deinit(void)
+ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), ALCbackend_Type type)
{
+ if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+ return ALC_TRUE;
+ return ALC_FALSE;
}
-void alc_oss_probe(enum DevProbe type)
+void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
@@ -535,3 +608,31 @@ void alc_oss_probe(enum DevProbe type)
break;
}
}
+
+ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ ALCplaybackOSS *backend;
+
+ backend = calloc(1, sizeof(*backend));
+ if(!backend) return NULL;
+
+ ALCplaybackOSS_Construct(backend, device);
+
+ return STATIC_CAST(ALCbackend, backend);
+ }
+ if(type == ALCbackend_Capture)
+ {
+ ALCcaptureOSS *backend;
+
+ backend = calloc(1, sizeof(*backend));
+ if(!backend) return NULL;
+
+ ALCcaptureOSS_Construct(backend, device);
+
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
diff --git a/Alc/backends/portaudio.c b/Alc/backends/portaudio.c
index 2f576639..162788fc 100644
--- a/Alc/backends/portaudio.c
+++ b/Alc/backends/portaudio.c
@@ -26,6 +26,7 @@
#include "alMain.h"
#include "alu.h"
+#include "compat.h"
#include <portaudio.h>
@@ -35,7 +36,7 @@ static const ALCchar pa_device[] = "PortAudio Default";
#ifdef HAVE_DYNLOAD
static void *pa_handle;
-#define MAKE_FUNC(x) static typeof(x) * p##x
+#define MAKE_FUNC(x) static __typeof(x) * p##x
MAKE_FUNC(Pa_Initialize);
MAKE_FUNC(Pa_Terminate);
MAKE_FUNC(Pa_GetErrorText);
@@ -127,31 +128,23 @@ typedef struct {
} pa_data;
-static int pa_callback(const void *inputBuffer, void *outputBuffer,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
- const PaStreamCallbackFlags statusFlags, void *userData)
+static int pa_callback(const void *UNUSED(inputBuffer), void *outputBuffer,
+ unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
+ const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
{
ALCdevice *device = (ALCdevice*)userData;
- (void)inputBuffer;
- (void)timeInfo;
- (void)statusFlags;
-
aluMixData(device, outputBuffer, framesPerBuffer);
return 0;
}
-static int pa_capture_cb(const void *inputBuffer, void *outputBuffer,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
- const PaStreamCallbackFlags statusFlags, void *userData)
+static int pa_capture_cb(const void *inputBuffer, void *UNUSED(outputBuffer),
+ unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
+ const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
{
ALCdevice *device = (ALCdevice*)userData;
pa_data *data = (pa_data*)device->ExtraData;
- (void)outputBuffer;
- (void)timeInfo;
- (void)statusFlags;
-
WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
return 0;
}
@@ -381,6 +374,9 @@ static void pa_close_capture(ALCdevice *device)
if(err != paNoError)
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
+ DestroyRingBuffer(data->ring);
+ data->ring = NULL;
+
free(data);
device->ExtraData = NULL;
}
@@ -431,8 +427,6 @@ static const BackendFuncs pa_funcs = {
pa_stop_capture,
pa_capture_samples,
pa_available_samples,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
ALCdevice_GetLatencyDefault
};
diff --git a/Alc/backends/pulseaudio.c b/Alc/backends/pulseaudio.c
index af44e3de..e2ae52ae 100644
--- a/Alc/backends/pulseaudio.c
+++ b/Alc/backends/pulseaudio.c
@@ -25,6 +25,10 @@
#include "alMain.h"
#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
#include <pulse/pulseaudio.h>
@@ -39,7 +43,7 @@
#ifdef HAVE_DYNLOAD
static void *pa_handle;
-#define MAKE_FUNC(x) static typeof(x) * p##x
+#define MAKE_FUNC(x) static __typeof(x) * p##x
MAKE_FUNC(pa_context_unref);
MAKE_FUNC(pa_sample_spec_valid);
MAKE_FUNC(pa_frame_size);
@@ -187,45 +191,6 @@ MAKE_FUNC(pa_stream_begin_write);
#endif
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-typedef struct {
- char *device_name;
-
- const void *cap_store;
- size_t cap_len;
- size_t cap_remain;
-
- ALCuint last_readable;
-
- pa_buffer_attr attr;
- pa_sample_spec spec;
-
- pa_threaded_mainloop *loop;
-
- ALvoid *thread;
- volatile ALboolean killNow;
-
- pa_stream *stream;
- pa_context *context;
-} pulse_data;
-
-typedef struct {
- char *name;
- char *device_name;
-} DevMap;
-
-
-static DevMap *allDevNameMap;
-static ALuint numDevNames;
-static DevMap *allCaptureDevNameMap;
-static ALuint numCaptureDevNames;
-static pa_context_flags_t pulse_ctx_flags;
-static pa_proplist *prop_filter;
-
-
static ALCboolean pulse_load(void)
{
ALCboolean ret = ALC_TRUE;
@@ -336,6 +301,15 @@ static ALCboolean pulse_load(void)
return ret;
}
+
+/* Global flags and properties */
+static pa_context_flags_t pulse_ctx_flags;
+static pa_proplist *prop_filter;
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
/* PulseAudio Event Callbacks */
static void context_state_callback(pa_context *context, void *pdata)
{
@@ -357,112 +331,202 @@ static void stream_state_callback(pa_stream *stream, void *pdata)
pa_threaded_mainloop_signal(loop, 0);
}
-static void stream_buffer_attr_callback(pa_stream *stream, void *pdata)
+static void stream_success_callback(pa_stream *UNUSED(stream), int UNUSED(success), void *pdata)
{
- ALCdevice *device = pdata;
- pulse_data *data = device->ExtraData;
-
- data->attr = *pa_stream_get_buffer_attr(stream);
- TRACE("minreq=%d, tlength=%d, prebuf=%d\n", data->attr.minreq, data->attr.tlength, data->attr.prebuf);
+ pa_threaded_mainloop *loop = pdata;
+ pa_threaded_mainloop_signal(loop, 0);
}
-static void context_state_callback2(pa_context *context, void *pdata)
+static void wait_for_operation(pa_operation *op, pa_threaded_mainloop *loop)
{
- ALCdevice *Device = pdata;
- pulse_data *data = Device->ExtraData;
-
- if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
+ if(op)
{
- ERR("Received context failure!\n");
- aluHandleDisconnect(Device);
+ while(pa_operation_get_state(op) == PA_OPERATION_RUNNING)
+ pa_threaded_mainloop_wait(loop);
+ pa_operation_unref(op);
}
- pa_threaded_mainloop_signal(data->loop, 0);
}
-static void stream_state_callback2(pa_stream *stream, void *pdata)
+
+static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)
{
- ALCdevice *Device = pdata;
- pulse_data *data = Device->ExtraData;
+ const char *name = "OpenAL Soft";
+ char path_name[PATH_MAX];
+ pa_context_state_t state;
+ pa_context *context;
+ int err;
- if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
+ if(pa_get_binary_name(path_name, sizeof(path_name)))
+ name = pa_path_get_filename(path_name);
+
+ context = pa_context_new(pa_threaded_mainloop_get_api(loop), name);
+ if(!context)
{
- ERR("Received stream failure!\n");
- aluHandleDisconnect(Device);
+ ERR("pa_context_new() failed\n");
+ return NULL;
}
- pa_threaded_mainloop_signal(data->loop, 0);
-}
-static void stream_success_callback(pa_stream *stream, int success, void *pdata)
-{
- ALCdevice *Device = pdata;
- pulse_data *data = Device->ExtraData;
- (void)stream;
- (void)success;
+ pa_context_set_state_callback(context, context_state_callback, loop);
+
+ if((err=pa_context_connect(context, NULL, pulse_ctx_flags, NULL)) >= 0)
+ {
+ while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)
+ {
+ if(!PA_CONTEXT_IS_GOOD(state))
+ {
+ err = pa_context_errno(context);
+ if(err > 0) err = -err;
+ break;
+ }
- pa_threaded_mainloop_signal(data->loop, 0);
+ pa_threaded_mainloop_wait(loop);
+ }
+ }
+ pa_context_set_state_callback(context, NULL, NULL);
+
+ if(err < 0)
+ {
+ if(!silent)
+ ERR("Context did not connect: %s\n", pa_strerror(err));
+ pa_context_unref(context);
+ return NULL;
+ }
+
+ return context;
}
-static void sink_info_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
-{
- ALCdevice *device = pdata;
- pulse_data *data = device->ExtraData;
- char chanmap_str[256] = "";
- const struct {
- const char *str;
- enum DevFmtChannels chans;
- } chanmaps[] = {
- { "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right",
- DevFmtX71 },
- { "front-left,front-right,front-center,lfe,rear-center,side-left,side-right",
- DevFmtX61 },
- { "front-left,front-right,front-center,lfe,rear-left,rear-right",
- DevFmtX51 },
- { "front-left,front-right,front-center,lfe,side-left,side-right",
- DevFmtX51Side },
- { "front-left,front-right,rear-left,rear-right", DevFmtQuad },
- { "front-left,front-right", DevFmtStereo },
- { "mono", DevFmtMono },
- { NULL, 0 }
- };
- int i;
- (void)context;
- if(eol)
+static ALCboolean pulse_open(pa_threaded_mainloop **loop, pa_context **context,
+ void(*state_cb)(pa_context*,void*), void *ptr)
+{
+ if(!(*loop = pa_threaded_mainloop_new()))
{
- pa_threaded_mainloop_signal(data->loop, 0);
- return;
+ ERR("pa_threaded_mainloop_new() failed!\n");
+ return ALC_FALSE;
+ }
+ if(pa_threaded_mainloop_start(*loop) < 0)
+ {
+ ERR("pa_threaded_mainloop_start() failed\n");
+ goto error;
}
- for(i = 0;chanmaps[i].str;i++)
+ pa_threaded_mainloop_lock(*loop);
+
+ *context = connect_context(*loop, AL_FALSE);
+ if(!*context)
{
- pa_channel_map map;
- if(!pa_channel_map_parse(&map, chanmaps[i].str))
- continue;
+ pa_threaded_mainloop_unlock(*loop);
+ pa_threaded_mainloop_stop(*loop);
+ goto error;
+ }
+ pa_context_set_state_callback(*context, state_cb, ptr);
- if(pa_channel_map_equal(&info->channel_map, &map)
+ pa_threaded_mainloop_unlock(*loop);
+ return ALC_TRUE;
+
+error:
+ pa_threaded_mainloop_free(*loop);
+ *loop = NULL;
+
+ return ALC_FALSE;
+}
+
+static void pulse_close(pa_threaded_mainloop *loop, pa_context *context,
+ pa_stream *stream)
+{
+ pa_threaded_mainloop_lock(loop);
+
+ if(stream)
+ {
+ pa_stream_set_moved_callback(stream, NULL, NULL);
#if PA_CHECK_VERSION(0,9,15)
- || (pa_channel_map_superset &&
- pa_channel_map_superset(&info->channel_map, &map))
+ if(pa_stream_set_buffer_attr_callback)
+ pa_stream_set_buffer_attr_callback(stream, NULL, NULL);
#endif
- )
- {
- device->FmtChans = chanmaps[i].chans;
- return;
- }
+ pa_stream_disconnect(stream);
+ pa_stream_unref(stream);
}
- pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
- ERR("Failed to find format for channel map:\n %s\n", chanmap_str);
+ pa_context_disconnect(context);
+ pa_context_unref(context);
+
+ pa_threaded_mainloop_unlock(loop);
+
+ pa_threaded_mainloop_stop(loop);
+ pa_threaded_mainloop_free(loop);
+}
+
+
+typedef struct {
+ char *name;
+ char *device_name;
+} DevMap;
+
+static DevMap *allDevNameMap;
+static ALuint numDevNames;
+static DevMap *allCaptureDevNameMap;
+static ALuint numCaptureDevNames;
+
+
+typedef struct ALCpulsePlayback {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ char *device_name;
+
+ pa_buffer_attr attr;
+ pa_sample_spec spec;
+
+ pa_threaded_mainloop *loop;
+
+ pa_stream *stream;
+ pa_context *context;
+
+ volatile ALboolean killNow;
+ althread_t thread;
+} ALCpulsePlayback;
+DECLARE_ALCBACKEND_VTABLE(ALCpulsePlayback);
+
+static void ALCpulsePlayback_deviceCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
+static void ALCpulsePlayback_probeDevices(void);
+
+static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata);
+static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata);
+static void ALCpulsePlayback_streamStateCallback(pa_stream *stream, void *pdata);
+static void ALCpulsePlayback_sinkInfoCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
+static void ALCpulsePlayback_sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
+static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata);
+static pa_stream *ALCpulsePlayback_connectStream(const char *device_name, pa_threaded_mainloop *loop,
+ pa_context *context, pa_stream_flags_t flags,
+ pa_buffer_attr *attr, pa_sample_spec *spec,
+ pa_channel_map *chanmap);
+static ALuint ALCpulsePlayback_mixerProc(ALvoid *ptr);
+
+static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCpulsePlayback, ALCbackend, void, Destruct)
+static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name);
+static void ALCpulsePlayback_close(ALCpulsePlayback *self);
+static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self);
+static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self);
+static void ALCpulsePlayback_stop(ALCpulsePlayback *self);
+static DECLARE_FORWARD2(ALCpulsePlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
+static DECLARE_FORWARD(ALCpulsePlayback, ALCbackend, ALCuint, availableSamples)
+static void ALCpulsePlayback_lock(ALCpulsePlayback *self);
+static void ALCpulsePlayback_unlock(ALCpulsePlayback *self);
+
+
+static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCpulsePlayback, ALCbackend, self);
}
-static void sink_device_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
+
+static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata)
{
pa_threaded_mainloop *loop = pdata;
void *temp;
ALuint i;
- (void)context;
-
if(eol)
{
pa_threaded_mainloop_signal(loop, 0);
@@ -487,172 +551,169 @@ static void sink_device_callback(pa_context *context, const pa_sink_info *info,
}
}
-static void source_device_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata)
+static void ALCpulsePlayback_probeDevices(void)
{
- pa_threaded_mainloop *loop = pdata;
- void *temp;
- ALuint i;
-
- (void)context;
+ pa_threaded_mainloop *loop;
- if(eol)
+ allDevNameMap = malloc(sizeof(DevMap) * 1);
+ if((loop=pa_threaded_mainloop_new()) &&
+ pa_threaded_mainloop_start(loop) >= 0)
{
- pa_threaded_mainloop_signal(loop, 0);
- return;
- }
+ pa_context *context;
- for(i = 0;i < numCaptureDevNames;i++)
- {
- if(strcmp(info->name, allCaptureDevNameMap[i].device_name) == 0)
- return;
- }
+ pa_threaded_mainloop_lock(loop);
+ context = connect_context(loop, AL_FALSE);
+ if(context)
+ {
+ pa_operation *o;
+ pa_stream_flags_t flags;
+ pa_sample_spec spec;
+ pa_stream *stream;
- TRACE("Got device \"%s\", \"%s\"\n", info->description, info->name);
+ flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
+ PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE;
- temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap));
- if(temp)
- {
- allCaptureDevNameMap = temp;
- allCaptureDevNameMap[numCaptureDevNames].name = strdup(info->description);
- allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name);
- numCaptureDevNames++;
+ spec.format = PA_SAMPLE_S16NE;
+ spec.rate = 44100;
+ spec.channels = 2;
+
+ stream = ALCpulsePlayback_connectStream(NULL, loop, context, flags,
+ NULL, &spec, NULL);
+ if(stream)
+ {
+ o = pa_context_get_sink_info_by_name(context, pa_stream_get_device_name(stream),
+ ALCpulsePlayback_deviceCallback, loop);
+ wait_for_operation(o, loop);
+
+ pa_stream_disconnect(stream);
+ pa_stream_unref(stream);
+ stream = NULL;
+ }
+
+ o = pa_context_get_sink_info_list(context, ALCpulsePlayback_deviceCallback, loop);
+ wait_for_operation(o, loop);
+
+ pa_context_disconnect(context);
+ pa_context_unref(context);
+ }
+ pa_threaded_mainloop_unlock(loop);
+ pa_threaded_mainloop_stop(loop);
}
+ if(loop)
+ pa_threaded_mainloop_free(loop);
}
-static void sink_name_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
-{
- ALCdevice *device = pdata;
- pulse_data *data = device->ExtraData;
- (void)context;
- if(eol)
- {
- pa_threaded_mainloop_signal(data->loop, 0);
- return;
- }
+static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata)
+{
+ ALCpulsePlayback *self = pdata;
- free(device->DeviceName);
- device->DeviceName = strdup(info->description);
+ self->attr = *pa_stream_get_buffer_attr(stream);
+ TRACE("minreq=%d, tlength=%d, prebuf=%d\n", self->attr.minreq, self->attr.tlength, self->attr.prebuf);
}
-static void source_name_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata)
+static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata)
{
- ALCdevice *device = pdata;
- pulse_data *data = device->ExtraData;
- (void)context;
-
- if(eol)
+ ALCpulsePlayback *self = pdata;
+ if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
{
- pa_threaded_mainloop_signal(data->loop, 0);
- return;
+ ERR("Received context failure!\n");
+ aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
}
-
- free(device->DeviceName);
- device->DeviceName = strdup(info->description);
+ pa_threaded_mainloop_signal(self->loop, 0);
}
-
-static void stream_moved_callback(pa_stream *stream, void *pdata)
+static void ALCpulsePlayback_streamStateCallback(pa_stream *stream, void *pdata)
{
- ALCdevice *device = pdata;
- pulse_data *data = device->ExtraData;
- (void)stream;
-
- free(data->device_name);
- data->device_name = strdup(pa_stream_get_device_name(data->stream));
-
- TRACE("Stream moved to %s\n", data->device_name);
+ ALCpulsePlayback *self = pdata;
+ if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
+ {
+ ERR("Received stream failure!\n");
+ aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+ }
+ pa_threaded_mainloop_signal(self->loop, 0);
}
-
-static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)
+static void ALCpulsePlayback_sinkInfoCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata)
{
- const char *name = "OpenAL Soft";
- char path_name[PATH_MAX];
- pa_context_state_t state;
- pa_context *context;
- int err;
-
- if(pa_get_binary_name(path_name, sizeof(path_name)))
- name = pa_path_get_filename(path_name);
+ ALCpulsePlayback *self = pdata;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ char chanmap_str[256] = "";
+ const struct {
+ const char *str;
+ enum DevFmtChannels chans;
+ } chanmaps[] = {
+ { "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right",
+ DevFmtX71 },
+ { "front-left,front-right,front-center,lfe,rear-center,side-left,side-right",
+ DevFmtX61 },
+ { "front-left,front-right,front-center,lfe,rear-left,rear-right",
+ DevFmtX51 },
+ { "front-left,front-right,front-center,lfe,side-left,side-right",
+ DevFmtX51Side },
+ { "front-left,front-right,rear-left,rear-right", DevFmtQuad },
+ { "front-left,front-right", DevFmtStereo },
+ { "mono", DevFmtMono },
+ { NULL, 0 }
+ };
+ int i;
- context = pa_context_new(pa_threaded_mainloop_get_api(loop), name);
- if(!context)
+ if(eol)
{
- ERR("pa_context_new() failed\n");
- return NULL;
+ pa_threaded_mainloop_signal(self->loop, 0);
+ return;
}
- pa_context_set_state_callback(context, context_state_callback, loop);
-
- if((err=pa_context_connect(context, NULL, pulse_ctx_flags, NULL)) >= 0)
+ for(i = 0;chanmaps[i].str;i++)
{
- while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)
- {
- if(!PA_CONTEXT_IS_GOOD(state))
- {
- err = pa_context_errno(context);
- if(err > 0) err = -err;
- break;
- }
+ pa_channel_map map;
+ if(!pa_channel_map_parse(&map, chanmaps[i].str))
+ continue;
- pa_threaded_mainloop_wait(loop);
+ if(pa_channel_map_equal(&info->channel_map, &map)
+#if PA_CHECK_VERSION(0,9,15)
+ || (pa_channel_map_superset &&
+ pa_channel_map_superset(&info->channel_map, &map))
+#endif
+ )
+ {
+ device->FmtChans = chanmaps[i].chans;
+ return;
}
}
- pa_context_set_state_callback(context, NULL, NULL);
- if(err < 0)
- {
- if(!silent)
- ERR("Context did not connect: %s\n", pa_strerror(err));
- pa_context_unref(context);
- return NULL;
- }
-
- return context;
+ pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
+ ERR("Failed to find format for channel map:\n %s\n", chanmap_str);
}
-static pa_stream *connect_playback_stream(const char *device_name,
- pa_threaded_mainloop *loop, pa_context *context,
- pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
- pa_channel_map *chanmap)
+static void ALCpulsePlayback_sinkNameCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata)
{
- pa_stream_state_t state;
- pa_stream *stream;
+ ALCpulsePlayback *self = pdata;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
- stream = pa_stream_new_with_proplist(context, "Playback Stream", spec, chanmap, prop_filter);
- if(!stream)
+ if(eol)
{
- ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context)));
- return NULL;
+ pa_threaded_mainloop_signal(self->loop, 0);
+ return;
}
- pa_stream_set_state_callback(stream, stream_state_callback, loop);
+ free(device->DeviceName);
+ device->DeviceName = strdup(info->description);
+}
- if(pa_stream_connect_playback(stream, device_name, attr, flags, NULL, NULL) < 0)
- {
- ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context)));
- pa_stream_unref(stream);
- return NULL;
- }
- while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
- {
- if(!PA_STREAM_IS_GOOD(state))
- {
- ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context)));
- pa_stream_unref(stream);
- return NULL;
- }
+static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata)
+{
+ ALCpulsePlayback *self = pdata;
- pa_threaded_mainloop_wait(loop);
- }
- pa_stream_set_state_callback(stream, NULL, NULL);
+ free(self->device_name);
+ self->device_name = strdup(pa_stream_get_device_name(stream));
- return stream;
+ TRACE("Stream moved to %s\n", self->device_name);
}
-static pa_stream *connect_record_stream(const char *device_name,
+
+static pa_stream *ALCpulsePlayback_connectStream(const char *device_name,
pa_threaded_mainloop *loop, pa_context *context,
pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
pa_channel_map *chanmap)
@@ -660,7 +721,7 @@ static pa_stream *connect_record_stream(const char *device_name,
pa_stream_state_t state;
pa_stream *stream;
- stream = pa_stream_new_with_proplist(context, "Capture Stream", spec, chanmap, prop_filter);
+ stream = pa_stream_new_with_proplist(context, "Playback Stream", spec, chanmap, prop_filter);
if(!stream)
{
ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context)));
@@ -669,7 +730,7 @@ static pa_stream *connect_record_stream(const char *device_name,
pa_stream_set_state_callback(stream, stream_state_callback, loop);
- if(pa_stream_connect_record(stream, device_name, attr, flags) < 0)
+ if(pa_stream_connect_playback(stream, device_name, attr, flags, NULL, NULL) < 0)
{
ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context)));
pa_stream_unref(stream);
@@ -693,137 +754,40 @@ static pa_stream *connect_record_stream(const char *device_name,
}
-static void wait_for_operation(pa_operation *op, pa_threaded_mainloop *loop)
-{
- if(op)
- {
- while(pa_operation_get_state(op) == PA_OPERATION_RUNNING)
- pa_threaded_mainloop_wait(loop);
- pa_operation_unref(op);
- }
-}
-
-
-static void probe_devices(ALboolean capture)
-{
- pa_threaded_mainloop *loop;
-
- if(capture == AL_FALSE)
- allDevNameMap = malloc(sizeof(DevMap) * 1);
- else
- allCaptureDevNameMap = malloc(sizeof(DevMap) * 1);
-
- if((loop=pa_threaded_mainloop_new()) &&
- pa_threaded_mainloop_start(loop) >= 0)
- {
- pa_context *context;
-
- pa_threaded_mainloop_lock(loop);
- context = connect_context(loop, AL_FALSE);
- if(context)
- {
- pa_operation *o;
-
- if(capture == AL_FALSE)
- {
- pa_stream_flags_t flags;
- pa_sample_spec spec;
- pa_stream *stream;
-
- flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
- PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE;
-
- spec.format = PA_SAMPLE_S16NE;
- spec.rate = 44100;
- spec.channels = 2;
-
- stream = connect_playback_stream(NULL, loop, context, flags,
- NULL, &spec, NULL);
- if(stream)
- {
- o = pa_context_get_sink_info_by_name(context, pa_stream_get_device_name(stream), sink_device_callback, loop);
- wait_for_operation(o, loop);
-
- pa_stream_disconnect(stream);
- pa_stream_unref(stream);
- stream = NULL;
- }
-
- o = pa_context_get_sink_info_list(context, sink_device_callback, loop);
- }
- else
- {
- pa_stream_flags_t flags;
- pa_sample_spec spec;
- pa_stream *stream;
-
- flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
- PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE;
-
- spec.format = PA_SAMPLE_S16NE;
- spec.rate = 44100;
- spec.channels = 1;
-
- stream = connect_record_stream(NULL, loop, context, flags,
- NULL, &spec, NULL);
- if(stream)
- {
- o = pa_context_get_source_info_by_name(context, pa_stream_get_device_name(stream), source_device_callback, loop);
- wait_for_operation(o, loop);
-
- pa_stream_disconnect(stream);
- pa_stream_unref(stream);
- stream = NULL;
- }
-
- o = pa_context_get_source_info_list(context, source_device_callback, loop);
- }
- wait_for_operation(o, loop);
-
- pa_context_disconnect(context);
- pa_context_unref(context);
- }
- pa_threaded_mainloop_unlock(loop);
- pa_threaded_mainloop_stop(loop);
- }
- if(loop)
- pa_threaded_mainloop_free(loop);
-}
-
-
-static ALuint PulseProc(ALvoid *param)
+static ALuint ALCpulsePlayback_mixerProc(ALvoid *ptr)
{
- ALCdevice *Device = param;
- pulse_data *data = Device->ExtraData;
+ ALCpulsePlayback *self = ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
ALuint buffer_size;
ALint update_size;
size_t frame_size;
ssize_t len;
SetRTPriority();
+ SetThreadName(MIXER_THREAD_NAME);
- pa_threaded_mainloop_lock(data->loop);
- frame_size = pa_frame_size(&data->spec);
- update_size = Device->UpdateSize * frame_size;
+ pa_threaded_mainloop_lock(self->loop);
+ frame_size = pa_frame_size(&self->spec);
+ update_size = device->UpdateSize * frame_size;
/* Sanitize buffer metrics, in case we actually have less than what we
* asked for. */
- buffer_size = minu(update_size*Device->NumUpdates, data->attr.tlength);
+ buffer_size = minu(update_size*device->NumUpdates, self->attr.tlength);
update_size = minu(update_size, buffer_size/2);
do {
- len = pa_stream_writable_size(data->stream) - data->attr.tlength +
+ len = pa_stream_writable_size(self->stream) - self->attr.tlength +
buffer_size;
if(len < update_size)
{
- if(pa_stream_is_corked(data->stream) == 1)
+ if(pa_stream_is_corked(self->stream) == 1)
{
pa_operation *o;
- o = pa_stream_cork(data->stream, 0, NULL, NULL);
+ o = pa_stream_cork(self->stream, 0, NULL, NULL);
if(o) pa_operation_unref(o);
}
- pa_threaded_mainloop_unlock(data->loop);
+ pa_threaded_mainloop_unlock(self->loop);
Sleep(1);
- pa_threaded_mainloop_lock(data->loop);
+ pa_threaded_mainloop_lock(self->loop);
continue;
}
len -= len%update_size;
@@ -836,117 +800,42 @@ static ALuint PulseProc(ALvoid *param)
#if PA_CHECK_VERSION(0,9,16)
if(!pa_stream_begin_write ||
- pa_stream_begin_write(data->stream, &buf, &newlen) < 0)
+ pa_stream_begin_write(self->stream, &buf, &newlen) < 0)
#endif
{
buf = pa_xmalloc(newlen);
free_func = pa_xfree;
}
- aluMixData(Device, buf, newlen/frame_size);
+ aluMixData(device, buf, newlen/frame_size);
- pa_stream_write(data->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);
+ pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);
len -= newlen;
}
- } while(!data->killNow && Device->Connected);
- pa_threaded_mainloop_unlock(data->loop);
+ } while(!self->killNow && device->Connected);
+ pa_threaded_mainloop_unlock(self->loop);
return 0;
}
-static ALCboolean pulse_open(ALCdevice *device)
-{
- pulse_data *data = pa_xmalloc(sizeof(pulse_data));
- memset(data, 0, sizeof(*data));
-
- if(!(data->loop = pa_threaded_mainloop_new()))
- {
- ERR("pa_threaded_mainloop_new() failed!\n");
- goto out;
- }
- if(pa_threaded_mainloop_start(data->loop) < 0)
- {
- ERR("pa_threaded_mainloop_start() failed\n");
- goto out;
- }
-
- pa_threaded_mainloop_lock(data->loop);
- device->ExtraData = data;
-
- data->context = connect_context(data->loop, AL_FALSE);
- if(!data->context)
- {
- pa_threaded_mainloop_unlock(data->loop);
- goto out;
- }
- pa_context_set_state_callback(data->context, context_state_callback2, device);
-
- pa_threaded_mainloop_unlock(data->loop);
- return ALC_TRUE;
-
-out:
- if(data->loop)
- {
- pa_threaded_mainloop_stop(data->loop);
- pa_threaded_mainloop_free(data->loop);
- }
-
- device->ExtraData = NULL;
- pa_xfree(data);
- return ALC_FALSE;
-}
-
-static void pulse_close(ALCdevice *device)
-{
- pulse_data *data = device->ExtraData;
-
- pa_threaded_mainloop_lock(data->loop);
-
- if(data->stream)
- {
- pa_stream_set_moved_callback(data->stream, NULL, NULL);
-#if PA_CHECK_VERSION(0,9,15)
- if(pa_stream_set_buffer_attr_callback)
- pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);
-#endif
- pa_stream_disconnect(data->stream);
- pa_stream_unref(data->stream);
- }
-
- pa_context_disconnect(data->context);
- pa_context_unref(data->context);
-
- pa_threaded_mainloop_unlock(data->loop);
-
- pa_threaded_mainloop_stop(data->loop);
- pa_threaded_mainloop_free(data->loop);
-
- free(data->device_name);
-
- device->ExtraData = NULL;
- pa_xfree(data);
-}
-
-/* OpenAL */
-static ALCenum pulse_open_playback(ALCdevice *device, const ALCchar *device_name)
+static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name)
{
const char *pulse_name = NULL;
pa_stream_flags_t flags;
pa_sample_spec spec;
- pulse_data *data;
pa_operation *o;
- if(device_name)
+ if(name)
{
ALuint i;
if(!allDevNameMap)
- probe_devices(AL_FALSE);
+ ALCpulsePlayback_probeDevices();
for(i = 0;i < numDevNames;i++)
{
- if(strcmp(device_name, allDevNameMap[i].name) == 0)
+ if(strcmp(name, allDevNameMap[i].name) == 0)
{
pulse_name = allDevNameMap[i].device_name;
break;
@@ -956,11 +845,10 @@ static ALCenum pulse_open_playback(ALCdevice *device, const ALCchar *device_name
return ALC_INVALID_VALUE;
}
- if(pulse_open(device) == ALC_FALSE)
+ if(!pulse_open(&self->loop, &self->context, ALCpulsePlayback_contextStateCallback, self))
return ALC_INVALID_VALUE;
- data = device->ExtraData;
- pa_threaded_mainloop_lock(data->loop);
+ pa_threaded_mainloop_lock(self->loop);
flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
PA_STREAM_FIX_CHANNELS;
@@ -971,58 +859,67 @@ static ALCenum pulse_open_playback(ALCdevice *device, const ALCchar *device_name
spec.rate = 44100;
spec.channels = 2;
- data->stream = connect_playback_stream(pulse_name, data->loop, data->context,
- flags, NULL, &spec, NULL);
- if(!data->stream)
+ self->stream = ALCpulsePlayback_connectStream(pulse_name, self->loop, self->context,
+ flags, NULL, &spec, NULL);
+ if(!self->stream)
{
- pa_threaded_mainloop_unlock(data->loop);
- pulse_close(device);
+ pa_threaded_mainloop_unlock(self->loop);
+ pulse_close(self->loop, self->context, self->stream);
+ self->loop = NULL;
+ self->context = NULL;
return ALC_INVALID_VALUE;
}
+ pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self);
- data->device_name = strdup(pa_stream_get_device_name(data->stream));
- o = pa_context_get_sink_info_by_name(data->context, data->device_name,
- sink_name_callback, device);
- wait_for_operation(o, data->loop);
-
- pa_stream_set_moved_callback(data->stream, stream_moved_callback, device);
+ self->device_name = strdup(pa_stream_get_device_name(self->stream));
+ o = pa_context_get_sink_info_by_name(self->context, self->device_name,
+ ALCpulsePlayback_sinkNameCallback, self);
+ wait_for_operation(o, self->loop);
- pa_threaded_mainloop_unlock(data->loop);
+ pa_threaded_mainloop_unlock(self->loop);
return ALC_NO_ERROR;
}
-static void pulse_close_playback(ALCdevice *device)
+static void ALCpulsePlayback_close(ALCpulsePlayback *self)
{
- pulse_close(device);
+ pulse_close(self->loop, self->context, self->stream);
+ self->loop = NULL;
+ self->context = NULL;
+ self->stream = NULL;
+
+ free(self->device_name);
+ self->device_name = NULL;
}
-static ALCboolean pulse_reset_playback(ALCdevice *device)
+static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
{
- pulse_data *data = device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
pa_stream_flags_t flags = 0;
pa_channel_map chanmap;
+ const char *mapname;
ALuint len;
- pa_threaded_mainloop_lock(data->loop);
+ pa_threaded_mainloop_lock(self->loop);
- if(data->stream)
+ if(self->stream)
{
- pa_stream_set_moved_callback(data->stream, NULL, NULL);
+ pa_stream_set_moved_callback(self->stream, NULL, NULL);
#if PA_CHECK_VERSION(0,9,15)
if(pa_stream_set_buffer_attr_callback)
- pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);
+ pa_stream_set_buffer_attr_callback(self->stream, NULL, NULL);
#endif
- pa_stream_disconnect(data->stream);
- pa_stream_unref(data->stream);
- data->stream = NULL;
+ pa_stream_disconnect(self->stream);
+ pa_stream_unref(self->stream);
+ self->stream = NULL;
}
if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
{
pa_operation *o;
- o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device);
- wait_for_operation(o, data->loop);
+ o = pa_context_get_sink_info_by_name(self->context, self->device_name,
+ ALCpulsePlayback_sinkInfoCallback, self);
+ wait_for_operation(o, self->loop);
}
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
flags |= PA_STREAM_FIX_RATE;
@@ -1039,152 +936,434 @@ static ALCboolean pulse_reset_playback(ALCdevice *device)
device->FmtType = DevFmtUByte;
/* fall-through */
case DevFmtUByte:
- data->spec.format = PA_SAMPLE_U8;
+ self->spec.format = PA_SAMPLE_U8;
break;
case DevFmtUShort:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
- data->spec.format = PA_SAMPLE_S16NE;
+ self->spec.format = PA_SAMPLE_S16NE;
break;
case DevFmtUInt:
device->FmtType = DevFmtInt;
/* fall-through */
case DevFmtInt:
- data->spec.format = PA_SAMPLE_S32NE;
+ self->spec.format = PA_SAMPLE_S32NE;
break;
case DevFmtFloat:
- data->spec.format = PA_SAMPLE_FLOAT32NE;
+ self->spec.format = PA_SAMPLE_FLOAT32NE;
break;
}
- data->spec.rate = device->Frequency;
- data->spec.channels = ChannelsFromDevFmt(device->FmtChans);
+ self->spec.rate = device->Frequency;
+ self->spec.channels = ChannelsFromDevFmt(device->FmtChans);
- if(pa_sample_spec_valid(&data->spec) == 0)
+ if(pa_sample_spec_valid(&self->spec) == 0)
{
ERR("Invalid sample format\n");
- pa_threaded_mainloop_unlock(data->loop);
+ pa_threaded_mainloop_unlock(self->loop);
return ALC_FALSE;
}
- if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
+ mapname = "(invalid)";
+ switch(device->FmtChans)
{
- ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);
- pa_threaded_mainloop_unlock(data->loop);
+ case DevFmtMono:
+ mapname = "mono";
+ break;
+ case DevFmtStereo:
+ mapname = "front-left,front-right";
+ break;
+ case DevFmtQuad:
+ mapname = "front-left,front-right,rear-left,rear-right";
+ break;
+ case DevFmtX51:
+ mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right";
+ break;
+ case DevFmtX51Side:
+ mapname = "front-left,front-right,front-center,lfe,side-left,side-right";
+ break;
+ case DevFmtX61:
+ mapname = "front-left,front-right,front-center,lfe,rear-center,side-left,side-right";
+ break;
+ case DevFmtX71:
+ mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right";
+ break;
+ }
+ if(!pa_channel_map_parse(&chanmap, mapname))
+ {
+ ERR("Failed to build channel map for %s\n", DevFmtChannelsString(device->FmtChans));
+ pa_threaded_mainloop_unlock(self->loop);
return ALC_FALSE;
}
SetDefaultWFXChannelOrder(device);
- data->attr.fragsize = -1;
- data->attr.prebuf = 0;
- data->attr.minreq = device->UpdateSize * pa_frame_size(&data->spec);
- data->attr.tlength = data->attr.minreq * maxu(device->NumUpdates, 2);
- data->attr.maxlength = -1;
+ self->attr.fragsize = -1;
+ self->attr.prebuf = 0;
+ self->attr.minreq = device->UpdateSize * pa_frame_size(&self->spec);
+ self->attr.tlength = self->attr.minreq * maxu(device->NumUpdates, 2);
+ self->attr.maxlength = -1;
- data->stream = connect_playback_stream(data->device_name, data->loop,
- data->context, flags, &data->attr,
- &data->spec, &chanmap);
- if(!data->stream)
+ self->stream = ALCpulsePlayback_connectStream(self->device_name, self->loop,
+ self->context, flags, &self->attr,
+ &self->spec, &chanmap);
+ if(!self->stream)
{
- pa_threaded_mainloop_unlock(data->loop);
+ pa_threaded_mainloop_unlock(self->loop);
return ALC_FALSE;
}
- pa_stream_set_state_callback(data->stream, stream_state_callback2, device);
+ pa_stream_set_state_callback(self->stream, ALCpulsePlayback_streamStateCallback, self);
+ pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self);
- data->spec = *(pa_stream_get_sample_spec(data->stream));
- if(device->Frequency != data->spec.rate)
+ self->spec = *(pa_stream_get_sample_spec(self->stream));
+ if(device->Frequency != self->spec.rate)
{
pa_operation *o;
/* Server updated our playback rate, so modify the buffer attribs
* accordingly. */
device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency *
- data->spec.rate + 0.5);
- data->attr.minreq = device->UpdateSize * pa_frame_size(&data->spec);
- data->attr.tlength = data->attr.minreq * clampu(device->NumUpdates, 2, 16);
- data->attr.maxlength = -1;
- data->attr.prebuf = 0;
+ self->spec.rate + 0.5);
+ self->attr.minreq = device->UpdateSize * pa_frame_size(&self->spec);
+ self->attr.tlength = self->attr.minreq * clampu(device->NumUpdates, 2, 16);
+ self->attr.maxlength = -1;
+ self->attr.prebuf = 0;
- o = pa_stream_set_buffer_attr(data->stream, &data->attr,
- stream_success_callback, device);
- wait_for_operation(o, data->loop);
+ o = pa_stream_set_buffer_attr(self->stream, &self->attr,
+ stream_success_callback, self->loop);
+ wait_for_operation(o, self->loop);
- device->Frequency = data->spec.rate;
+ device->Frequency = self->spec.rate;
}
- pa_stream_set_moved_callback(data->stream, stream_moved_callback, device);
#if PA_CHECK_VERSION(0,9,15)
if(pa_stream_set_buffer_attr_callback)
- pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);
+ pa_stream_set_buffer_attr_callback(self->stream, ALCpulsePlayback_bufferAttrCallback, self);
#endif
- stream_buffer_attr_callback(data->stream, device);
+ ALCpulsePlayback_bufferAttrCallback(self->stream, self);
- len = data->attr.minreq / pa_frame_size(&data->spec);
+ len = self->attr.minreq / pa_frame_size(&self->spec);
if((CPUCapFlags&CPU_CAP_SSE))
len = (len+3)&~3;
device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5);
device->NumUpdates = clampu(device->NumUpdates, 2, 16);
device->UpdateSize = len;
- pa_threaded_mainloop_unlock(data->loop);
+ pa_threaded_mainloop_unlock(self->loop);
return ALC_TRUE;
}
-static ALCboolean pulse_start_playback(ALCdevice *device)
+static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self)
{
- pulse_data *data = device->ExtraData;
-
- data->thread = StartThread(PulseProc, device);
- if(!data->thread)
+ if(!StartThread(&self->thread, ALCpulsePlayback_mixerProc, self))
return ALC_FALSE;
-
return ALC_TRUE;
}
-static void pulse_stop_playback(ALCdevice *device)
+static void ALCpulsePlayback_stop(ALCpulsePlayback *self)
{
- pulse_data *data = device->ExtraData;
pa_operation *o;
- if(!data->stream)
+ if(!self->stream)
return;
- data->killNow = AL_TRUE;
- if(data->thread)
+ self->killNow = AL_TRUE;
+ if(self->thread)
+ {
+ StopThread(self->thread);
+ self->thread = NULL;
+ }
+ self->killNow = AL_FALSE;
+
+ pa_threaded_mainloop_lock(self->loop);
+
+ o = pa_stream_cork(self->stream, 1, stream_success_callback, self->loop);
+ wait_for_operation(o, self->loop);
+
+ pa_threaded_mainloop_unlock(self->loop);
+}
+
+
+static void ALCpulsePlayback_lock(ALCpulsePlayback *self)
+{
+ pa_threaded_mainloop_lock(self->loop);
+}
+
+static void ALCpulsePlayback_unlock(ALCpulsePlayback *self)
+{
+ pa_threaded_mainloop_unlock(self->loop);
+}
+
+
+static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self)
+{
+ pa_usec_t latency = 0;
+ int neg;
+
+ if(pa_stream_get_latency(self->stream, &latency, &neg) != 0)
{
- StopThread(data->thread);
- data->thread = NULL;
+ ERR("Failed to get stream latency!\n");
+ return 0;
}
- data->killNow = AL_FALSE;
- pa_threaded_mainloop_lock(data->loop);
+ if(neg) latency = 0;
+ return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
+}
- o = pa_stream_cork(data->stream, 1, stream_success_callback, device);
- wait_for_operation(o, data->loop);
- pa_threaded_mainloop_unlock(data->loop);
+static void ALCpulsePlayback_Delete(ALCpulsePlayback *self)
+{
+ free(self);
}
+DEFINE_ALCBACKEND_VTABLE(ALCpulsePlayback);
+
+
+typedef struct ALCpulseCapture {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ char *device_name;
+
+ const void *cap_store;
+ size_t cap_len;
+ size_t cap_remain;
+
+ ALCuint last_readable;
+
+ pa_buffer_attr attr;
+ pa_sample_spec spec;
-static ALCenum pulse_open_capture(ALCdevice *device, const ALCchar *device_name)
+ pa_threaded_mainloop *loop;
+
+ pa_stream *stream;
+ pa_context *context;
+} ALCpulseCapture;
+DECLARE_ALCBACKEND_VTABLE(ALCpulseCapture);
+
+static void ALCpulseCapture_deviceCallback(pa_context *context, const pa_source_info *info, int eol, void *pdata);
+static void ALCpulseCapture_probeDevices(void);
+
+static void ALCpulseCapture_contextStateCallback(pa_context *context, void *pdata);
+static void ALCpulseCapture_streamStateCallback(pa_stream *stream, void *pdata);
+static void ALCpulseCapture_sourceNameCallback(pa_context *context, const pa_source_info *info, int eol, void *pdata);
+static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata);
+static pa_stream *ALCpulseCapture_connectStream(const char *device_name,
+ pa_threaded_mainloop *loop, pa_context *context,
+ pa_stream_flags_t flags, pa_buffer_attr *attr,
+ pa_sample_spec *spec, pa_channel_map *chanmap);
+
+static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCpulseCapture, ALCbackend, void, Destruct)
+static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name);
+static void ALCpulseCapture_close(ALCpulseCapture *self);
+static DECLARE_FORWARD(ALCpulseCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self);
+static void ALCpulseCapture_stop(ALCpulseCapture *self);
+static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self);
+static void ALCpulseCapture_lock(ALCpulseCapture *self);
+static void ALCpulseCapture_unlock(ALCpulseCapture *self);
+
+
+static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device)
{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCpulseCapture, ALCbackend, self);
+}
+
+
+static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa_source_info *info, int eol, void *pdata)
+{
+ pa_threaded_mainloop *loop = pdata;
+ void *temp;
+ ALuint i;
+
+ if(eol)
+ {
+ pa_threaded_mainloop_signal(loop, 0);
+ return;
+ }
+
+ for(i = 0;i < numCaptureDevNames;i++)
+ {
+ if(strcmp(info->name, allCaptureDevNameMap[i].device_name) == 0)
+ return;
+ }
+
+ TRACE("Got device \"%s\", \"%s\"\n", info->description, info->name);
+
+ temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap));
+ if(temp)
+ {
+ allCaptureDevNameMap = temp;
+ allCaptureDevNameMap[numCaptureDevNames].name = strdup(info->description);
+ allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name);
+ numCaptureDevNames++;
+ }
+}
+
+static void ALCpulseCapture_probeDevices(void)
+{
+ pa_threaded_mainloop *loop;
+
+ allCaptureDevNameMap = malloc(sizeof(DevMap) * 1);
+ if((loop=pa_threaded_mainloop_new()) &&
+ pa_threaded_mainloop_start(loop) >= 0)
+ {
+ pa_context *context;
+
+ pa_threaded_mainloop_lock(loop);
+ context = connect_context(loop, AL_FALSE);
+ if(context)
+ {
+ pa_operation *o;
+ pa_stream_flags_t flags;
+ pa_sample_spec spec;
+ pa_stream *stream;
+
+ flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
+ PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE;
+
+ spec.format = PA_SAMPLE_S16NE;
+ spec.rate = 44100;
+ spec.channels = 1;
+
+ stream = ALCpulseCapture_connectStream(NULL, loop, context, flags,
+ NULL, &spec, NULL);
+ if(stream)
+ {
+ o = pa_context_get_source_info_by_name(context, pa_stream_get_device_name(stream),
+ ALCpulseCapture_deviceCallback, loop);
+ wait_for_operation(o, loop);
+
+ pa_stream_disconnect(stream);
+ pa_stream_unref(stream);
+ stream = NULL;
+ }
+
+ o = pa_context_get_source_info_list(context, ALCpulseCapture_deviceCallback, loop);
+ wait_for_operation(o, loop);
+
+ pa_context_disconnect(context);
+ pa_context_unref(context);
+ }
+ pa_threaded_mainloop_unlock(loop);
+ pa_threaded_mainloop_stop(loop);
+ }
+ if(loop)
+ pa_threaded_mainloop_free(loop);
+}
+
+
+static void ALCpulseCapture_contextStateCallback(pa_context *context, void *pdata)
+{
+ ALCpulseCapture *self = pdata;
+ if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
+ {
+ ERR("Received context failure!\n");
+ aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+ }
+ pa_threaded_mainloop_signal(self->loop, 0);
+}
+
+static void ALCpulseCapture_streamStateCallback(pa_stream *stream, void *pdata)
+{
+ ALCpulseCapture *self = pdata;
+ if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
+ {
+ ERR("Received stream failure!\n");
+ aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+ }
+ pa_threaded_mainloop_signal(self->loop, 0);
+}
+
+
+static void ALCpulseCapture_sourceNameCallback(pa_context *UNUSED(context), const pa_source_info *info, int eol, void *pdata)
+{
+ ALCpulseCapture *self = pdata;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+
+ if(eol)
+ {
+ pa_threaded_mainloop_signal(self->loop, 0);
+ return;
+ }
+
+ free(device->DeviceName);
+ device->DeviceName = strdup(info->description);
+}
+
+
+static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata)
+{
+ ALCpulseCapture *self = pdata;
+
+ free(self->device_name);
+ self->device_name = strdup(pa_stream_get_device_name(stream));
+
+ TRACE("Stream moved to %s\n", self->device_name);
+}
+
+
+static pa_stream *ALCpulseCapture_connectStream(const char *device_name,
+ pa_threaded_mainloop *loop, pa_context *context,
+ pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
+ pa_channel_map *chanmap)
+{
+ pa_stream_state_t state;
+ pa_stream *stream;
+
+ stream = pa_stream_new_with_proplist(context, "Capture Stream", spec, chanmap, prop_filter);
+ if(!stream)
+ {
+ ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context)));
+ return NULL;
+ }
+
+ pa_stream_set_state_callback(stream, stream_state_callback, loop);
+
+ if(pa_stream_connect_record(stream, device_name, attr, flags) < 0)
+ {
+ ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context)));
+ pa_stream_unref(stream);
+ return NULL;
+ }
+
+ while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
+ {
+ if(!PA_STREAM_IS_GOOD(state))
+ {
+ ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context)));
+ pa_stream_unref(stream);
+ return NULL;
+ }
+
+ pa_threaded_mainloop_wait(loop);
+ }
+ pa_stream_set_state_callback(stream, NULL, NULL);
+
+ return stream;
+}
+
+
+static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
const char *pulse_name = NULL;
pa_stream_flags_t flags = 0;
pa_channel_map chanmap;
- pulse_data *data;
pa_operation *o;
ALuint samples;
- if(device_name)
+ if(name)
{
ALuint i;
if(!allCaptureDevNameMap)
- probe_devices(AL_TRUE);
+ ALCpulseCapture_probeDevices();
for(i = 0;i < numCaptureDevNames;i++)
{
- if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0)
+ if(strcmp(name, allCaptureDevNameMap[i].name) == 0)
{
pulse_name = allCaptureDevNameMap[i].device_name;
break;
@@ -1194,158 +1373,164 @@ static ALCenum pulse_open_capture(ALCdevice *device, const ALCchar *device_name)
return ALC_INVALID_VALUE;
}
- if(pulse_open(device) == ALC_FALSE)
+ if(!pulse_open(&self->loop, &self->context, ALCpulseCapture_contextStateCallback, self))
return ALC_INVALID_VALUE;
- data = device->ExtraData;
- pa_threaded_mainloop_lock(data->loop);
+ pa_threaded_mainloop_lock(self->loop);
- data->spec.rate = device->Frequency;
- data->spec.channels = ChannelsFromDevFmt(device->FmtChans);
+ self->spec.rate = device->Frequency;
+ self->spec.channels = ChannelsFromDevFmt(device->FmtChans);
switch(device->FmtType)
{
case DevFmtUByte:
- data->spec.format = PA_SAMPLE_U8;
+ self->spec.format = PA_SAMPLE_U8;
break;
case DevFmtShort:
- data->spec.format = PA_SAMPLE_S16NE;
+ self->spec.format = PA_SAMPLE_S16NE;
break;
case DevFmtInt:
- data->spec.format = PA_SAMPLE_S32NE;
+ self->spec.format = PA_SAMPLE_S32NE;
break;
case DevFmtFloat:
- data->spec.format = PA_SAMPLE_FLOAT32NE;
+ self->spec.format = PA_SAMPLE_FLOAT32NE;
break;
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
- pa_threaded_mainloop_unlock(data->loop);
+ pa_threaded_mainloop_unlock(self->loop);
goto fail;
}
- if(pa_sample_spec_valid(&data->spec) == 0)
+ if(pa_sample_spec_valid(&self->spec) == 0)
{
ERR("Invalid sample format\n");
- pa_threaded_mainloop_unlock(data->loop);
+ pa_threaded_mainloop_unlock(self->loop);
goto fail;
}
- if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
+ if(!pa_channel_map_init_auto(&chanmap, self->spec.channels, PA_CHANNEL_MAP_WAVEEX))
{
- ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);
- pa_threaded_mainloop_unlock(data->loop);
+ ERR("Couldn't build map for channel count (%d)!\n", self->spec.channels);
+ pa_threaded_mainloop_unlock(self->loop);
goto fail;
}
samples = device->UpdateSize * device->NumUpdates;
samples = maxu(samples, 100 * device->Frequency / 1000);
- data->attr.minreq = -1;
- data->attr.prebuf = -1;
- data->attr.maxlength = samples * pa_frame_size(&data->spec);
- data->attr.tlength = -1;
- data->attr.fragsize = minu(samples, 50*device->Frequency/1000) *
- pa_frame_size(&data->spec);
+ self->attr.minreq = -1;
+ self->attr.prebuf = -1;
+ self->attr.maxlength = samples * pa_frame_size(&self->spec);
+ self->attr.tlength = -1;
+ self->attr.fragsize = minu(samples, 50*device->Frequency/1000) *
+ pa_frame_size(&self->spec);
flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY;
if(!GetConfigValueBool("pulse", "allow-moves", 0))
flags |= PA_STREAM_DONT_MOVE;
- data->stream = connect_record_stream(pulse_name, data->loop, data->context,
- flags, &data->attr, &data->spec,
- &chanmap);
- if(!data->stream)
+ self->stream = ALCpulseCapture_connectStream(pulse_name, self->loop, self->context,
+ flags, &self->attr, &self->spec,
+ &chanmap);
+ if(!self->stream)
{
- pa_threaded_mainloop_unlock(data->loop);
+ pa_threaded_mainloop_unlock(self->loop);
goto fail;
}
- pa_stream_set_state_callback(data->stream, stream_state_callback2, device);
+ pa_stream_set_moved_callback(self->stream, ALCpulseCapture_streamMovedCallback, self);
+ pa_stream_set_state_callback(self->stream, ALCpulseCapture_streamStateCallback, self);
- data->device_name = strdup(pa_stream_get_device_name(data->stream));
- o = pa_context_get_source_info_by_name(data->context, data->device_name,
- source_name_callback, device);
- wait_for_operation(o, data->loop);
+ self->device_name = strdup(pa_stream_get_device_name(self->stream));
+ o = pa_context_get_source_info_by_name(self->context, self->device_name,
+ ALCpulseCapture_sourceNameCallback, self);
+ wait_for_operation(o, self->loop);
- pa_stream_set_moved_callback(data->stream, stream_moved_callback, device);
-
- pa_threaded_mainloop_unlock(data->loop);
+ pa_threaded_mainloop_unlock(self->loop);
return ALC_NO_ERROR;
fail:
- pulse_close(device);
+ pulse_close(self->loop, self->context, self->stream);
+ self->loop = NULL;
+ self->context = NULL;
+ self->stream = NULL;
+
return ALC_INVALID_VALUE;
}
-static void pulse_close_capture(ALCdevice *device)
+static void ALCpulseCapture_close(ALCpulseCapture *self)
{
- pulse_close(device);
+ pulse_close(self->loop, self->context, self->stream);
+ self->loop = NULL;
+ self->context = NULL;
+ self->stream = NULL;
+
+ free(self->device_name);
+ self->device_name = NULL;
}
-static void pulse_start_capture(ALCdevice *device)
+static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self)
{
- pulse_data *data = device->ExtraData;
pa_operation *o;
+ o = pa_stream_cork(self->stream, 0, stream_success_callback, self->loop);
+ wait_for_operation(o, self->loop);
- o = pa_stream_cork(data->stream, 0, stream_success_callback, device);
- wait_for_operation(o, data->loop);
+ return ALC_TRUE;
}
-static void pulse_stop_capture(ALCdevice *device)
+static void ALCpulseCapture_stop(ALCpulseCapture *self)
{
- pulse_data *data = device->ExtraData;
pa_operation *o;
-
- o = pa_stream_cork(data->stream, 1, stream_success_callback, device);
- wait_for_operation(o, data->loop);
+ o = pa_stream_cork(self->stream, 1, stream_success_callback, self->loop);
+ wait_for_operation(o, self->loop);
}
-static ALCenum pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
+static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples)
{
- pulse_data *data = device->ExtraData;
- ALCuint todo = samples * pa_frame_size(&data->spec);
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ ALCuint todo = samples * pa_frame_size(&self->spec);
/* Capture is done in fragment-sized chunks, so we loop until we get all
* that's available */
- data->last_readable -= todo;
+ self->last_readable -= todo;
while(todo > 0)
{
size_t rem = todo;
- if(data->cap_len == 0)
+ if(self->cap_len == 0)
{
pa_stream_state_t state;
- state = pa_stream_get_state(data->stream);
+ state = pa_stream_get_state(self->stream);
if(!PA_STREAM_IS_GOOD(state))
{
aluHandleDisconnect(device);
break;
}
- if(pa_stream_peek(data->stream, &data->cap_store, &data->cap_len) < 0)
+ if(pa_stream_peek(self->stream, &self->cap_store, &self->cap_len) < 0)
{
ERR("pa_stream_peek() failed: %s\n",
- pa_strerror(pa_context_errno(data->context)));
+ pa_strerror(pa_context_errno(self->context)));
aluHandleDisconnect(device);
break;
}
- data->cap_remain = data->cap_len;
+ self->cap_remain = self->cap_len;
}
- if(rem > data->cap_remain)
- rem = data->cap_remain;
+ if(rem > self->cap_remain)
+ rem = self->cap_remain;
- memcpy(buffer, data->cap_store, rem);
+ memcpy(buffer, self->cap_store, rem);
buffer = (ALbyte*)buffer + rem;
todo -= rem;
- data->cap_store = (ALbyte*)data->cap_store + rem;
- data->cap_remain -= rem;
- if(data->cap_remain == 0)
+ self->cap_store = (ALbyte*)self->cap_store + rem;
+ self->cap_remain -= rem;
+ if(self->cap_remain == 0)
{
- pa_stream_drop(data->stream);
- data->cap_len = 0;
+ pa_stream_drop(self->stream);
+ self->cap_len = 0;
}
}
if(todo > 0)
@@ -1354,77 +1539,70 @@ static ALCenum pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint
return ALC_NO_ERROR;
}
-static ALCuint pulse_available_samples(ALCdevice *device)
+static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
{
- pulse_data *data = device->ExtraData;
- size_t readable = data->cap_remain;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ size_t readable = self->cap_remain;
if(device->Connected)
{
- ssize_t got = pa_stream_readable_size(data->stream);
+ ssize_t got = pa_stream_readable_size(self->stream);
if(got < 0)
{
ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got));
aluHandleDisconnect(device);
}
- else if((size_t)got > data->cap_len)
- readable += got - data->cap_len;
+ else if((size_t)got > self->cap_len)
+ readable += got - self->cap_len;
}
- if(data->last_readable < readable)
- data->last_readable = readable;
- return data->last_readable / pa_frame_size(&data->spec);
+ if(self->last_readable < readable)
+ self->last_readable = readable;
+ return self->last_readable / pa_frame_size(&self->spec);
}
-static void pulse_lock(ALCdevice *device)
+static void ALCpulseCapture_lock(ALCpulseCapture *self)
{
- pulse_data *data = device->ExtraData;
- pa_threaded_mainloop_lock(data->loop);
+ pa_threaded_mainloop_lock(self->loop);
}
-static void pulse_unlock(ALCdevice *device)
+static void ALCpulseCapture_unlock(ALCpulseCapture *self)
{
- pulse_data *data = device->ExtraData;
- pa_threaded_mainloop_unlock(data->loop);
+ pa_threaded_mainloop_unlock(self->loop);
}
-static ALint64 pulse_get_latency(ALCdevice *device)
+static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self)
{
- pulse_data *data = device->ExtraData;
pa_usec_t latency = 0;
int neg;
- if(pa_stream_get_latency(data->stream, &latency, &neg) == 0)
+ if(pa_stream_get_latency(self->stream, &latency, &neg) != 0)
{
- if(neg)
- latency = 0;
- return (ALint64)minu64(latency, MAKEU64(0x7fffffff,0xffffffff)/1000) * 1000;
+ ERR("Failed to get stream latency!\n");
+ return 0;
}
- ERR("Failed to get stream latency!\n");
- return 0;
+
+ if(neg) latency = 0;
+ return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
}
-static const BackendFuncs pulse_funcs = {
- pulse_open_playback,
- pulse_close_playback,
- pulse_reset_playback,
- pulse_start_playback,
- pulse_stop_playback,
- pulse_open_capture,
- pulse_close_capture,
- pulse_start_capture,
- pulse_stop_capture,
- pulse_capture_samples,
- pulse_available_samples,
- pulse_lock,
- pulse_unlock,
- pulse_get_latency
-};
-
-ALCboolean alc_pulse_init(BackendFuncs *func_list)
+static void ALCpulseCapture_Delete(ALCpulseCapture *self)
+{
+ free(self);
+}
+
+DEFINE_ALCBACKEND_VTABLE(ALCpulseCapture);
+
+
+typedef struct ALCpulseBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCpulseBackendFactory;
+#define ALCPULSEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCpulseBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory* UNUSED(self))
{
ALCboolean ret = ALC_FALSE;
@@ -1445,7 +1623,6 @@ ALCboolean alc_pulse_init(BackendFuncs *func_list)
context = connect_context(loop, AL_TRUE);
if(context)
{
- *func_list = pulse_funcs;
ret = ALC_TRUE;
/* Some libraries (Phonon, Qt) set some pulseaudio properties
@@ -1469,7 +1646,7 @@ ALCboolean alc_pulse_init(BackendFuncs *func_list)
return ret;
}
-void alc_pulse_deinit(void)
+static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory* UNUSED(self))
{
ALuint i;
@@ -1498,7 +1675,14 @@ void alc_pulse_deinit(void)
/* PulseAudio doesn't like being CloseLib'd sometimes */
}
-void alc_pulse_probe(enum DevProbe type)
+static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+ return ALC_TRUE;
+ return ALC_FALSE;
+}
+
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type)
{
ALuint i;
@@ -1514,7 +1698,7 @@ void alc_pulse_probe(enum DevProbe type)
allDevNameMap = NULL;
numDevNames = 0;
- probe_devices(AL_FALSE);
+ ALCpulsePlayback_probeDevices();
for(i = 0;i < numDevNames;i++)
AppendAllDevicesList(allDevNameMap[i].name);
@@ -1530,7 +1714,7 @@ void alc_pulse_probe(enum DevProbe type)
allCaptureDevNameMap = NULL;
numCaptureDevNames = 0;
- probe_devices(AL_TRUE);
+ ALCpulseCapture_probeDevices();
for(i = 0;i < numCaptureDevNames;i++)
AppendCaptureDeviceList(allCaptureDevNameMap[i].name);
@@ -1538,17 +1722,75 @@ void alc_pulse_probe(enum DevProbe type)
}
}
-#else
+static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ ALCpulsePlayback *backend;
+
+ backend = calloc(1, sizeof(*backend));
+ if(!backend) return NULL;
+
+ ALCpulsePlayback_Construct(backend, device);
+
+ return STATIC_CAST(ALCbackend, backend);
+ }
+ if(type == ALCbackend_Capture)
+ {
+ ALCpulseCapture *backend;
+
+ backend = calloc(1, sizeof(*backend));
+ if(!backend) return NULL;
+
+ ALCpulseCapture_Construct(backend, device);
+
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory);
+
+
+#else /* PA_API_VERSION == 12 */
#warning "Unsupported API version, backend will be unavailable!"
-ALCboolean alc_pulse_init(BackendFuncs *func_list)
-{ return ALC_FALSE; (void)func_list; }
+typedef struct ALCpulseBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCpulseBackendFactory;
+#define ALCPULSEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCpulseBackendFactory, ALCbackendFactory) } }
-void alc_pulse_deinit(void)
-{ }
+static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory* UNUSED(self))
+{
+ return ALC_FALSE;
+}
-void alc_pulse_probe(enum DevProbe type)
-{ (void)type; }
+static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory* UNUSED(self))
+{
+}
-#endif
+static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UNUSED(self), ALCbackend_Type UNUSED(type))
+{
+ return ALC_FALSE;
+}
+
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type))
+{
+}
+
+static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory* UNUSED(self), ALCdevice* UNUSED(device), ALCbackend_Type UNUSED(type))
+{
+ return NULL;
+}
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory);
+
+#endif /* PA_API_VERSION == 12 */
+
+ALCbackendFactory *ALCpulseBackendFactory_getFactory(void)
+{
+ static ALCpulseBackendFactory factory = ALCPULSEBACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
+}
diff --git a/Alc/backends/qsa.c b/Alc/backends/qsa.c
new file mode 100644
index 00000000..c9762f85
--- /dev/null
+++ b/Alc/backends/qsa.c
@@ -0,0 +1,1169 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2011-2013 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 <stdlib.h>
+#include <stdio.h>
+#include <sched.h>
+#include <errno.h>
+#include <memory.h>
+#include <sys/select.h>
+#include <sys/asoundlib.h>
+#include <sys/neutrino.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+
+
+typedef struct
+{
+ snd_pcm_t* pcmHandle;
+ int audio_fd;
+
+ snd_pcm_channel_setup_t csetup;
+ snd_pcm_channel_params_t cparams;
+
+ ALvoid* buffer;
+ ALsizei size;
+
+ volatile int killNow;
+ althread_t thread;
+} qsa_data;
+
+typedef struct
+{
+ ALCchar* name;
+ int card;
+ int dev;
+} DevMap;
+
+static const ALCchar qsaDevice[]="QSA Default";
+static DevMap* allDevNameMap;
+static ALuint numDevNames;
+static DevMap* allCaptureDevNameMap;
+static ALuint numCaptureDevNames;
+
+static const struct
+{
+ int32_t format;
+} formatlist[]=
+{
+ {SND_PCM_SFMT_FLOAT_LE},
+ {SND_PCM_SFMT_S32_LE},
+ {SND_PCM_SFMT_U32_LE},
+ {SND_PCM_SFMT_S16_LE},
+ {SND_PCM_SFMT_U16_LE},
+ {SND_PCM_SFMT_S8},
+ {SND_PCM_SFMT_U8},
+ {0},
+};
+
+static const struct
+{
+ int32_t rate;
+} ratelist[]=
+{
+ {192000},
+ {176400},
+ {96000},
+ {88200},
+ {48000},
+ {44100},
+ {32000},
+ {24000},
+ {22050},
+ {16000},
+ {12000},
+ {11025},
+ {8000},
+ {0},
+};
+
+static const struct
+{
+ int32_t channels;
+} channellist[]=
+{
+ {8},
+ {7},
+ {6},
+ {4},
+ {2},
+ {1},
+ {0},
+};
+
+static DevMap* deviceList(int type, ALuint* count)
+{
+ snd_ctl_t* handle;
+ snd_pcm_info_t pcminfo;
+ int max_cards, card, err, dev, num_devices, idx;
+ DevMap* dev_list;
+ char name[1024];
+ struct snd_ctl_hw_info info;
+ void* temp;
+
+ idx=0;
+ num_devices=0;
+ max_cards=snd_cards();
+
+ if (max_cards<=0)
+ {
+ return 0;
+ }
+
+ dev_list=malloc(sizeof(DevMap)*1);
+ dev_list[0].name=strdup(qsaDevice);
+ num_devices=1;
+
+ for (card=0; card<max_cards; card++)
+ {
+ if ((err=snd_ctl_open(&handle, card))<0)
+ {
+ continue;
+ }
+ if ((err=snd_ctl_hw_info(handle, &info))<0)
+ {
+ snd_ctl_close(handle);
+ continue;
+ }
+
+ for (dev=0; dev<(int)info.pcmdevs; dev++)
+ {
+ if ((err=snd_ctl_pcm_info(handle, dev, &pcminfo)) < 0)
+ {
+ continue;
+ }
+
+ if ((type==SND_PCM_CHANNEL_PLAYBACK && (pcminfo.flags&SND_PCM_INFO_PLAYBACK)) ||
+ (type==SND_PCM_CHANNEL_CAPTURE && (pcminfo.flags&SND_PCM_INFO_CAPTURE)))
+ {
+ temp=realloc(dev_list, sizeof(DevMap)*(num_devices+1));
+ if (temp)
+ {
+ dev_list=temp;
+ snprintf(name, sizeof(name), "%s [%s] (hw:%d,%d)", info.name, pcminfo.name, card, dev);
+ dev_list[num_devices].name=strdup(name);
+ dev_list[num_devices].card=card;
+ dev_list[num_devices].dev=dev;
+ num_devices++;
+ }
+ }
+ }
+ snd_ctl_close (handle);
+ }
+
+ *count=num_devices;
+
+ return dev_list;
+}
+
+
+FORCE_ALIGN static ALuint qsa_proc_playback(ALvoid* ptr)
+{
+ ALCdevice* device=(ALCdevice*)ptr;
+ qsa_data* data=(qsa_data*)device->ExtraData;
+ char* write_ptr;
+ int avail;
+ snd_pcm_channel_status_t status;
+ struct sched_param param;
+ fd_set wfds;
+ int selectret;
+ struct timeval timeout;
+
+ SetRTPriority();
+ SetThreadName(MIXER_THREAD_NAME);
+
+ /* Increase default 10 priority to 11 to avoid jerky sound */
+ SchedGet(0, 0, &param);
+ param.sched_priority=param.sched_curpriority+1;
+ SchedSet(0, 0, SCHED_NOCHANGE, &param);
+
+ ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+
+ while (!data->killNow)
+ {
+ ALint len=data->size;
+ write_ptr=data->buffer;
+
+ avail=len/frame_size;
+ aluMixData(device, write_ptr, avail);
+
+ while (len>0 && !data->killNow)
+ {
+ FD_ZERO(&wfds);
+ FD_SET(data->audio_fd, &wfds);
+ timeout.tv_sec=2;
+ timeout.tv_usec=0;
+
+ /* Select also works like time slice to OS */
+ selectret=select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
+ switch (selectret)
+ {
+ case -1:
+ aluHandleDisconnect(device);
+ return 1;
+ case 0:
+ break;
+ default:
+ if (FD_ISSET(data->audio_fd, &wfds))
+ {
+ break;
+ }
+ break;
+ }
+
+ int wrote=snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
+
+ if (wrote<=0)
+ {
+ if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
+ {
+ continue;
+ }
+
+ memset(&status, 0, sizeof (status));
+ status.channel=SND_PCM_CHANNEL_PLAYBACK;
+
+ snd_pcm_plugin_status(data->pcmHandle, &status);
+
+ /* we need to reinitialize the sound channel if we've underrun the buffer */
+ if ((status.status==SND_PCM_STATUS_UNDERRUN) ||
+ (status.status==SND_PCM_STATUS_READY))
+ {
+ if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
+ {
+ aluHandleDisconnect(device);
+ break;
+ }
+ }
+ }
+ else
+ {
+ write_ptr+=wrote;
+ len-=wrote;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/************/
+/* Playback */
+/************/
+
+static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
+{
+ qsa_data* data;
+ char driver[64];
+ int status;
+ int card, dev;
+
+ strncpy(driver, GetConfigValue("qsa", "device", qsaDevice), sizeof(driver)-1);
+ driver[sizeof(driver)-1]=0;
+
+ data=(qsa_data*)calloc(1, sizeof(qsa_data));
+ if (data==NULL)
+ {
+ return ALC_OUT_OF_MEMORY;
+ }
+
+ if (!deviceName)
+ {
+ deviceName=driver;
+ }
+
+ if (strcmp(deviceName, qsaDevice)==0)
+ {
+ if (!deviceName)
+ {
+ deviceName=qsaDevice;
+ }
+
+ status=snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK);
+ }
+ else
+ {
+ size_t idx;
+
+ if (!allDevNameMap)
+ {
+ allDevNameMap=deviceList(SND_PCM_CHANNEL_PLAYBACK, &numDevNames);
+ }
+
+ for (idx=0; idx<numDevNames; idx++)
+ {
+ if (allDevNameMap[idx].name && strcmp(deviceName, allDevNameMap[idx].name)==0)
+ {
+ if (idx>0)
+ {
+ break;
+ }
+ }
+ }
+ if (idx==numDevNames)
+ {
+ free(data);
+ return ALC_INVALID_DEVICE;
+ }
+
+ status=snd_pcm_open(&data->pcmHandle, allDevNameMap[idx].card, allDevNameMap[idx].dev, SND_PCM_OPEN_PLAYBACK);
+ }
+
+ if (status<0)
+ {
+ free(data);
+ return ALC_INVALID_DEVICE;
+ }
+
+ data->audio_fd=snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
+ if (data->audio_fd<0)
+ {
+ free(data);
+ return ALC_INVALID_DEVICE;
+ }
+
+ device->DeviceName=strdup(deviceName);
+ device->ExtraData=data;
+
+ return ALC_NO_ERROR;
+}
+
+static void qsa_close_playback(ALCdevice* device)
+{
+ qsa_data* data=(qsa_data*)device->ExtraData;
+
+ if (data->buffer!=NULL)
+ {
+ free(data->buffer);
+ data->buffer=NULL;
+ }
+
+ snd_pcm_close(data->pcmHandle);
+ free(data);
+
+ device->ExtraData=NULL;
+}
+
+static ALCboolean qsa_reset_playback(ALCdevice* device)
+{
+ qsa_data* data=(qsa_data*)device->ExtraData;
+ int32_t format=-1;
+
+ switch(device->FmtType)
+ {
+ case DevFmtByte:
+ format=SND_PCM_SFMT_S8;
+ break;
+ case DevFmtUByte:
+ format=SND_PCM_SFMT_U8;
+ break;
+ case DevFmtShort:
+ format=SND_PCM_SFMT_S16_LE;
+ break;
+ case DevFmtUShort:
+ format=SND_PCM_SFMT_U16_LE;
+ break;
+ case DevFmtInt:
+ format=SND_PCM_SFMT_S32_LE;
+ break;
+ case DevFmtUInt:
+ format=SND_PCM_SFMT_U32_LE;
+ break;
+ case DevFmtFloat:
+ format=SND_PCM_SFMT_FLOAT_LE;
+ break;
+ }
+
+ /* we actually don't want to block on writes */
+ snd_pcm_nonblock_mode(data->pcmHandle, 1);
+ /* Disable mmap to control data transfer to the audio device */
+ snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
+ snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_BUFFER_PARTIAL_BLOCKS);
+
+ // configure a sound channel
+ memset(&data->cparams, 0, sizeof(data->cparams));
+ data->cparams.channel=SND_PCM_CHANNEL_PLAYBACK;
+ data->cparams.mode=SND_PCM_MODE_BLOCK;
+ data->cparams.start_mode=SND_PCM_START_FULL;
+ data->cparams.stop_mode=SND_PCM_STOP_STOP;
+
+ data->cparams.buf.block.frag_size=device->UpdateSize*
+ ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
+ data->cparams.buf.block.frags_max=device->NumUpdates;
+ data->cparams.buf.block.frags_min=device->NumUpdates;
+
+ data->cparams.format.interleave=1;
+ data->cparams.format.rate=device->Frequency;
+ data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
+ data->cparams.format.format=format;
+
+ if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
+ {
+ int original_rate=data->cparams.format.rate;
+ int original_voices=data->cparams.format.voices;
+ int original_format=data->cparams.format.format;
+ int it;
+ int jt;
+
+ for (it=0; it<1; it++)
+ {
+ /* Check for second pass */
+ if (it==1)
+ {
+ original_rate=ratelist[0].rate;
+ original_voices=channellist[0].channels;
+ original_format=formatlist[0].format;
+ }
+
+ do {
+ /* At first downgrade sample format */
+ jt=0;
+ do {
+ if (formatlist[jt].format==data->cparams.format.format)
+ {
+ data->cparams.format.format=formatlist[jt+1].format;
+ break;
+ }
+ if (formatlist[jt].format==0)
+ {
+ data->cparams.format.format=0;
+ break;
+ }
+ jt++;
+ } while(1);
+
+ if (data->cparams.format.format==0)
+ {
+ data->cparams.format.format=original_format;
+
+ /* At secod downgrade sample rate */
+ jt=0;
+ do {
+ if (ratelist[jt].rate==data->cparams.format.rate)
+ {
+ data->cparams.format.rate=ratelist[jt+1].rate;
+ break;
+ }
+ if (ratelist[jt].rate==0)
+ {
+ data->cparams.format.rate=0;
+ break;
+ }
+ jt++;
+ } while(1);
+
+ if (data->cparams.format.rate==0)
+ {
+ data->cparams.format.rate=original_rate;
+ data->cparams.format.format=original_format;
+
+ /* At third downgrade channels number */
+ jt=0;
+ do {
+ if(channellist[jt].channels==data->cparams.format.voices)
+ {
+ data->cparams.format.voices=channellist[jt+1].channels;
+ break;
+ }
+ if (channellist[jt].channels==0)
+ {
+ data->cparams.format.voices=0;
+ break;
+ }
+ jt++;
+ } while(1);
+ }
+
+ if (data->cparams.format.voices==0)
+ {
+ break;
+ }
+ }
+
+ data->cparams.buf.block.frag_size=device->UpdateSize*
+ data->cparams.format.voices*
+ snd_pcm_format_width(data->cparams.format.format)/8;
+ data->cparams.buf.block.frags_max=device->NumUpdates;
+ data->cparams.buf.block.frags_min=device->NumUpdates;
+ if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
+ {
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ } while(1);
+
+ if (data->cparams.format.voices!=0)
+ {
+ break;
+ }
+ }
+
+ if (data->cparams.format.voices==0)
+ {
+ return ALC_FALSE;
+ }
+ }
+
+ if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
+ {
+ return ALC_FALSE;
+ }
+
+ memset(&data->csetup, 0, sizeof(data->csetup));
+ data->csetup.channel=SND_PCM_CHANNEL_PLAYBACK;
+ if (snd_pcm_plugin_setup(data->pcmHandle, &data->csetup)<0)
+ {
+ return ALC_FALSE;
+ }
+
+ /* now fill back to the our AL device */
+ device->Frequency=data->cparams.format.rate;
+
+ switch (data->cparams.format.voices)
+ {
+ case 1:
+ device->FmtChans=DevFmtMono;
+ break;
+ case 2:
+ device->FmtChans=DevFmtStereo;
+ break;
+ case 4:
+ device->FmtChans=DevFmtQuad;
+ break;
+ case 6:
+ device->FmtChans=DevFmtX51;
+ break;
+ case 7:
+ device->FmtChans=DevFmtX61;
+ break;
+ case 8:
+ device->FmtChans=DevFmtX71;
+ break;
+ default:
+ device->FmtChans=DevFmtMono;
+ break;
+ }
+
+ switch (data->cparams.format.format)
+ {
+ case SND_PCM_SFMT_S8:
+ device->FmtType=DevFmtByte;
+ break;
+ case SND_PCM_SFMT_U8:
+ device->FmtType=DevFmtUByte;
+ break;
+ case SND_PCM_SFMT_S16_LE:
+ device->FmtType=DevFmtShort;
+ break;
+ case SND_PCM_SFMT_U16_LE:
+ device->FmtType=DevFmtUShort;
+ break;
+ case SND_PCM_SFMT_S32_LE:
+ device->FmtType=DevFmtInt;
+ break;
+ case SND_PCM_SFMT_U32_LE:
+ device->FmtType=DevFmtUInt;
+ break;
+ case SND_PCM_SFMT_FLOAT_LE:
+ device->FmtType=DevFmtFloat;
+ break;
+ default:
+ device->FmtType=DevFmtShort;
+ break;
+ }
+
+ SetDefaultChannelOrder(device);
+
+ device->UpdateSize=data->csetup.buf.block.frag_size/
+ (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType));
+ device->NumUpdates=data->csetup.buf.block.frags;
+
+ data->size=data->csetup.buf.block.frag_size;
+ data->buffer=malloc(data->size);
+ if (!data->buffer)
+ {
+ return ALC_FALSE;
+ }
+
+ return ALC_TRUE;
+}
+
+static ALCboolean qsa_start_playback(ALCdevice* device)
+{
+ qsa_data *data = (qsa_data*)device->ExtraData;
+
+ if(!StartThread(&data->thread, qsa_proc_playback, device))
+ return ALC_FALSE;
+
+ return ALC_TRUE;
+}
+
+static void qsa_stop_playback(ALCdevice* device)
+{
+ qsa_data* data=(qsa_data*)device->ExtraData;
+
+ if (data->thread)
+ {
+ data->killNow=1;
+ StopThread(data->thread);
+ data->thread=NULL;
+ }
+ data->killNow=0;
+}
+
+/***********/
+/* Capture */
+/***********/
+
+static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
+{
+ qsa_data* data;
+ int format=-1;
+ char driver[64];
+ int card, dev;
+ int status;
+
+ strncpy(driver, GetConfigValue("qsa", "capture", qsaDevice), sizeof(driver)-1);
+ driver[sizeof(driver)-1]=0;
+
+ data=(qsa_data*)calloc(1, sizeof(qsa_data));
+ if (data==NULL)
+ {
+ return ALC_OUT_OF_MEMORY;
+ }
+
+ if (!deviceName)
+ {
+ deviceName=driver;
+ }
+
+ if (strcmp(deviceName, qsaDevice)==0)
+ {
+ if (!deviceName)
+ {
+ deviceName=qsaDevice;
+ }
+
+ status=snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE);
+ }
+ else
+ {
+ size_t idx;
+
+ if (!allCaptureDevNameMap)
+ {
+ allCaptureDevNameMap=deviceList(SND_PCM_CHANNEL_CAPTURE, &numDevNames);
+ }
+
+ for (idx=0; idx<numDevNames; idx++)
+ {
+ if (allCaptureDevNameMap[idx].name && strcmp(deviceName, allCaptureDevNameMap[idx].name)==0)
+ {
+ if (idx>0)
+ {
+ break;
+ }
+ }
+ }
+ if (idx==numDevNames)
+ {
+ free(data);
+ return ALC_INVALID_DEVICE;
+ }
+
+ status=snd_pcm_open(&data->pcmHandle, allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev, SND_PCM_OPEN_CAPTURE);
+ }
+
+ if (status<0)
+ {
+ free(data);
+ return ALC_INVALID_DEVICE;
+ }
+
+ data->audio_fd=snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE);
+ if (data->audio_fd<0)
+ {
+ free(data);
+ return ALC_INVALID_DEVICE;
+ }
+
+ device->DeviceName=strdup(deviceName);
+ device->ExtraData=data;
+
+ switch (device->FmtType)
+ {
+ case DevFmtByte:
+ format=SND_PCM_SFMT_S8;
+ break;
+ case DevFmtUByte:
+ format=SND_PCM_SFMT_U8;
+ break;
+ case DevFmtShort:
+ format=SND_PCM_SFMT_S16_LE;
+ break;
+ case DevFmtUShort:
+ format=SND_PCM_SFMT_U16_LE;
+ break;
+ case DevFmtInt:
+ format=SND_PCM_SFMT_S32_LE;
+ break;
+ case DevFmtUInt:
+ format=SND_PCM_SFMT_U32_LE;
+ break;
+ case DevFmtFloat:
+ format=SND_PCM_SFMT_FLOAT_LE;
+ break;
+ }
+
+ /* we actually don't want to block on reads */
+ snd_pcm_nonblock_mode(data->pcmHandle, 1);
+ /* Disable mmap to control data transfer to the audio device */
+ snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
+
+ /* configure a sound channel */
+ memset(&data->cparams, 0, sizeof(data->cparams));
+ data->cparams.mode=SND_PCM_MODE_BLOCK;
+ data->cparams.channel=SND_PCM_CHANNEL_CAPTURE;
+ data->cparams.start_mode=SND_PCM_START_GO;
+ data->cparams.stop_mode=SND_PCM_STOP_STOP;
+
+ data->cparams.buf.block.frag_size=device->UpdateSize*
+ ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
+ data->cparams.buf.block.frags_max=device->NumUpdates;
+ data->cparams.buf.block.frags_min=device->NumUpdates;
+
+ data->cparams.format.interleave=1;
+ data->cparams.format.rate=device->Frequency;
+ data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
+ data->cparams.format.format=format;
+
+ if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
+ {
+ int original_rate=data->cparams.format.rate;
+ int original_voices=data->cparams.format.voices;
+ int original_format=data->cparams.format.format;
+ int it;
+ int jt;
+
+ for (it=0; it<1; it++)
+ {
+ /* Check for second pass */
+ if (it==1)
+ {
+ original_rate=ratelist[0].rate;
+ original_voices=channellist[0].channels;
+ original_format=formatlist[0].format;
+ }
+
+ do {
+ /* At first downgrade sample format */
+ jt=0;
+ do {
+ if (formatlist[jt].format==data->cparams.format.format)
+ {
+ data->cparams.format.format=formatlist[jt+1].format;
+ break;
+ }
+ if (formatlist[jt].format==0)
+ {
+ data->cparams.format.format=0;
+ break;
+ }
+ jt++;
+ } while(1);
+
+ if (data->cparams.format.format==0)
+ {
+ data->cparams.format.format=original_format;
+
+ /* At secod downgrade sample rate */
+ jt=0;
+ do {
+ if (ratelist[jt].rate==data->cparams.format.rate)
+ {
+ data->cparams.format.rate=ratelist[jt+1].rate;
+ break;
+ }
+ if (ratelist[jt].rate==0)
+ {
+ data->cparams.format.rate=0;
+ break;
+ }
+ jt++;
+ } while(1);
+
+ if (data->cparams.format.rate==0)
+ {
+ data->cparams.format.rate=original_rate;
+ data->cparams.format.format=original_format;
+
+ /* At third downgrade channels number */
+ jt=0;
+ do {
+ if(channellist[jt].channels==data->cparams.format.voices)
+ {
+ data->cparams.format.voices=channellist[jt+1].channels;
+ break;
+ }
+ if (channellist[jt].channels==0)
+ {
+ data->cparams.format.voices=0;
+ break;
+ }
+ jt++;
+ } while(1);
+ }
+
+ if (data->cparams.format.voices==0)
+ {
+ break;
+ }
+ }
+
+ data->cparams.buf.block.frag_size=device->UpdateSize*
+ data->cparams.format.voices*
+ snd_pcm_format_width(data->cparams.format.format)/8;
+ data->cparams.buf.block.frags_max=device->NumUpdates;
+ data->cparams.buf.block.frags_min=device->NumUpdates;
+ if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
+ {
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ } while(1);
+
+ if (data->cparams.format.voices!=0)
+ {
+ break;
+ }
+ }
+
+ if (data->cparams.format.voices==0)
+ {
+ return ALC_INVALID_VALUE;
+ }
+ }
+
+ /* now fill back to the our AL device */
+ device->Frequency=data->cparams.format.rate;
+
+ switch (data->cparams.format.voices)
+ {
+ case 1:
+ device->FmtChans=DevFmtMono;
+ break;
+ case 2:
+ device->FmtChans=DevFmtStereo;
+ break;
+ case 4:
+ device->FmtChans=DevFmtQuad;
+ break;
+ case 6:
+ device->FmtChans=DevFmtX51;
+ break;
+ case 7:
+ device->FmtChans=DevFmtX61;
+ break;
+ case 8:
+ device->FmtChans=DevFmtX71;
+ break;
+ default:
+ device->FmtChans=DevFmtMono;
+ break;
+ }
+
+ switch (data->cparams.format.format)
+ {
+ case SND_PCM_SFMT_S8:
+ device->FmtType=DevFmtByte;
+ break;
+ case SND_PCM_SFMT_U8:
+ device->FmtType=DevFmtUByte;
+ break;
+ case SND_PCM_SFMT_S16_LE:
+ device->FmtType=DevFmtShort;
+ break;
+ case SND_PCM_SFMT_U16_LE:
+ device->FmtType=DevFmtUShort;
+ break;
+ case SND_PCM_SFMT_S32_LE:
+ device->FmtType=DevFmtInt;
+ break;
+ case SND_PCM_SFMT_U32_LE:
+ device->FmtType=DevFmtUInt;
+ break;
+ case SND_PCM_SFMT_FLOAT_LE:
+ device->FmtType=DevFmtFloat;
+ break;
+ default:
+ device->FmtType=DevFmtShort;
+ break;
+ }
+
+ return ALC_NO_ERROR;
+}
+
+static void qsa_close_capture(ALCdevice* device)
+{
+ qsa_data* data=(qsa_data*)device->ExtraData;
+
+ if (data->pcmHandle!=NULL)
+ {
+ snd_pcm_close(data->pcmHandle);
+ }
+ free(data);
+ device->ExtraData=NULL;
+}
+
+static void qsa_start_capture(ALCdevice* device)
+{
+ qsa_data* data=(qsa_data*)device->ExtraData;
+ int rstatus;
+
+ if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
+ {
+ ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
+ return;
+ }
+
+ memset(&data->csetup, 0, sizeof(data->csetup));
+ data->csetup.channel=SND_PCM_CHANNEL_CAPTURE;
+ if ((rstatus=snd_pcm_plugin_setup(data->pcmHandle, &data->csetup))<0)
+ {
+ ERR("capture setup failed: %s\n", snd_strerror(rstatus));
+ return;
+ }
+
+ snd_pcm_capture_go(data->pcmHandle);
+
+ device->UpdateSize=data->csetup.buf.block.frag_size/
+ (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType));
+ device->NumUpdates=data->csetup.buf.block.frags;
+}
+
+static void qsa_stop_capture(ALCdevice* device)
+{
+ qsa_data* data=(qsa_data*)device->ExtraData;
+
+ snd_pcm_capture_flush(data->pcmHandle);
+}
+
+static ALCuint qsa_available_samples(ALCdevice* device)
+{
+ qsa_data* data=(qsa_data*)device->ExtraData;
+ snd_pcm_channel_status_t status;
+ ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ ALint free_size;
+ int rstatus;
+
+ memset(&status, 0, sizeof (status));
+ status.channel=SND_PCM_CHANNEL_CAPTURE;
+ snd_pcm_plugin_status(data->pcmHandle, &status);
+ if ((status.status==SND_PCM_STATUS_OVERRUN) ||
+ (status.status==SND_PCM_STATUS_READY))
+ {
+ if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
+ {
+ ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
+ aluHandleDisconnect(device);
+ return 0;
+ }
+
+ snd_pcm_capture_go(data->pcmHandle);
+ return 0;
+ }
+
+ free_size=data->csetup.buf.block.frag_size*data->csetup.buf.block.frags;
+ free_size-=status.free;
+
+ return free_size/frame_size;
+}
+
+static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
+{
+ qsa_data* data=(qsa_data*)device->ExtraData;
+ char* read_ptr;
+ snd_pcm_channel_status_t status;
+ fd_set rfds;
+ int selectret;
+ struct timeval timeout;
+ int bytes_read;
+ ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ ALint len=samples*frame_size;
+ int rstatus;
+
+ read_ptr=buffer;
+
+ while (len>0)
+ {
+ FD_ZERO(&rfds);
+ FD_SET(data->audio_fd, &rfds);
+ timeout.tv_sec=2;
+ timeout.tv_usec=0;
+
+ /* Select also works like time slice to OS */
+ bytes_read=0;
+ selectret=select(data->audio_fd+1, &rfds, NULL, NULL, &timeout);
+ switch (selectret)
+ {
+ case -1:
+ aluHandleDisconnect(device);
+ return ALC_INVALID_DEVICE;
+ case 0:
+ break;
+ default:
+ if (FD_ISSET(data->audio_fd, &rfds))
+ {
+ bytes_read=snd_pcm_plugin_read(data->pcmHandle, read_ptr, len);
+ break;
+ }
+ break;
+ }
+
+ if (bytes_read<=0)
+ {
+ if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
+ {
+ continue;
+ }
+
+ memset(&status, 0, sizeof (status));
+ status.channel=SND_PCM_CHANNEL_CAPTURE;
+ snd_pcm_plugin_status(data->pcmHandle, &status);
+
+ /* we need to reinitialize the sound channel if we've overrun the buffer */
+ if ((status.status==SND_PCM_STATUS_OVERRUN) ||
+ (status.status==SND_PCM_STATUS_READY))
+ {
+ if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
+ {
+ ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
+ aluHandleDisconnect(device);
+ return ALC_INVALID_DEVICE;
+ }
+ snd_pcm_capture_go(data->pcmHandle);
+ }
+ }
+ else
+ {
+ read_ptr+=bytes_read;
+ len-=bytes_read;
+ }
+ }
+
+ return ALC_NO_ERROR;
+}
+
+static ALint64 qsa_get_latency(ALCdevice* device)
+{
+ ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+
+ return (ALint64)(device->UpdateSize*device->NumUpdates/frame_size)*
+ 1000000000/device->Frequency;
+}
+
+BackendFuncs qsa_funcs=
+{
+ qsa_open_playback,
+ qsa_close_playback,
+ qsa_reset_playback,
+ qsa_start_playback,
+ qsa_stop_playback,
+ qsa_open_capture,
+ qsa_close_capture,
+ qsa_start_capture,
+ qsa_stop_capture,
+ qsa_capture_samples,
+ qsa_available_samples,
+ qsa_get_latency,
+};
+
+ALCboolean alc_qsa_init(BackendFuncs* func_list)
+{
+ *func_list=qsa_funcs;
+
+ return ALC_TRUE;
+}
+
+void alc_qsa_deinit(void)
+{
+ ALuint i;
+
+ for (i=0; i<numDevNames; ++i)
+ {
+ free(allDevNameMap[i].name);
+ }
+ free(allDevNameMap);
+ allDevNameMap=NULL;
+ numDevNames=0;
+
+ for (i=0; i<numCaptureDevNames; ++i)
+ {
+ free(allCaptureDevNameMap[i].name);
+ }
+ free(allCaptureDevNameMap);
+ allCaptureDevNameMap=NULL;
+ numCaptureDevNames=0;
+}
+
+void alc_qsa_probe(enum DevProbe type)
+{
+ ALuint i;
+
+ switch (type)
+ {
+ case ALL_DEVICE_PROBE:
+ for (i=0; i<numDevNames; ++i)
+ {
+ free(allDevNameMap[i].name);
+ }
+ free(allDevNameMap);
+
+ allDevNameMap=deviceList(SND_PCM_CHANNEL_PLAYBACK, &numDevNames);
+ for (i=0; i<numDevNames; ++i)
+ {
+ AppendAllDevicesList(allDevNameMap[i].name);
+ }
+ break;
+ case CAPTURE_DEVICE_PROBE:
+ for (i=0; i<numCaptureDevNames; ++i)
+ {
+ free(allCaptureDevNameMap[i].name);
+ }
+ free(allCaptureDevNameMap);
+
+ allCaptureDevNameMap=deviceList(SND_PCM_CHANNEL_CAPTURE, &numCaptureDevNames);
+ for (i=0; i<numCaptureDevNames; ++i)
+ {
+ AppendCaptureDeviceList(allCaptureDevNameMap[i].name);
+ }
+ break;
+ }
+}
diff --git a/Alc/backends/sndio.c b/Alc/backends/sndio.c
index 2be88746..80aebfd1 100644
--- a/Alc/backends/sndio.c
+++ b/Alc/backends/sndio.c
@@ -26,6 +26,7 @@
#include "alMain.h"
#include "alu.h"
+#include "threads.h"
#include <sndio.h>
@@ -46,7 +47,7 @@ typedef struct {
ALsizei data_size;
volatile int killNow;
- ALvoid *thread;
+ althread_t thread;
} sndio_data;
@@ -58,6 +59,7 @@ static ALuint sndio_proc(ALvoid *ptr)
size_t wrote;
SetRTPriority();
+ SetThreadName(MIXER_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
@@ -222,8 +224,7 @@ static ALCboolean sndio_start_playback(ALCdevice *device)
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
data->mix_data = calloc(1, data->data_size);
- data->thread = StartThread(sndio_proc, device);
- if(data->thread == NULL)
+ if(!StartThread(&data->thread, sndio_proc, device))
{
sio_stop(data->sndHandle);
free(data->mix_data);
@@ -266,8 +267,6 @@ static const BackendFuncs sndio_funcs = {
NULL,
NULL,
NULL,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
ALCdevice_GetLatencyDefault
};
diff --git a/Alc/backends/solaris.c b/Alc/backends/solaris.c
index 1c781387..700131c8 100644
--- a/Alc/backends/solaris.c
+++ b/Alc/backends/solaris.c
@@ -33,6 +33,8 @@
#include "alMain.h"
#include "alu.h"
+#include "threads.h"
+#include "compat.h"
#include <sys/audioio.h>
@@ -43,11 +45,12 @@ static const char *solaris_driver = "/dev/audio";
typedef struct {
int fd;
- volatile int killNow;
- ALvoid *thread;
ALubyte *mix_data;
int data_size;
+
+ volatile int killNow;
+ althread_t thread;
} solaris_data;
@@ -59,6 +62,7 @@ static ALuint SolarisProc(ALvoid *ptr)
int wrote;
SetRTPriority();
+ SetThreadName(MIXER_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
@@ -207,8 +211,7 @@ static ALCboolean solaris_start_playback(ALCdevice *device)
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
data->mix_data = calloc(1, data->data_size);
- data->thread = StartThread(SolarisProc, device);
- if(data->thread == NULL)
+ if(!StartThread(&data->thread, SolarisProc, device))
{
free(data->mix_data);
data->mix_data = NULL;
@@ -250,8 +253,6 @@ static const BackendFuncs solaris_funcs = {
NULL,
NULL,
NULL,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
ALCdevice_GetLatencyDefault
};
diff --git a/Alc/backends/wave.c b/Alc/backends/wave.c
index be528c9a..2fafc4b9 100644
--- a/Alc/backends/wave.c
+++ b/Alc/backends/wave.c
@@ -23,12 +23,15 @@
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
+#include <errno.h>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include "alMain.h"
#include "alu.h"
+#include "threads.h"
+#include "compat.h"
typedef struct {
@@ -39,7 +42,7 @@ typedef struct {
ALuint size;
volatile int killNow;
- ALvoid *thread;
+ althread_t thread;
} wave_data;
@@ -93,6 +96,8 @@ static ALuint WaveProc(ALvoid *ptr)
const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 /
Device->Frequency / 2;
+ SetThreadName(MIXER_THREAD_NAME);
+
frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
done = 0;
@@ -146,7 +151,7 @@ static ALuint WaveProc(ALvoid *ptr)
{
fs = fwrite(data->buffer, frameSize, Device->UpdateSize,
data->f);
- fs = fs;
+ (void)fs;
}
if(ferror(data->f))
{
@@ -257,7 +262,7 @@ static ALCboolean wave_reset_playback(ALCdevice *device)
fwrite32le(channel_masks[channels], data->f);
// 16 byte GUID, sub-type format
val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f);
- val = val;
+ (void)val;
fprintf(data->f, "data");
fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close
@@ -286,8 +291,7 @@ static ALCboolean wave_start_playback(ALCdevice *device)
return ALC_FALSE;
}
- data->thread = StartThread(WaveProc, device);
- if(data->thread == NULL)
+ if(!StartThread(&data->thread, WaveProc, device))
{
free(data->buffer);
data->buffer = NULL;
@@ -339,8 +343,6 @@ static const BackendFuncs wave_funcs = {
NULL,
NULL,
NULL,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
ALCdevice_GetLatencyDefault
};
diff --git a/Alc/backends/winmm.c b/Alc/backends/winmm.c
index 4786d9b4..7082a874 100644
--- a/Alc/backends/winmm.c
+++ b/Alc/backends/winmm.c
@@ -29,6 +29,7 @@
#include "alMain.h"
#include "alu.h"
+#include "threads.h"
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
@@ -146,14 +147,11 @@ static void ProbeCaptureDevices(void)
Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and
returns to the application (for more data)
*/
-static void CALLBACK WaveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
+static void CALLBACK WaveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
{
ALCdevice *Device = (ALCdevice*)instance;
WinMMData *data = Device->ExtraData;
- (void)device;
- (void)param2;
-
if(msg != WOM_DONE)
return;
@@ -167,7 +165,7 @@ static void CALLBACK WaveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR instance,
Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its
audio data.
*/
-static DWORD WINAPI PlaybackThreadProc(LPVOID param)
+FORCE_ALIGN static DWORD WINAPI PlaybackThreadProc(LPVOID param)
{
ALCdevice *Device = (ALCdevice*)param;
WinMMData *data = Device->ExtraData;
@@ -178,6 +176,7 @@ static DWORD WINAPI PlaybackThreadProc(LPVOID param)
FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
SetRTPriority();
+ SetThreadName(MIXER_THREAD_NAME);
while(GetMessage(&msg, NULL, 0, 0))
{
@@ -213,14 +212,11 @@ static DWORD WINAPI PlaybackThreadProc(LPVOID param)
Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
returns to the application (with more data)
*/
-static void CALLBACK WaveInProc(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
+static void CALLBACK WaveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
{
ALCdevice *Device = (ALCdevice*)instance;
WinMMData *data = Device->ExtraData;
- (void)device;
- (void)param2;
-
if(msg != WIM_DATA)
return;
@@ -243,6 +239,7 @@ static DWORD WINAPI CaptureThreadProc(LPVOID param)
MSG msg;
FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
+ SetThreadName("alsoft-record");
while(GetMessage(&msg, NULL, 0, 0))
{
@@ -716,8 +713,6 @@ static const BackendFuncs WinMMFuncs = {
WinMMStopCapture,
WinMMCaptureSamples,
WinMMAvailableSamples,
- ALCdevice_LockDefault,
- ALCdevice_UnlockDefault,
ALCdevice_GetLatencyDefault
};
diff --git a/Alc/compat.h b/Alc/compat.h
new file mode 100644
index 00000000..dbbcce28
--- /dev/null
+++ b/Alc/compat.h
@@ -0,0 +1,65 @@
+#ifndef AL_COMPAT_H
+#define AL_COMPAT_H
+
+#include "AL/al.h"
+
+#ifdef _WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+typedef DWORD althread_key_t;
+int althread_key_create(althread_key_t *key, void (*callback)(void*));
+int althread_key_delete(althread_key_t key);
+void *althread_getspecific(althread_key_t key);
+int althread_setspecific(althread_key_t key, void *val);
+
+typedef LONG althread_once_t;
+#define ALTHREAD_ONCE_INIT 0
+void althread_once(althread_once_t *once, void (*callback)(void));
+
+inline int alsched_yield(void)
+{ SwitchToThread(); return 0; }
+
+WCHAR *strdupW(const WCHAR *str);
+
+#define HAVE_DYNLOAD 1
+
+#else
+
+#include <pthread.h>
+
+typedef pthread_mutex_t CRITICAL_SECTION;
+void InitializeCriticalSection(CRITICAL_SECTION *cs);
+void DeleteCriticalSection(CRITICAL_SECTION *cs);
+void EnterCriticalSection(CRITICAL_SECTION *cs);
+void LeaveCriticalSection(CRITICAL_SECTION *cs);
+
+ALuint timeGetTime(void);
+void Sleep(ALuint t);
+
+#define althread_key_t pthread_key_t
+#define althread_key_create pthread_key_create
+#define althread_key_delete pthread_key_delete
+#define althread_getspecific pthread_getspecific
+#define althread_setspecific pthread_setspecific
+
+#define althread_once_t pthread_once_t
+#define ALTHREAD_ONCE_INIT PTHREAD_ONCE_INIT
+#define althread_once pthread_once
+
+#define alsched_yield sched_yield
+
+#if defined(HAVE_DLFCN_H)
+#define HAVE_DYNLOAD 1
+#endif
+
+#endif
+
+#ifdef HAVE_DYNLOAD
+void *LoadLib(const char *name);
+void CloseLib(void *handle);
+void *GetSymbol(void *handle, const char *name);
+#endif
+
+#endif /* AL_COMPAT_H */
diff --git a/Alc/effects/autowah.c b/Alc/effects/autowah.c
new file mode 100644
index 00000000..9a45e233
--- /dev/null
+++ b/Alc/effects/autowah.c
@@ -0,0 +1,275 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Anis A. Hireche, Nasca Octavian Paul
+ * 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 <stdlib.h>
+
+#include "config.h"
+#include "alu.h"
+#include "alFilter.h"
+#include "alError.h"
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+
+
+/* Auto-wah is simply a low-pass filter with a cutoff frequency that shifts up
+ * or down depending on the input signal, and a resonant peak at the cutoff.
+ *
+ * Currently, we assume a cutoff frequency range of 500hz (no amplitude) to
+ * 3khz (peak gain). Peak gain is assumed to be in normalized scale.
+ */
+
+typedef struct ALautowahState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ /* Effect gains for each channel */
+ ALfloat Gain[MaxChannels];
+
+ /* Effect parameters */
+ ALfloat AttackRate;
+ ALfloat ReleaseRate;
+ ALfloat Resonance;
+ ALfloat PeakGain;
+ ALfloat GainCtrl;
+ ALfloat Frequency;
+
+ /* Samples processing */
+ ALfilterState LowPass;
+} ALautowahState;
+
+static ALvoid ALautowahState_Destruct(ALautowahState *UNUSED(state))
+{
+}
+
+static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device)
+{
+ state->Frequency = device->Frequency;
+ return AL_TRUE;
+}
+
+static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, const ALeffectslot *slot)
+{
+ ALfloat attackTime, releaseTime;
+ ALfloat gain;
+
+ attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency;
+ releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency;
+
+ state->AttackRate = 1.0f / attackTime;
+ state->ReleaseRate = 1.0f / releaseTime;
+ state->PeakGain = slot->EffectProps.Autowah.PeakGain;
+ state->Resonance = slot->EffectProps.Autowah.Resonance;
+
+ gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
+ SetGains(device, gain, state->Gain);
+}
+
+static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
+{
+ ALuint it, kt;
+ ALuint base;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALfloat temps[64];
+ ALuint td = minu(SamplesToDo-base, 64);
+ ALfloat gain = state->GainCtrl;
+
+ for(it = 0;it < td;it++)
+ {
+ ALfloat smp = SamplesIn[it+base];
+ ALfloat alpha, w0;
+ ALfloat amplitude;
+ ALfloat cutoff;
+
+ /* Similar to compressor, we get the current amplitude of the
+ * incoming signal, and attack or release to reach it. */
+ amplitude = fabs(smp);
+ if(amplitude > gain)
+ gain = minf(gain+state->AttackRate, amplitude);
+ else if(amplitude < gain)
+ gain = maxf(gain-state->ReleaseRate, amplitude);
+ gain = maxf(gain, GAIN_SILENCE_THRESHOLD);
+
+ /* FIXME: What range does the filter cover? */
+ cutoff = lerp(1000.0f, (ALfloat)LOWPASSFREQREF, minf(gain/state->PeakGain, 1.0f));
+
+ /* The code below is like calling ALfilterState_setParams with
+ * ALfilterType_LowPass. However, instead of passing a bandwidth,
+ * we use the resonance property for Q. This also inlines the call.
+ */
+ w0 = F_2PI * cutoff / state->Frequency;
+
+ /* FIXME: Resonance controls the resonant peak, or Q. How? Not sure
+ * that Q = resonance*0.1. */
+ alpha = sinf(w0) / (2.0f * state->Resonance*0.1f);
+ state->LowPass.b[0] = (1.0f - cosf(w0)) / 2.0f;
+ state->LowPass.b[1] = 1.0f - cosf(w0);
+ state->LowPass.b[2] = (1.0f - cosf(w0)) / 2.0f;
+ state->LowPass.a[0] = 1.0f + alpha;
+ state->LowPass.a[1] = -2.0f * cosf(w0);
+ state->LowPass.a[2] = 1.0f - alpha;
+
+ state->LowPass.b[2] /= state->LowPass.a[0];
+ state->LowPass.b[1] /= state->LowPass.a[0];
+ state->LowPass.b[0] /= state->LowPass.a[0];
+ state->LowPass.a[2] /= state->LowPass.a[0];
+ state->LowPass.a[1] /= state->LowPass.a[0];
+ state->LowPass.a[0] /= state->LowPass.a[0];
+
+ temps[it] = ALfilterState_processSingle(&state->LowPass, smp);
+ }
+ state->GainCtrl = gain;
+
+ for(kt = 0;kt < MaxChannels;kt++)
+ {
+ ALfloat gain = state->Gain[kt];
+ if(!(gain > GAIN_SILENCE_THRESHOLD))
+ continue;
+
+ for(it = 0;it < td;it++)
+ SamplesOut[kt][base+it] += gain * temps[it];
+ }
+
+ base += td;
+ }
+}
+
+static void ALautowahState_Delete(ALautowahState *state)
+{
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
+
+
+typedef struct ALautowahStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALautowahStateFactory;
+
+static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory))
+{
+ ALautowahState *state;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALautowahState, ALeffectState, state);
+
+ state->AttackRate = 0.0f;
+ state->ReleaseRate = 0.0f;
+ state->Resonance = 0.0f;
+ state->PeakGain = 1.0f;
+ state->GainCtrl = 1.0f;
+
+ ALfilterState_clear(&state->LowPass);
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory);
+
+ALeffectStateFactory *ALautowahStateFactory_getFactory(void)
+{
+ static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } };
+
+ return STATIC_CAST(ALeffectStateFactory, &AutowahFactory);
+}
+
+
+void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALautowah_setParami(effect, context, param, vals[0]);
+}
+void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_AUTOWAH_ATTACK_TIME:
+ if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Autowah.AttackTime = val;
+ break;
+
+ case AL_AUTOWAH_RELEASE_TIME:
+ if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Autowah.ReleaseTime = val;
+ break;
+
+ case AL_AUTOWAH_RESONANCE:
+ if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Autowah.Resonance = val;
+ break;
+
+ case AL_AUTOWAH_PEAK_GAIN:
+ if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Autowah.PeakGain = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALautowah_setParamf(effect, context, param, vals[0]);
+}
+
+void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALautowah_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALautowah_getParami(effect, context, param, vals);
+}
+void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_AUTOWAH_ATTACK_TIME:
+ *val = props->Autowah.AttackTime;
+ break;
+
+ case AL_AUTOWAH_RELEASE_TIME:
+ *val = props->Autowah.ReleaseTime;
+ break;
+
+ case AL_AUTOWAH_RESONANCE:
+ *val = props->Autowah.Resonance;
+ break;
+
+ case AL_AUTOWAH_PEAK_GAIN:
+ *val = props->Autowah.PeakGain;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ALautowah_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALautowah);
diff --git a/Alc/effects/chorus.c b/Alc/effects/chorus.c
new file mode 100644
index 00000000..4945faf2
--- /dev/null
+++ b/Alc/effects/chorus.c
@@ -0,0 +1,381 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Mike Gorchak
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+typedef struct ALchorusState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ ALfloat *SampleBuffer[2];
+ ALuint BufferLength;
+ ALuint offset;
+ ALuint lfo_range;
+ ALfloat lfo_scale;
+ ALint lfo_disp;
+
+ /* Gains for left and right sides */
+ ALfloat Gain[2][MaxChannels];
+
+ /* effect parameters */
+ ALint waveform;
+ ALint delay;
+ ALfloat depth;
+ ALfloat feedback;
+} ALchorusState;
+
+static ALvoid ALchorusState_Destruct(ALchorusState *state)
+{
+ free(state->SampleBuffer[0]);
+ state->SampleBuffer[0] = NULL;
+ state->SampleBuffer[1] = NULL;
+}
+
+static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
+{
+ ALuint maxlen;
+ ALuint it;
+
+ maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1;
+ maxlen = NextPowerOf2(maxlen);
+
+ if(maxlen != state->BufferLength)
+ {
+ void *temp;
+
+ temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
+ if(!temp) return AL_FALSE;
+ state->SampleBuffer[0] = temp;
+ state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
+
+ state->BufferLength = maxlen;
+ }
+
+ for(it = 0;it < state->BufferLength;it++)
+ {
+ state->SampleBuffer[0][it] = 0.0f;
+ state->SampleBuffer[1][it] = 0.0f;
+ }
+
+ return AL_TRUE;
+}
+
+static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot)
+{
+ ALfloat frequency = (ALfloat)Device->Frequency;
+ ALfloat rate;
+ ALint phase;
+
+ state->waveform = Slot->EffectProps.Chorus.Waveform;
+ state->depth = Slot->EffectProps.Chorus.Depth;
+ state->feedback = Slot->EffectProps.Chorus.Feedback;
+ state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency);
+
+ /* Gains for left and right sides */
+ ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
+ ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
+
+ phase = Slot->EffectProps.Chorus.Phase;
+ rate = Slot->EffectProps.Chorus.Rate;
+ if(!(rate > 0.0f))
+ {
+ state->lfo_scale = 0.0f;
+ state->lfo_range = 1;
+ state->lfo_disp = 0;
+ }
+ else
+ {
+ /* Calculate LFO coefficient */
+ state->lfo_range = fastf2u(frequency/rate + 0.5f);
+ switch(state->waveform)
+ {
+ case AL_CHORUS_WAVEFORM_TRIANGLE:
+ state->lfo_scale = 4.0f / state->lfo_range;
+ break;
+ case AL_CHORUS_WAVEFORM_SINUSOID:
+ state->lfo_scale = F_2PI / state->lfo_range;
+ break;
+ }
+
+ /* Calculate lfo phase displacement */
+ state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
+ }
+}
+
+static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
+{
+ ALfloat lfo_value;
+
+ lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
+ lfo_value *= state->depth * state->delay;
+ *delay_left = fastf2i(lfo_value) + state->delay;
+
+ offset += state->lfo_disp;
+ lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
+ lfo_value *= state->depth * state->delay;
+ *delay_right = fastf2i(lfo_value) + state->delay;
+}
+
+static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
+{
+ ALfloat lfo_value;
+
+ lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
+ lfo_value *= state->depth * state->delay;
+ *delay_left = fastf2i(lfo_value) + state->delay;
+
+ offset += state->lfo_disp;
+ lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
+ lfo_value *= state->depth * state->delay;
+ *delay_right = fastf2i(lfo_value) + state->delay;
+}
+
+#define DECL_TEMPLATE(Func) \
+static void Process##Func(ALchorusState *state, const ALuint SamplesToDo, \
+ const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
+{ \
+ const ALuint bufmask = state->BufferLength-1; \
+ ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
+ ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
+ ALuint offset = state->offset; \
+ const ALfloat feedback = state->feedback; \
+ ALuint it; \
+ \
+ for(it = 0;it < SamplesToDo;it++) \
+ { \
+ ALint delay_left, delay_right; \
+ Func(&delay_left, &delay_right, offset, state); \
+ \
+ out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
+ leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
+ \
+ out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
+ rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
+ \
+ offset++; \
+ } \
+ state->offset = offset; \
+}
+
+DECL_TEMPLATE(Triangle)
+DECL_TEMPLATE(Sinusoid)
+
+#undef DECL_TEMPLATE
+
+static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ ALuint it, kt;
+ ALuint base;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALfloat temps[64][2];
+ ALuint td = minu(SamplesToDo-base, 64);
+
+ if(state->waveform == AL_CHORUS_WAVEFORM_TRIANGLE)
+ ProcessTriangle(state, td, SamplesIn+base, temps);
+ else if(state->waveform == AL_CHORUS_WAVEFORM_SINUSOID)
+ ProcessSinusoid(state, td, SamplesIn+base, temps);
+
+ for(kt = 0;kt < MaxChannels;kt++)
+ {
+ ALfloat gain = state->Gain[0][kt];
+ if(gain > GAIN_SILENCE_THRESHOLD)
+ {
+ for(it = 0;it < td;it++)
+ SamplesOut[kt][it+base] += temps[it][0] * gain;
+ }
+
+ gain = state->Gain[1][kt];
+ if(gain > GAIN_SILENCE_THRESHOLD)
+ {
+ for(it = 0;it < td;it++)
+ SamplesOut[kt][it+base] += temps[it][1] * gain;
+ }
+ }
+
+ base += td;
+ }
+}
+
+static void ALchorusState_Delete(ALchorusState *state)
+{
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
+
+
+typedef struct ALchorusStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALchorusStateFactory;
+
+static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(factory))
+{
+ ALchorusState *state;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALchorusState, ALeffectState, state);
+
+ state->BufferLength = 0;
+ state->SampleBuffer[0] = NULL;
+ state->SampleBuffer[1] = NULL;
+ state->offset = 0;
+ state->lfo_range = 1;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALchorusStateFactory);
+
+
+ALeffectStateFactory *ALchorusStateFactory_getFactory(void)
+{
+ static ALchorusStateFactory ChorusFactory = { { GET_VTABLE2(ALchorusStateFactory, ALeffectStateFactory) } };
+
+ return STATIC_CAST(ALeffectStateFactory, &ChorusFactory);
+}
+
+
+void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_CHORUS_WAVEFORM:
+ if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Chorus.Waveform = val;
+ break;
+
+ case AL_CHORUS_PHASE:
+ if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Chorus.Phase = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALchorus_setParami(effect, context, param, vals[0]);
+}
+void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_CHORUS_RATE:
+ if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Chorus.Rate = val;
+ break;
+
+ case AL_CHORUS_DEPTH:
+ if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Chorus.Depth = val;
+ break;
+
+ case AL_CHORUS_FEEDBACK:
+ if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Chorus.Feedback = val;
+ break;
+
+ case AL_CHORUS_DELAY:
+ if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Chorus.Delay = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALchorus_setParamf(effect, context, param, vals[0]);
+}
+
+void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_CHORUS_WAVEFORM:
+ *val = props->Chorus.Waveform;
+ break;
+
+ case AL_CHORUS_PHASE:
+ *val = props->Chorus.Phase;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALchorus_getParami(effect, context, param, vals);
+}
+void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_CHORUS_RATE:
+ *val = props->Chorus.Rate;
+ break;
+
+ case AL_CHORUS_DEPTH:
+ *val = props->Chorus.Depth;
+ break;
+
+ case AL_CHORUS_FEEDBACK:
+ *val = props->Chorus.Feedback;
+ break;
+
+ case AL_CHORUS_DELAY:
+ *val = props->Chorus.Delay;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ALchorus_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALchorus);
diff --git a/Alc/effects/compressor.c b/Alc/effects/compressor.c
new file mode 100644
index 00000000..14c0ed10
--- /dev/null
+++ b/Alc/effects/compressor.c
@@ -0,0 +1,223 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Anis A. Hireche
+ * 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 <stdlib.h>
+
+#include "config.h"
+#include "alError.h"
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alu.h"
+
+
+typedef struct ALcompressorState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ /* Effect gains for each channel */
+ ALfloat Gain[MaxChannels];
+
+ /* Effect parameters */
+ ALboolean Enabled;
+ ALfloat AttackRate;
+ ALfloat ReleaseRate;
+ ALfloat GainCtrl;
+} ALcompressorState;
+
+static ALvoid ALcompressorState_Destruct(ALcompressorState *UNUSED(state))
+{
+}
+
+static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
+{
+ const ALfloat attackTime = device->Frequency * 0.2f; /* 200ms Attack */
+ const ALfloat releaseTime = device->Frequency * 0.4f; /* 400ms Release */
+
+ state->AttackRate = 1.0f / attackTime;
+ state->ReleaseRate = 1.0f / releaseTime;
+
+ return AL_TRUE;
+}
+
+static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *Device, const ALeffectslot *Slot)
+{
+ ALfloat gain;
+
+ state->Enabled = Slot->EffectProps.Compressor.OnOff;
+
+ gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
+ SetGains(Device, gain, state->Gain);
+}
+
+static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
+{
+ ALuint it, kt;
+ ALuint base;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALfloat temps[64];
+ ALuint td = minu(SamplesToDo-base, 64);
+
+ if(state->Enabled)
+ {
+ ALfloat output, smp, amplitude;
+ ALfloat gain = state->GainCtrl;
+
+ for(it = 0;it < td;it++)
+ {
+ smp = SamplesIn[it+base];
+
+ amplitude = fabs(smp);
+ if(amplitude > gain)
+ gain = minf(gain+state->AttackRate, amplitude);
+ else if(amplitude < gain)
+ gain = maxf(gain-state->ReleaseRate, amplitude);
+ output = 1.0 / clampf(gain, 0.5f, 2.0f);
+
+ temps[it] = smp * output;
+ }
+
+ state->GainCtrl = gain;
+ }
+ else
+ {
+ ALfloat output, smp, amplitude;
+ ALfloat gain = state->GainCtrl;
+
+ for(it = 0;it < td;it++)
+ {
+ smp = SamplesIn[it+base];
+
+ amplitude = 1.0f;
+ if(amplitude > gain)
+ gain = minf(gain+state->AttackRate, amplitude);
+ else if(amplitude < gain)
+ gain = maxf(gain-state->ReleaseRate, amplitude);
+ output = 1.0f / clampf(gain, 0.5f, 2.0f);
+
+ temps[it] = smp * output;
+ }
+
+ state->GainCtrl = gain;
+ }
+
+
+ for(kt = 0;kt < MaxChannels;kt++)
+ {
+ ALfloat gain = state->Gain[kt];
+ if(!(gain > GAIN_SILENCE_THRESHOLD))
+ continue;
+
+ for(it = 0;it < td;it++)
+ SamplesOut[kt][base+it] += gain * temps[it];
+ }
+
+ base += td;
+ }
+}
+
+static void ALcompressorState_Delete(ALcompressorState *state)
+{
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
+
+
+typedef struct ALcompressorStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALcompressorStateFactory;
+
+static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *UNUSED(factory))
+{
+ ALcompressorState *state;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALcompressorState, ALeffectState, state);
+
+ state->Enabled = AL_TRUE;
+ state->AttackRate = 0.0f;
+ state->ReleaseRate = 0.0f;
+ state->GainCtrl = 1.0f;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALcompressorStateFactory);
+
+ALeffectStateFactory *ALcompressorStateFactory_getFactory(void)
+{
+ static ALcompressorStateFactory CompressorFactory = { { GET_VTABLE2(ALcompressorStateFactory, ALeffectStateFactory) } };
+
+ return STATIC_CAST(ALeffectStateFactory, &CompressorFactory);
+}
+
+
+void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_COMPRESSOR_ONOFF:
+ if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Compressor.OnOff = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALcompressor_setParami(effect, context, param, vals[0]);
+}
+void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALcompressor_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALcompressor_setParamf(effect, context, param, vals[0]);
+}
+
+void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_COMPRESSOR_ONOFF:
+ *val = props->Compressor.OnOff;
+ break;
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALcompressor_getParami(effect, context, param, vals);
+}
+void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALcompressor_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ALcompressor_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALcompressor);
diff --git a/Alc/effects/dedicated.c b/Alc/effects/dedicated.c
new file mode 100644
index 00000000..fe57c7d8
--- /dev/null
+++ b/Alc/effects/dedicated.c
@@ -0,0 +1,167 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2011 by Chris Robinson.
+ * 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 <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+typedef struct ALdedicatedState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ ALfloat gains[MaxChannels];
+} ALdedicatedState;
+
+
+static ALvoid ALdedicatedState_Destruct(ALdedicatedState *UNUSED(state))
+{
+}
+
+static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
+{
+ return AL_TRUE;
+}
+
+static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot)
+{
+ ALfloat Gain;
+ ALsizei s;
+
+ Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain;
+ if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
+ ComputeAngleGains(device, atan2f(0.0f, 1.0f), 0.0f, Gain, state->gains);
+ else if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
+ {
+ for(s = 0;s < MaxChannels;s++)
+ state->gains[s] = 0.0f;
+ state->gains[LFE] = Gain;
+ }
+}
+
+static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ const ALfloat *gains = state->gains;
+ ALuint i, c;
+
+ for(c = 0;c < MaxChannels;c++)
+ {
+ if(!(gains[c] > GAIN_SILENCE_THRESHOLD))
+ continue;
+
+ for(i = 0;i < SamplesToDo;i++)
+ SamplesOut[c][i] = SamplesIn[i] * gains[c];
+ }
+}
+
+static void ALdedicatedState_Delete(ALdedicatedState *state)
+{
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
+
+
+typedef struct ALdedicatedStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALdedicatedStateFactory;
+
+ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory))
+{
+ ALdedicatedState *state;
+ ALsizei s;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALdedicatedState, ALeffectState, state);
+
+ for(s = 0;s < MaxChannels;s++)
+ state->gains[s] = 0.0f;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdedicatedStateFactory);
+
+
+ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void)
+{
+ static ALdedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(ALdedicatedStateFactory, ALeffectStateFactory) } };
+
+ return STATIC_CAST(ALeffectStateFactory, &DedicatedFactory);
+}
+
+
+void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALdedicated_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALdedicated_setParami(effect, context, param, vals[0]);
+}
+void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_DEDICATED_GAIN:
+ if(!(val >= 0.0f && isfinite(val)))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Dedicated.Gain = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALdedicated_setParamf(effect, context, param, vals[0]);
+}
+
+void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALdedicated_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALdedicated_getParami(effect, context, param, vals);
+}
+void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_DEDICATED_GAIN:
+ *val = props->Dedicated.Gain;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ALdedicated_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALdedicated);
diff --git a/Alc/effects/distortion.c b/Alc/effects/distortion.c
new file mode 100644
index 00000000..e39a8197
--- /dev/null
+++ b/Alc/effects/distortion.c
@@ -0,0 +1,300 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Mike Gorchak
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+typedef struct ALdistortionState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ /* Effect gains for each channel */
+ ALfloat Gain[MaxChannels];
+
+ /* Effect parameters */
+ ALfilterState lowpass;
+ ALfilterState bandpass;
+ ALfloat attenuation;
+ ALfloat edge_coeff;
+} ALdistortionState;
+
+static ALvoid ALdistortionState_Destruct(ALdistortionState *UNUSED(state))
+{
+}
+
+static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device))
+{
+ return AL_TRUE;
+}
+
+static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Device, const ALeffectslot *Slot)
+{
+ ALfloat frequency = (ALfloat)Device->Frequency;
+ ALfloat bandwidth;
+ ALfloat cutoff;
+ ALfloat edge;
+ ALfloat gain;
+
+ /* Store distorted signal attenuation settings */
+ state->attenuation = Slot->EffectProps.Distortion.Gain;
+
+ /* Store waveshaper edge settings */
+ edge = sinf(Slot->EffectProps.Distortion.Edge * (F_PI_2));
+ edge = minf(edge, 0.99f);
+ state->edge_coeff = 2.0f * edge / (1.0f-edge);
+
+ /* Lowpass filter */
+ cutoff = Slot->EffectProps.Distortion.LowpassCutoff;
+ /* Bandwidth value is constant in octaves */
+ bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
+ ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f,
+ cutoff / (frequency*4.0f), bandwidth);
+
+ /* Bandpass filter */
+ cutoff = Slot->EffectProps.Distortion.EQCenter;
+ /* Convert bandwidth in Hz to octaves */
+ bandwidth = Slot->EffectProps.Distortion.EQBandwidth / (cutoff * 0.67f);
+ ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
+ cutoff / (frequency*4.0f), bandwidth);
+
+ gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
+ SetGains(Device, gain, state->Gain);
+}
+
+static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ const ALfloat fc = state->edge_coeff;
+ float oversample_buffer[64][4];
+ ALuint base;
+ ALuint it;
+ ALuint ot;
+ ALuint kt;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALfloat temps[64];
+ ALuint td = minu(SamplesToDo-base, 64);
+
+ /* Perform 4x oversampling to avoid aliasing. */
+ /* Oversampling greatly improves distortion */
+ /* quality and allows to implement lowpass and */
+ /* bandpass filters using high frequencies, at */
+ /* which classic IIR filters became unstable. */
+
+ /* Fill oversample buffer using zero stuffing */
+ for(it = 0;it < td;it++)
+ {
+ oversample_buffer[it][0] = SamplesIn[it+base];
+ oversample_buffer[it][1] = 0.0f;
+ oversample_buffer[it][2] = 0.0f;
+ oversample_buffer[it][3] = 0.0f;
+ }
+
+ /* First step, do lowpass filtering of original signal, */
+ /* additionally perform buffer interpolation and lowpass */
+ /* cutoff for oversampling (which is fortunately first */
+ /* step of distortion). So combine three operations into */
+ /* the one. */
+ for(it = 0;it < td;it++)
+ {
+ for(ot = 0;ot < 4;ot++)
+ {
+ ALfloat smp;
+ smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]);
+
+ /* Restore signal power by multiplying sample by amount of oversampling */
+ oversample_buffer[it][ot] = smp * 4.0f;
+ }
+ }
+
+ for(it = 0;it < td;it++)
+ {
+ /* Second step, do distortion using waveshaper function */
+ /* to emulate signal processing during tube overdriving. */
+ /* Three steps of waveshaping are intended to modify */
+ /* waveform without boost/clipping/attenuation process. */
+ for(ot = 0;ot < 4;ot++)
+ {
+ ALfloat smp = oversample_buffer[it][ot];
+
+ smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
+ smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
+ smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
+
+ /* Third step, do bandpass filtering of distorted signal */
+ smp = ALfilterState_processSingle(&state->bandpass, smp);
+ oversample_buffer[it][ot] = smp;
+ }
+
+ /* Fourth step, final, do attenuation and perform decimation, */
+ /* store only one sample out of 4. */
+ temps[it] = oversample_buffer[it][0] * state->attenuation;
+ }
+
+ for(kt = 0;kt < MaxChannels;kt++)
+ {
+ ALfloat gain = state->Gain[kt];
+ if(!(gain > GAIN_SILENCE_THRESHOLD))
+ continue;
+
+ for(it = 0;it < td;it++)
+ SamplesOut[kt][base+it] += gain * temps[it];
+ }
+
+ base += td;
+ }
+}
+
+static void ALdistortionState_Delete(ALdistortionState *state)
+{
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
+
+
+typedef struct ALdistortionStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALdistortionStateFactory;
+
+static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *UNUSED(factory))
+{
+ ALdistortionState *state;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALdistortionState, ALeffectState, state);
+
+ ALfilterState_clear(&state->lowpass);
+ ALfilterState_clear(&state->bandpass);
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory);
+
+
+ALeffectStateFactory *ALdistortionStateFactory_getFactory(void)
+{
+ static ALdistortionStateFactory DistortionFactory = { { GET_VTABLE2(ALdistortionStateFactory, ALeffectStateFactory) } };
+
+ return STATIC_CAST(ALeffectStateFactory, &DistortionFactory);
+}
+
+
+void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALdistortion_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALdistortion_setParami(effect, context, param, vals[0]);
+}
+void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_DISTORTION_EDGE:
+ if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Distortion.Edge = val;
+ break;
+
+ case AL_DISTORTION_GAIN:
+ if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Distortion.Gain = val;
+ break;
+
+ case AL_DISTORTION_LOWPASS_CUTOFF:
+ if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Distortion.LowpassCutoff = val;
+ break;
+
+ case AL_DISTORTION_EQCENTER:
+ if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Distortion.EQCenter = val;
+ break;
+
+ case AL_DISTORTION_EQBANDWIDTH:
+ if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Distortion.EQBandwidth = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALdistortion_setParamf(effect, context, param, vals[0]);
+}
+
+void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALdistortion_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALdistortion_getParami(effect, context, param, vals);
+}
+void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_DISTORTION_EDGE:
+ *val = props->Distortion.Edge;
+ break;
+
+ case AL_DISTORTION_GAIN:
+ *val = props->Distortion.Gain;
+ break;
+
+ case AL_DISTORTION_LOWPASS_CUTOFF:
+ *val = props->Distortion.LowpassCutoff;
+ break;
+
+ case AL_DISTORTION_EQCENTER:
+ *val = props->Distortion.EQCenter;
+ break;
+
+ case AL_DISTORTION_EQBANDWIDTH:
+ *val = props->Distortion.EQBandwidth;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ALdistortion_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALdistortion);
diff --git a/Alc/effects/echo.c b/Alc/effects/echo.c
new file mode 100644
index 00000000..7471c3dd
--- /dev/null
+++ b/Alc/effects/echo.c
@@ -0,0 +1,296 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2009 by Chris Robinson.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+typedef struct ALechoState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ ALfloat *SampleBuffer;
+ ALuint BufferLength;
+
+ // The echo is two tap. The delay is the number of samples from before the
+ // current offset
+ struct {
+ ALuint delay;
+ } Tap[2];
+ ALuint Offset;
+ /* The panning gains for the two taps */
+ ALfloat Gain[2][MaxChannels];
+
+ ALfloat FeedGain;
+
+ ALfilterState Filter;
+} ALechoState;
+
+static ALvoid ALechoState_Destruct(ALechoState *state)
+{
+ free(state->SampleBuffer);
+ state->SampleBuffer = NULL;
+}
+
+static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
+{
+ ALuint maxlen, i;
+
+ // Use the next power of 2 for the buffer length, so the tap offsets can be
+ // wrapped using a mask instead of a modulo
+ maxlen = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
+ maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
+ maxlen = NextPowerOf2(maxlen);
+
+ if(maxlen != state->BufferLength)
+ {
+ void *temp;
+
+ temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
+ if(!temp) return AL_FALSE;
+ state->SampleBuffer = temp;
+ state->BufferLength = maxlen;
+ }
+ for(i = 0;i < state->BufferLength;i++)
+ state->SampleBuffer[i] = 0.0f;
+
+ return AL_TRUE;
+}
+
+static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot)
+{
+ ALuint frequency = Device->Frequency;
+ ALfloat lrpan, gain;
+ ALfloat dirGain;
+
+ state->Tap[0].delay = fastf2u(Slot->EffectProps.Echo.Delay * frequency) + 1;
+ state->Tap[1].delay = fastf2u(Slot->EffectProps.Echo.LRDelay * frequency);
+ state->Tap[1].delay += state->Tap[0].delay;
+
+ lrpan = Slot->EffectProps.Echo.Spread;
+
+ state->FeedGain = Slot->EffectProps.Echo.Feedback;
+
+ ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
+ 1.0f - Slot->EffectProps.Echo.Damping,
+ (ALfloat)LOWPASSFREQREF/frequency, 0.0f);
+
+ gain = Slot->Gain;
+ dirGain = fabsf(lrpan);
+
+ /* First tap panning */
+ ComputeAngleGains(Device, atan2f(-lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[0]);
+
+ /* Second tap panning */
+ ComputeAngleGains(Device, atan2f(+lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[1]);
+}
+
+static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ const ALuint mask = state->BufferLength-1;
+ const ALuint tap1 = state->Tap[0].delay;
+ const ALuint tap2 = state->Tap[1].delay;
+ ALuint offset = state->Offset;
+ ALfloat smp;
+ ALuint base;
+ ALuint i, k;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALfloat temps[64][2];
+ ALuint td = minu(SamplesToDo-base, 64);
+
+ for(i = 0;i < td;i++)
+ {
+ /* First tap */
+ temps[i][0] = state->SampleBuffer[(offset-tap1) & mask];
+ /* Second tap */
+ temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
+
+ // Apply damping and feedback gain to the second tap, and mix in the
+ // new sample
+ smp = ALfilterState_processSingle(&state->Filter, temps[i][1]+SamplesIn[i+base]);
+ state->SampleBuffer[offset&mask] = smp * state->FeedGain;
+ offset++;
+ }
+
+ for(k = 0;k < MaxChannels;k++)
+ {
+ ALfloat gain = state->Gain[0][k];
+ if(gain > GAIN_SILENCE_THRESHOLD)
+ {
+ for(i = 0;i < td;i++)
+ SamplesOut[k][i+base] += temps[i][0] * gain;
+ }
+
+ gain = state->Gain[1][k];
+ if(gain > GAIN_SILENCE_THRESHOLD)
+ {
+ for(i = 0;i < td;i++)
+ SamplesOut[k][i+base] += temps[i][1] * gain;
+ }
+ }
+
+ base += td;
+ }
+
+ state->Offset = offset;
+}
+
+static void ALechoState_Delete(ALechoState *state)
+{
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
+
+
+typedef struct ALechoStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALechoStateFactory;
+
+ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
+{
+ ALechoState *state;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALechoState, ALeffectState, state);
+
+ state->BufferLength = 0;
+ state->SampleBuffer = NULL;
+
+ state->Tap[0].delay = 0;
+ state->Tap[1].delay = 0;
+ state->Offset = 0;
+
+ ALfilterState_clear(&state->Filter);
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory);
+
+ALeffectStateFactory *ALechoStateFactory_getFactory(void)
+{
+ static ALechoStateFactory EchoFactory = { { GET_VTABLE2(ALechoStateFactory, ALeffectStateFactory) } };
+
+ return STATIC_CAST(ALeffectStateFactory, &EchoFactory);
+}
+
+
+void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALecho_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALecho_setParami(effect, context, param, vals[0]);
+}
+void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_ECHO_DELAY:
+ if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Echo.Delay = val;
+ break;
+
+ case AL_ECHO_LRDELAY:
+ if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Echo.LRDelay = val;
+ break;
+
+ case AL_ECHO_DAMPING:
+ if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Echo.Damping = val;
+ break;
+
+ case AL_ECHO_FEEDBACK:
+ if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Echo.Feedback = val;
+ break;
+
+ case AL_ECHO_SPREAD:
+ if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Echo.Spread = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALecho_setParamf(effect, context, param, vals[0]);
+}
+
+void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALecho_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALecho_getParami(effect, context, param, vals);
+}
+void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_ECHO_DELAY:
+ *val = props->Echo.Delay;
+ break;
+
+ case AL_ECHO_LRDELAY:
+ *val = props->Echo.LRDelay;
+ break;
+
+ case AL_ECHO_DAMPING:
+ *val = props->Echo.Damping;
+ break;
+
+ case AL_ECHO_FEEDBACK:
+ *val = props->Echo.Feedback;
+ break;
+
+ case AL_ECHO_SPREAD:
+ *val = props->Echo.Spread;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ALecho_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALecho);
diff --git a/Alc/effects/equalizer.c b/Alc/effects/equalizer.c
new file mode 100644
index 00000000..d5bd2caf
--- /dev/null
+++ b/Alc/effects/equalizer.c
@@ -0,0 +1,337 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Mike Gorchak
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+/* The document "Effects Extension Guide.pdf" says that low and high *
+ * frequencies are cutoff frequencies. This is not fully correct, they *
+ * are corner frequencies for low and high shelf filters. If they were *
+ * just cutoff frequencies, there would be no need in cutoff frequency *
+ * gains, which are present. Documentation for "Creative Proteus X2" *
+ * software describes 4-band equalizer functionality in a much better *
+ * way. This equalizer seems to be a predecessor of OpenAL 4-band *
+ * equalizer. With low and high shelf filters we are able to cutoff *
+ * frequencies below and/or above corner frequencies using attenuation *
+ * gains (below 1.0) and amplify all low and/or high frequencies using *
+ * gains above 1.0. *
+ * *
+ * Low-shelf Low Mid Band High Mid Band High-shelf *
+ * corner center center corner *
+ * frequency frequency frequency frequency *
+ * 50Hz..800Hz 200Hz..3000Hz 1000Hz..8000Hz 4000Hz..16000Hz *
+ * *
+ * | | | | *
+ * | | | | *
+ * B -----+ /--+--\ /--+--\ +----- *
+ * O |\ | | | | | | /| *
+ * O | \ - | - - | - / | *
+ * S + | \ | | | | | | / | *
+ * T | | | | | | | | | | *
+ * ---------+---------------+------------------+---------------+-------- *
+ * C | | | | | | | | | | *
+ * U - | / | | | | | | \ | *
+ * T | / - | - - | - \ | *
+ * O |/ | | | | | | \| *
+ * F -----+ \--+--/ \--+--/ +----- *
+ * F | | | | *
+ * | | | | *
+ * *
+ * Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
+ * up to +18dB amplification. Band width varies from 0.01 up to 1.0 in *
+ * octaves for two mid bands. *
+ * *
+ * Implementation is based on the "Cookbook formulae for audio EQ biquad *
+ * filter coefficients" by Robert Bristow-Johnson *
+ * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
+
+typedef struct ALequalizerState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ /* Effect gains for each channel */
+ ALfloat Gain[MaxChannels];
+
+ /* Effect parameters */
+ ALfilterState filter[4];
+} ALequalizerState;
+
+static ALvoid ALequalizerState_Destruct(ALequalizerState *UNUSED(state))
+{
+}
+
+static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device))
+{
+ return AL_TRUE;
+}
+
+static ALvoid ALequalizerState_update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot)
+{
+ ALfloat frequency = (ALfloat)device->Frequency;
+ ALfloat gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
+
+ SetGains(device, gain, state->Gain);
+
+ /* Calculate coefficients for the each type of filter */
+ ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf,
+ sqrtf(slot->EffectProps.Equalizer.LowGain),
+ slot->EffectProps.Equalizer.LowCutoff/frequency,
+ 0.0f);
+
+ ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking,
+ sqrtf(slot->EffectProps.Equalizer.Mid1Gain),
+ slot->EffectProps.Equalizer.Mid1Center/frequency,
+ slot->EffectProps.Equalizer.Mid1Width);
+
+ ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking,
+ sqrtf(slot->EffectProps.Equalizer.Mid2Gain),
+ slot->EffectProps.Equalizer.Mid2Center/frequency,
+ slot->EffectProps.Equalizer.Mid2Width);
+
+ ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf,
+ sqrtf(slot->EffectProps.Equalizer.HighGain),
+ slot->EffectProps.Equalizer.HighCutoff/frequency,
+ 0.0f);
+}
+
+static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ ALuint base;
+ ALuint it;
+ ALuint kt;
+ ALuint ft;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALfloat temps[64];
+ ALuint td = minu(SamplesToDo-base, 64);
+
+ for(it = 0;it < td;it++)
+ {
+ ALfloat smp = SamplesIn[base+it];
+
+ for(ft = 0;ft < 4;ft++)
+ smp = ALfilterState_processSingle(&state->filter[ft], smp);
+
+ temps[it] = smp;
+ }
+
+ for(kt = 0;kt < MaxChannels;kt++)
+ {
+ ALfloat gain = state->Gain[kt];
+ if(!(gain > GAIN_SILENCE_THRESHOLD))
+ continue;
+
+ for(it = 0;it < td;it++)
+ SamplesOut[kt][base+it] += gain * temps[it];
+ }
+
+ base += td;
+ }
+}
+
+static void ALequalizerState_Delete(ALequalizerState *state)
+{
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
+
+
+typedef struct ALequalizerStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALequalizerStateFactory;
+
+ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory))
+{
+ ALequalizerState *state;
+ int it;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALequalizerState, ALeffectState, state);
+
+ /* Initialize sample history only on filter creation to avoid */
+ /* sound clicks if filter settings were changed in runtime. */
+ for(it = 0; it < 4; it++)
+ ALfilterState_clear(&state->filter[it]);
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory);
+
+ALeffectStateFactory *ALequalizerStateFactory_getFactory(void)
+{
+ static ALequalizerStateFactory EqualizerFactory = { { GET_VTABLE2(ALequalizerStateFactory, ALeffectStateFactory) } };
+
+ return STATIC_CAST(ALeffectStateFactory, &EqualizerFactory);
+}
+
+
+void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALequalizer_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALequalizer_setParami(effect, context, param, vals[0]);
+}
+void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_EQUALIZER_LOW_GAIN:
+ if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Equalizer.LowGain = val;
+ break;
+
+ case AL_EQUALIZER_LOW_CUTOFF:
+ if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Equalizer.LowCutoff = val;
+ break;
+
+ case AL_EQUALIZER_MID1_GAIN:
+ if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Equalizer.Mid1Gain = val;
+ break;
+
+ case AL_EQUALIZER_MID1_CENTER:
+ if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Equalizer.Mid1Center = val;
+ break;
+
+ case AL_EQUALIZER_MID1_WIDTH:
+ if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Equalizer.Mid1Width = val;
+ break;
+
+ case AL_EQUALIZER_MID2_GAIN:
+ if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Equalizer.Mid2Gain = val;
+ break;
+
+ case AL_EQUALIZER_MID2_CENTER:
+ if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Equalizer.Mid2Center = val;
+ break;
+
+ case AL_EQUALIZER_MID2_WIDTH:
+ if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Equalizer.Mid2Width = val;
+ break;
+
+ case AL_EQUALIZER_HIGH_GAIN:
+ if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Equalizer.HighGain = val;
+ break;
+
+ case AL_EQUALIZER_HIGH_CUTOFF:
+ if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Equalizer.HighCutoff = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALequalizer_setParamf(effect, context, param, vals[0]);
+}
+
+void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALequalizer_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALequalizer_getParami(effect, context, param, vals);
+}
+void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_EQUALIZER_LOW_GAIN:
+ *val = props->Equalizer.LowGain;
+ break;
+
+ case AL_EQUALIZER_LOW_CUTOFF:
+ *val = props->Equalizer.LowCutoff;
+ break;
+
+ case AL_EQUALIZER_MID1_GAIN:
+ *val = props->Equalizer.Mid1Gain;
+ break;
+
+ case AL_EQUALIZER_MID1_CENTER:
+ *val = props->Equalizer.Mid1Center;
+ break;
+
+ case AL_EQUALIZER_MID1_WIDTH:
+ *val = props->Equalizer.Mid1Width;
+ break;
+
+ case AL_EQUALIZER_MID2_GAIN:
+ *val = props->Equalizer.Mid2Gain;
+ break;
+
+ case AL_EQUALIZER_MID2_CENTER:
+ *val = props->Equalizer.Mid2Center;
+ break;
+
+ case AL_EQUALIZER_MID2_WIDTH:
+ *val = props->Equalizer.Mid2Width;
+ break;
+
+ case AL_EQUALIZER_HIGH_GAIN:
+ *val = props->Equalizer.HighGain;
+ break;
+
+ case AL_EQUALIZER_HIGH_CUTOFF:
+ *val = props->Equalizer.HighCutoff;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ALequalizer_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALequalizer);
diff --git a/Alc/effects/flanger.c b/Alc/effects/flanger.c
new file mode 100644
index 00000000..a94cd365
--- /dev/null
+++ b/Alc/effects/flanger.c
@@ -0,0 +1,380 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Mike Gorchak
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+typedef struct ALflangerState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ ALfloat *SampleBuffer[2];
+ ALuint BufferLength;
+ ALuint offset;
+ ALuint lfo_range;
+ ALfloat lfo_scale;
+ ALint lfo_disp;
+
+ /* Gains for left and right sides */
+ ALfloat Gain[2][MaxChannels];
+
+ /* effect parameters */
+ ALint waveform;
+ ALint delay;
+ ALfloat depth;
+ ALfloat feedback;
+} ALflangerState;
+
+static ALvoid ALflangerState_Destruct(ALflangerState *state)
+{
+ free(state->SampleBuffer[0]);
+ state->SampleBuffer[0] = NULL;
+ state->SampleBuffer[1] = NULL;
+}
+
+static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device)
+{
+ ALuint maxlen;
+ ALuint it;
+
+ maxlen = fastf2u(AL_FLANGER_MAX_DELAY * 3.0f * Device->Frequency) + 1;
+ maxlen = NextPowerOf2(maxlen);
+
+ if(maxlen != state->BufferLength)
+ {
+ void *temp;
+
+ temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
+ if(!temp) return AL_FALSE;
+ state->SampleBuffer[0] = temp;
+ state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
+
+ state->BufferLength = maxlen;
+ }
+
+ for(it = 0;it < state->BufferLength;it++)
+ {
+ state->SampleBuffer[0][it] = 0.0f;
+ state->SampleBuffer[1][it] = 0.0f;
+ }
+
+ return AL_TRUE;
+}
+
+static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, const ALeffectslot *Slot)
+{
+ ALfloat frequency = (ALfloat)Device->Frequency;
+ ALfloat rate;
+ ALint phase;
+
+ state->waveform = Slot->EffectProps.Flanger.Waveform;
+ state->depth = Slot->EffectProps.Flanger.Depth;
+ state->feedback = Slot->EffectProps.Flanger.Feedback;
+ state->delay = fastf2i(Slot->EffectProps.Flanger.Delay * frequency);
+
+ /* Gains for left and right sides */
+ ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
+ ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
+
+ phase = Slot->EffectProps.Flanger.Phase;
+ rate = Slot->EffectProps.Flanger.Rate;
+ if(!(rate > 0.0f))
+ {
+ state->lfo_scale = 0.0f;
+ state->lfo_range = 1;
+ state->lfo_disp = 0;
+ }
+ else
+ {
+ /* Calculate LFO coefficient */
+ state->lfo_range = fastf2u(frequency/rate + 0.5f);
+ switch(state->waveform)
+ {
+ case AL_FLANGER_WAVEFORM_TRIANGLE:
+ state->lfo_scale = 4.0f / state->lfo_range;
+ break;
+ case AL_FLANGER_WAVEFORM_SINUSOID:
+ state->lfo_scale = F_2PI / state->lfo_range;
+ break;
+ }
+
+ /* Calculate lfo phase displacement */
+ state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
+ }
+}
+
+static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
+{
+ ALfloat lfo_value;
+
+ lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
+ lfo_value *= state->depth * state->delay;
+ *delay_left = fastf2i(lfo_value) + state->delay;
+
+ offset += state->lfo_disp;
+ lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
+ lfo_value *= state->depth * state->delay;
+ *delay_right = fastf2i(lfo_value) + state->delay;
+}
+
+static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
+{
+ ALfloat lfo_value;
+
+ lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
+ lfo_value *= state->depth * state->delay;
+ *delay_left = fastf2i(lfo_value) + state->delay;
+
+ offset += state->lfo_disp;
+ lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
+ lfo_value *= state->depth * state->delay;
+ *delay_right = fastf2i(lfo_value) + state->delay;
+}
+
+#define DECL_TEMPLATE(Func) \
+static void Process##Func(ALflangerState *state, const ALuint SamplesToDo, \
+ const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
+{ \
+ const ALuint bufmask = state->BufferLength-1; \
+ ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
+ ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
+ ALuint offset = state->offset; \
+ const ALfloat feedback = state->feedback; \
+ ALuint it; \
+ \
+ for(it = 0;it < SamplesToDo;it++) \
+ { \
+ ALint delay_left, delay_right; \
+ Func(&delay_left, &delay_right, offset, state); \
+ \
+ out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
+ leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
+ \
+ out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
+ rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
+ \
+ offset++; \
+ } \
+ state->offset = offset; \
+}
+
+DECL_TEMPLATE(Triangle)
+DECL_TEMPLATE(Sinusoid)
+
+#undef DECL_TEMPLATE
+
+static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ ALuint it, kt;
+ ALuint base;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALfloat temps[64][2];
+ ALuint td = minu(SamplesToDo-base, 64);
+
+ if(state->waveform == AL_FLANGER_WAVEFORM_TRIANGLE)
+ ProcessTriangle(state, td, SamplesIn+base, temps);
+ else if(state->waveform == AL_FLANGER_WAVEFORM_SINUSOID)
+ ProcessSinusoid(state, td, SamplesIn+base, temps);
+
+ for(kt = 0;kt < MaxChannels;kt++)
+ {
+ ALfloat gain = state->Gain[0][kt];
+ if(gain > GAIN_SILENCE_THRESHOLD)
+ {
+ for(it = 0;it < td;it++)
+ SamplesOut[kt][it+base] += temps[it][0] * gain;
+ }
+
+ gain = state->Gain[1][kt];
+ if(gain > GAIN_SILENCE_THRESHOLD)
+ {
+ for(it = 0;it < td;it++)
+ SamplesOut[kt][it+base] += temps[it][1] * gain;
+ }
+ }
+
+ base += td;
+ }
+}
+
+static void ALflangerState_Delete(ALflangerState *state)
+{
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
+
+
+typedef struct ALflangerStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALflangerStateFactory;
+
+ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factory))
+{
+ ALflangerState *state;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALflangerState, ALeffectState, state);
+
+ state->BufferLength = 0;
+ state->SampleBuffer[0] = NULL;
+ state->SampleBuffer[1] = NULL;
+ state->offset = 0;
+ state->lfo_range = 1;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALflangerStateFactory);
+
+ALeffectStateFactory *ALflangerStateFactory_getFactory(void)
+{
+ static ALflangerStateFactory FlangerFactory = { { GET_VTABLE2(ALflangerStateFactory, ALeffectStateFactory) } };
+
+ return STATIC_CAST(ALeffectStateFactory, &FlangerFactory);
+}
+
+
+void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_FLANGER_WAVEFORM:
+ if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Flanger.Waveform = val;
+ break;
+
+ case AL_FLANGER_PHASE:
+ if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Flanger.Phase = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALflanger_setParami(effect, context, param, vals[0]);
+}
+void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_FLANGER_RATE:
+ if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Flanger.Rate = val;
+ break;
+
+ case AL_FLANGER_DEPTH:
+ if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Flanger.Depth = val;
+ break;
+
+ case AL_FLANGER_FEEDBACK:
+ if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Flanger.Feedback = val;
+ break;
+
+ case AL_FLANGER_DELAY:
+ if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Flanger.Delay = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALflanger_setParamf(effect, context, param, vals[0]);
+}
+
+void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_FLANGER_WAVEFORM:
+ *val = props->Flanger.Waveform;
+ break;
+
+ case AL_FLANGER_PHASE:
+ *val = props->Flanger.Phase;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALflanger_getParami(effect, context, param, vals);
+}
+void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_FLANGER_RATE:
+ *val = props->Flanger.Rate;
+ break;
+
+ case AL_FLANGER_DEPTH:
+ *val = props->Flanger.Depth;
+ break;
+
+ case AL_FLANGER_FEEDBACK:
+ *val = props->Flanger.Feedback;
+ break;
+
+ case AL_FLANGER_DELAY:
+ *val = props->Flanger.Delay;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ALflanger_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALflanger);
diff --git a/Alc/effects/modulator.c b/Alc/effects/modulator.c
new file mode 100644
index 00000000..17ca7e17
--- /dev/null
+++ b/Alc/effects/modulator.c
@@ -0,0 +1,306 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2009 by Chris Robinson.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+typedef struct ALmodulatorState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ enum {
+ SINUSOID,
+ SAWTOOTH,
+ SQUARE
+ } Waveform;
+
+ ALuint index;
+ ALuint step;
+
+ ALfloat Gain[MaxChannels];
+
+ ALfilterState Filter;
+} ALmodulatorState;
+
+#define WAVEFORM_FRACBITS 24
+#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
+#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
+
+static inline ALfloat Sin(ALuint index)
+{
+ return sinf(index*(F_2PI/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
+}
+
+static inline ALfloat Saw(ALuint index)
+{
+ return (ALfloat)index / WAVEFORM_FRACONE;
+}
+
+static inline ALfloat Square(ALuint index)
+{
+ return (ALfloat)((index >> (WAVEFORM_FRACBITS - 1)) & 1);
+}
+
+#define DECL_TEMPLATE(func) \
+static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \
+ const ALfloat *restrict SamplesIn, \
+ ALfloat (*restrict SamplesOut)[BUFFERSIZE]) \
+{ \
+ const ALuint step = state->step; \
+ ALuint index = state->index; \
+ ALuint base; \
+ \
+ for(base = 0;base < SamplesToDo;) \
+ { \
+ ALfloat temps[64]; \
+ ALuint td = minu(SamplesToDo-base, 64); \
+ ALuint i, k; \
+ \
+ for(i = 0;i < td;i++) \
+ { \
+ ALfloat samp; \
+ samp = SamplesIn[base+i]; \
+ samp = ALfilterState_processSingle(&state->Filter, samp); \
+ \
+ index += step; \
+ index &= WAVEFORM_FRACMASK; \
+ temps[i] = samp * func(index); \
+ } \
+ \
+ for(k = 0;k < MaxChannels;k++) \
+ { \
+ ALfloat gain = state->Gain[k]; \
+ if(!(gain > GAIN_SILENCE_THRESHOLD)) \
+ continue; \
+ \
+ for(i = 0;i < td;i++) \
+ SamplesOut[k][base+i] += gain * temps[i]; \
+ } \
+ \
+ base += td; \
+ } \
+ state->index = index; \
+}
+
+DECL_TEMPLATE(Sin)
+DECL_TEMPLATE(Saw)
+DECL_TEMPLATE(Square)
+
+#undef DECL_TEMPLATE
+
+
+static ALvoid ALmodulatorState_Destruct(ALmodulatorState *UNUSED(state))
+{
+}
+
+static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device))
+{
+ return AL_TRUE;
+}
+
+static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device, const ALeffectslot *Slot)
+{
+ ALfloat gain, cw, a;
+
+ if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
+ state->Waveform = SINUSOID;
+ else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
+ state->Waveform = SAWTOOTH;
+ else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)
+ state->Waveform = SQUARE;
+
+ state->step = fastf2u(Slot->EffectProps.Modulator.Frequency*WAVEFORM_FRACONE /
+ Device->Frequency);
+ if(state->step == 0) state->step = 1;
+
+ /* Custom filter coeffs, which match the old version instead of a low-shelf. */
+ cw = cosf(F_2PI * Slot->EffectProps.Modulator.HighPassCutoff / Device->Frequency);
+ a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
+
+ state->Filter.b[0] = a;
+ state->Filter.b[1] = -a;
+ state->Filter.b[2] = 0.0f;
+ state->Filter.a[0] = 1.0f;
+ state->Filter.a[1] = -a;
+ state->Filter.a[2] = 0.0f;
+
+ gain = sqrtf(1.0f/Device->NumChan) * Slot->Gain;
+ SetGains(Device, gain, state->Gain);
+}
+
+static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ switch(state->Waveform)
+ {
+ case SINUSOID:
+ ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut);
+ break;
+
+ case SAWTOOTH:
+ ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut);
+ break;
+
+ case SQUARE:
+ ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut);
+ break;
+ }
+}
+
+static void ALmodulatorState_Delete(ALmodulatorState *state)
+{
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
+
+
+typedef struct ALmodulatorStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALmodulatorStateFactory;
+
+static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UNUSED(factory))
+{
+ ALmodulatorState *state;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALmodulatorState, ALeffectState, state);
+
+ state->index = 0;
+ state->step = 1;
+
+ ALfilterState_clear(&state->Filter);
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALmodulatorStateFactory);
+
+ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void)
+{
+ static ALmodulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ALmodulatorStateFactory, ALeffectStateFactory) } };
+
+ return STATIC_CAST(ALeffectStateFactory, &ModulatorFactory);
+}
+
+
+void ALmodulator_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_RING_MODULATOR_FREQUENCY:
+ if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Modulator.Frequency = val;
+ break;
+
+ case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+ if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Modulator.HighPassCutoff = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALmodulator_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALmodulator_setParamf(effect, context, param, vals[0]);
+}
+void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_RING_MODULATOR_FREQUENCY:
+ case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+ ALmodulator_setParamf(effect, context, param, (ALfloat)val);
+ break;
+
+ case AL_RING_MODULATOR_WAVEFORM:
+ if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Modulator.Waveform = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALmodulator_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALmodulator_setParami(effect, context, param, vals[0]);
+}
+
+void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_RING_MODULATOR_FREQUENCY:
+ *val = (ALint)props->Modulator.Frequency;
+ break;
+ case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+ *val = (ALint)props->Modulator.HighPassCutoff;
+ break;
+ case AL_RING_MODULATOR_WAVEFORM:
+ *val = props->Modulator.Waveform;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALmodulator_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALmodulator_getParami(effect, context, param, vals);
+}
+void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_RING_MODULATOR_FREQUENCY:
+ *val = props->Modulator.Frequency;
+ break;
+ case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+ *val = props->Modulator.HighPassCutoff;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALmodulator_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ALmodulator_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALmodulator);
diff --git a/Alc/effects/null.c b/Alc/effects/null.c
new file mode 100644
index 00000000..816a2525
--- /dev/null
+++ b/Alc/effects/null.c
@@ -0,0 +1,155 @@
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+
+
+typedef struct ALnullState {
+ DERIVE_FROM_TYPE(ALeffectState);
+} ALnullState;
+
+
+/* This destructs (not free!) the effect state. It's called only when the
+ * effect slot is no longer used.
+ */
+static ALvoid ALnullState_Destruct(ALnullState* UNUSED(state))
+{
+}
+
+/* This updates the device-dependant effect state. This is called on
+ * initialization and any time the device parameters (eg. playback frequency,
+ * format) have been changed.
+ */
+static ALboolean ALnullState_deviceUpdate(ALnullState* UNUSED(state), ALCdevice* UNUSED(device))
+{
+ return AL_TRUE;
+}
+
+/* This updates the effect state. This is called any time the effect is
+ * (re)loaded into a slot.
+ */
+static ALvoid ALnullState_update(ALnullState* UNUSED(state), ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot))
+{
+}
+
+/* This processes the effect state, for the given number of samples from the
+ * input to the output buffer. The result should be added to the output buffer,
+ * not replace it.
+ */
+static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloat *restrict UNUSED(samplesIn), ALfloat (*restrict samplesOut)[BUFFERSIZE])
+{
+ /* NOTE: Couldn't use the UNUSED macro on samplesOut due to the way GCC's
+ * __attribute__ declaration interacts with the parenthesis. */
+ (void)samplesOut;
+}
+
+/* This frees the memory used by the object, after it has been destructed. */
+static void ALnullState_Delete(ALnullState *state)
+{
+ free(state);
+}
+
+/* Define the forwards and the ALeffectState vtable for this type. */
+DEFINE_ALEFFECTSTATE_VTABLE(ALnullState);
+
+
+typedef struct ALnullStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALnullStateFactory;
+
+/* Creates ALeffectState objects of the appropriate type. */
+ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory))
+{
+ ALnullState *state;
+
+ state = calloc(1, sizeof(*state));
+ if(!state) return NULL;
+ /* Set vtables for inherited types. */
+ SET_VTABLE2(ALnullState, ALeffectState, state);
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+/* Define the ALeffectStateFactory vtable for this type. */
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory);
+
+ALeffectStateFactory *ALnullStateFactory_getFactory(void)
+{
+ static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } };
+
+ return STATIC_CAST(ALeffectStateFactory, &NullFactory);
+}
+
+
+void ALnull_setParami(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{
+ switch(param)
+ {
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALnull_setParamiv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals))
+{
+ switch(param)
+ {
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALnull_setParamf(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
+{
+ switch(param)
+ {
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALnull_setParamfv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals))
+{
+ switch(param)
+ {
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+
+void ALnull_getParami(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val))
+{
+ switch(param)
+ {
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALnull_getParamiv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals))
+{
+ switch(param)
+ {
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALnull_getParamf(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val))
+{
+ switch(param)
+ {
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALnull_getParamfv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals))
+{
+ switch(param)
+ {
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+
+DEFINE_ALEFFECT_VTABLE(ALnull);
diff --git a/Alc/alcReverb.c b/Alc/effects/reverb.c
index b7dcdab9..75ec76e3 100644
--- a/Alc/alcReverb.c
+++ b/Alc/effects/reverb.c
@@ -31,6 +31,7 @@
#include "alFilter.h"
#include "alError.h"
+
typedef struct DelayLine
{
// The delay lines use sample lengths that are powers of 2 to allow the
@@ -39,18 +40,19 @@ typedef struct DelayLine
ALfloat *Line;
} DelayLine;
-typedef struct ALverbState {
- // Must be first in all effects!
- ALeffectState state;
+typedef struct ALreverbState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ ALboolean IsEax;
// All delay lines are allocated as a single buffer to reduce memory
// fragmentation and management code.
ALfloat *SampleBuffer;
ALuint TotalSamples;
- // Master effect low-pass filter (2 chained 1-pole filters).
- FILTER LpFilter;
- ALfloat LpHistory[2];
+ // Master effect filters
+ ALfilterState LpFilter;
+ ALfilterState HpFilter; // EAX only
struct {
// Modulator delay line.
@@ -160,7 +162,7 @@ typedef struct ALverbState {
/* Temporary storage used when processing, before deinterlacing. */
ALfloat ReverbSamples[BUFFERSIZE][4];
ALfloat EarlySamples[BUFFERSIZE][4];
-} ALverbState;
+} ALreverbState;
/* This is a user config option for modifying the overall output of the reverb
* effect.
@@ -222,24 +224,24 @@ static const ALfloat LATE_LINE_MULTIPLIER = 4.0f;
// Basic delay line input/output routines.
-static __inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset)
+static inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset)
{
return Delay->Line[offset&Delay->Mask];
}
-static __inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in)
+static inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in)
{
Delay->Line[offset&Delay->Mask] = in;
}
// Attenuated delay line output routine.
-static __inline ALfloat AttenuatedDelayLineOut(DelayLine *Delay, ALuint offset, ALfloat coeff)
+static inline ALfloat AttenuatedDelayLineOut(DelayLine *Delay, ALuint offset, ALfloat coeff)
{
return coeff * Delay->Line[offset&Delay->Mask];
}
// Basic attenuated all-pass input/output routine.
-static __inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff)
+static inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff)
{
ALfloat out, feed;
@@ -255,7 +257,7 @@ static __inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint
// Given an input sample, this function produces modulation for the late
// reverb.
-static __inline ALfloat EAXModulation(ALverbState *State, ALfloat in)
+static inline ALfloat EAXModulation(ALreverbState *State, ALfloat in)
{
ALfloat sinus, frac;
ALuint offset;
@@ -264,7 +266,7 @@ static __inline ALfloat EAXModulation(ALverbState *State, ALfloat in)
// Calculate the sinus rythm (dependent on modulation time and the
// sampling rate). The center of the sinus is moved to reduce the delay
// of the effect when the time or depth are low.
- sinus = 1.0f - cosf(F_PI*2.0f * State->Mod.Index / State->Mod.Range);
+ sinus = 1.0f - cosf(F_2PI * State->Mod.Index / State->Mod.Range);
// The depth determines the range over which to read the input samples
// from, so it must be filtered to reduce the distortion caused by even
@@ -292,7 +294,7 @@ static __inline ALfloat EAXModulation(ALverbState *State, ALfloat in)
}
// Delay line output routine for early reflections.
-static __inline ALfloat EarlyDelayLineOut(ALverbState *State, ALuint index)
+static inline ALfloat EarlyDelayLineOut(ALreverbState *State, ALuint index)
{
return AttenuatedDelayLineOut(&State->Early.Delay[index],
State->Offset - State->Early.Offset[index],
@@ -301,7 +303,7 @@ static __inline ALfloat EarlyDelayLineOut(ALverbState *State, ALuint index)
// Given an input sample, this function produces four-channel output for the
// early reflections.
-static __inline ALvoid EarlyReflection(ALverbState *State, ALfloat in, ALfloat *RESTRICT out)
+static inline ALvoid EarlyReflection(ALreverbState *State, ALfloat in, ALfloat *restrict out)
{
ALfloat d[4], v, f[4];
@@ -346,7 +348,7 @@ static __inline ALvoid EarlyReflection(ALverbState *State, ALfloat in, ALfloat *
}
// All-pass input/output routine for late reverb.
-static __inline ALfloat LateAllPassInOut(ALverbState *State, ALuint index, ALfloat in)
+static inline ALfloat LateAllPassInOut(ALreverbState *State, ALuint index, ALfloat in)
{
return AllpassInOut(&State->Late.ApDelay[index],
State->Offset - State->Late.ApOffset[index],
@@ -355,7 +357,7 @@ static __inline ALfloat LateAllPassInOut(ALverbState *State, ALuint index, ALflo
}
// Delay line output routine for late reverb.
-static __inline ALfloat LateDelayLineOut(ALverbState *State, ALuint index)
+static inline ALfloat LateDelayLineOut(ALreverbState *State, ALuint index)
{
return AttenuatedDelayLineOut(&State->Late.Delay[index],
State->Offset - State->Late.Offset[index],
@@ -363,7 +365,7 @@ 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)
+static inline ALfloat LateLowPassInOut(ALreverbState *State, ALuint index, ALfloat in)
{
in = lerp(in, State->Late.LpSample[index], State->Late.LpCoeff[index]);
State->Late.LpSample[index] = in;
@@ -372,7 +374,7 @@ static __inline ALfloat LateLowPassInOut(ALverbState *State, ALuint index, ALflo
// Given four decorrelated input samples, this function produces four-channel
// output for the late reverb.
-static __inline ALvoid LateReverb(ALverbState *State, const ALfloat *RESTRICT in, ALfloat *RESTRICT out)
+static inline ALvoid LateReverb(ALreverbState *State, const ALfloat *restrict in, ALfloat *restrict out)
{
ALfloat d[4], f[4];
@@ -443,7 +445,7 @@ static __inline ALvoid LateReverb(ALverbState *State, const ALfloat *RESTRICT in
// Given an input sample, this function mixes echo into the four-channel late
// reverb.
-static __inline ALvoid EAXEcho(ALverbState *State, ALfloat in, ALfloat *RESTRICT late)
+static inline ALvoid EAXEcho(ALreverbState *State, ALfloat in, ALfloat *restrict late)
{
ALfloat out, feed;
@@ -477,12 +479,12 @@ static __inline ALvoid EAXEcho(ALverbState *State, ALfloat in, ALfloat *RESTRICT
// Perform the non-EAX reverb pass on a given input sample, resulting in
// four-channel output.
-static __inline ALvoid VerbPass(ALverbState *State, ALfloat in, ALfloat *RESTRICT out)
+static inline ALvoid VerbPass(ALreverbState *State, ALfloat in, ALfloat *restrict out)
{
ALfloat feed, late[4], taps[4];
- // Low-pass filter the incoming sample.
- in = lpFilter2P(&State->LpFilter, 0, in);
+ // Filter the incoming sample.
+ in = ALfilterState_processSingle(&State->LpFilter, in);
// Feed the initial delay line.
DelayLineIn(&State->Delay, State->Offset, in);
@@ -516,12 +518,13 @@ static __inline ALvoid VerbPass(ALverbState *State, ALfloat in, ALfloat *RESTRIC
// Perform the EAX reverb pass on a given input sample, resulting in four-
// channel output.
-static __inline ALvoid EAXVerbPass(ALverbState *State, ALfloat in, ALfloat *RESTRICT early, ALfloat *RESTRICT late)
+static inline ALvoid EAXVerbPass(ALreverbState *State, ALfloat in, ALfloat *restrict early, ALfloat *restrict late)
{
ALfloat feed, taps[4];
// Low-pass filter the incoming sample.
- in = lpFilter2P(&State->LpFilter, 0, in);
+ in = ALfilterState_processSingle(&State->LpFilter, in);
+ in = ALfilterState_processSingle(&State->HpFilter, in);
// Perform any modulation on the input.
in = EAXModulation(State, in);
@@ -553,12 +556,9 @@ static __inline ALvoid EAXVerbPass(ALverbState *State, ALfloat in, ALfloat *REST
State->Offset++;
}
-// This processes the reverb state, given the input samples and an output
-// buffer.
-static ALvoid VerbProcess(ALeffectState *effect, ALuint SamplesToDo, const ALfloat *RESTRICT SamplesIn, ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE])
+static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
- ALverbState *State = (ALverbState*)effect;
- ALfloat (*RESTRICT out)[4] = State->ReverbSamples;
+ ALfloat (*restrict out)[4] = State->ReverbSamples;
ALuint index, c;
/* Process reverb for these samples. */
@@ -568,21 +568,18 @@ static ALvoid VerbProcess(ALeffectState *effect, ALuint SamplesToDo, const ALflo
for(c = 0;c < MaxChannels;c++)
{
ALfloat gain = State->Gain[c];
- if(gain > 0.00001f)
- {
- for(index = 0;index < SamplesToDo;index++)
- SamplesOut[c][index] += gain * out[index][c&3];
- }
+ if(!(gain > GAIN_SILENCE_THRESHOLD))
+ continue;
+
+ for(index = 0;index < SamplesToDo;index++)
+ SamplesOut[c][index] += gain * out[index][c&3];
}
}
-// This processes the EAX reverb state, given the input samples and an output
-// buffer.
-static ALvoid EAXVerbProcess(ALeffectState *effect, ALuint SamplesToDo, const ALfloat *RESTRICT SamplesIn, ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE])
+static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
- ALverbState *State = (ALverbState*)effect;
- ALfloat (*RESTRICT early)[4] = State->EarlySamples;
- ALfloat (*RESTRICT late)[4] = State->ReverbSamples;
+ ALfloat (*restrict early)[4] = State->EarlySamples;
+ ALfloat (*restrict late)[4] = State->ReverbSamples;
ALuint index, c;
/* Process reverb for these samples. */
@@ -591,15 +588,16 @@ static ALvoid EAXVerbProcess(ALeffectState *effect, ALuint SamplesToDo, const AL
for(c = 0;c < MaxChannels;c++)
{
- ALfloat earlyGain = State->Early.PanGain[c];
- ALfloat lateGain = State->Late.PanGain[c];
+ ALfloat earlyGain, lateGain;
- if(earlyGain > 0.00001f)
+ earlyGain = State->Early.PanGain[c];
+ if(earlyGain > GAIN_SILENCE_THRESHOLD)
{
for(index = 0;index < SamplesToDo;index++)
SamplesOut[c][index] += earlyGain*early[index][c&3];
}
- if(lateGain > 0.00001f)
+ lateGain = State->Late.PanGain[c];
+ if(lateGain > GAIN_SILENCE_THRESHOLD)
{
for(index = 0;index < SamplesToDo;index++)
SamplesOut[c][index] += lateGain*late[index][c&3];
@@ -607,10 +605,17 @@ static ALvoid EAXVerbProcess(ALeffectState *effect, ALuint SamplesToDo, const AL
}
}
+static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ if(State->IsEax)
+ ALreverbState_processEax(State, SamplesToDo, SamplesIn, SamplesOut);
+ else
+ ALreverbState_processStandard(State, SamplesToDo, SamplesIn, SamplesOut);
+}
// Given the allocated sample buffer, this function updates each delay line
// offset.
-static __inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLine *Delay)
+static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLine *Delay)
{
Delay->Line = &sampleBuffer[(ALintptrEXT)Delay->Line];
}
@@ -634,7 +639,7 @@ static ALuint CalcLineLength(ALfloat length, ALintptrEXT offset, ALuint frequenc
* for all lines given the sample rate (frequency). If an allocation failure
* occurs, it returns AL_FALSE.
*/
-static ALboolean AllocLines(ALuint frequency, ALverbState *State)
+static ALboolean AllocLines(ALuint frequency, ALreverbState *State)
{
ALuint totalSamples, index;
ALfloat length;
@@ -722,12 +727,8 @@ static ALboolean AllocLines(ALuint frequency, ALverbState *State)
return AL_TRUE;
}
-// This updates the device-dependant EAX reverb state. This is called on
-// initialization and any time the device parameters (eg. playback frequency,
-// format) have been changed.
-static ALboolean ReverbDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
+static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device)
{
- ALverbState *State = (ALverbState*)effect;
ALuint frequency = Device->Frequency, index;
// Allocate the delay lines.
@@ -760,28 +761,21 @@ static ALboolean ReverbDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
// Calculate a decay coefficient given the length of each cycle and the time
// until the decay reaches -60 dB.
-static __inline ALfloat CalcDecayCoeff(ALfloat length, ALfloat decayTime)
+static inline ALfloat CalcDecayCoeff(ALfloat length, ALfloat decayTime)
{
return powf(0.001f/*-60 dB*/, length/decayTime);
}
// Calculate a decay length from a coefficient and the time until the decay
// reaches -60 dB.
-static __inline ALfloat CalcDecayLength(ALfloat coeff, ALfloat decayTime)
+static inline ALfloat CalcDecayLength(ALfloat coeff, ALfloat decayTime)
{
return log10f(coeff) * decayTime / log10f(0.001f)/*-60 dB*/;
}
-// Calculate the high frequency parameter for the I3DL2 coefficient
-// calculation.
-static __inline ALfloat CalcI3DL2HFreq(ALfloat hfRef, ALuint frequency)
-{
- return cosf(F_PI*2.0f * hfRef / frequency);
-}
-
// Calculate an attenuation to be applied to the input of any echo models to
// compensate for modal density and decay time.
-static __inline ALfloat CalcDensityGain(ALfloat a)
+static inline ALfloat CalcDensityGain(ALfloat a)
{
/* The energy of a signal can be obtained by finding the area under the
* squared signal. This takes the form of Sum(x_n^2), where x is the
@@ -800,7 +794,7 @@ static __inline ALfloat CalcDensityGain(ALfloat a)
}
// Calculate the mixing matrix coefficients given a diffusion factor.
-static __inline ALvoid CalcMatrixCoeffs(ALfloat diffusion, ALfloat *x, ALfloat *y)
+static inline ALvoid CalcMatrixCoeffs(ALfloat diffusion, ALfloat *x, ALfloat *y)
{
ALfloat n, t;
@@ -835,7 +829,7 @@ static ALfloat CalcLimitedHfRatio(ALfloat hfRatio, ALfloat airAbsorptionGainHF,
// Calculate the coefficient for a HF (and eventually LF) decay damping
// filter.
-static __inline ALfloat CalcDampingCoeff(ALfloat hfRatio, ALfloat length, ALfloat decayTime, ALfloat decayCoeff, ALfloat cw)
+static inline ALfloat CalcDampingCoeff(ALfloat hfRatio, ALfloat length, ALfloat decayTime, ALfloat decayCoeff, ALfloat cw)
{
ALfloat coeff, g;
@@ -850,7 +844,14 @@ static __inline ALfloat CalcDampingCoeff(ALfloat hfRatio, ALfloat length, ALfloa
// Damping is done with a 1-pole filter, so g needs to be squared.
g *= g;
- coeff = lpCoeffCalc(g, cw);
+ if(g < 0.9999f) /* 1-epsilon */
+ {
+ /* Be careful with gains < 0.001, as that causes the coefficient
+ * head towards 1, which will flatten the signal. */
+ g = maxf(g, 0.001f);
+ coeff = (1 - g*cw - sqrtf(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.
@@ -862,7 +863,7 @@ static __inline ALfloat CalcDampingCoeff(ALfloat hfRatio, ALfloat length, ALfloa
// Update the EAX modulation index, range, and depth. Keep in mind that this
// kind of vibrato is additive and not multiplicative as one may expect. The
// downswing will sound stronger than the upswing.
-static ALvoid UpdateModulator(ALfloat modTime, ALfloat modDepth, ALuint frequency, ALverbState *State)
+static ALvoid UpdateModulator(ALfloat modTime, ALfloat modDepth, ALuint frequency, ALreverbState *State)
{
ALuint range;
@@ -892,7 +893,7 @@ static ALvoid UpdateModulator(ALfloat modTime, ALfloat modDepth, ALuint frequenc
}
// Update the offsets for the initial effect delay line.
-static ALvoid UpdateDelayLine(ALfloat earlyDelay, ALfloat lateDelay, ALuint frequency, ALverbState *State)
+static ALvoid UpdateDelayLine(ALfloat earlyDelay, ALfloat lateDelay, ALuint frequency, ALreverbState *State)
{
// Calculate the initial delay taps.
State->DelayTap[0] = fastf2u(earlyDelay * frequency);
@@ -900,7 +901,7 @@ static ALvoid UpdateDelayLine(ALfloat earlyDelay, ALfloat lateDelay, ALuint freq
}
// Update the early reflections gain and line coefficients.
-static ALvoid UpdateEarlyLines(ALfloat reverbGain, ALfloat earlyGain, ALfloat lateDelay, ALverbState *State)
+static ALvoid UpdateEarlyLines(ALfloat reverbGain, ALfloat earlyGain, ALfloat lateDelay, ALreverbState *State)
{
ALuint index;
@@ -917,7 +918,7 @@ static ALvoid UpdateEarlyLines(ALfloat reverbGain, ALfloat earlyGain, ALfloat la
}
// Update the offsets for the decorrelator line.
-static ALvoid UpdateDecorrelator(ALfloat density, ALuint frequency, ALverbState *State)
+static ALvoid UpdateDecorrelator(ALfloat density, ALuint frequency, ALreverbState *State)
{
ALuint index;
ALfloat length;
@@ -938,7 +939,7 @@ static ALvoid UpdateDecorrelator(ALfloat density, ALuint frequency, ALverbState
}
// Update the late reverb gains, line lengths, and line coefficients.
-static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix, ALfloat density, ALfloat decayTime, ALfloat diffusion, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALverbState *State)
+static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix, ALfloat density, ALfloat decayTime, ALfloat diffusion, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State)
{
ALfloat length;
ALuint index;
@@ -996,7 +997,7 @@ static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix
// Update the echo gain, line offset, line coefficients, and mixing
// coefficients.
-static ALvoid UpdateEchoLine(ALfloat reverbGain, ALfloat lateGain, ALfloat echoTime, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALverbState *State)
+static ALvoid UpdateEchoLine(ALfloat reverbGain, ALfloat lateGain, ALfloat echoTime, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State)
{
// Update the offset and coefficient for the echo delay line.
State->Echo.Offset = fastf2u(echoTime * frequency);
@@ -1028,7 +1029,7 @@ static ALvoid UpdateEchoLine(ALfloat reverbGain, ALfloat lateGain, ALfloat echoT
}
// Update the early and late 3D panning gains.
-static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALverbState *State)
+static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALreverbState *State)
{
ALfloat earlyPan[3] = { ReflectionsPan[0], ReflectionsPan[1],
ReflectionsPan[2] };
@@ -1037,7 +1038,6 @@ static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *Reflection
ALfloat ambientGain;
ALfloat dirGain;
ALfloat length;
- ALuint index;
Gain *= ReverbBoost;
@@ -1063,221 +1063,720 @@ static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *Reflection
}
dirGain = sqrtf(earlyPan[0]*earlyPan[0] + earlyPan[2]*earlyPan[2]);
- for(index = 0;index < MaxChannels;index++)
- State->Early.PanGain[index] = 0.0f;
ComputeAngleGains(Device, atan2f(earlyPan[0], earlyPan[2]), (1.0f-dirGain)*F_PI,
lerp(ambientGain, 1.0f, dirGain) * Gain, State->Early.PanGain);
dirGain = sqrtf(latePan[0]*latePan[0] + latePan[2]*latePan[2]);
- for(index = 0;index < MaxChannels;index++)
- State->Late.PanGain[index] = 0.0f;
ComputeAngleGains(Device, atan2f(latePan[0], latePan[2]), (1.0f-dirGain)*F_PI,
lerp(ambientGain, 1.0f, dirGain) * Gain, State->Late.PanGain);
}
-// This updates the EAX reverb state. This is called any time the EAX reverb
-// effect is loaded into a slot.
-static ALvoid ReverbUpdate(ALeffectState *effect, ALCdevice *Device, const ALeffectslot *Slot)
+static ALvoid ALreverbState_update(ALreverbState *State, ALCdevice *Device, const ALeffectslot *Slot)
{
- ALverbState *State = (ALverbState*)effect;
ALuint frequency = Device->Frequency;
- ALboolean isEAX = AL_FALSE;
- ALfloat cw, x, y, hfRatio;
+ ALfloat lfscale, hfscale, hfRatio;
+ ALfloat cw, x, y;
+
+ if(Slot->EffectType == AL_EFFECT_EAXREVERB && !EmulateEAXReverb)
+ State->IsEax = AL_TRUE;
+ else if(Slot->EffectType == AL_EFFECT_REVERB || EmulateEAXReverb)
+ State->IsEax = AL_FALSE;
- if(Slot->effect.type == AL_EFFECT_EAXREVERB && !EmulateEAXReverb)
+ // Calculate the master low-pass filter (from the master effect HF gain).
+ if(State->IsEax)
{
- State->state.Process = EAXVerbProcess;
- isEAX = AL_TRUE;
+ hfscale = Slot->EffectProps.Reverb.HFReference / frequency;
+ ALfilterState_setParams(&State->LpFilter, ALfilterType_HighShelf,
+ Slot->EffectProps.Reverb.GainHF,
+ hfscale, 0.0f);
+ lfscale = Slot->EffectProps.Reverb.LFReference / frequency;
+ ALfilterState_setParams(&State->HpFilter, ALfilterType_LowShelf,
+ Slot->EffectProps.Reverb.GainLF,
+ lfscale, 0.0f);
}
- else if(Slot->effect.type == AL_EFFECT_REVERB || EmulateEAXReverb)
+ else
{
- State->state.Process = VerbProcess;
- isEAX = AL_FALSE;
+ hfscale = (ALfloat)LOWPASSFREQREF / frequency;
+ ALfilterState_setParams(&State->LpFilter, ALfilterType_HighShelf,
+ Slot->EffectProps.Reverb.GainHF,
+ hfscale, 0.0f);
}
- // Calculate the master low-pass filter (from the master effect HF gain).
- if(isEAX) cw = CalcI3DL2HFreq(Slot->effect.Reverb.HFReference, frequency);
- else cw = CalcI3DL2HFreq(LOWPASSFREQREF, frequency);
- // This is done with 2 chained 1-pole filters, so no need to square g.
- State->LpFilter.coeff = lpCoeffCalc(Slot->effect.Reverb.GainHF, cw);
-
- if(isEAX)
+ if(State->IsEax)
{
// Update the modulator line.
- UpdateModulator(Slot->effect.Reverb.ModulationTime,
- Slot->effect.Reverb.ModulationDepth,
+ UpdateModulator(Slot->EffectProps.Reverb.ModulationTime,
+ Slot->EffectProps.Reverb.ModulationDepth,
frequency, State);
}
// Update the initial effect delay.
- UpdateDelayLine(Slot->effect.Reverb.ReflectionsDelay,
- Slot->effect.Reverb.LateReverbDelay,
+ UpdateDelayLine(Slot->EffectProps.Reverb.ReflectionsDelay,
+ Slot->EffectProps.Reverb.LateReverbDelay,
frequency, State);
// Update the early lines.
- UpdateEarlyLines(Slot->effect.Reverb.Gain,
- Slot->effect.Reverb.ReflectionsGain,
- Slot->effect.Reverb.LateReverbDelay, State);
+ UpdateEarlyLines(Slot->EffectProps.Reverb.Gain,
+ Slot->EffectProps.Reverb.ReflectionsGain,
+ Slot->EffectProps.Reverb.LateReverbDelay, State);
// Update the decorrelator.
- UpdateDecorrelator(Slot->effect.Reverb.Density, frequency, State);
+ UpdateDecorrelator(Slot->EffectProps.Reverb.Density, frequency, State);
// Get the mixing matrix coefficients (x and y).
- CalcMatrixCoeffs(Slot->effect.Reverb.Diffusion, &x, &y);
+ CalcMatrixCoeffs(Slot->EffectProps.Reverb.Diffusion, &x, &y);
// Then divide x into y to simplify the matrix calculation.
State->Late.MixCoeff = y / x;
// If the HF limit parameter is flagged, calculate an appropriate limit
// based on the air absorption parameter.
- hfRatio = Slot->effect.Reverb.DecayHFRatio;
- if(Slot->effect.Reverb.DecayHFLimit &&
- Slot->effect.Reverb.AirAbsorptionGainHF < 1.0f)
+ hfRatio = Slot->EffectProps.Reverb.DecayHFRatio;
+ if(Slot->EffectProps.Reverb.DecayHFLimit &&
+ Slot->EffectProps.Reverb.AirAbsorptionGainHF < 1.0f)
hfRatio = CalcLimitedHfRatio(hfRatio,
- Slot->effect.Reverb.AirAbsorptionGainHF,
- Slot->effect.Reverb.DecayTime);
+ Slot->EffectProps.Reverb.AirAbsorptionGainHF,
+ Slot->EffectProps.Reverb.DecayTime);
+ cw = cosf(F_2PI * hfscale);
// Update the late lines.
- UpdateLateLines(Slot->effect.Reverb.Gain, Slot->effect.Reverb.LateReverbGain,
- x, Slot->effect.Reverb.Density, Slot->effect.Reverb.DecayTime,
- Slot->effect.Reverb.Diffusion, hfRatio, cw, frequency, State);
+ UpdateLateLines(Slot->EffectProps.Reverb.Gain, Slot->EffectProps.Reverb.LateReverbGain,
+ x, Slot->EffectProps.Reverb.Density, Slot->EffectProps.Reverb.DecayTime,
+ Slot->EffectProps.Reverb.Diffusion, hfRatio, cw, frequency, State);
- if(isEAX)
+ if(State->IsEax)
{
// Update the echo line.
- UpdateEchoLine(Slot->effect.Reverb.Gain, Slot->effect.Reverb.LateReverbGain,
- Slot->effect.Reverb.EchoTime, Slot->effect.Reverb.DecayTime,
- Slot->effect.Reverb.Diffusion, Slot->effect.Reverb.EchoDepth,
+ UpdateEchoLine(Slot->EffectProps.Reverb.Gain, Slot->EffectProps.Reverb.LateReverbGain,
+ Slot->EffectProps.Reverb.EchoTime, Slot->EffectProps.Reverb.DecayTime,
+ Slot->EffectProps.Reverb.Diffusion, Slot->EffectProps.Reverb.EchoDepth,
hfRatio, cw, frequency, State);
// Update early and late 3D panning.
- Update3DPanning(Device, Slot->effect.Reverb.ReflectionsPan,
- Slot->effect.Reverb.LateReverbPan, Slot->Gain, State);
+ Update3DPanning(Device, Slot->EffectProps.Reverb.ReflectionsPan,
+ Slot->EffectProps.Reverb.LateReverbPan, Slot->Gain, State);
}
else
{
- ALfloat gain = Slot->Gain;
- ALuint index;
-
/* Update channel gains */
- gain *= sqrtf(2.0f/Device->NumChan) * ReverbBoost;
- for(index = 0;index < MaxChannels;index++)
- State->Gain[index] = 0.0f;
- for(index = 0;index < Device->NumChan;index++)
- {
- enum Channel chan = Device->Speaker2Chan[index];
- State->Gain[chan] = gain;
- }
+ ALfloat gain = sqrtf(2.0f/Device->NumChan) * ReverbBoost * Slot->Gain;
+ SetGains(Device, gain, State->Gain);
}
}
-// This destroys the reverb state. It should be called only when the effect
-// slot has a different (or no) effect loaded over the reverb effect.
-static ALvoid ReverbDestroy(ALeffectState *effect)
+
+static ALvoid ALreverbState_Destruct(ALreverbState *State)
{
- ALverbState *State = (ALverbState*)effect;
- if(State)
- {
- free(State->SampleBuffer);
- State->SampleBuffer = NULL;
- free(State);
- }
+ free(State->SampleBuffer);
+ State->SampleBuffer = NULL;
}
-// This creates the reverb state. It should be called only when the reverb
-// effect is loaded into a slot that doesn't already have a reverb effect.
-ALeffectState *ReverbCreate(void)
+static void ALreverbState_Delete(ALreverbState *state)
{
- ALverbState *State = NULL;
- ALuint index;
+ free(state);
+}
- State = malloc(sizeof(ALverbState));
- if(!State)
- return NULL;
+DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState);
- State->state.Destroy = ReverbDestroy;
- State->state.DeviceUpdate = ReverbDeviceUpdate;
- State->state.Update = ReverbUpdate;
- State->state.Process = VerbProcess;
- State->TotalSamples = 0;
- State->SampleBuffer = NULL;
+typedef struct ALreverbStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALreverbStateFactory;
+
+static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(factory))
+{
+ ALreverbState *state;
+ ALuint index;
+
+ state = malloc(sizeof(ALreverbState));
+ if(!state) return NULL;
+ SET_VTABLE2(ALreverbState, ALeffectState, state);
- State->LpFilter.coeff = 0.0f;
- State->LpFilter.history[0] = 0.0f;
- State->LpFilter.history[1] = 0.0f;
+ state->TotalSamples = 0;
+ state->SampleBuffer = NULL;
- State->Mod.Delay.Mask = 0;
- State->Mod.Delay.Line = NULL;
- State->Mod.Index = 0;
- State->Mod.Range = 1;
- State->Mod.Depth = 0.0f;
- State->Mod.Coeff = 0.0f;
- State->Mod.Filter = 0.0f;
+ ALfilterState_clear(&state->LpFilter);
+ ALfilterState_clear(&state->HpFilter);
- State->Delay.Mask = 0;
- State->Delay.Line = NULL;
- State->DelayTap[0] = 0;
- State->DelayTap[1] = 0;
+ state->Mod.Delay.Mask = 0;
+ state->Mod.Delay.Line = NULL;
+ state->Mod.Index = 0;
+ state->Mod.Range = 1;
+ state->Mod.Depth = 0.0f;
+ state->Mod.Coeff = 0.0f;
+ state->Mod.Filter = 0.0f;
- State->Early.Gain = 0.0f;
+ state->Delay.Mask = 0;
+ state->Delay.Line = NULL;
+ state->DelayTap[0] = 0;
+ state->DelayTap[1] = 0;
+
+ state->Early.Gain = 0.0f;
for(index = 0;index < 4;index++)
{
- State->Early.Coeff[index] = 0.0f;
- State->Early.Delay[index].Mask = 0;
- State->Early.Delay[index].Line = NULL;
- State->Early.Offset[index] = 0;
+ state->Early.Coeff[index] = 0.0f;
+ state->Early.Delay[index].Mask = 0;
+ state->Early.Delay[index].Line = NULL;
+ state->Early.Offset[index] = 0;
}
- State->Decorrelator.Mask = 0;
- State->Decorrelator.Line = NULL;
- State->DecoTap[0] = 0;
- State->DecoTap[1] = 0;
- State->DecoTap[2] = 0;
+ state->Decorrelator.Mask = 0;
+ state->Decorrelator.Line = NULL;
+ state->DecoTap[0] = 0;
+ state->DecoTap[1] = 0;
+ state->DecoTap[2] = 0;
- State->Late.Gain = 0.0f;
- State->Late.DensityGain = 0.0f;
- State->Late.ApFeedCoeff = 0.0f;
- State->Late.MixCoeff = 0.0f;
+ state->Late.Gain = 0.0f;
+ state->Late.DensityGain = 0.0f;
+ state->Late.ApFeedCoeff = 0.0f;
+ state->Late.MixCoeff = 0.0f;
for(index = 0;index < 4;index++)
{
- State->Late.ApCoeff[index] = 0.0f;
- State->Late.ApDelay[index].Mask = 0;
- State->Late.ApDelay[index].Line = NULL;
- State->Late.ApOffset[index] = 0;
-
- State->Late.Coeff[index] = 0.0f;
- State->Late.Delay[index].Mask = 0;
- State->Late.Delay[index].Line = NULL;
- State->Late.Offset[index] = 0;
-
- State->Late.LpCoeff[index] = 0.0f;
- State->Late.LpSample[index] = 0.0f;
+ state->Late.ApCoeff[index] = 0.0f;
+ state->Late.ApDelay[index].Mask = 0;
+ state->Late.ApDelay[index].Line = NULL;
+ state->Late.ApOffset[index] = 0;
+
+ state->Late.Coeff[index] = 0.0f;
+ state->Late.Delay[index].Mask = 0;
+ state->Late.Delay[index].Line = NULL;
+ state->Late.Offset[index] = 0;
+
+ state->Late.LpCoeff[index] = 0.0f;
+ state->Late.LpSample[index] = 0.0f;
}
for(index = 0;index < MaxChannels;index++)
{
- State->Early.PanGain[index] = 0.0f;
- State->Late.PanGain[index] = 0.0f;
+ state->Early.PanGain[index] = 0.0f;
+ state->Late.PanGain[index] = 0.0f;
+ }
+
+ state->Echo.DensityGain = 0.0f;
+ state->Echo.Delay.Mask = 0;
+ state->Echo.Delay.Line = NULL;
+ state->Echo.ApDelay.Mask = 0;
+ state->Echo.ApDelay.Line = NULL;
+ state->Echo.Coeff = 0.0f;
+ state->Echo.ApFeedCoeff = 0.0f;
+ state->Echo.ApCoeff = 0.0f;
+ state->Echo.Offset = 0;
+ state->Echo.ApOffset = 0;
+ state->Echo.LpCoeff = 0.0f;
+ state->Echo.LpSample = 0.0f;
+ state->Echo.MixCoeff[0] = 0.0f;
+ state->Echo.MixCoeff[1] = 0.0f;
+
+ state->Offset = 0;
+
+ state->Gain = state->Late.PanGain;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALreverbStateFactory);
+
+ALeffectStateFactory *ALreverbStateFactory_getFactory(void)
+{
+ static ALreverbStateFactory ReverbFactory = { { GET_VTABLE2(ALreverbStateFactory, ALeffectStateFactory) } };
+
+ return STATIC_CAST(ALeffectStateFactory, &ReverbFactory);
+}
+
+
+void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_EAXREVERB_DECAY_HFLIMIT:
+ if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.DecayHFLimit = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALeaxreverb_setParami(effect, context, param, vals[0]);
+}
+void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_EAXREVERB_DENSITY:
+ if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.Density = val;
+ break;
+
+ case AL_EAXREVERB_DIFFUSION:
+ if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.Diffusion = val;
+ break;
+
+ case AL_EAXREVERB_GAIN:
+ if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.Gain = val;
+ break;
+
+ case AL_EAXREVERB_GAINHF:
+ if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.GainHF = val;
+ break;
+
+ case AL_EAXREVERB_GAINLF:
+ if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.GainLF = val;
+ break;
+
+ case AL_EAXREVERB_DECAY_TIME:
+ if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.DecayTime = val;
+ break;
+
+ case AL_EAXREVERB_DECAY_HFRATIO:
+ if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.DecayHFRatio = val;
+ break;
+
+ case AL_EAXREVERB_DECAY_LFRATIO:
+ if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.DecayLFRatio = val;
+ break;
+
+ case AL_EAXREVERB_REFLECTIONS_GAIN:
+ if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.ReflectionsGain = val;
+ break;
+
+ case AL_EAXREVERB_REFLECTIONS_DELAY:
+ if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.ReflectionsDelay = val;
+ break;
+
+ case AL_EAXREVERB_LATE_REVERB_GAIN:
+ if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.LateReverbGain = val;
+ break;
+
+ case AL_EAXREVERB_LATE_REVERB_DELAY:
+ if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.LateReverbDelay = val;
+ break;
+
+ case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
+ if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.AirAbsorptionGainHF = val;
+ break;
+
+ case AL_EAXREVERB_ECHO_TIME:
+ if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.EchoTime = val;
+ break;
+
+ case AL_EAXREVERB_ECHO_DEPTH:
+ if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.EchoDepth = val;
+ break;
+
+ case AL_EAXREVERB_MODULATION_TIME:
+ if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.ModulationTime = val;
+ break;
+
+ case AL_EAXREVERB_MODULATION_DEPTH:
+ if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.ModulationDepth = val;
+ break;
+
+ case AL_EAXREVERB_HFREFERENCE:
+ if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.HFReference = val;
+ break;
+
+ case AL_EAXREVERB_LFREFERENCE:
+ if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.LFReference = val;
+ break;
+
+ case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
+ if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.RoomRolloffFactor = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_EAXREVERB_REFLECTIONS_PAN:
+ if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ LockContext(context);
+ props->Reverb.ReflectionsPan[0] = vals[0];
+ props->Reverb.ReflectionsPan[1] = vals[1];
+ props->Reverb.ReflectionsPan[2] = vals[2];
+ UnlockContext(context);
+ break;
+ case AL_EAXREVERB_LATE_REVERB_PAN:
+ if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ LockContext(context);
+ props->Reverb.LateReverbPan[0] = vals[0];
+ props->Reverb.LateReverbPan[1] = vals[1];
+ props->Reverb.LateReverbPan[2] = vals[2];
+ UnlockContext(context);
+ break;
+
+ default:
+ ALeaxreverb_setParamf(effect, context, param, vals[0]);
+ break;
+ }
+}
+
+void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_EAXREVERB_DECAY_HFLIMIT:
+ *val = props->Reverb.DecayHFLimit;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALeaxreverb_getParami(effect, context, param, vals);
+}
+void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_EAXREVERB_DENSITY:
+ *val = props->Reverb.Density;
+ break;
+
+ case AL_EAXREVERB_DIFFUSION:
+ *val = props->Reverb.Diffusion;
+ break;
+
+ case AL_EAXREVERB_GAIN:
+ *val = props->Reverb.Gain;
+ break;
+
+ case AL_EAXREVERB_GAINHF:
+ *val = props->Reverb.GainHF;
+ break;
+
+ case AL_EAXREVERB_GAINLF:
+ *val = props->Reverb.GainLF;
+ break;
+
+ case AL_EAXREVERB_DECAY_TIME:
+ *val = props->Reverb.DecayTime;
+ break;
+
+ case AL_EAXREVERB_DECAY_HFRATIO:
+ *val = props->Reverb.DecayHFRatio;
+ break;
+
+ case AL_EAXREVERB_DECAY_LFRATIO:
+ *val = props->Reverb.DecayLFRatio;
+ break;
+
+ case AL_EAXREVERB_REFLECTIONS_GAIN:
+ *val = props->Reverb.ReflectionsGain;
+ break;
+
+ case AL_EAXREVERB_REFLECTIONS_DELAY:
+ *val = props->Reverb.ReflectionsDelay;
+ break;
+
+ case AL_EAXREVERB_LATE_REVERB_GAIN:
+ *val = props->Reverb.LateReverbGain;
+ break;
+
+ case AL_EAXREVERB_LATE_REVERB_DELAY:
+ *val = props->Reverb.LateReverbDelay;
+ break;
+
+ case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
+ *val = props->Reverb.AirAbsorptionGainHF;
+ break;
+
+ case AL_EAXREVERB_ECHO_TIME:
+ *val = props->Reverb.EchoTime;
+ break;
+
+ case AL_EAXREVERB_ECHO_DEPTH:
+ *val = props->Reverb.EchoDepth;
+ break;
+
+ case AL_EAXREVERB_MODULATION_TIME:
+ *val = props->Reverb.ModulationTime;
+ break;
+
+ case AL_EAXREVERB_MODULATION_DEPTH:
+ *val = props->Reverb.ModulationDepth;
+ break;
+
+ case AL_EAXREVERB_HFREFERENCE:
+ *val = props->Reverb.HFReference;
+ break;
+
+ case AL_EAXREVERB_LFREFERENCE:
+ *val = props->Reverb.LFReference;
+ break;
+
+ case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
+ *val = props->Reverb.RoomRolloffFactor;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_EAXREVERB_REFLECTIONS_PAN:
+ LockContext(context);
+ vals[0] = props->Reverb.ReflectionsPan[0];
+ vals[1] = props->Reverb.ReflectionsPan[1];
+ vals[2] = props->Reverb.ReflectionsPan[2];
+ UnlockContext(context);
+ break;
+ case AL_EAXREVERB_LATE_REVERB_PAN:
+ LockContext(context);
+ vals[0] = props->Reverb.LateReverbPan[0];
+ vals[1] = props->Reverb.LateReverbPan[1];
+ vals[2] = props->Reverb.LateReverbPan[2];
+ UnlockContext(context);
+ break;
+
+ default:
+ ALeaxreverb_getParamf(effect, context, param, vals);
+ break;
+ }
+}
+
+DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
+
+void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_REVERB_DECAY_HFLIMIT:
+ if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.DecayHFLimit = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
+}
+void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALreverb_setParami(effect, context, param, vals[0]);
+}
+void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_REVERB_DENSITY:
+ if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.Density = val;
+ break;
+
+ case AL_REVERB_DIFFUSION:
+ if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.Diffusion = val;
+ break;
+
+ case AL_REVERB_GAIN:
+ if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.Gain = val;
+ break;
+
+ case AL_REVERB_GAINHF:
+ if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.GainHF = val;
+ break;
+
+ case AL_REVERB_DECAY_TIME:
+ if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.DecayTime = val;
+ break;
+
+ case AL_REVERB_DECAY_HFRATIO:
+ if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.DecayHFRatio = val;
+ break;
+
+ case AL_REVERB_REFLECTIONS_GAIN:
+ if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.ReflectionsGain = val;
+ break;
+
+ case AL_REVERB_REFLECTIONS_DELAY:
+ if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.ReflectionsDelay = val;
+ break;
+
+ case AL_REVERB_LATE_REVERB_GAIN:
+ if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.LateReverbGain = val;
+ break;
+
+ case AL_REVERB_LATE_REVERB_DELAY:
+ if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.LateReverbDelay = val;
+ break;
+
+ case AL_REVERB_AIR_ABSORPTION_GAINHF:
+ if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.AirAbsorptionGainHF = val;
+ break;
+
+ case AL_REVERB_ROOM_ROLLOFF_FACTOR:
+ if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Reverb.RoomRolloffFactor = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALreverb_setParamf(effect, context, param, vals[0]);
+}
+
+void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_REVERB_DECAY_HFLIMIT:
+ *val = props->Reverb.DecayHFLimit;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALreverb_getParami(effect, context, param, vals);
+}
+void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_REVERB_DENSITY:
+ *val = props->Reverb.Density;
+ break;
+
+ case AL_REVERB_DIFFUSION:
+ *val = props->Reverb.Diffusion;
+ break;
+
+ case AL_REVERB_GAIN:
+ *val = props->Reverb.Gain;
+ break;
+
+ case AL_REVERB_GAINHF:
+ *val = props->Reverb.GainHF;
+ break;
+
+ case AL_REVERB_DECAY_TIME:
+ *val = props->Reverb.DecayTime;
+ break;
+
+ case AL_REVERB_DECAY_HFRATIO:
+ *val = props->Reverb.DecayHFRatio;
+ break;
+
+ case AL_REVERB_REFLECTIONS_GAIN:
+ *val = props->Reverb.ReflectionsGain;
+ break;
+
+ case AL_REVERB_REFLECTIONS_DELAY:
+ *val = props->Reverb.ReflectionsDelay;
+ break;
- State->Echo.DensityGain = 0.0f;
- State->Echo.Delay.Mask = 0;
- State->Echo.Delay.Line = NULL;
- State->Echo.ApDelay.Mask = 0;
- State->Echo.ApDelay.Line = NULL;
- State->Echo.Coeff = 0.0f;
- State->Echo.ApFeedCoeff = 0.0f;
- State->Echo.ApCoeff = 0.0f;
- State->Echo.Offset = 0;
- State->Echo.ApOffset = 0;
- State->Echo.LpCoeff = 0.0f;
- State->Echo.LpSample = 0.0f;
- State->Echo.MixCoeff[0] = 0.0f;
- State->Echo.MixCoeff[1] = 0.0f;
-
- State->Offset = 0;
-
- State->Gain = State->Late.PanGain;
-
- return &State->state;
+ case AL_REVERB_LATE_REVERB_GAIN:
+ *val = props->Reverb.LateReverbGain;
+ break;
+
+ case AL_REVERB_LATE_REVERB_DELAY:
+ *val = props->Reverb.LateReverbDelay;
+ break;
+
+ case AL_REVERB_AIR_ABSORPTION_GAINHF:
+ *val = props->Reverb.AirAbsorptionGainHF;
+ break;
+
+ case AL_REVERB_ROOM_ROLLOFF_FACTOR:
+ *val = props->Reverb.RoomRolloffFactor;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ALreverb_getParamf(effect, context, param, vals);
}
+
+DEFINE_ALEFFECT_VTABLE(ALreverb);
diff --git a/Alc/evtqueue.h b/Alc/evtqueue.h
new file mode 100644
index 00000000..95702d79
--- /dev/null
+++ b/Alc/evtqueue.h
@@ -0,0 +1,31 @@
+#ifndef AL_EVTQUEUE_H
+#define AL_EVTQUEUE_H
+
+#include "AL/al.h"
+
+#include "alMain.h"
+
+typedef struct MidiEvent {
+ ALuint64 time;
+ ALuint event;
+ union {
+ ALuint val[2];
+ struct {
+ ALvoid *data;
+ ALsizei size;
+ } sysex;
+ } param;
+} MidiEvent;
+
+typedef struct EvtQueue {
+ MidiEvent *events;
+ ALsizei pos;
+ ALsizei size;
+ ALsizei maxsize;
+} EvtQueue;
+
+void InitEvtQueue(EvtQueue *queue);
+void ResetEvtQueue(EvtQueue *queue);
+ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt);
+
+#endif /* AL_EVTQUEUE_H */
diff --git a/Alc/helpers.c b/Alc/helpers.c
index 6358f044..f8a5f13b 100644
--- a/Alc/helpers.c
+++ b/Alc/helpers.c
@@ -28,6 +28,7 @@
#include <malloc.h>
#endif
+#ifndef AL_NO_UID_DEFS
#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
#define INITGUID
#include <windows.h>
@@ -52,12 +53,17 @@ DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xa
DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
#endif
#endif
+#endif /* AL_NO_UID_DEFS */
+
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#ifdef HAVE_CPUID_H
#include <cpuid.h>
#endif
+#ifdef HAVE_SYS_SYSCONF_H
+#include <sys/sysconf.h>
+#endif
#ifdef HAVE_FLOAT_H
#include <float.h>
#endif
@@ -66,6 +72,27 @@ DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,
#endif
#include "alMain.h"
+#include "atomic.h"
+#include "uintmap.h"
+#include "compat.h"
+
+
+extern inline RefCount IncrementRef(volatile RefCount *ptr);
+extern inline RefCount DecrementRef(volatile RefCount *ptr);
+extern inline int ExchangeInt(volatile int *ptr, int newval);
+extern inline void *ExchangePtr(XchgPtr *ptr, void *newval);
+extern inline ALboolean CompExchangeInt(volatile int *ptr, int oldval, int newval);
+extern inline ALboolean CompExchangePtr(XchgPtr *ptr, void *oldval, void *newval);
+
+extern inline void LockUIntMapRead(UIntMap *map);
+extern inline void UnlockUIntMapRead(UIntMap *map);
+extern inline void LockUIntMapWrite(UIntMap *map);
+extern inline void UnlockUIntMapWrite(UIntMap *map);
+
+extern inline ALuint NextPowerOf2(ALuint value);
+extern inline ALint fastf2i(ALfloat f);
+extern inline ALuint fastf2u(ALfloat f);
+
ALuint CPUCapFlags = 0;
@@ -104,10 +131,12 @@ void FillCPUCaps(ALuint capfilter)
if(maxfunc >= 1 &&
__get_cpuid(1, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
{
-#ifdef bit_SSE
- if((cpuinf[0].regs[3]&bit_SSE))
+ if((cpuinf[0].regs[3]&(1<<25)))
+ {
caps |= CPU_CAP_SSE;
-#endif
+ if((cpuinf[0].regs[3]&(1<<26)))
+ caps |= CPU_CAP_SSE2;
+ }
}
}
#elif defined(HAVE_WINDOWS_H)
@@ -119,7 +148,11 @@ void FillCPUCaps(ALuint capfilter)
else
{
if(IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE))
+ {
caps |= CPU_CAP_SSE;
+ if(IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE))
+ caps |= CPU_CAP_SSE2;
+ }
}
#endif
#ifdef HAVE_NEON
@@ -127,9 +160,10 @@ void FillCPUCaps(ALuint capfilter)
caps |= CPU_CAP_NEON;
#endif
- TRACE("Got caps:%s%s%s\n", ((caps&CPU_CAP_SSE)?((capfilter&CPU_CAP_SSE)?" SSE":" (SSE)"):""),
- ((caps&CPU_CAP_NEON)?((capfilter&CPU_CAP_NEON)?" Neon":" (Neon)"):""),
- ((!caps)?" -none-":""));
+ TRACE("Got caps:%s%s%s%s\n", ((caps&CPU_CAP_SSE)?((capfilter&CPU_CAP_SSE)?" SSE":" (SSE)"):""),
+ ((caps&CPU_CAP_SSE2)?((capfilter&CPU_CAP_SSE2)?" SSE2":" (SSE2)"):""),
+ ((caps&CPU_CAP_NEON)?((capfilter&CPU_CAP_NEON)?" Neon":" (Neon)"):""),
+ ((!caps)?" -none-":""));
CPUCapFlags = caps & capfilter;
}
@@ -192,25 +226,30 @@ void al_free(void *ptr)
void SetMixerFPUMode(FPUCtl *ctl)
{
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
- unsigned short fpuState;
- __asm__ __volatile__("fnstcw %0" : "=m" (*&fpuState));
- ctl->state = fpuState;
- fpuState &= ~0x300; /* clear precision to single */
- fpuState |= 0xC00; /* set round-to-zero */
- __asm__ __volatile__("fldcw %0" : : "m" (*&fpuState));
-#ifdef HAVE_SSE
+#ifdef HAVE_FENV_H
+ fegetenv(STATIC_CAST(fenv_t, ctl));
+#if defined(__GNUC__) && defined(HAVE_SSE)
+ if((CPUCapFlags&CPU_CAP_SSE))
+ __asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
+#endif
+
+#ifdef FE_TOWARDZERO
+ fesetround(FE_TOWARDZERO);
+#endif
+#if defined(__GNUC__) && defined(HAVE_SSE)
if((CPUCapFlags&CPU_CAP_SSE))
{
- int sseState;
- __asm__ __volatile__("stmxcsr %0" : "=m" (*&sseState));
- ctl->sse_state = sseState;
- sseState |= 0x0C00; /* set round-to-zero */
+ int sseState = ctl->sse_state;
+ sseState |= 0x6000; /* set round-to-zero */
sseState |= 0x8000; /* set flush-to-zero */
+ if((CPUCapFlags&CPU_CAP_SSE2))
+ sseState |= 0x0040; /* set denormals-are-zero */
__asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
}
#endif
+
#elif defined(HAVE___CONTROL87_2)
+
int mode;
__control87_2(0, 0, &ctl->state, NULL);
__control87_2(_RC_CHOP|_PC_24, _MCW_RC|_MCW_PC, &mode, NULL);
@@ -221,54 +260,54 @@ void SetMixerFPUMode(FPUCtl *ctl)
__control87_2(_RC_CHOP|_DN_FLUSH, _MCW_RC|_MCW_DN, NULL, &mode);
}
#endif
+
#elif defined(HAVE__CONTROLFP)
+
ctl->state = _controlfp(0, 0);
(void)_controlfp(_RC_CHOP|_PC_24, _MCW_RC|_MCW_PC);
-#elif defined(HAVE_FESETROUND)
- ctl->state = fegetround();
-#ifdef FE_TOWARDZERO
- fesetround(FE_TOWARDZERO);
-#endif
#endif
}
void RestoreFPUMode(const FPUCtl *ctl)
{
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
- unsigned short fpuState = ctl->state;
- __asm__ __volatile__("fldcw %0" : : "m" (*&fpuState));
-#ifdef HAVE_SSE
+#ifdef HAVE_FENV_H
+ fesetenv(STATIC_CAST(fenv_t, ctl));
+#if defined(__GNUC__) && defined(HAVE_SSE)
if((CPUCapFlags&CPU_CAP_SSE))
__asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state));
#endif
+
#elif defined(HAVE___CONTROL87_2)
+
int mode;
__control87_2(ctl->state, _MCW_RC|_MCW_PC, &mode, NULL);
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
__control87_2(ctl->sse_state, _MCW_RC|_MCW_DN, NULL, &mode);
#endif
+
#elif defined(HAVE__CONTROLFP)
+
_controlfp(ctl->state, _MCW_RC|_MCW_PC);
-#elif defined(HAVE_FESETROUND)
- fesetround(ctl->state);
#endif
}
#ifdef _WIN32
-void pthread_once(pthread_once_t *once, void (*callback)(void))
+extern inline int alsched_yield(void);
+
+void althread_once(althread_once_t *once, void (*callback)(void))
{
LONG ret;
while((ret=InterlockedExchange(once, 1)) == 1)
- sched_yield();
+ alsched_yield();
if(ret == 0)
callback();
InterlockedExchange(once, 2);
}
-int pthread_key_create(pthread_key_t *key, void (*callback)(void*))
+int althread_key_create(althread_key_t *key, void (*callback)(void*))
{
*key = TlsAlloc();
if(callback)
@@ -276,17 +315,17 @@ int pthread_key_create(pthread_key_t *key, void (*callback)(void*))
return 0;
}
-int pthread_key_delete(pthread_key_t key)
+int althread_key_delete(althread_key_t key)
{
InsertUIntMapEntry(&TlsDestructor, key, NULL);
TlsFree(key);
return 0;
}
-void *pthread_getspecific(pthread_key_t key)
+void *althread_getspecific(althread_key_t key)
{ return TlsGetValue(key); }
-int pthread_setspecific(pthread_key_t key, void *val)
+int althread_setspecific(althread_key_t key, void *val)
{
TlsSetValue(key, val);
return 0;
@@ -330,6 +369,8 @@ WCHAR *strdupW(const WCHAR *str)
#include <pthread_np.h>
#endif
#include <sched.h>
+#include <time.h>
+#include <sys/time.h>
void InitializeCriticalSection(CRITICAL_SECTION *cs)
{
@@ -449,20 +490,13 @@ void *GetSymbol(void *handle, const char *name)
void al_print(const char *type, const char *func, const char *fmt, ...)
{
- char str[256];
- int i;
+ va_list ap;
- i = snprintf(str, sizeof(str), "AL lib: %s %s: ", type, func);
- if(i > 0 && (unsigned int)i < sizeof(str))
- {
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(str+i, sizeof(str)-i, fmt, ap);
- va_end(ap);
- }
- str[sizeof(str)-1] = 0;
+ va_start(ap, fmt);
+ fprintf(LogFile, "AL lib: %s %s: ", type, func);
+ vfprintf(LogFile, fmt, ap);
+ va_end(ap);
- fprintf(LogFile, "%s", str);
fflush(LogFile);
}
@@ -495,7 +529,7 @@ void SetRTPriority(void)
static void Lock(volatile ALenum *l)
{
while(ExchangeInt(l, AL_TRUE) == AL_TRUE)
- sched_yield();
+ alsched_yield();
}
static void Unlock(volatile ALenum *l)
diff --git a/Alc/hrtf.c b/Alc/hrtf.c
index 11922cc7..49bb97a3 100644
--- a/Alc/hrtf.c
+++ b/Alc/hrtf.c
@@ -92,7 +92,7 @@ static void CalcEvIndices(const struct Hrtf *Hrtf, ALfloat ev, ALuint *evidx, AL
*/
static void CalcAzIndices(const struct Hrtf *Hrtf, ALuint evidx, ALfloat az, ALuint *azidx, ALfloat *azmu)
{
- az = (F_PI*2.0f + az) * Hrtf->azCount[evidx] / (F_PI*2.0f);
+ az = (F_2PI + az) * Hrtf->azCount[evidx] / (F_2PI);
azidx[0] = fastf2u(az) % Hrtf->azCount[evidx];
azidx[1] = (azidx[0] + 1) % Hrtf->azCount[evidx];
*azmu = az - floorf(az);
@@ -783,6 +783,30 @@ const struct Hrtf *GetHrtf(ALCdevice *device)
return NULL;
}
+void FindHrtfFormat(const ALCdevice *device, enum DevFmtChannels *chans, ALCuint *srate)
+{
+ const struct Hrtf *hrtf = &DefaultHrtf;
+
+ if(device->Frequency != DefaultHrtf.sampleRate)
+ {
+ hrtf = LoadedHrtfs;
+ while(hrtf != NULL)
+ {
+ if(device->Frequency == hrtf->sampleRate)
+ break;
+ hrtf = hrtf->next;
+ }
+
+ if(hrtf == NULL)
+ hrtf = LoadHrtf(device->Frequency);
+ if(hrtf == NULL)
+ hrtf = &DefaultHrtf;
+ }
+
+ *chans = DevFmtStereo;
+ *srate = hrtf->sampleRate;
+}
+
void FreeHrtfs(void)
{
struct Hrtf *Hrtf = NULL;
diff --git a/Alc/midi/base.c b/Alc/midi/base.c
new file mode 100644
index 00000000..25dd19d9
--- /dev/null
+++ b/Alc/midi/base.c
@@ -0,0 +1,278 @@
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "midi/base.h"
+
+#include "alMidi.h"
+#include "alMain.h"
+#include "alError.h"
+#include "alThunk.h"
+#include "evtqueue.h"
+#include "rwlock.h"
+#include "alu.h"
+
+
+/* Microsecond resolution */
+#define TICKS_PER_SECOND (1000000)
+
+/* MIDI events */
+#define SYSEX_EVENT (0xF0)
+
+
+void InitEvtQueue(EvtQueue *queue)
+{
+ queue->events = NULL;
+ queue->maxsize = 0;
+ queue->size = 0;
+ queue->pos = 0;
+}
+
+void ResetEvtQueue(EvtQueue *queue)
+{
+ ALsizei i;
+ for(i = 0;i < queue->size;i++)
+ {
+ if(queue->events[i].event == SYSEX_EVENT)
+ {
+ free(queue->events[i].param.sysex.data);
+ queue->events[i].param.sysex.data = NULL;
+ }
+ }
+
+ free(queue->events);
+ queue->events = NULL;
+ queue->maxsize = 0;
+ queue->size = 0;
+ queue->pos = 0;
+}
+
+ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt)
+{
+ ALsizei pos;
+
+ if(queue->maxsize == queue->size)
+ {
+ if(queue->pos > 0)
+ {
+ /* Queue has some stale entries, remove them to make space for more
+ * events. */
+ for(pos = 0;pos < queue->pos;pos++)
+ {
+ if(queue->events[pos].event == SYSEX_EVENT)
+ {
+ free(queue->events[pos].param.sysex.data);
+ queue->events[pos].param.sysex.data = NULL;
+ }
+ }
+ memmove(&queue->events[0], &queue->events[queue->pos],
+ (queue->size-queue->pos)*sizeof(queue->events[0]));
+ queue->size -= queue->pos;
+ queue->pos = 0;
+ }
+ else
+ {
+ /* Queue is full, double the allocated space. */
+ void *temp = NULL;
+ ALsizei newsize;
+
+ newsize = (queue->maxsize ? (queue->maxsize<<1) : 16);
+ if(newsize > queue->maxsize)
+ temp = realloc(queue->events, newsize * sizeof(queue->events[0]));
+ if(!temp)
+ return AL_OUT_OF_MEMORY;
+
+ queue->events = temp;
+ queue->maxsize = newsize;
+ }
+ }
+
+ pos = queue->pos;
+ if(queue->size > 0)
+ {
+ ALsizei high = queue->size - 1;
+ while(pos < high)
+ {
+ ALsizei mid = pos + (high-pos)/2;
+ if(queue->events[mid].time < evt->time)
+ pos = mid + 1;
+ else
+ high = mid;
+ }
+ while(pos < queue->size && queue->events[pos].time <= evt->time)
+ pos++;
+
+ if(pos < queue->size)
+ memmove(&queue->events[pos+1], &queue->events[pos],
+ (queue->size-pos)*sizeof(queue->events[0]));
+ }
+
+ queue->events[pos] = *evt;
+ queue->size++;
+
+ return AL_NO_ERROR;
+}
+
+
+void MidiSynth_Construct(MidiSynth *self, ALCdevice *device)
+{
+ InitEvtQueue(&self->EventQueue);
+
+ RWLockInit(&self->Lock);
+
+ self->Soundfonts = NULL;
+ self->NumSoundfonts = 0;
+
+ self->Gain = 1.0f;
+ self->State = AL_INITIAL;
+
+ self->LastEvtTime = 0;
+ self->NextEvtTime = UINT64_MAX;
+ self->SamplesSinceLast = 0.0;
+ self->SamplesToNext = 0.0;
+
+ self->SamplesPerTick = (ALdouble)device->Frequency / TICKS_PER_SECOND;
+}
+
+void MidiSynth_Destruct(MidiSynth *self)
+{
+ ALsizei i;
+
+ for(i = 0;i < self->NumSoundfonts;i++)
+ DecrementRef(&self->Soundfonts[i]->ref);
+ free(self->Soundfonts);
+ self->Soundfonts = NULL;
+ self->NumSoundfonts = 0;
+
+ ResetEvtQueue(&self->EventQueue);
+}
+
+
+ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids)
+{
+ ALCdevice *device = context->Device;
+ ALsoundfont **sfonts;
+ ALsizei i;
+
+ if(self->State != AL_INITIAL && self->State != AL_STOPPED)
+ return AL_INVALID_OPERATION;
+
+ sfonts = calloc(1, count * sizeof(sfonts[0]));
+ if(!sfonts) return AL_OUT_OF_MEMORY;
+
+ for(i = 0;i < count;i++)
+ {
+ if(ids[i] == 0)
+ sfonts[i] = ALsoundfont_getDefSoundfont(context);
+ else if(!(sfonts[i]=LookupSfont(device, ids[i])))
+ {
+ free(sfonts);
+ return AL_INVALID_VALUE;
+ }
+ }
+
+ for(i = 0;i < count;i++)
+ IncrementRef(&sfonts[i]->ref);
+ sfonts = ExchangePtr((XchgPtr*)&self->Soundfonts, sfonts);
+ count = ExchangeInt(&self->NumSoundfonts, count);
+
+ for(i = 0;i < count;i++)
+ DecrementRef(&sfonts[i]->ref);
+ free(sfonts);
+
+ return AL_NO_ERROR;
+}
+
+extern inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain);
+extern inline ALfloat MidiSynth_getGain(const MidiSynth *self);
+extern inline void MidiSynth_setState(MidiSynth *self, ALenum state);
+extern inline ALenum MidiSynth_getState(const MidiSynth *self);
+
+void MidiSynth_stop(MidiSynth *self)
+{
+ ResetEvtQueue(&self->EventQueue);
+
+ self->LastEvtTime = 0;
+ self->NextEvtTime = UINT64_MAX;
+ self->SamplesSinceLast = 0.0;
+ self->SamplesToNext = 0.0;
+}
+
+extern inline void MidiSynth_reset(MidiSynth *self);
+
+ALuint64 MidiSynth_getTime(const MidiSynth *self)
+{
+ ALuint64 time = self->LastEvtTime + (self->SamplesSinceLast/self->SamplesPerTick);
+ return clampu64(time, self->LastEvtTime, self->NextEvtTime);
+}
+
+extern inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self);
+
+void MidiSynth_setSampleRate(MidiSynth *self, ALdouble srate)
+{
+ ALdouble sampletickrate = srate / TICKS_PER_SECOND;
+
+ self->SamplesSinceLast = self->SamplesSinceLast * sampletickrate / self->SamplesPerTick;
+ self->SamplesToNext = self->SamplesToNext * sampletickrate / self->SamplesPerTick;
+ self->SamplesPerTick = sampletickrate;
+}
+
+extern inline void MidiSynth_update(MidiSynth *self, ALCdevice *device);
+
+ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2)
+{
+ MidiEvent entry;
+ ALenum err;
+
+ entry.time = time;
+ entry.event = event;
+ entry.param.val[0] = param1;
+ entry.param.val[1] = param2;
+
+ err = InsertEvtQueue(&self->EventQueue, &entry);
+ if(err != AL_NO_ERROR) return err;
+
+ if(entry.time < self->NextEvtTime)
+ {
+ self->NextEvtTime = entry.time;
+
+ self->SamplesToNext = (self->NextEvtTime - self->LastEvtTime) * self->SamplesPerTick;
+ self->SamplesToNext -= self->SamplesSinceLast;
+ }
+
+ return AL_NO_ERROR;
+}
+
+ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size)
+{
+ MidiEvent entry;
+ ALenum err;
+
+ entry.time = time;
+ entry.event = SYSEX_EVENT;
+ entry.param.sysex.size = size;
+ entry.param.sysex.data = malloc(size);
+ if(!entry.param.sysex.data)
+ return AL_OUT_OF_MEMORY;
+ memcpy(entry.param.sysex.data, data, size);
+
+ err = InsertEvtQueue(&self->EventQueue, &entry);
+ if(err != AL_NO_ERROR)
+ {
+ free(entry.param.sysex.data);
+ return err;
+ }
+
+ if(entry.time < self->NextEvtTime)
+ {
+ self->NextEvtTime = entry.time;
+
+ self->SamplesToNext = (self->NextEvtTime - self->LastEvtTime) * self->SamplesPerTick;
+ self->SamplesToNext -= self->SamplesSinceLast;
+ }
+
+ return AL_NO_ERROR;
+}
diff --git a/Alc/midi/base.h b/Alc/midi/base.h
new file mode 100644
index 00000000..f900c941
--- /dev/null
+++ b/Alc/midi/base.h
@@ -0,0 +1,128 @@
+#ifndef AL_MIDI_BASE_H
+#define AL_MIDI_BASE_H
+
+#include "alMain.h"
+#include "atomic.h"
+#include "evtqueue.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ALsoundfont;
+
+typedef size_t (*ReaderCb)(void *ptr, size_t size, void *stream);
+typedef struct Reader {
+ ReaderCb cb;
+ void *ptr;
+ int error;
+} Reader;
+#define READ(x_, buf_, len_) ((x_)->cb((buf_), (len_), (x_)->ptr))
+#define READERR(x_) ((x_)->error)
+
+ALboolean loadSf2(Reader *stream, struct ALsoundfont *sfont, ALCcontext *context);
+
+
+struct MidiSynthVtable;
+
+typedef struct MidiSynth {
+ EvtQueue EventQueue;
+
+ ALuint64 LastEvtTime;
+ ALuint64 NextEvtTime;
+ ALdouble SamplesSinceLast;
+ ALdouble SamplesToNext;
+
+ ALdouble SamplesPerTick;
+
+ /* NOTE: This rwlock is for the state and soundfont. The EventQueue and
+ * related must instead use the device lock as they're used in the mixer
+ * thread.
+ */
+ RWLock Lock;
+
+ struct ALsoundfont **Soundfonts;
+ ALsizei NumSoundfonts;
+
+ volatile ALfloat Gain;
+ volatile ALenum State;
+
+ const struct MidiSynthVtable *vtbl;
+} MidiSynth;
+
+void MidiSynth_Construct(MidiSynth *self, ALCdevice *device);
+void MidiSynth_Destruct(MidiSynth *self);
+ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
+inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain) { self->Gain = gain; }
+inline ALfloat MidiSynth_getGain(const MidiSynth *self) { return self->Gain; }
+inline void MidiSynth_setState(MidiSynth *self, ALenum state) { ExchangeInt(&self->State, state); }
+inline ALenum MidiSynth_getState(const MidiSynth *self) { return self->State; }
+void MidiSynth_stop(MidiSynth *self);
+inline void MidiSynth_reset(MidiSynth *self) { MidiSynth_stop(self); }
+ALuint64 MidiSynth_getTime(const MidiSynth *self);
+inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self)
+{
+ if(self->EventQueue.pos == self->EventQueue.size)
+ return UINT64_MAX;
+ return self->EventQueue.events[self->EventQueue.pos].time;
+}
+void MidiSynth_setSampleRate(MidiSynth *self, ALdouble srate);
+inline void MidiSynth_update(MidiSynth *self, ALCdevice *device)
+{ MidiSynth_setSampleRate(self, device->Frequency); }
+ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2);
+ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size);
+
+
+struct MidiSynthVtable {
+ void (*const Destruct)(MidiSynth *self);
+
+ ALenum (*const selectSoundfonts)(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
+
+ void (*const setGain)(MidiSynth *self, ALfloat gain);
+ void (*const setState)(MidiSynth *self, ALenum state);
+
+ void (*const stop)(MidiSynth *self);
+ void (*const reset)(MidiSynth *self);
+
+ void (*const update)(MidiSynth *self, ALCdevice *device);
+ void (*const process)(MidiSynth *self, ALuint samples, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
+
+ void (*const Delete)(MidiSynth *self);
+};
+
+#define DEFINE_MIDISYNTH_VTABLE(T) \
+DECLARE_THUNK(T, MidiSynth, void, Destruct) \
+DECLARE_THUNK3(T, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) \
+DECLARE_THUNK1(T, MidiSynth, void, setGain, ALfloat) \
+DECLARE_THUNK1(T, MidiSynth, void, setState, ALenum) \
+DECLARE_THUNK(T, MidiSynth, void, stop) \
+DECLARE_THUNK(T, MidiSynth, void, reset) \
+DECLARE_THUNK1(T, MidiSynth, void, update, ALCdevice*) \
+DECLARE_THUNK2(T, MidiSynth, void, process, ALuint, ALfloatBUFFERSIZE*restrict) \
+DECLARE_THUNK(T, MidiSynth, void, Delete) \
+ \
+static const struct MidiSynthVtable T##_MidiSynth_vtable = { \
+ T##_MidiSynth_Destruct, \
+ \
+ T##_MidiSynth_selectSoundfonts, \
+ T##_MidiSynth_setGain, \
+ T##_MidiSynth_setState, \
+ T##_MidiSynth_stop, \
+ T##_MidiSynth_reset, \
+ T##_MidiSynth_update, \
+ T##_MidiSynth_process, \
+ \
+ T##_MidiSynth_Delete, \
+}
+
+
+MidiSynth *FSynth_create(ALCdevice *device);
+MidiSynth *DSynth_create(ALCdevice *device);
+
+MidiSynth *SynthCreate(ALCdevice *device);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AL_MIDI_BASE_H */
diff --git a/Alc/midi/dummy.c b/Alc/midi/dummy.c
new file mode 100644
index 00000000..71c03efb
--- /dev/null
+++ b/Alc/midi/dummy.c
@@ -0,0 +1,95 @@
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "alMain.h"
+#include "alError.h"
+#include "evtqueue.h"
+#include "rwlock.h"
+#include "alu.h"
+
+#include "midi/base.h"
+
+typedef struct DSynth {
+ DERIVE_FROM_TYPE(MidiSynth);
+} DSynth;
+
+static void DSynth_Construct(DSynth *self, ALCdevice *device);
+static DECLARE_FORWARD(DSynth, MidiSynth, void, Destruct)
+static DECLARE_FORWARD3(DSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*)
+static DECLARE_FORWARD1(DSynth, MidiSynth, void, setGain, ALfloat)
+static DECLARE_FORWARD1(DSynth, MidiSynth, void, setState, ALenum)
+static DECLARE_FORWARD(DSynth, MidiSynth, void, stop)
+static DECLARE_FORWARD(DSynth, MidiSynth, void, reset)
+static DECLARE_FORWARD1(DSynth, MidiSynth, void, update, ALCdevice*)
+static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
+static void DSynth_Delete(DSynth *self);
+DEFINE_MIDISYNTH_VTABLE(DSynth);
+
+
+static void DSynth_Construct(DSynth *self, ALCdevice *device)
+{
+ MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
+ SET_VTABLE2(DSynth, MidiSynth, self);
+}
+
+
+static void DSynth_processQueue(DSynth *self, ALuint64 time)
+{
+ EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
+
+ while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
+ queue->pos++;
+}
+
+static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloatBUFFERSIZE*restrict UNUSED(DryBuffer))
+{
+ MidiSynth *synth = STATIC_CAST(MidiSynth, self);
+
+ if(synth->State != AL_PLAYING)
+ return;
+
+ synth->SamplesSinceLast += SamplesToDo;
+ synth->SamplesToNext -= SamplesToDo;
+ while(synth->SamplesToNext < 1.0f)
+ {
+ ALuint64 time = synth->NextEvtTime;
+ if(time == UINT64_MAX)
+ {
+ synth->SamplesToNext = 0.0;
+ break;
+ }
+
+ synth->SamplesSinceLast -= (time - synth->LastEvtTime) * synth->SamplesPerTick;
+ synth->SamplesSinceLast = maxd(synth->SamplesSinceLast, 0.0);
+ synth->LastEvtTime = time;
+ DSynth_processQueue(self, time);
+
+ synth->NextEvtTime = MidiSynth_getNextEvtTime(synth);
+ if(synth->NextEvtTime != UINT64_MAX)
+ synth->SamplesToNext += (synth->NextEvtTime - synth->LastEvtTime) * synth->SamplesPerTick;
+ }
+}
+
+
+static void DSynth_Delete(DSynth *self)
+{
+ free(self);
+}
+
+
+MidiSynth *DSynth_create(ALCdevice *device)
+{
+ DSynth *synth = calloc(1, sizeof(*synth));
+ if(!synth)
+ {
+ ERR("Failed to allocate DSynth\n");
+ return NULL;
+ }
+ DSynth_Construct(synth, device);
+ return STATIC_CAST(MidiSynth, synth);
+}
diff --git a/Alc/midi/fluidsynth.c b/Alc/midi/fluidsynth.c
new file mode 100644
index 00000000..9d58f87b
--- /dev/null
+++ b/Alc/midi/fluidsynth.c
@@ -0,0 +1,843 @@
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "midi/base.h"
+
+#include "alMain.h"
+#include "alError.h"
+#include "alMidi.h"
+#include "evtqueue.h"
+#include "rwlock.h"
+#include "alu.h"
+
+#ifdef HAVE_FLUIDSYNTH
+
+#include <fluidsynth.h>
+
+
+/* MIDI events */
+#define SYSEX_EVENT (0xF0)
+
+/* MIDI controllers */
+#define CTRL_BANKSELECT_MSB (0)
+#define CTRL_BANKSELECT_LSB (32)
+#define CTRL_ALLNOTESOFF (123)
+
+
+static int getGenInput(ALenum input)
+{
+ switch(input)
+ {
+ case AL_ONE_SOFT: return FLUID_MOD_NONE;
+ case AL_NOTEON_VELOCITY_SOFT: return FLUID_MOD_VELOCITY;
+ case AL_NOTEON_KEY_SOFT: return FLUID_MOD_KEY;
+ case AL_KEYPRESSURE_SOFT: return FLUID_MOD_KEYPRESSURE;
+ case AL_CHANNELPRESSURE_SOFT: return FLUID_MOD_CHANNELPRESSURE;
+ case AL_PITCHBEND_SOFT: return FLUID_MOD_PITCHWHEEL;
+ case AL_PITCHBEND_SENSITIVITY_SOFT: return FLUID_MOD_PITCHWHEELSENS;
+ }
+ return input&0x7F;
+}
+
+static int getGenFlags(ALenum input, ALenum type, ALenum form)
+{
+ int ret = 0;
+
+ switch(type)
+ {
+ case AL_UNORM_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE; break;
+ case AL_UNORM_REV_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE; break;
+ case AL_SNORM_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE; break;
+ case AL_SNORM_REV_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE; break;
+ }
+ switch(form)
+ {
+ case AL_LINEAR_SOFT: ret |= FLUID_MOD_LINEAR; break;
+ case AL_CONCAVE_SOFT: ret |= FLUID_MOD_CONCAVE; break;
+ case AL_CONVEX_SOFT: ret |= FLUID_MOD_CONVEX; break;
+ case AL_SWITCH_SOFT: ret |= FLUID_MOD_SWITCH; break;
+ }
+ /* Source input values less than 128 correspond to a MIDI continuous
+ * controller. Otherwise, it's a general controller. */
+ if(input < 128) ret |= FLUID_MOD_CC;
+ else ret |= FLUID_MOD_GC;
+
+ return ret;
+}
+
+static enum fluid_gen_type getSf2Gen(ALenum gen)
+{
+ switch(gen)
+ {
+ case AL_MOD_LFO_TO_PITCH_SOFT: return GEN_MODLFOTOPITCH;
+ case AL_VIBRATO_LFO_TO_PITCH_SOFT: return GEN_VIBLFOTOPITCH;
+ case AL_MOD_ENV_TO_PITCH_SOFT: return GEN_MODENVTOPITCH;
+ case AL_FILTER_CUTOFF_SOFT: return GEN_FILTERFC;
+ case AL_FILTER_RESONANCE_SOFT: return GEN_FILTERQ;
+ case AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT: return GEN_MODLFOTOFILTERFC;
+ case AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT: return GEN_MODENVTOFILTERFC;
+ case AL_MOD_LFO_TO_VOLUME_SOFT: return GEN_MODLFOTOVOL;
+ case AL_CHORUS_SEND_SOFT: return GEN_CHORUSSEND;
+ case AL_REVERB_SEND_SOFT: return GEN_REVERBSEND;
+ case AL_PAN_SOFT: return GEN_PAN;
+ case AL_MOD_LFO_DELAY_SOFT: return GEN_MODLFODELAY;
+ case AL_MOD_LFO_FREQUENCY_SOFT: return GEN_MODLFOFREQ;
+ case AL_VIBRATO_LFO_DELAY_SOFT: return GEN_VIBLFODELAY;
+ case AL_VIBRATO_LFO_FREQUENCY_SOFT: return GEN_VIBLFOFREQ;
+ case AL_MOD_ENV_DELAYTIME_SOFT: return GEN_MODENVDELAY;
+ case AL_MOD_ENV_ATTACKTIME_SOFT: return GEN_MODENVATTACK;
+ case AL_MOD_ENV_HOLDTIME_SOFT: return GEN_MODENVHOLD;
+ case AL_MOD_ENV_DECAYTIME_SOFT: return GEN_MODENVDECAY;
+ case AL_MOD_ENV_SUSTAINVOLUME_SOFT: return GEN_MODENVSUSTAIN;
+ case AL_MOD_ENV_RELEASETIME_SOFT: return GEN_MODENVRELEASE;
+ case AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOMODENVHOLD;
+ case AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOMODENVDECAY;
+ case AL_VOLUME_ENV_DELAYTIME_SOFT: return GEN_VOLENVDELAY;
+ case AL_VOLUME_ENV_ATTACKTIME_SOFT: return GEN_VOLENVATTACK;
+ case AL_VOLUME_ENV_HOLDTIME_SOFT: return GEN_VOLENVHOLD;
+ case AL_VOLUME_ENV_DECAYTIME_SOFT: return GEN_VOLENVDECAY;
+ case AL_VOLUME_ENV_SUSTAINVOLUME_SOFT: return GEN_VOLENVSUSTAIN;
+ case AL_VOLUME_ENV_RELEASETIME_SOFT: return GEN_VOLENVRELEASE;
+ case AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOVOLENVHOLD;
+ case AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOVOLENVDECAY;
+ case AL_ATTENUATION_SOFT: return GEN_ATTENUATION;
+ case AL_TUNING_COARSE_SOFT: return GEN_COARSETUNE;
+ case AL_TUNING_FINE_SOFT: return GEN_FINETUNE;
+ case AL_TUNING_SCALE_SOFT: return GEN_SCALETUNE;
+ }
+ ERR("Unhandled generator: 0x%04x\n", gen);
+ return 0;
+}
+
+static int getSf2LoopMode(ALenum mode)
+{
+ switch(mode)
+ {
+ case AL_NONE: return 0;
+ case AL_LOOP_CONTINUOUS_SOFT: return 1;
+ case AL_LOOP_UNTIL_RELEASE_SOFT: return 3;
+ }
+ return 0;
+}
+
+static int getSampleType(ALenum type)
+{
+ switch(type)
+ {
+ case AL_MONO_SOFT: return FLUID_SAMPLETYPE_MONO;
+ case AL_RIGHT_SOFT: return FLUID_SAMPLETYPE_RIGHT;
+ case AL_LEFT_SOFT: return FLUID_SAMPLETYPE_LEFT;
+ }
+ return FLUID_SAMPLETYPE_MONO;
+}
+
+typedef struct FSample {
+ DERIVE_FROM_TYPE(fluid_sample_t);
+
+ ALfontsound *Sound;
+
+ fluid_mod_t *Mods;
+ ALsizei NumMods;
+} FSample;
+
+static void FSample_Construct(FSample *self, ALfontsound *sound, ALsoundfont *sfont)
+{
+ fluid_sample_t *sample = STATIC_CAST(fluid_sample_t, self);
+ memset(sample->name, 0, sizeof(sample->name));
+ sample->start = sound->Start;
+ sample->end = sound->End;
+ sample->loopstart = sound->LoopStart;
+ sample->loopend = sound->LoopEnd;
+ sample->samplerate = sound->SampleRate;
+ sample->origpitch = sound->PitchKey;
+ sample->pitchadj = sound->PitchCorrection;
+ sample->sampletype = getSampleType(sound->SampleType);
+ sample->valid = 1;
+ sample->data = sfont->Samples;
+
+ sample->amplitude_that_reaches_noise_floor_is_valid = 0;
+ sample->amplitude_that_reaches_noise_floor = 0.0;
+
+ sample->refcount = 0;
+
+ sample->notify = NULL;
+
+ sample->userdata = self;
+
+ self->Sound = sound;
+
+ self->NumMods = 0;
+ self->Mods = calloc(sound->ModulatorMap.size, sizeof(self->Mods[0]));
+ if(self->Mods)
+ {
+ ALsizei i;
+
+ self->NumMods = sound->ModulatorMap.size;
+ for(i = 0;i < self->NumMods;i++)
+ {
+ ALsfmodulator *mod = sound->ModulatorMap.array[i].value;
+ fluid_mod_set_source1(&self->Mods[i], getGenInput(mod->Source[0].Input),
+ getGenFlags(mod->Source[0].Input, mod->Source[0].Type,
+ mod->Source[0].Form));
+ fluid_mod_set_source2(&self->Mods[i], getGenInput(mod->Source[1].Input),
+ getGenFlags(mod->Source[1].Input, mod->Source[1].Type,
+ mod->Source[1].Form));
+ fluid_mod_set_amount(&self->Mods[i], mod->Amount);
+ fluid_mod_set_dest(&self->Mods[i], getSf2Gen(mod->Dest));
+ self->Mods[i].next = NULL;
+ }
+ }
+}
+
+static void FSample_Destruct(FSample *self)
+{
+ free(self->Mods);
+ self->Mods = NULL;
+ self->NumMods = 0;
+}
+
+
+typedef struct FPreset {
+ DERIVE_FROM_TYPE(fluid_preset_t);
+
+ char Name[16];
+
+ int Preset;
+ int Bank;
+
+ FSample *Samples;
+ ALsizei NumSamples;
+} FPreset;
+
+static char* FPreset_getName(fluid_preset_t *preset);
+static int FPreset_getPreset(fluid_preset_t *preset);
+static int FPreset_getBank(fluid_preset_t *preset);
+static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int velocity);
+
+static void FPreset_Construct(FPreset *self, ALsfpreset *preset, fluid_sfont_t *parent, ALsoundfont *sfont)
+{
+ STATIC_CAST(fluid_preset_t, self)->data = self;
+ STATIC_CAST(fluid_preset_t, self)->sfont = parent;
+ STATIC_CAST(fluid_preset_t, self)->free = NULL;
+ STATIC_CAST(fluid_preset_t, self)->get_name = FPreset_getName;
+ STATIC_CAST(fluid_preset_t, self)->get_banknum = FPreset_getBank;
+ STATIC_CAST(fluid_preset_t, self)->get_num = FPreset_getPreset;
+ STATIC_CAST(fluid_preset_t, self)->noteon = FPreset_noteOn;
+ STATIC_CAST(fluid_preset_t, self)->notify = NULL;
+
+ memset(self->Name, 0, sizeof(self->Name));
+ self->Preset = preset->Preset;
+ self->Bank = preset->Bank;
+
+ self->NumSamples = 0;
+ self->Samples = calloc(1, preset->NumSounds * sizeof(self->Samples[0]));
+ if(self->Samples)
+ {
+ ALsizei i;
+ self->NumSamples = preset->NumSounds;
+ for(i = 0;i < self->NumSamples;i++)
+ FSample_Construct(&self->Samples[i], preset->Sounds[i], sfont);
+ }
+}
+
+static void FPreset_Destruct(FPreset *self)
+{
+ ALsizei i;
+
+ for(i = 0;i < self->NumSamples;i++)
+ FSample_Destruct(&self->Samples[i]);
+ free(self->Samples);
+ self->Samples = NULL;
+ self->NumSamples = 0;
+}
+
+static ALboolean FPreset_canDelete(FPreset *self)
+{
+ ALsizei i;
+
+ for(i = 0;i < self->NumSamples;i++)
+ {
+ if(fluid_sample_refcount(STATIC_CAST(fluid_sample_t, &self->Samples[i])) != 0)
+ return AL_FALSE;
+ }
+ return AL_TRUE;
+}
+
+static char* FPreset_getName(fluid_preset_t *preset)
+{
+ return ((FPreset*)preset->data)->Name;
+}
+
+static int FPreset_getPreset(fluid_preset_t *preset)
+{
+ return ((FPreset*)preset->data)->Preset;
+}
+
+static int FPreset_getBank(fluid_preset_t *preset)
+{
+ return ((FPreset*)preset->data)->Bank;
+}
+
+static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int vel)
+{
+ FPreset *self = ((FPreset*)preset->data);
+ ALsizei i;
+
+ for(i = 0;i < self->NumSamples;i++)
+ {
+ FSample *sample = &self->Samples[i];
+ ALfontsound *sound = sample->Sound;
+ fluid_voice_t *voice;
+ ALsizei m;
+
+ if(!(key >= sound->MinKey && key <= sound->MaxKey && vel >= sound->MinVelocity && vel <= sound->MaxVelocity))
+ continue;
+
+ voice = fluid_synth_alloc_voice(synth, STATIC_CAST(fluid_sample_t, sample), channel, key, vel);
+ if(voice == NULL)
+ return FLUID_FAILED;
+
+ fluid_voice_gen_set(voice, GEN_MODLFOTOPITCH, sound->ModLfoToPitch);
+ fluid_voice_gen_set(voice, GEN_VIBLFOTOPITCH, sound->VibratoLfoToPitch);
+ fluid_voice_gen_set(voice, GEN_MODENVTOPITCH, sound->ModEnvToPitch);
+ fluid_voice_gen_set(voice, GEN_FILTERFC, sound->FilterCutoff);
+ fluid_voice_gen_set(voice, GEN_FILTERQ, sound->FilterQ);
+ fluid_voice_gen_set(voice, GEN_MODLFOTOFILTERFC, sound->ModLfoToFilterCutoff);
+ fluid_voice_gen_set(voice, GEN_MODENVTOFILTERFC, sound->ModEnvToFilterCutoff);
+ fluid_voice_gen_set(voice, GEN_MODLFOTOVOL, sound->ModLfoToVolume);
+ fluid_voice_gen_set(voice, GEN_CHORUSSEND, sound->ChorusSend);
+ fluid_voice_gen_set(voice, GEN_REVERBSEND, sound->ReverbSend);
+ fluid_voice_gen_set(voice, GEN_PAN, sound->Pan);
+ fluid_voice_gen_set(voice, GEN_MODLFODELAY, sound->ModLfo.Delay);
+ fluid_voice_gen_set(voice, GEN_MODLFOFREQ, sound->ModLfo.Frequency);
+ fluid_voice_gen_set(voice, GEN_VIBLFODELAY, sound->VibratoLfo.Delay);
+ fluid_voice_gen_set(voice, GEN_VIBLFOFREQ, sound->VibratoLfo.Frequency);
+ fluid_voice_gen_set(voice, GEN_MODENVDELAY, sound->ModEnv.DelayTime);
+ fluid_voice_gen_set(voice, GEN_MODENVATTACK, sound->ModEnv.AttackTime);
+ fluid_voice_gen_set(voice, GEN_MODENVHOLD, sound->ModEnv.HoldTime);
+ fluid_voice_gen_set(voice, GEN_MODENVDECAY, sound->ModEnv.DecayTime);
+ fluid_voice_gen_set(voice, GEN_MODENVSUSTAIN, sound->ModEnv.SustainAttn);
+ fluid_voice_gen_set(voice, GEN_MODENVRELEASE, sound->ModEnv.ReleaseTime);
+ fluid_voice_gen_set(voice, GEN_KEYTOMODENVHOLD, sound->ModEnv.KeyToHoldTime);
+ fluid_voice_gen_set(voice, GEN_KEYTOMODENVDECAY, sound->ModEnv.KeyToDecayTime);
+ fluid_voice_gen_set(voice, GEN_VOLENVDELAY, sound->VolEnv.DelayTime);
+ fluid_voice_gen_set(voice, GEN_VOLENVATTACK, sound->VolEnv.AttackTime);
+ fluid_voice_gen_set(voice, GEN_VOLENVHOLD, sound->VolEnv.HoldTime);
+ fluid_voice_gen_set(voice, GEN_VOLENVDECAY, sound->VolEnv.DecayTime);
+ fluid_voice_gen_set(voice, GEN_VOLENVSUSTAIN, sound->VolEnv.SustainAttn);
+ fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, sound->VolEnv.ReleaseTime);
+ fluid_voice_gen_set(voice, GEN_KEYTOVOLENVHOLD, sound->VolEnv.KeyToHoldTime);
+ fluid_voice_gen_set(voice, GEN_KEYTOVOLENVDECAY, sound->VolEnv.KeyToDecayTime);
+ fluid_voice_gen_set(voice, GEN_ATTENUATION, sound->Attenuation);
+ fluid_voice_gen_set(voice, GEN_COARSETUNE, sound->CoarseTuning);
+ fluid_voice_gen_set(voice, GEN_FINETUNE, sound->FineTuning);
+ fluid_voice_gen_set(voice, GEN_SAMPLEMODE, getSf2LoopMode(sound->LoopMode));
+ fluid_voice_gen_set(voice, GEN_SCALETUNE, sound->TuningScale);
+ fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, sound->ExclusiveClass);
+ for(m = 0;m < sample->NumMods;m++)
+ fluid_voice_add_mod(voice, &sample->Mods[m], FLUID_VOICE_OVERWRITE);
+
+ fluid_synth_start_voice(synth, voice);
+ }
+
+ return FLUID_OK;
+}
+
+
+typedef struct FSfont {
+ DERIVE_FROM_TYPE(fluid_sfont_t);
+
+ char Name[16];
+
+ FPreset *Presets;
+ ALsizei NumPresets;
+
+ ALsizei CurrentPos;
+} FSfont;
+
+static int FSfont_free(fluid_sfont_t *sfont);
+static char* FSfont_getName(fluid_sfont_t *sfont);
+static fluid_preset_t* FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum);
+static void FSfont_iterStart(fluid_sfont_t *sfont);
+static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset);
+
+static void FSfont_Construct(FSfont *self, ALsoundfont *sfont)
+{
+ STATIC_CAST(fluid_sfont_t, self)->data = self;
+ STATIC_CAST(fluid_sfont_t, self)->id = FLUID_FAILED;
+ STATIC_CAST(fluid_sfont_t, self)->free = FSfont_free;
+ STATIC_CAST(fluid_sfont_t, self)->get_name = FSfont_getName;
+ STATIC_CAST(fluid_sfont_t, self)->get_preset = FSfont_getPreset;
+ STATIC_CAST(fluid_sfont_t, self)->iteration_start = FSfont_iterStart;
+ STATIC_CAST(fluid_sfont_t, self)->iteration_next = FSfont_iterNext;
+
+ memset(self->Name, 0, sizeof(self->Name));
+ self->CurrentPos = 0;
+ self->NumPresets = 0;
+ self->Presets = calloc(1, sfont->NumPresets * sizeof(self->Presets[0]));
+ if(self->Presets)
+ {
+ ALsizei i;
+ self->NumPresets = sfont->NumPresets;
+ for(i = 0;i < self->NumPresets;i++)
+ FPreset_Construct(&self->Presets[i], sfont->Presets[i], STATIC_CAST(fluid_sfont_t, self), sfont);
+ }
+}
+
+static void FSfont_Destruct(FSfont *self)
+{
+ ALsizei i;
+
+ for(i = 0;i < self->NumPresets;i++)
+ FPreset_Destruct(&self->Presets[i]);
+ free(self->Presets);
+ self->Presets = NULL;
+ self->NumPresets = 0;
+ self->CurrentPos = 0;
+}
+
+static int FSfont_free(fluid_sfont_t *sfont)
+{
+ FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
+ ALsizei i;
+
+ for(i = 0;i < self->NumPresets;i++)
+ {
+ if(!FPreset_canDelete(&self->Presets[i]))
+ return 1;
+ }
+
+ FSfont_Destruct(self);
+ free(self);
+ return 0;
+}
+
+static char* FSfont_getName(fluid_sfont_t *sfont)
+{
+ return STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->Name;
+}
+
+static fluid_preset_t *FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum)
+{
+ FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
+ ALsizei i;
+
+ for(i = 0;i < self->NumPresets;i++)
+ {
+ FPreset *preset = &self->Presets[i];
+ if(preset->Bank == (int)bank && preset->Preset == (int)prenum)
+ return STATIC_CAST(fluid_preset_t, preset);
+ }
+
+ return NULL;
+}
+
+static void FSfont_iterStart(fluid_sfont_t *sfont)
+{
+ STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->CurrentPos = 0;
+}
+
+static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset)
+{
+ FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
+ if(self->CurrentPos >= self->NumPresets)
+ return 0;
+ *preset = *STATIC_CAST(fluid_preset_t, &self->Presets[self->CurrentPos++]);
+ preset->free = NULL;
+ return 1;
+}
+
+
+typedef struct FSynth {
+ DERIVE_FROM_TYPE(MidiSynth);
+ DERIVE_FROM_TYPE(fluid_sfloader_t);
+
+ fluid_settings_t *Settings;
+ fluid_synth_t *Synth;
+ int *FontIDs;
+ ALsizei NumFontIDs;
+
+ ALboolean ForceGM2BankSelect;
+ ALfloat GainScale;
+} FSynth;
+
+static void FSynth_Construct(FSynth *self, ALCdevice *device);
+static void FSynth_Destruct(FSynth *self);
+static ALboolean FSynth_init(FSynth *self, ALCdevice *device);
+static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
+static void FSynth_setGain(FSynth *self, ALfloat gain);
+static void FSynth_setState(FSynth *self, ALenum state);
+static void FSynth_stop(FSynth *self);
+static void FSynth_reset(FSynth *self);
+static void FSynth_update(FSynth *self, ALCdevice *device);
+static void FSynth_processQueue(FSynth *self, ALuint64 time);
+static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
+static void FSynth_Delete(FSynth *self);
+DEFINE_MIDISYNTH_VTABLE(FSynth);
+
+static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename);
+
+
+static void FSynth_Construct(FSynth *self, ALCdevice *device)
+{
+ MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
+ SET_VTABLE2(FSynth, MidiSynth, self);
+
+ STATIC_CAST(fluid_sfloader_t, self)->data = self;
+ STATIC_CAST(fluid_sfloader_t, self)->free = NULL;
+ STATIC_CAST(fluid_sfloader_t, self)->load = FSynth_loadSfont;
+
+ self->Settings = NULL;
+ self->Synth = NULL;
+ self->FontIDs = NULL;
+ self->NumFontIDs = 0;
+ self->ForceGM2BankSelect = AL_FALSE;
+ self->GainScale = 0.2f;
+}
+
+static void FSynth_Destruct(FSynth *self)
+{
+ ALsizei i;
+
+ for(i = 0;i < self->NumFontIDs;i++)
+ fluid_synth_sfunload(self->Synth, self->FontIDs[i], 0);
+ free(self->FontIDs);
+ self->FontIDs = NULL;
+ self->NumFontIDs = 0;
+
+ if(self->Synth != NULL)
+ delete_fluid_synth(self->Synth);
+ self->Synth = NULL;
+
+ if(self->Settings != NULL)
+ delete_fluid_settings(self->Settings);
+ self->Settings = NULL;
+
+ MidiSynth_Destruct(STATIC_CAST(MidiSynth, self));
+}
+
+static ALboolean FSynth_init(FSynth *self, ALCdevice *device)
+{
+ ALfloat vol;
+
+ if(ConfigValueFloat("midi", "volume", &vol))
+ {
+ if(!(vol <= 0.0f))
+ {
+ ERR("MIDI volume %f clamped to 0\n", vol);
+ vol = 0.0f;
+ }
+ self->GainScale = powf(10.0f, vol / 20.0f);
+ }
+
+ self->Settings = new_fluid_settings();
+ if(!self->Settings)
+ {
+ ERR("Failed to create FluidSettings\n");
+ return AL_FALSE;
+ }
+
+ fluid_settings_setint(self->Settings, "synth.polyphony", 256);
+ fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale);
+ fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
+
+ self->Synth = new_fluid_synth(self->Settings);
+ if(!self->Synth)
+ {
+ ERR("Failed to create FluidSynth\n");
+ return AL_FALSE;
+ }
+
+ fluid_synth_add_sfloader(self->Synth, STATIC_CAST(fluid_sfloader_t, self));
+
+ return AL_TRUE;
+}
+
+
+static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename)
+{
+ FSynth *self = STATIC_UPCAST(FSynth, fluid_sfloader_t, loader);
+ FSfont *sfont;
+ int idx;
+
+ if(!filename || sscanf(filename, "_al_internal %d", &idx) != 1)
+ return NULL;
+ if(idx < 0 || idx >= STATIC_CAST(MidiSynth, self)->NumSoundfonts)
+ {
+ ERR("Received invalid soundfont index %d (max: %d)\n", idx, STATIC_CAST(MidiSynth, self)->NumSoundfonts);
+ return NULL;
+ }
+
+ sfont = calloc(1, sizeof(sfont[0]));
+ if(!sfont) return NULL;
+
+ FSfont_Construct(sfont, STATIC_CAST(MidiSynth, self)->Soundfonts[idx]);
+ return STATIC_CAST(fluid_sfont_t, sfont);
+}
+
+static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids)
+{
+ int *fontid;
+ ALenum ret;
+ ALsizei i;
+
+ ret = MidiSynth_selectSoundfonts(STATIC_CAST(MidiSynth, self), context, count, ids);
+ if(ret != AL_NO_ERROR) return ret;
+
+ ALCdevice_Lock(context->Device);
+ for(i = 0;i < 16;i++)
+ fluid_synth_all_sounds_off(self->Synth, i);
+ ALCdevice_Unlock(context->Device);
+
+ fontid = malloc(count * sizeof(fontid[0]));
+ if(fontid)
+ {
+ for(i = 0;i < STATIC_CAST(MidiSynth, self)->NumSoundfonts;i++)
+ {
+ char name[16];
+ snprintf(name, sizeof(name), "_al_internal %d", i);
+
+ fontid[i] = fluid_synth_sfload(self->Synth, name, 0);
+ if(fontid[i] == FLUID_FAILED)
+ ERR("Failed to load selected soundfont %d\n", i);
+ }
+
+ fontid = ExchangePtr((XchgPtr*)&self->FontIDs, fontid);
+ count = ExchangeInt(&self->NumFontIDs, count);
+ }
+ else
+ {
+ ERR("Failed to allocate space for %d font IDs!\n", count);
+ fontid = ExchangePtr((XchgPtr*)&self->FontIDs, NULL);
+ count = ExchangeInt(&self->NumFontIDs, 0);
+ }
+
+ for(i = 0;i < count;i++)
+ fluid_synth_sfunload(self->Synth, fontid[i], 0);
+ free(fontid);
+
+ return ret;
+}
+
+
+static void FSynth_setGain(FSynth *self, ALfloat gain)
+{
+ fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale * gain);
+ fluid_synth_set_gain(self->Synth, self->GainScale * gain);
+ MidiSynth_setGain(STATIC_CAST(MidiSynth, self), gain);
+}
+
+
+static void FSynth_setState(FSynth *self, ALenum state)
+{
+ MidiSynth_setState(STATIC_CAST(MidiSynth, self), state);
+}
+
+static void FSynth_stop(FSynth *self)
+{
+ MidiSynth *synth = STATIC_CAST(MidiSynth, self);
+ ALsizei chan;
+
+ /* Make sure all pending events are processed. */
+ while(!(synth->SamplesToNext >= 1.0))
+ {
+ ALuint64 time = synth->NextEvtTime;
+ if(time == UINT64_MAX)
+ break;
+
+ synth->SamplesSinceLast -= (time - synth->LastEvtTime) * synth->SamplesPerTick;
+ synth->SamplesSinceLast = maxd(synth->SamplesSinceLast, 0.0);
+ synth->LastEvtTime = time;
+ FSynth_processQueue(self, time);
+
+ synth->NextEvtTime = MidiSynth_getNextEvtTime(synth);
+ if(synth->NextEvtTime != UINT64_MAX)
+ synth->SamplesToNext += (synth->NextEvtTime - synth->LastEvtTime) * synth->SamplesPerTick;
+ }
+
+ /* All notes off */
+ for(chan = 0;chan < 16;chan++)
+ fluid_synth_cc(self->Synth, chan, CTRL_ALLNOTESOFF, 0);
+
+ MidiSynth_stop(STATIC_CAST(MidiSynth, self));
+}
+
+static void FSynth_reset(FSynth *self)
+{
+ /* Reset to power-up status. */
+ fluid_synth_system_reset(self->Synth);
+
+ MidiSynth_reset(STATIC_CAST(MidiSynth, self));
+}
+
+
+static void FSynth_update(FSynth *self, ALCdevice *device)
+{
+ fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
+ fluid_synth_set_sample_rate(self->Synth, device->Frequency);
+ MidiSynth_update(STATIC_CAST(MidiSynth, self), device);
+}
+
+
+static void FSynth_processQueue(FSynth *self, ALuint64 time)
+{
+ EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
+
+ while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
+ {
+ const MidiEvent *evt = &queue->events[queue->pos];
+
+ if(evt->event == SYSEX_EVENT)
+ {
+ static const ALbyte gm2_on[] = { 0x7E, 0x7F, 0x09, 0x03 };
+ static const ALbyte gm2_off[] = { 0x7E, 0x7F, 0x09, 0x02 };
+ int handled = 0;
+
+ fluid_synth_sysex(self->Synth, evt->param.sysex.data, evt->param.sysex.size, NULL, NULL, &handled, 0);
+ if(!handled && evt->param.sysex.size >= (ALsizei)sizeof(gm2_on))
+ {
+ if(memcmp(evt->param.sysex.data, gm2_on, sizeof(gm2_on)) == 0)
+ self->ForceGM2BankSelect = AL_TRUE;
+ else if(memcmp(evt->param.sysex.data, gm2_off, sizeof(gm2_off)) == 0)
+ self->ForceGM2BankSelect = AL_FALSE;
+ }
+ }
+ else switch((evt->event&0xF0))
+ {
+ case AL_NOTEOFF_SOFT:
+ fluid_synth_noteoff(self->Synth, (evt->event&0x0F), evt->param.val[0]);
+ break;
+ case AL_NOTEON_SOFT:
+ fluid_synth_noteon(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]);
+ break;
+ case AL_KEYPRESSURE_SOFT:
+ break;
+
+ case AL_CONTROLLERCHANGE_SOFT:
+ if(self->ForceGM2BankSelect)
+ {
+ int chan = (evt->event&0x0F);
+ if(evt->param.val[0] == CTRL_BANKSELECT_MSB)
+ {
+ if(evt->param.val[1] == 120 && (chan == 9 || chan == 10))
+ fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_DRUM);
+ else if(evt->param.val[1] == 121)
+ fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_MELODIC);
+ break;
+ }
+ if(evt->param.val[0] == CTRL_BANKSELECT_LSB)
+ {
+ fluid_synth_bank_select(self->Synth, chan, evt->param.val[1]);
+ break;
+ }
+ }
+ fluid_synth_cc(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]);
+ break;
+ case AL_PROGRAMCHANGE_SOFT:
+ fluid_synth_program_change(self->Synth, (evt->event&0x0F), evt->param.val[0]);
+ break;
+
+ case AL_CHANNELPRESSURE_SOFT:
+ fluid_synth_channel_pressure(self->Synth, (evt->event&0x0F), evt->param.val[0]);
+ break;
+
+ case AL_PITCHBEND_SOFT:
+ fluid_synth_pitch_bend(self->Synth, (evt->event&0x0F), (evt->param.val[0]&0x7F) |
+ ((evt->param.val[1]&0x7F)<<7));
+ break;
+ }
+
+ queue->pos++;
+ }
+}
+
+static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE])
+{
+ MidiSynth *synth = STATIC_CAST(MidiSynth, self);
+ ALenum state = synth->State;
+ ALuint total = 0;
+
+ if(state == AL_INITIAL)
+ return;
+ if(state != AL_PLAYING)
+ {
+ fluid_synth_write_float(self->Synth, SamplesToDo, DryBuffer[FrontLeft], 0, 1,
+ DryBuffer[FrontRight], 0, 1);
+ return;
+ }
+
+ while(total < SamplesToDo)
+ {
+ if(synth->SamplesToNext >= 1.0)
+ {
+ ALuint todo = minu(SamplesToDo - total, fastf2u(synth->SamplesToNext));
+
+ fluid_synth_write_float(self->Synth, todo,
+ &DryBuffer[FrontLeft][total], 0, 1,
+ &DryBuffer[FrontRight][total], 0, 1);
+ total += todo;
+ synth->SamplesSinceLast += todo;
+ synth->SamplesToNext -= todo;
+ }
+ else
+ {
+ ALuint64 time = synth->NextEvtTime;
+ if(time == UINT64_MAX)
+ {
+ synth->SamplesSinceLast += SamplesToDo-total;
+ fluid_synth_write_float(self->Synth, SamplesToDo-total,
+ &DryBuffer[FrontLeft][total], 0, 1,
+ &DryBuffer[FrontRight][total], 0, 1);
+ break;
+ }
+
+ synth->SamplesSinceLast -= (time - synth->LastEvtTime) * synth->SamplesPerTick;
+ synth->SamplesSinceLast = maxd(synth->SamplesSinceLast, 0.0);
+ synth->LastEvtTime = time;
+ FSynth_processQueue(self, time);
+
+ synth->NextEvtTime = MidiSynth_getNextEvtTime(synth);
+ if(synth->NextEvtTime != UINT64_MAX)
+ synth->SamplesToNext += (synth->NextEvtTime - synth->LastEvtTime) * synth->SamplesPerTick;
+ }
+ }
+}
+
+
+static void FSynth_Delete(FSynth *self)
+{
+ free(self);
+}
+
+
+MidiSynth *FSynth_create(ALCdevice *device)
+{
+ FSynth *synth = calloc(1, sizeof(*synth));
+ if(!synth)
+ {
+ ERR("Failed to allocate FSynth\n");
+ return NULL;
+ }
+ FSynth_Construct(synth, device);
+
+ if(FSynth_init(synth, device) == AL_FALSE)
+ {
+ DELETE_OBJ(STATIC_CAST(MidiSynth, synth));
+ return NULL;
+ }
+
+ return STATIC_CAST(MidiSynth, synth);
+}
+
+#else
+
+MidiSynth *FSynth_create(ALCdevice* UNUSED(device))
+{
+ return NULL;
+}
+
+#endif
diff --git a/Alc/midi/sf2load.c b/Alc/midi/sf2load.c
new file mode 100644
index 00000000..5bba345f
--- /dev/null
+++ b/Alc/midi/sf2load.c
@@ -0,0 +1,1436 @@
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alMidi.h"
+#include "alError.h"
+#include "alu.h"
+
+#include "midi/base.h"
+
+
+static ALuint read_le32(Reader *stream)
+{
+ ALubyte buf[4];
+ if(READ(stream, buf, 4) != 4)
+ {
+ READERR(stream) = 1;
+ return 0;
+ }
+ return (buf[3]<<24) | (buf[2]<<16) | (buf[1]<<8) | buf[0];
+}
+static ALushort read_le16(Reader *stream)
+{
+ ALubyte buf[2];
+ if(READ(stream, buf, 2) != 2)
+ {
+ READERR(stream) = 1;
+ return 0;
+ }
+ return (buf[1]<<8) | buf[0];
+}
+static ALubyte read_8(Reader *stream)
+{
+ ALubyte buf[1];
+ if(READ(stream, buf, 1) != 1)
+ {
+ READERR(stream) = 1;
+ return 0;
+ }
+ return buf[0];
+}
+static void skip(Reader *stream, ALuint amt)
+{
+ while(amt > 0 && !READERR(stream))
+ {
+ char buf[4096];
+ size_t got;
+
+ got = READ(stream, buf, minu(sizeof(buf), amt));
+ if(got == 0) READERR(stream) = 1;
+
+ amt -= got;
+ }
+}
+
+typedef struct Generator {
+ ALushort mGenerator;
+ ALushort mAmount;
+} Generator;
+static void Generator_read(Generator *self, Reader *stream)
+{
+ self->mGenerator = read_le16(stream);
+ self->mAmount = read_le16(stream);
+}
+
+static const ALint DefaultGenValue[60] = {
+ 0, /* 0 - startAddrOffset */
+ 0, /* 1 - endAddrOffset */
+ 0, /* 2 - startloopAddrOffset */
+ 0, /* 3 - endloopAddrOffset */
+ 0, /* 4 - startAddrCoarseOffset */
+ 0, /* 5 - modLfoToPitch */
+ 0, /* 6 - vibLfoToPitch */
+ 0, /* 7 - modEnvToPitch */
+ 13500, /* 8 - initialFilterFc */
+ 0, /* 9 - initialFilterQ */
+ 0, /* 10 - modLfoToFilterFc */
+ 0, /* 11 - modEnvToFilterFc */
+ 0, /* 12 - endAddrCoarseOffset */
+ 0, /* 13 - modLfoToVolume */
+ 0, /* 14 - */
+ 0, /* 15 - chorusEffectsSend */
+ 0, /* 16 - reverbEffectsSend */
+ 0, /* 17 - pan */
+ 0, /* 18 - */
+ 0, /* 19 - */
+ 0, /* 20 - */
+ -12000, /* 21 - delayModLFO */
+ 0, /* 22 - freqModLFO */
+ -12000, /* 23 - delayVibLFO */
+ 0, /* 24 - freqVibLFO */
+ -12000, /* 25 - delayModEnv */
+ -12000, /* 26 - attackModEnv */
+ -12000, /* 27 - holdModEnv */
+ -12000, /* 28 - decayModEnv */
+ 0, /* 29 - sustainModEnv */
+ -12000, /* 30 - releaseModEnv */
+ 0, /* 31 - keynumToModEnvHold */
+ 0, /* 32 - keynumToModEnvDecay */
+ -12000, /* 33 - delayVolEnv */
+ -12000, /* 34 - attackVolEnv */
+ -12000, /* 35 - holdVolEnv */
+ -12000, /* 36 - decayVolEnv */
+ 0, /* 37 - sustainVolEnv */
+ -12000, /* 38 - releaseVolEnv */
+ 0, /* 39 - keynumToVolEnvHold */
+ 0, /* 40 - keynumToVolEnvDecay */
+ 0, /* 41 - */
+ 0, /* 42 - */
+ 0, /* 43 - keyRange */
+ 0, /* 44 - velRange */
+ 0, /* 45 - startloopAddrCoarseOffset */
+ 0, /* 46 - keynum */
+ 0, /* 47 - velocity */
+ 0, /* 48 - initialAttenuation */
+ 0, /* 49 - */
+ 0, /* 50 - endloopAddrCoarseOffset */
+ 0, /* 51 - corseTune */
+ 0, /* 52 - fineTune */
+ 0, /* 53 - */
+ 0, /* 54 - sampleModes */
+ 0, /* 55 - */
+ 100, /* 56 - scaleTuning */
+ 0, /* 57 - exclusiveClass */
+ 0, /* 58 - overridingRootKey */
+ 0, /* 59 - */
+};
+
+typedef struct Modulator {
+ ALushort mSrcOp;
+ ALushort mDstOp;
+ ALshort mAmount;
+ ALushort mAmtSrcOp;
+ ALushort mTransOp;
+} Modulator;
+static void Modulator_read(Modulator *self, Reader *stream)
+{
+ self->mSrcOp = read_le16(stream);
+ self->mDstOp = read_le16(stream);
+ self->mAmount = read_le16(stream);
+ self->mAmtSrcOp = read_le16(stream);
+ self->mTransOp = read_le16(stream);
+}
+
+typedef struct Zone {
+ ALushort mGenIdx;
+ ALushort mModIdx;
+} Zone;
+static void Zone_read(Zone *self, Reader *stream)
+{
+ self->mGenIdx = read_le16(stream);
+ self->mModIdx = read_le16(stream);
+}
+
+typedef struct PresetHeader {
+ ALchar mName[20];
+ ALushort mPreset; /* MIDI program number */
+ ALushort mBank;
+ ALushort mZoneIdx;
+ ALuint mLibrary;
+ ALuint mGenre;
+ ALuint mMorphology;
+} PresetHeader;
+static void PresetHeader_read(PresetHeader *self, Reader *stream)
+{
+ if(READ(stream, self->mName, sizeof(self->mName)) != sizeof(self->mName))
+ READERR(stream) = 1;
+ self->mPreset = read_le16(stream);
+ self->mBank = read_le16(stream);
+ self->mZoneIdx = read_le16(stream);
+ self->mLibrary = read_le32(stream);
+ self->mGenre = read_le32(stream);
+ self->mMorphology = read_le32(stream);
+}
+
+typedef struct InstrumentHeader {
+ ALchar mName[20];
+ ALushort mZoneIdx;
+} InstrumentHeader;
+static void InstrumentHeader_read(InstrumentHeader *self, Reader *stream)
+{
+ if(READ(stream, self->mName, sizeof(self->mName)) != sizeof(self->mName))
+ READERR(stream) = 1;
+ self->mZoneIdx = read_le16(stream);
+}
+
+typedef struct SampleHeader {
+ ALchar mName[20]; // 20 bytes
+ ALuint mStart;
+ ALuint mEnd;
+ ALuint mStartloop;
+ ALuint mEndloop;
+ ALuint mSampleRate;
+ ALubyte mOriginalKey;
+ ALbyte mCorrection;
+ ALushort mSampleLink;
+ ALushort mSampleType;
+} SampleHeader;
+static void SampleHeader_read(SampleHeader *self, Reader *stream)
+{
+ if(READ(stream, self->mName, sizeof(self->mName)) != sizeof(self->mName))
+ READERR(stream) = 1;
+ self->mStart = read_le32(stream);
+ self->mEnd = read_le32(stream);
+ self->mStartloop = read_le32(stream);
+ self->mEndloop = read_le32(stream);
+ self->mSampleRate = read_le32(stream);
+ self->mOriginalKey = read_8(stream);
+ self->mCorrection = read_8(stream);
+ self->mSampleLink = read_le16(stream);
+ self->mSampleType = read_le16(stream);
+}
+
+
+typedef struct Soundfont {
+ ALuint ifil;
+ ALchar *irom;
+
+ PresetHeader *phdr;
+ ALsizei phdr_size;
+
+ Zone *pbag;
+ ALsizei pbag_size;
+ Modulator *pmod;
+ ALsizei pmod_size;
+ Generator *pgen;
+ ALsizei pgen_size;
+
+ InstrumentHeader *inst;
+ ALsizei inst_size;
+
+ Zone *ibag;
+ ALsizei ibag_size;
+ Modulator *imod;
+ ALsizei imod_size;
+ Generator *igen;
+ ALsizei igen_size;
+
+ SampleHeader *shdr;
+ ALsizei shdr_size;
+} Soundfont;
+
+static void Soundfont_Construct(Soundfont *self)
+{
+ self->ifil = 0;
+ self->irom = NULL;
+
+ self->phdr = NULL;
+ self->phdr_size = 0;
+
+ self->pbag = NULL;
+ self->pbag_size = 0;
+ self->pmod = NULL;
+ self->pmod_size = 0;
+ self->pgen = NULL;
+ self->pgen_size = 0;
+
+ self->inst = NULL;
+ self->inst_size = 0;
+
+ self->ibag = NULL;
+ self->ibag_size = 0;
+ self->imod = NULL;
+ self->imod_size = 0;
+ self->igen = NULL;
+ self->igen_size = 0;
+
+ self->shdr = NULL;
+ self->shdr_size = 0;
+}
+
+static void Soundfont_Destruct(Soundfont *self)
+{
+ free(self->irom);
+ self->irom = NULL;
+
+ free(self->phdr);
+ self->phdr = NULL;
+ self->phdr_size = 0;
+
+ free(self->pbag);
+ self->pbag = NULL;
+ self->pbag_size = 0;
+ free(self->pmod);
+ self->pmod = NULL;
+ self->pmod_size = 0;
+ free(self->pgen);
+ self->pgen = NULL;
+ self->pgen_size = 0;
+
+ free(self->inst);
+ self->inst = NULL;
+ self->inst_size = 0;
+
+ free(self->ibag);
+ self->ibag = NULL;
+ self->ibag_size = 0;
+ free(self->imod);
+ self->imod = NULL;
+ self->imod_size = 0;
+ free(self->igen);
+ self->igen = NULL;
+ self->igen_size = 0;
+
+ free(self->shdr);
+ self->shdr = NULL;
+ self->shdr_size = 0;
+}
+
+
+#define FOURCC(a,b,c,d) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a))
+#define FOURCCARGS(x) (char)((x)&0xff), (char)(((x)>>8)&0xff), (char)(((x)>>16)&0xff), (char)(((x)>>24)&0xff)
+typedef struct RiffHdr {
+ ALuint mCode;
+ ALuint mSize;
+} RiffHdr;
+static void RiffHdr_read(RiffHdr *self, Reader *stream)
+{
+ self->mCode = read_le32(stream);
+ self->mSize = read_le32(stream);
+}
+
+
+typedef struct GenModList {
+ Generator *gens;
+ ALsizei gens_size;
+ ALsizei gens_max;
+
+ Modulator *mods;
+ ALsizei mods_size;
+ ALsizei mods_max;
+} GenModList;
+
+static void GenModList_Construct(GenModList *self)
+{
+ self->gens = NULL;
+ self->gens_size = 0;
+ self->gens_max = 0;
+
+ self->mods = NULL;
+ self->mods_size = 0;
+ self->mods_max = 0;
+}
+
+static void GenModList_Destruct(GenModList *self)
+{
+ free(self->gens);
+ self->gens = NULL;
+ self->gens_size = 0;
+ self->gens_max = 0;
+
+ free(self->mods);
+ self->mods = NULL;
+ self->mods_size = 0;
+ self->mods_max = 0;
+}
+
+static GenModList GenModList_clone(const GenModList *self)
+{
+ GenModList ret;
+
+ ret.gens = malloc(self->gens_max * sizeof(ret.gens[0]));
+ memcpy(ret.gens, self->gens, self->gens_size * sizeof(ret.gens[0]));
+ ret.gens_size = self->gens_size;
+ ret.gens_max = self->gens_max;
+
+ ret.mods = malloc(self->mods_max * sizeof(ret.mods[0]));
+ memcpy(ret.mods, self->mods, self->mods_size * sizeof(ret.mods[0]));
+ ret.mods_size = self->mods_size;
+ ret.mods_max = self->mods_max;
+
+ return ret;
+}
+
+static void GenModList_insertGen(GenModList *self, const Generator *gen, ALboolean ispreset)
+{
+ Generator *i = self->gens;
+ Generator *end = i + self->gens_size;
+ for(;i != end;i++)
+ {
+ if(i->mGenerator == gen->mGenerator)
+ {
+ i->mAmount = gen->mAmount;
+ return;
+ }
+ }
+
+ if(ispreset &&
+ (gen->mGenerator == 0 || gen->mGenerator == 1 || gen->mGenerator == 2 ||
+ gen->mGenerator == 3 || gen->mGenerator == 4 || gen->mGenerator == 12 ||
+ gen->mGenerator == 45 || gen->mGenerator == 46 || gen->mGenerator == 47 ||
+ gen->mGenerator == 50 || gen->mGenerator == 54 || gen->mGenerator == 57 ||
+ gen->mGenerator == 58))
+ return;
+
+ if(self->gens_size == self->gens_max)
+ {
+ void *temp = NULL;
+ ALsizei newsize;
+
+ newsize = (self->gens_max ? self->gens_max<<1 : 1);
+ if(newsize > self->gens_max)
+ temp = realloc(self->gens, newsize * sizeof(self->gens[0]));
+ if(!temp)
+ {
+ ERR("Failed to increase generator storage to %d elements (from %d)\n",
+ newsize, self->gens_max);
+ return;
+ }
+
+ self->gens = temp;
+ self->gens_max = newsize;
+ }
+
+ self->gens[self->gens_size] = *gen;
+ self->gens_size++;
+}
+static void GenModList_accumGen(GenModList *self, const Generator *gen)
+{
+ Generator *i = self->gens;
+ Generator *end = i + self->gens_size;
+ for(;i != end;i++)
+ {
+ if(i->mGenerator == gen->mGenerator)
+ {
+ if(gen->mGenerator == 43 || gen->mGenerator == 44)
+ {
+ /* Range generators accumulate by taking the intersection of
+ * the two ranges.
+ */
+ ALushort low = maxu(i->mAmount&0x00ff, gen->mAmount&0x00ff);
+ ALushort high = minu(i->mAmount&0xff00, gen->mAmount&0xff00);
+ i->mAmount = low | high;
+ }
+ else
+ i->mAmount += gen->mAmount;
+ return;
+ }
+ }
+
+ if(self->gens_size == self->gens_max)
+ {
+ void *temp = NULL;
+ ALsizei newsize;
+
+ newsize = (self->gens_max ? self->gens_max<<1 : 1);
+ if(newsize > self->gens_max)
+ temp = realloc(self->gens, newsize * sizeof(self->gens[0]));
+ if(!temp)
+ {
+ ERR("Failed to increase generator storage to %d elements (from %d)\n",
+ newsize, self->gens_max);
+ return;
+ }
+
+ self->gens = temp;
+ self->gens_max = newsize;
+ }
+
+ self->gens[self->gens_size] = *gen;
+ if(gen->mGenerator < 60)
+ self->gens[self->gens_size].mAmount += DefaultGenValue[gen->mGenerator];
+ self->gens_size++;
+}
+
+static void GenModList_insertMod(GenModList *self, const Modulator *mod)
+{
+ Modulator *i = self->mods;
+ Modulator *end = i + self->mods_size;
+ for(;i != end;i++)
+ {
+ if(i->mDstOp == mod->mDstOp && i->mSrcOp == mod->mSrcOp &&
+ i->mAmtSrcOp == mod->mAmtSrcOp && i->mTransOp == mod->mTransOp)
+ {
+ i->mAmount = mod->mAmount;
+ return;
+ }
+ }
+
+ if(self->mods_size == self->mods_max)
+ {
+ void *temp = NULL;
+ ALsizei newsize;
+
+ newsize = (self->mods_max ? self->mods_max<<1 : 1);
+ if(newsize > self->mods_max)
+ temp = realloc(self->mods, newsize * sizeof(self->mods[0]));
+ if(!temp)
+ {
+ ERR("Failed to increase modulator storage to %d elements (from %d)\n",
+ newsize, self->mods_max);
+ return;
+ }
+
+ self->mods = temp;
+ self->mods_max = newsize;
+ }
+
+ self->mods[self->mods_size] = *mod;
+ self->mods_size++;
+}
+static void GenModList_accumMod(GenModList *self, const Modulator *mod)
+{
+ Modulator *i = self->mods;
+ Modulator *end = i + self->mods_size;
+ for(;i != end;i++)
+ {
+ if(i->mDstOp == mod->mDstOp && i->mSrcOp == mod->mSrcOp &&
+ i->mAmtSrcOp == mod->mAmtSrcOp && i->mTransOp == mod->mTransOp)
+ {
+ i->mAmount += mod->mAmount;
+ return;
+ }
+ }
+
+ if(self->mods_size == self->mods_max)
+ {
+ void *temp = NULL;
+ ALsizei newsize;
+
+ newsize = (self->mods_max ? self->mods_max<<1 : 1);
+ if(newsize > self->mods_max)
+ temp = realloc(self->mods, newsize * sizeof(self->mods[0]));
+ if(!temp)
+ {
+ ERR("Failed to increase modulator storage to %d elements (from %d)\n",
+ newsize, self->mods_max);
+ return;
+ }
+
+ self->mods = temp;
+ self->mods_max = newsize;
+ }
+
+ self->mods[self->mods_size] = *mod;
+ if(mod->mSrcOp == 0x0502 && mod->mDstOp == 48 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 960;
+ else if(mod->mSrcOp == 0x0102 && mod->mDstOp == 8 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += -2400;
+ else if(mod->mSrcOp == 0x000D && mod->mDstOp == 6 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 50;
+ else if(mod->mSrcOp == 0x0081 && mod->mDstOp == 6 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 50;
+ else if(mod->mSrcOp == 0x0582 && mod->mDstOp == 48 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 960;
+ else if(mod->mSrcOp == 0x028A && mod->mDstOp == 17 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 1000;
+ else if(mod->mSrcOp == 0x058B && mod->mDstOp == 48 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 960;
+ else if(mod->mSrcOp == 0x00DB && mod->mDstOp == 16 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 200;
+ else if(mod->mSrcOp == 0x00DD && mod->mDstOp == 15 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 200;
+ /*else if(mod->mSrcOp == 0x020E && mod->mDstOp == ?initialpitch? && mod->mAmtSrcOp == 0x0010 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 12700;*/
+ self->mods_size++;
+}
+
+
+#define ERROR_GOTO(lbl_, ...) do { \
+ ERR(__VA_ARGS__); \
+ goto lbl_; \
+} while(0)
+
+static ALboolean ensureFontSanity(const Soundfont *sfont)
+{
+ ALsizei i;
+
+ for(i = 0;i < sfont->phdr_size-1;i++)
+ {
+ if(sfont->phdr[i].mZoneIdx >= sfont->pbag_size)
+ {
+ WARN("Preset %d has invalid zone index %d (max: %d)\n", i,
+ sfont->phdr[i].mZoneIdx, sfont->pbag_size);
+ return AL_FALSE;
+ }
+ if(sfont->phdr[i+1].mZoneIdx < sfont->phdr[i].mZoneIdx)
+ {
+ WARN("Preset %d has invalid zone index (%d does not follow %d)\n", i+1,
+ sfont->phdr[i+1].mZoneIdx, sfont->phdr[i].mZoneIdx);
+ return AL_FALSE;
+ }
+ }
+ if(sfont->phdr[i].mZoneIdx >= sfont->pbag_size)
+ {
+ WARN("Preset %d has invalid zone index %d (max: %d)\n", i,
+ sfont->phdr[i].mZoneIdx, sfont->pbag_size);
+ return AL_FALSE;
+ }
+
+ for(i = 0;i < sfont->pbag_size-1;i++)
+ {
+ if(sfont->pbag[i].mGenIdx >= sfont->pgen_size)
+ {
+ WARN("Preset zone %d has invalid generator index %d (max: %d)\n", i,
+ sfont->pbag[i].mGenIdx, sfont->pgen_size);
+ return AL_FALSE;
+ }
+ if(sfont->pbag[i+1].mGenIdx < sfont->pbag[i].mGenIdx)
+ {
+ WARN("Preset zone %d has invalid generator index (%d does not follow %d)\n", i+1,
+ sfont->pbag[i+1].mGenIdx, sfont->pbag[i].mGenIdx);
+ return AL_FALSE;
+ }
+ if(sfont->pbag[i].mModIdx >= sfont->pmod_size)
+ {
+ WARN("Preset zone %d has invalid modulator index %d (max: %d)\n", i,
+ sfont->pbag[i].mModIdx, sfont->pmod_size);
+ return AL_FALSE;
+ }
+ if(sfont->pbag[i+1].mModIdx < sfont->pbag[i].mModIdx)
+ {
+ WARN("Preset zone %d has invalid modulator index (%d does not follow %d)\n", i+1,
+ sfont->pbag[i+1].mModIdx, sfont->pbag[i].mModIdx);
+ return AL_FALSE;
+ }
+ }
+ if(sfont->pbag[i].mGenIdx >= sfont->pgen_size)
+ {
+ WARN("Preset zone %d has invalid generator index %d (max: %d)\n", i,
+ sfont->pbag[i].mGenIdx, sfont->pgen_size);
+ return AL_FALSE;
+ }
+ if(sfont->pbag[i].mModIdx >= sfont->pmod_size)
+ {
+ WARN("Preset zone %d has invalid modulator index %d (max: %d)\n", i,
+ sfont->pbag[i].mModIdx, sfont->pmod_size);
+ return AL_FALSE;
+ }
+
+
+ for(i = 0;i < sfont->inst_size-1;i++)
+ {
+ if(sfont->inst[i].mZoneIdx >= sfont->ibag_size)
+ {
+ WARN("Instrument %d has invalid zone index %d (max: %d)\n", i,
+ sfont->inst[i].mZoneIdx, sfont->ibag_size);
+ return AL_FALSE;
+ }
+ if(sfont->inst[i+1].mZoneIdx < sfont->inst[i].mZoneIdx)
+ {
+ WARN("Instrument %d has invalid zone index (%d does not follow %d)\n", i+1,
+ sfont->inst[i+1].mZoneIdx, sfont->inst[i].mZoneIdx);
+ return AL_FALSE;
+ }
+ }
+ if(sfont->inst[i].mZoneIdx >= sfont->ibag_size)
+ {
+ WARN("Instrument %d has invalid zone index %d (max: %d)\n", i,
+ sfont->inst[i].mZoneIdx, sfont->ibag_size);
+ return AL_FALSE;
+ }
+
+ for(i = 0;i < sfont->ibag_size-1;i++)
+ {
+ if(sfont->ibag[i].mGenIdx >= sfont->igen_size)
+ {
+ WARN("Instrument zone %d has invalid generator index %d (max: %d)\n", i,
+ sfont->ibag[i].mGenIdx, sfont->igen_size);
+ return AL_FALSE;
+ }
+ if(sfont->ibag[i+1].mGenIdx < sfont->ibag[i].mGenIdx)
+ {
+ WARN("Instrument zone %d has invalid generator index (%d does not follow %d)\n", i+1,
+ sfont->ibag[i+1].mGenIdx, sfont->ibag[i].mGenIdx);
+ return AL_FALSE;
+ }
+ if(sfont->ibag[i].mModIdx >= sfont->imod_size)
+ {
+ WARN("Instrument zone %d has invalid modulator index %d (max: %d)\n", i,
+ sfont->ibag[i].mModIdx, sfont->imod_size);
+ return AL_FALSE;
+ }
+ if(sfont->ibag[i+1].mModIdx < sfont->ibag[i].mModIdx)
+ {
+ WARN("Instrument zone %d has invalid modulator index (%d does not follow %d)\n", i+1,
+ sfont->ibag[i+1].mModIdx, sfont->ibag[i].mModIdx);
+ return AL_FALSE;
+ }
+ }
+ if(sfont->ibag[i].mGenIdx >= sfont->igen_size)
+ {
+ WARN("Instrument zone %d has invalid generator index %d (max: %d)\n", i,
+ sfont->ibag[i].mGenIdx, sfont->igen_size);
+ return AL_FALSE;
+ }
+ if(sfont->ibag[i].mModIdx >= sfont->imod_size)
+ {
+ WARN("Instrument zone %d has invalid modulator index %d (max: %d)\n", i,
+ sfont->ibag[i].mModIdx, sfont->imod_size);
+ return AL_FALSE;
+ }
+
+
+ for(i = 0;i < sfont->shdr_size-1;i++)
+ {
+ if((sfont->shdr[i].mSampleType&0x8000) && sfont->irom == NULL)
+ {
+ WARN("Sample header %d has ROM sample type without an irom sub-chunk\n", i);
+ return AL_FALSE;
+ }
+ }
+
+
+ return AL_TRUE;
+}
+
+static ALboolean checkZone(const GenModList *zone, const PresetHeader *preset, const InstrumentHeader *inst, const SampleHeader *samp)
+{
+ ALsizei i;
+
+ for(i = 0;i < zone->gens_size;i++)
+ {
+ if(zone->gens[i].mGenerator == 43 || zone->gens[i].mGenerator == 44)
+ {
+ int high = zone->gens[i].mAmount>>8;
+ int low = zone->gens[i].mAmount&0xff;
+
+ if(!(low >= 0 && high <= 127 && high >= low))
+ {
+ TRACE("Preset \"%s\", inst \"%s\", sample \"%s\": invalid %s range %d...%d\n",
+ preset->mName, inst->mName, samp->mName,
+ (zone->gens[i].mGenerator == 43) ? "key" :
+ (zone->gens[i].mGenerator == 44) ? "velocity" : "(unknown)",
+ low, high);
+ return AL_FALSE;
+ }
+ }
+ }
+
+ return AL_TRUE;
+}
+
+static ALenum getModSrcInput(int input)
+{
+ if(input == 0) return AL_ONE_SOFT;
+ if(input == 2) return AL_NOTEON_VELOCITY_SOFT;
+ if(input == 3) return AL_NOTEON_KEY_SOFT;
+ if(input == 10) return AL_KEYPRESSURE_SOFT;
+ if(input == 13) return AL_CHANNELPRESSURE_SOFT;
+ if(input == 14) return AL_PITCHBEND_SOFT;
+ if(input == 16) return AL_PITCHBEND_SENSITIVITY_SOFT;
+ if((input&0x80))
+ {
+ input ^= 0x80;
+ if(input > 0 && input < 120 && !(input == 6 || (input >= 32 && input <= 63) ||
+ (input >= 98 && input <= 101)))
+ return input;
+ input ^= 0x80;
+ }
+ ERR("Unhandled modulator source input: 0x%02x\n", input);
+ return AL_INVALID;
+}
+
+static ALenum getModSrcType(int type)
+{
+ if(type == 0x0000) return AL_UNORM_SOFT;
+ if(type == 0x0100) return AL_UNORM_REV_SOFT;
+ if(type == 0x0200) return AL_SNORM_SOFT;
+ if(type == 0x0300) return AL_SNORM_REV_SOFT;
+ ERR("Unhandled modulator source type: 0x%04x\n", type);
+ return AL_INVALID;
+}
+
+static ALenum getModSrcForm(int form)
+{
+ if(form == 0x0000) return AL_LINEAR_SOFT;
+ if(form == 0x0400) return AL_CONCAVE_SOFT;
+ if(form == 0x0800) return AL_CONVEX_SOFT;
+ if(form == 0x0C00) return AL_SWITCH_SOFT;
+ ERR("Unhandled modulator source form: 0x%04x\n", form);
+ return AL_INVALID;
+}
+
+static ALenum getModTransOp(int op)
+{
+ if(op == 0) return AL_LINEAR_SOFT;
+ if(op == 2) return AL_ABSOLUTE_SOFT;
+ ERR("Unhandled modulator transform op: 0x%04x\n", op);
+ return AL_INVALID;
+}
+
+static ALenum getLoopMode(int mode)
+{
+ if(mode == 0) return AL_NONE;
+ if(mode == 1) return AL_LOOP_CONTINUOUS_SOFT;
+ if(mode == 3) return AL_LOOP_UNTIL_RELEASE_SOFT;
+ ERR("Unhandled loop mode: %d\n", mode);
+ return AL_NONE;
+}
+
+static ALenum getSampleType(int type)
+{
+ if(type == 1) return AL_MONO_SOFT;
+ if(type == 2) return AL_RIGHT_SOFT;
+ if(type == 4) return AL_LEFT_SOFT;
+ if(type == 8)
+ {
+ WARN("Sample type \"linked\" ignored; pretending mono\n");
+ return AL_MONO_SOFT;
+ }
+ ERR("Unhandled sample type: 0x%04x\n", type);
+ return AL_MONO_SOFT;
+}
+
+static void fillZone(ALfontsound *sound, ALCcontext *context, const GenModList *zone)
+{
+ static const ALenum Gen2Param[60] = {
+ 0, /* 0 - startAddrOffset */
+ 0, /* 1 - endAddrOffset */
+ 0, /* 2 - startloopAddrOffset */
+ 0, /* 3 - endloopAddrOffset */
+ 0, /* 4 - startAddrCoarseOffset */
+ AL_MOD_LFO_TO_PITCH_SOFT, /* 5 - modLfoToPitch */
+ AL_VIBRATO_LFO_TO_PITCH_SOFT, /* 6 - vibLfoToPitch */
+ AL_MOD_ENV_TO_PITCH_SOFT, /* 7 - modEnvToPitch */
+ AL_FILTER_CUTOFF_SOFT, /* 8 - initialFilterFc */
+ AL_FILTER_RESONANCE_SOFT, /* 9 - initialFilterQ */
+ AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT, /* 10 - modLfoToFilterFc */
+ AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT, /* 11 - modEnvToFilterFc */
+ 0, /* 12 - endAddrCoarseOffset */
+ AL_MOD_LFO_TO_VOLUME_SOFT, /* 13 - modLfoToVolume */
+ 0, /* 14 - */
+ AL_CHORUS_SEND_SOFT, /* 15 - chorusEffectsSend */
+ AL_REVERB_SEND_SOFT, /* 16 - reverbEffectsSend */
+ AL_PAN_SOFT, /* 17 - pan */
+ 0, /* 18 - */
+ 0, /* 19 - */
+ 0, /* 20 - */
+ AL_MOD_LFO_DELAY_SOFT, /* 21 - delayModLFO */
+ AL_MOD_LFO_FREQUENCY_SOFT, /* 22 - freqModLFO */
+ AL_VIBRATO_LFO_DELAY_SOFT, /* 23 - delayVibLFO */
+ AL_VIBRATO_LFO_FREQUENCY_SOFT, /* 24 - freqVibLFO */
+ AL_MOD_ENV_DELAYTIME_SOFT, /* 25 - delayModEnv */
+ AL_MOD_ENV_ATTACKTIME_SOFT, /* 26 - attackModEnv */
+ AL_MOD_ENV_HOLDTIME_SOFT, /* 27 - holdModEnv */
+ AL_MOD_ENV_DECAYTIME_SOFT, /* 28 - decayModEnv */
+ AL_MOD_ENV_SUSTAINVOLUME_SOFT, /* 29 - sustainModEnv */
+ AL_MOD_ENV_RELEASETIME_SOFT, /* 30 - releaseModEnv */
+ AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT, /* 31 - keynumToModEnvHold */
+ AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT, /* 32 - keynumToModEnvDecay */
+ AL_VOLUME_ENV_DELAYTIME_SOFT, /* 33 - delayVolEnv */
+ AL_VOLUME_ENV_ATTACKTIME_SOFT, /* 34 - attackVolEnv */
+ AL_VOLUME_ENV_HOLDTIME_SOFT, /* 35 - holdVolEnv */
+ AL_VOLUME_ENV_DECAYTIME_SOFT, /* 36 - decayVolEnv */
+ AL_VOLUME_ENV_SUSTAINVOLUME_SOFT, /* 37 - sustainVolEnv */
+ AL_VOLUME_ENV_RELEASETIME_SOFT, /* 38 - releaseVolEnv */
+ AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT, /* 39 - keynumToVolEnvHold */
+ AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT, /* 40 - keynumToVolEnvDecay */
+ 0, /* 41 - */
+ 0, /* 42 - */
+ AL_KEY_RANGE_SOFT, /* 43 - keyRange */
+ AL_VELOCITY_RANGE_SOFT, /* 44 - velRange */
+ 0, /* 45 - startloopAddrCoarseOffset */
+ 0, /* 46 - keynum */
+ 0, /* 47 - velocity */
+ AL_ATTENUATION_SOFT, /* 48 - initialAttenuation */
+ 0, /* 49 - */
+ 0, /* 50 - endloopAddrCoarseOffset */
+ AL_TUNING_COARSE_SOFT, /* 51 - corseTune */
+ AL_TUNING_FINE_SOFT, /* 52 - fineTune */
+ 0, /* 53 - */
+ AL_LOOP_MODE_SOFT, /* 54 - sampleModes */
+ 0, /* 55 - */
+ AL_TUNING_SCALE_SOFT, /* 56 - scaleTuning */
+ AL_EXCLUSIVE_CLASS_SOFT, /* 57 - exclusiveClass */
+ AL_BASE_KEY_SOFT, /* 58 - overridingRootKey */
+ 0, /* 59 - */
+ };
+ const Generator *gen, *gen_end;
+
+ if(zone->mods)
+ {
+ ALsizei i;
+ for(i = 0;i < zone->mods_size;i++)
+ {
+ ALenum src0in = getModSrcInput(zone->mods[i].mSrcOp&0xFF);
+ ALenum src0type = getModSrcType(zone->mods[i].mSrcOp&0x0300);
+ ALenum src0form = getModSrcForm(zone->mods[i].mSrcOp&0xFC00);
+ ALenum src1in = getModSrcInput(zone->mods[i].mAmtSrcOp&0xFF);
+ ALenum src1type = getModSrcType(zone->mods[i].mAmtSrcOp&0x0300);
+ ALenum src1form = getModSrcForm(zone->mods[i].mAmtSrcOp&0xFC00);
+ ALenum trans = getModTransOp(zone->mods[i].mTransOp);
+ ALenum dst = (zone->mods[i].mDstOp < 60) ? Gen2Param[zone->mods[i].mDstOp] : 0;
+ if(!dst || dst == AL_KEY_RANGE_SOFT || dst == AL_VELOCITY_RANGE_SOFT ||
+ dst == AL_LOOP_MODE_SOFT || dst == AL_EXCLUSIVE_CLASS_SOFT ||
+ dst == AL_BASE_KEY_SOFT)
+ ERR("Unhandled modulator destination: %d\n", zone->mods[i].mDstOp);
+ else if(src0in != AL_INVALID && src0form != AL_INVALID && src0type != AL_INVALID &&
+ src1in != AL_INVALID && src1form != AL_INVALID && src0type != AL_INVALID &&
+ trans != AL_INVALID)
+ {
+ ALfontsound_setModStagei(sound, context, i, AL_SOURCE0_INPUT_SOFT, src0in);
+ ALfontsound_setModStagei(sound, context, i, AL_SOURCE0_TYPE_SOFT, src0type);
+ ALfontsound_setModStagei(sound, context, i, AL_SOURCE0_FORM_SOFT, src0form);
+ ALfontsound_setModStagei(sound, context, i, AL_SOURCE1_INPUT_SOFT, src1in);
+ ALfontsound_setModStagei(sound, context, i, AL_SOURCE1_TYPE_SOFT, src1type);
+ ALfontsound_setModStagei(sound, context, i, AL_SOURCE1_FORM_SOFT, src1form);
+ ALfontsound_setModStagei(sound, context, i, AL_AMOUNT_SOFT, zone->mods[i].mAmount);
+ ALfontsound_setModStagei(sound, context, i, AL_TRANSFORM_OP_SOFT, trans);
+ ALfontsound_setModStagei(sound, context, i, AL_DESTINATION_SOFT, dst);
+ }
+ }
+ }
+
+ gen = zone->gens;
+ gen_end = gen + zone->gens_size;
+ for(;gen != gen_end;gen++)
+ {
+ ALint value = (ALshort)gen->mAmount;
+ if(gen->mGenerator == 0)
+ sound->Start += value;
+ else if(gen->mGenerator == 1)
+ sound->End += value;
+ else if(gen->mGenerator == 2)
+ sound->LoopStart += value;
+ else if(gen->mGenerator == 3)
+ sound->LoopEnd += value;
+ else if(gen->mGenerator == 4)
+ sound->Start += value<<15;
+ else if(gen->mGenerator == 12)
+ sound->End += value<<15;
+ else if(gen->mGenerator == 45)
+ sound->LoopStart += value<<15;
+ else if(gen->mGenerator == 50)
+ sound->LoopEnd += value<<15;
+ else if(gen->mGenerator == 43)
+ {
+ sound->MinKey = mini((value&0xff), 127);
+ sound->MaxKey = mini(((value>>8)&0xff), 127);
+ }
+ else if(gen->mGenerator == 44)
+ {
+ sound->MinVelocity = mini((value&0xff), 127);
+ sound->MaxVelocity = mini(((value>>8)&0xff), 127);
+ }
+ else
+ {
+ ALenum param = 0;
+ if(gen->mGenerator < 60)
+ param = Gen2Param[gen->mGenerator];
+ if(param)
+ {
+ if(param == AL_BASE_KEY_SOFT)
+ {
+ if(!(value >= 0 && value <= 127))
+ {
+ if(value != -1)
+ WARN("Invalid overridingRootKey generator value %d\n", value);
+ continue;
+ }
+ }
+ if(param == AL_FILTER_RESONANCE_SOFT || param == AL_ATTENUATION_SOFT)
+ value = maxi(0, value);
+ else if(param == AL_CHORUS_SEND_SOFT || param == AL_REVERB_SEND_SOFT)
+ value = clampi(value, 0, 1000);
+ else if(param == AL_LOOP_MODE_SOFT)
+ value = getLoopMode(value);
+ ALfontsound_setPropi(sound, context, param, value);
+ }
+ else if(gen->mGenerator < 256)
+ {
+ static ALboolean warned[256];
+ if(!warned[gen->mGenerator])
+ {
+ warned[gen->mGenerator] = AL_TRUE;
+ ERR("Unhandled generator %d\n", gen->mGenerator);
+ }
+ }
+ }
+ }
+}
+
+static void processInstrument(ALfontsound ***sounds, ALsizei *sounds_size, ALCcontext *context, InstrumentHeader *inst, const PresetHeader *preset, const Soundfont *sfont, const GenModList *pzone)
+{
+ const Generator *gen, *gen_end;
+ const Modulator *mod, *mod_end;
+ const Zone *zone, *zone_end;
+ GenModList gzone;
+ ALvoid *temp;
+
+ if((inst+1)->mZoneIdx == inst->mZoneIdx)
+ ERR("Instrument with no zones!");
+
+ GenModList_Construct(&gzone);
+ zone = sfont->ibag + inst->mZoneIdx;
+ zone_end = sfont->ibag + (inst+1)->mZoneIdx;
+ if(zone_end-zone > 1)
+ {
+ gen = sfont->igen + zone->mGenIdx;
+ gen_end = sfont->igen + (zone+1)->mGenIdx;
+
+ // If no generators, or last generator is not a sample, this is a global zone
+ for(;gen != gen_end;gen++)
+ {
+ if(gen->mGenerator == 53)
+ break;
+ }
+
+ if(gen == gen_end)
+ {
+ gen = sfont->igen + zone->mGenIdx;
+ gen_end = sfont->igen + (zone+1)->mGenIdx;
+ for(;gen != gen_end;gen++)
+ GenModList_insertGen(&gzone, gen, AL_FALSE);
+
+ mod = sfont->imod + zone->mModIdx;
+ mod_end = sfont->imod + (zone+1)->mModIdx;
+ for(;mod != mod_end;mod++)
+ GenModList_insertMod(&gzone, mod);
+
+ zone++;
+ }
+ }
+
+ temp = realloc(*sounds, (zone_end-zone + *sounds_size)*sizeof((*sounds)[0]));
+ if(!temp)
+ {
+ ERR("Failed reallocating fontsound storage to %ld elements (from %d)\n",
+ (zone_end-zone + *sounds_size), *sounds_size);
+ return;
+ }
+ *sounds = temp;
+ for(;zone != zone_end;zone++)
+ {
+ GenModList lzone = GenModList_clone(&gzone);
+ mod = sfont->imod + zone->mModIdx;
+ mod_end = sfont->imod + (zone+1)->mModIdx;
+ for(;mod != mod_end;mod++)
+ GenModList_insertMod(&lzone, mod);
+
+ gen = sfont->igen + zone->mGenIdx;
+ gen_end = sfont->igen + (zone+1)->mGenIdx;
+ for(;gen != gen_end;gen++)
+ {
+ if(gen->mGenerator == 53)
+ {
+ const SampleHeader *samp;
+ ALfontsound *sound;
+
+ if(gen->mAmount >= sfont->shdr_size-1)
+ {
+ ERR("Generator %ld has invalid sample ID (%d of %d)\n",
+ (long)(gen-sfont->igen), gen->mAmount, sfont->shdr_size-1);
+ break;
+ }
+ samp = &sfont->shdr[gen->mAmount];
+
+ gen = pzone->gens;
+ gen_end = gen + pzone->gens_size;
+ for(;gen != gen_end;gen++)
+ GenModList_accumGen(&lzone, gen);
+
+ mod = pzone->mods;
+ mod_end = mod + pzone->mods_size;
+ for(;mod != mod_end;mod++)
+ GenModList_accumMod(&lzone, mod);
+
+ if(!checkZone(&lzone, preset, inst, samp))
+ break;
+ /* Ignore ROM samples for now. */
+ if((samp->mSampleType&0x8000))
+ break;
+
+ sound = NewFontsound(context);
+ (*sounds)[(*sounds_size)++] = sound;
+ ALfontsound_setPropi(sound, context, AL_SAMPLE_START_SOFT, samp->mStart);
+ ALfontsound_setPropi(sound, context, AL_SAMPLE_END_SOFT, samp->mEnd);
+ ALfontsound_setPropi(sound, context, AL_SAMPLE_LOOP_START_SOFT, samp->mStartloop);
+ ALfontsound_setPropi(sound, context, AL_SAMPLE_LOOP_END_SOFT, samp->mEndloop);
+ ALfontsound_setPropi(sound, context, AL_SAMPLE_RATE_SOFT, samp->mSampleRate);
+ ALfontsound_setPropi(sound, context, AL_BASE_KEY_SOFT, (samp->mOriginalKey <= 127) ? samp->mOriginalKey : 60);
+ ALfontsound_setPropi(sound, context, AL_KEY_CORRECTION_SOFT, samp->mCorrection);
+ ALfontsound_setPropi(sound, context, AL_SAMPLE_TYPE_SOFT, getSampleType(samp->mSampleType&0x7fff));
+ fillZone(sound, context, &lzone);
+
+ break;
+ }
+ GenModList_insertGen(&lzone, gen, AL_FALSE);
+ }
+
+ GenModList_Destruct(&lzone);
+ }
+
+ GenModList_Destruct(&gzone);
+}
+
+ALboolean loadSf2(Reader *stream, ALsoundfont *soundfont, ALCcontext *context)
+{
+ ALsfpreset **presets = NULL;
+ ALsizei presets_size = 0;
+ ALuint ltype;
+ Soundfont sfont;
+ RiffHdr riff;
+ RiffHdr list;
+ ALsizei i;
+
+ Soundfont_Construct(&sfont);
+
+ RiffHdr_read(&riff, stream);
+ if(riff.mCode != FOURCC('R','I','F','F'))
+ ERROR_GOTO(error, "Invalid Format, expected RIFF got '%c%c%c%c'\n", FOURCCARGS(riff.mCode));
+ if((ltype=read_le32(stream)) != FOURCC('s','f','b','k'))
+ ERROR_GOTO(error, "Invalid Format, expected sfbk got '%c%c%c%c'\n", FOURCCARGS(ltype));
+
+ if(READERR(stream) != 0)
+ ERROR_GOTO(error, "Error reading file header\n");
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('L','I','S','T'))
+ ERROR_GOTO(error, "Invalid Format, expected LIST (INFO) got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((ltype=read_le32(stream)) != FOURCC('I','N','F','O'))
+ ERROR_GOTO(error, "Invalid Format, expected INFO got '%c%c%c%c'\n", FOURCCARGS(ltype));
+ list.mSize -= 4;
+ while(list.mSize > 0 && !READERR(stream))
+ {
+ RiffHdr info;
+
+ RiffHdr_read(&info, stream);
+ list.mSize -= 8;
+ if(info.mCode == FOURCC('i','f','i','l'))
+ {
+ if(info.mSize != 4)
+ ERR("Invalid ifil chunk size: %d\n", info.mSize);
+ else
+ {
+ ALushort major = read_le16(stream);
+ ALushort minor = read_le16(stream);
+
+ list.mSize -= 4;
+ info.mSize -= 4;
+
+ if(major != 2)
+ ERROR_GOTO(error, "Unsupported SF2 format version: %d.%02d\n", major, minor);
+ TRACE("SF2 format version: %d.%02d\n", major, minor);
+
+ sfont.ifil = (major<<16) | minor;
+ }
+ }
+ else if(info.mCode == FOURCC('i','r','o','m'))
+ {
+ if(info.mSize == 0 || (info.mSize&1))
+ ERR("Invalid irom size: %d\n", info.mSize);
+ else
+ {
+ free(sfont.irom);
+ sfont.irom = calloc(1, info.mSize+1);
+ READ(stream, sfont.irom, info.mSize);
+
+ list.mSize -= info.mSize;
+ info.mSize -= info.mSize;
+
+ TRACE("SF2 ROM ID: %s\n", sfont.irom);
+ }
+ }
+ list.mSize -= info.mSize;
+ skip(stream, info.mSize);
+ }
+
+ if(READERR(stream) != 0)
+ ERROR_GOTO(error, "Error reading INFO chunk\n");
+ if(sfont.ifil == 0)
+ ERROR_GOTO(error, "Missing ifil sub-chunk\n");
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('L','I','S','T'))
+ ERROR_GOTO(error, "Invalid Format, expected LIST (sdta) got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((ltype=read_le32(stream)) != FOURCC('s','d','t','a'))
+ ERROR_GOTO(error, "Invalid Format, expected sdta got '%c%c%c%c'\n", FOURCCARGS(ltype));
+ list.mSize -= 4;
+ {
+ ALbyte *ptr;
+ RiffHdr smpl;
+
+ RiffHdr_read(&smpl, stream);
+ if(smpl.mCode != FOURCC('s','m','p','l'))
+ ERROR_GOTO(error, "Invalid Format, expected smpl got '%c%c%c%c'\n", FOURCCARGS(smpl.mCode));
+ list.mSize -= 8;
+
+ if(smpl.mSize > list.mSize)
+ ERROR_GOTO(error, "Invalid Format, sample chunk size mismatch\n");
+
+ if(!(ptr=realloc(soundfont->Samples, smpl.mSize)))
+ SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, error);
+ soundfont->Samples = (ALshort*)ptr;
+ soundfont->NumSamples = smpl.mSize/2;
+
+ if(IS_LITTLE_ENDIAN)
+ READ(stream, ptr, smpl.mSize);
+ else
+ {
+ while(smpl.mSize > 0)
+ {
+ ALbyte buf[4096];
+ ALuint todo = minu(smpl.mSize, sizeof(buf));
+ ALuint i;
+
+ READ(stream, buf, todo);
+ for(i = 0;i < todo;i++)
+ ptr[i] = buf[i^1];
+ }
+ }
+ list.mSize -= smpl.mSize;
+
+ skip(stream, list.mSize);
+ }
+
+ if(READERR(stream) != 0)
+ ERROR_GOTO(error, "Error reading sdta chunk\n");
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('L','I','S','T'))
+ ERROR_GOTO(error, "Invalid Format, expected LIST (pdta) got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((ltype=read_le32(stream)) != FOURCC('p','d','t','a'))
+ ERROR_GOTO(error, "Invalid Format, expected pdta got '%c%c%c%c'\n", FOURCCARGS(ltype));
+
+ //
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('p','h','d','r'))
+ ERROR_GOTO(error, "Invalid Format, expected phdr got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%38) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad phdr size: %u\n", list.mSize);
+ sfont.phdr_size = list.mSize/38;
+ sfont.phdr = calloc(sfont.phdr_size, sizeof(sfont.phdr[0]));
+ for(i = 0;i < sfont.phdr_size;i++)
+ PresetHeader_read(&sfont.phdr[i], stream);
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('p','b','a','g'))
+ ERROR_GOTO(error, "Invalid Format, expected pbag got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%4) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad pbag size: %u\n", list.mSize);
+ sfont.pbag_size = list.mSize/4;
+ sfont.pbag = calloc(sfont.pbag_size, sizeof(sfont.pbag[0]));
+ for(i = 0;i < sfont.pbag_size;i++)
+ Zone_read(&sfont.pbag[i], stream);
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('p','m','o','d'))
+ ERROR_GOTO(error, "Invalid Format, expected pmod got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%10) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad pmod size: %u\n", list.mSize);
+ sfont.pmod_size = list.mSize/10;
+ sfont.pmod = calloc(sfont.pmod_size, sizeof(sfont.pmod[0]));
+ for(i = 0;i < sfont.pmod_size;i++)
+ Modulator_read(&sfont.pmod[i], stream);
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('p','g','e','n'))
+ ERROR_GOTO(error, "Invalid Format, expected pgen got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%4) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad pgen size: %u\n", list.mSize);
+ sfont.pgen_size = list.mSize/4;
+ sfont.pgen = calloc(sfont.pgen_size, sizeof(sfont.pgen[0]));
+ for(i = 0;i < sfont.pgen_size;i++)
+ Generator_read(&sfont.pgen[i], stream);
+
+ //
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('i','n','s','t'))
+ ERROR_GOTO(error, "Invalid Format, expected inst got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%22) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad inst size: %u\n", list.mSize);
+ sfont.inst_size = list.mSize/22;
+ sfont.inst = calloc(sfont.inst_size, sizeof(sfont.inst[0]));
+ for(i = 0;i < sfont.inst_size;i++)
+ InstrumentHeader_read(&sfont.inst[i], stream);
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('i','b','a','g'))
+ ERROR_GOTO(error, "Invalid Format, expected ibag got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%4) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad ibag size: %u\n", list.mSize);
+ sfont.ibag_size = list.mSize/4;
+ sfont.ibag = calloc(sfont.ibag_size, sizeof(sfont.ibag[0]));
+ for(i = 0;i < sfont.ibag_size;i++)
+ Zone_read(&sfont.ibag[i], stream);
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('i','m','o','d'))
+ ERROR_GOTO(error, "Invalid Format, expected imod got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%10) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad imod size: %u\n", list.mSize);
+ sfont.imod_size = list.mSize/10;
+ sfont.imod = calloc(sfont.imod_size, sizeof(sfont.imod[0]));
+ for(i = 0;i < sfont.imod_size;i++)
+ Modulator_read(&sfont.imod[i], stream);
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('i','g','e','n'))
+ ERROR_GOTO(error, "Invalid Format, expected igen got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%4) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad igen size: %u\n", list.mSize);
+ sfont.igen_size = list.mSize/4;
+ sfont.igen = calloc(sfont.igen_size, sizeof(sfont.igen[0]));
+ for(i = 0;i < sfont.igen_size;i++)
+ Generator_read(&sfont.igen[i], stream);
+
+ //
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('s','h','d','r'))
+ ERROR_GOTO(error, "Invalid Format, expected shdr got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%46) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad shdr size: %u\n", list.mSize);
+ sfont.shdr_size = list.mSize/46;
+ sfont.shdr = calloc(sfont.shdr_size, sizeof(sfont.shdr[0]));
+ for(i = 0;i < sfont.shdr_size;i++)
+ SampleHeader_read(&sfont.shdr[i], stream);
+
+ if(READERR(stream) != 0)
+ ERROR_GOTO(error, "Error reading pdta chunk\n");
+
+ if(!ensureFontSanity(&sfont))
+ goto error;
+
+ presets = calloc(1, (sfont.phdr_size-1)*sizeof(presets[0]));
+ if(!presets)
+ ERROR_GOTO(error, "Error allocating presets\n");
+
+ for(i = 0;i < sfont.phdr_size-1;i++)
+ {
+ const Generator *gen, *gen_end;
+ const Modulator *mod, *mod_end;
+ const Zone *zone, *zone_end;
+ ALfontsound **sounds = NULL;
+ ALsizei sounds_size = 0;
+ GenModList gzone;
+
+ if(sfont.phdr[i+1].mZoneIdx == sfont.phdr[i].mZoneIdx)
+ continue;
+
+ GenModList_Construct(&gzone);
+ zone = sfont.pbag + sfont.phdr[i].mZoneIdx;
+ zone_end = sfont.pbag + sfont.phdr[i+1].mZoneIdx;
+ if(zone_end-zone > 1)
+ {
+ gen = sfont.pgen + zone->mGenIdx;
+ gen_end = sfont.pgen + (zone+1)->mGenIdx;
+
+ // If no generators, or last generator is not an instrument, this is a global zone
+ for(;gen != gen_end;gen++)
+ {
+ if(gen->mGenerator == 41)
+ break;
+ }
+
+ if(gen == gen_end)
+ {
+ gen = sfont.pgen + zone->mGenIdx;
+ gen_end = sfont.pgen + (zone+1)->mGenIdx;
+ for(;gen != gen_end;gen++)
+ GenModList_insertGen(&gzone, gen, AL_TRUE);
+
+ mod = sfont.pmod + zone->mModIdx;
+ mod_end = sfont.pmod + (zone+1)->mModIdx;
+ for(;mod != mod_end;mod++)
+ GenModList_insertMod(&gzone, mod);
+
+ zone++;
+ }
+ }
+
+ for(;zone != zone_end;zone++)
+ {
+ GenModList lzone = GenModList_clone(&gzone);
+
+ mod = sfont.pmod + zone->mModIdx;
+ mod_end = sfont.pmod + (zone+1)->mModIdx;
+ for(;mod != mod_end;mod++)
+ GenModList_insertMod(&lzone, mod);
+
+ gen = sfont.pgen + zone->mGenIdx;
+ gen_end = sfont.pgen + (zone+1)->mGenIdx;
+ for(;gen != gen_end;gen++)
+ {
+ if(gen->mGenerator == 41)
+ {
+ if(gen->mAmount >= sfont.inst_size-1)
+ ERR("Generator %ld has invalid instrument ID (%d of %d)\n",
+ (long)(gen-sfont.pgen), gen->mAmount, sfont.inst_size-1);
+ else
+ processInstrument(&sounds, &sounds_size, context,
+ &sfont.inst[gen->mAmount], &sfont.phdr[i], &sfont, &lzone);
+ break;
+ }
+ GenModList_insertGen(&lzone, gen, AL_TRUE);
+ }
+ GenModList_Destruct(&lzone);
+ }
+
+ if(sounds_size > 0)
+ {
+ ALsizei j;
+
+ presets[presets_size] = NewPreset(context);
+ presets[presets_size]->Preset = sfont.phdr[i].mPreset;
+ presets[presets_size]->Bank = sfont.phdr[i].mBank;
+
+ for(j = 0;j < sounds_size;j++)
+ IncrementRef(&sounds[j]->ref);
+ sounds = ExchangePtr((XchgPtr*)&presets[presets_size]->Sounds, sounds);
+ ExchangeInt(&presets[presets_size]->NumSounds, sounds_size);
+ presets_size++;
+ }
+ free(sounds);
+
+ GenModList_Destruct(&gzone);
+ }
+
+ for(i = 0;i < presets_size;i++)
+ IncrementRef(&presets[i]->ref);
+ presets = ExchangePtr((XchgPtr*)&soundfont->Presets, presets);
+ ExchangeInt(&soundfont->NumPresets, presets_size);
+
+ free(presets);
+
+ Soundfont_Destruct(&sfont);
+
+ return AL_TRUE;
+
+error:
+ if(presets)
+ {
+ ALCdevice *device = context->Device;
+ for(i = 0;i < presets_size;i++)
+ DeletePreset(presets[i], device);
+ free(presets);
+ }
+
+ Soundfont_Destruct(&sfont);
+
+ return AL_FALSE;
+}
diff --git a/Alc/mixer.c b/Alc/mixer.c
index f75a7803..acd6c610 100644
--- a/Alc/mixer.c
+++ b/Alc/mixer.c
@@ -37,13 +37,13 @@
#include "bs2b.h"
-static __inline ALfloat Sample_ALbyte(ALbyte val)
+static inline ALfloat Sample_ALbyte(ALbyte val)
{ return val * (1.0f/127.0f); }
-static __inline ALfloat Sample_ALshort(ALshort val)
+static inline ALfloat Sample_ALshort(ALshort val)
{ return val * (1.0f/32767.0f); }
-static __inline ALfloat Sample_ALfloat(ALfloat val)
+static inline ALfloat Sample_ALfloat(ALfloat val)
{ return val; }
#define DECL_TEMPLATE(T) \
@@ -84,13 +84,13 @@ static void SilenceData(ALfloat *dst, ALuint samples)
}
-static void Filter2P(FILTER *filter, ALuint chan, ALfloat *RESTRICT dst,
- const ALfloat *RESTRICT src, ALuint numsamples)
+static void DoFilter(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src,
+ ALuint numsamples)
{
ALuint i;
for(i = 0;i < numsamples;i++)
- dst[i] = lpFilter2P(filter, chan, src[i]);
- dst[i] = lpFilter2PC(filter, chan, src[i]);
+ dst[i] = ALfilterState_processSingle(filter, src[i]);
+ dst[i] = ALfilterState_processSingleC(filter, src[i]);
}
@@ -131,7 +131,7 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo)
const ALuint BufferPadding = ResamplerPadding[Resampler];
ALuint SrcBufferSize, DstBufferSize;
- /* Figure out how many buffer bytes will be needed */
+ /* Figure out how many buffer samples will be needed */
DataSize64 = SamplesToDo-OutPos+1;
DataSize64 *= increment;
DataSize64 += DataPosFrac+FRACTIONMASK;
@@ -328,7 +328,7 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo)
{
DirectParams *directparms = &Source->Params.Direct;
- Filter2P(&directparms->iirFilter, chan, SrcData, ResampledData,
+ DoFilter(&directparms->LpFilter[chan], SrcData, ResampledData,
DstBufferSize);
Source->Params.DryMix(directparms, SrcData, chan, OutPos,
SamplesToDo, DstBufferSize);
@@ -337,10 +337,10 @@ ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo)
for(j = 0;j < Device->NumAuxSends;j++)
{
SendParams *sendparms = &Source->Params.Send[j];
- if(!sendparms->Slot)
+ if(!sendparms->OutBuffer)
continue;
- Filter2P(&sendparms->iirFilter, chan, SrcData, ResampledData,
+ DoFilter(&sendparms->LpFilter[chan], SrcData, ResampledData,
DstBufferSize);
Source->Params.WetMix(sendparms, SrcData, OutPos,
SamplesToDo, DstBufferSize);
diff --git a/Alc/mixer_c.c b/Alc/mixer_c.c
index d9c8ca25..36d8bf5a 100644
--- a/Alc/mixer_c.c
+++ b/Alc/mixer_c.c
@@ -8,24 +8,23 @@
#include "alAuxEffectSlot.h"
-static __inline ALfloat point32(const ALfloat *vals, ALuint frac)
-{ return vals[0]; (void)frac; }
-static __inline ALfloat lerp32(const ALfloat *vals, ALuint frac)
+static inline ALfloat point32(const ALfloat *vals, ALuint UNUSED(frac))
+{ return vals[0]; }
+static inline ALfloat lerp32(const ALfloat *vals, ALuint frac)
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
-static __inline ALfloat cubic32(const ALfloat *vals, ALuint frac)
+static inline ALfloat cubic32(const ALfloat *vals, ALuint frac)
{ return cubic(vals[-1], vals[0], vals[1], vals[2], frac * (1.0f/FRACTIONONE)); }
-void Resample_copy32_C(const ALfloat *data, ALuint frac,
- ALuint increment, ALfloat *RESTRICT OutBuffer, ALuint BufferSize)
+void Resample_copy32_C(const ALfloat *data, ALuint UNUSED(frac),
+ ALuint increment, ALfloat *restrict OutBuffer, ALuint BufferSize)
{
- (void)frac;
assert(increment==FRACTIONONE);
memcpy(OutBuffer, data, (BufferSize+1)*sizeof(ALfloat));
}
#define DECL_TEMPLATE(Sampler) \
void Resample_##Sampler##_C(const ALfloat *data, ALuint frac, \
- ALuint increment, ALfloat *RESTRICT OutBuffer, ALuint BufferSize) \
+ ALuint increment, ALfloat *restrict OutBuffer, ALuint BufferSize) \
{ \
ALuint pos = 0; \
ALuint i; \
@@ -47,27 +46,22 @@ DECL_TEMPLATE(cubic32)
#undef DECL_TEMPLATE
-static __inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*RESTRICT Values)[2],
- const ALuint IrSize,
- ALfloat (*RESTRICT Coeffs)[2],
- const ALfloat (*RESTRICT CoeffStep)[2],
- ALfloat left, ALfloat right)
+static inline void ApplyCoeffsStep(const ALuint IrSize,
+ ALfloat (*restrict Coeffs)[2],
+ const ALfloat (*restrict CoeffStep)[2])
{
ALuint c;
for(c = 0;c < IrSize;c++)
{
- const ALuint off = (Offset+c)&HRIR_MASK;
- Values[off][0] += Coeffs[c][0] * left;
- Values[off][1] += Coeffs[c][1] * right;
Coeffs[c][0] += CoeffStep[c][0];
Coeffs[c][1] += CoeffStep[c][1];
}
}
-static __inline void ApplyCoeffs(ALuint Offset, ALfloat (*RESTRICT Values)[2],
- const ALuint IrSize,
- ALfloat (*RESTRICT Coeffs)[2],
- ALfloat left, ALfloat right)
+static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
+ const ALuint IrSize,
+ ALfloat (*restrict Coeffs)[2],
+ ALfloat left, ALfloat right)
{
ALuint c;
for(c = 0;c < IrSize;c++)
@@ -83,12 +77,12 @@ static __inline void ApplyCoeffs(ALuint Offset, ALfloat (*RESTRICT Values)[2],
#undef SUFFIX
-void MixDirect_C(const DirectParams *params, const ALfloat *RESTRICT data, ALuint srcchan,
+void MixDirect_C(const DirectParams *params, const ALfloat *restrict data, ALuint srcchan,
ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize)
{
- ALfloat (*RESTRICT DryBuffer)[BUFFERSIZE] = params->OutBuffer;
- ALfloat *RESTRICT ClickRemoval = params->ClickRemoval;
- ALfloat *RESTRICT PendingClicks = params->PendingClicks;
+ ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer;
+ ALfloat *restrict ClickRemoval = params->ClickRemoval;
+ ALfloat *restrict PendingClicks = params->PendingClicks;
ALfloat DrySend;
ALuint pos;
ALuint c;
@@ -96,36 +90,36 @@ void MixDirect_C(const DirectParams *params, const ALfloat *RESTRICT data, ALuin
for(c = 0;c < MaxChannels;c++)
{
DrySend = params->Gains[srcchan][c];
- if(DrySend < 0.00001f)
+ if(!(DrySend > GAIN_SILENCE_THRESHOLD))
continue;
if(OutPos == 0)
ClickRemoval[c] -= data[0]*DrySend;
for(pos = 0;pos < BufferSize;pos++)
- DryBuffer[c][OutPos+pos] += data[pos]*DrySend;
+ OutBuffer[c][OutPos+pos] += data[pos]*DrySend;
if(OutPos+pos == SamplesToDo)
PendingClicks[c] += data[pos]*DrySend;
}
}
-void MixSend_C(const SendParams *params, const ALfloat *RESTRICT data,
+void MixSend_C(const SendParams *params, const ALfloat *restrict data,
ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize)
{
- ALeffectslot *Slot = params->Slot;
- ALfloat (*RESTRICT WetBuffer)[BUFFERSIZE] = Slot->WetBuffer;
- ALfloat *RESTRICT WetClickRemoval = Slot->ClickRemoval;
- ALfloat *RESTRICT WetPendingClicks = Slot->PendingClicks;
- ALfloat WetSend = params->Gain;
+ ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer;
+ ALfloat *restrict ClickRemoval = params->ClickRemoval;
+ ALfloat *restrict PendingClicks = params->PendingClicks;
+ ALfloat WetSend;
ALuint pos;
- if(WetSend < 0.00001f)
+ WetSend = params->Gain;
+ if(!(WetSend > GAIN_SILENCE_THRESHOLD))
return;
if(OutPos == 0)
- WetClickRemoval[0] -= data[0] * WetSend;
+ ClickRemoval[0] -= data[0] * WetSend;
for(pos = 0;pos < BufferSize;pos++)
- WetBuffer[0][OutPos+pos] += data[pos] * WetSend;
+ OutBuffer[0][OutPos+pos] += data[pos] * WetSend;
if(OutPos+pos == SamplesToDo)
- WetPendingClicks[0] += data[pos] * WetSend;
+ PendingClicks[0] += data[pos] * WetSend;
}
diff --git a/Alc/mixer_defs.h b/Alc/mixer_defs.h
index 6d3390c8..5e43af15 100644
--- a/Alc/mixer_defs.h
+++ b/Alc/mixer_defs.h
@@ -9,23 +9,23 @@ struct DirectParams;
struct SendParams;
/* C resamplers */
-void Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *RESTRICT dst, ALuint dstlen);
-void Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *RESTRICT dst, ALuint dstlen);
-void Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *RESTRICT dst, ALuint dstlen);
-void Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *RESTRICT dst, ALuint dstlen);
+void Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
+void Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
+void Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
+void Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
/* C mixers */
-void MixDirect_Hrtf_C(const struct DirectParams*,const ALfloat*RESTRICT,ALuint,ALuint,ALuint,ALuint);
-void MixDirect_C(const struct DirectParams*,const ALfloat*RESTRICT,ALuint,ALuint,ALuint,ALuint);
-void MixSend_C(const struct SendParams*,const ALfloat*RESTRICT,ALuint,ALuint,ALuint);
+void MixDirect_Hrtf_C(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint);
+void MixDirect_C(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint);
+void MixSend_C(const struct SendParams*,const ALfloat*restrict,ALuint,ALuint,ALuint);
/* SSE mixers */
-void MixDirect_Hrtf_SSE(const struct DirectParams*,const ALfloat*RESTRICT,ALuint,ALuint,ALuint,ALuint);
-void MixDirect_SSE(const struct DirectParams*,const ALfloat*RESTRICT,ALuint,ALuint,ALuint,ALuint);
-void MixSend_SSE(const struct SendParams*,const ALfloat*RESTRICT,ALuint,ALuint,ALuint);
+void MixDirect_Hrtf_SSE(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint);
+void MixDirect_SSE(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint);
+void MixSend_SSE(const struct SendParams*,const ALfloat*restrict,ALuint,ALuint,ALuint);
/* Neon mixers */
-void MixDirect_Hrtf_Neon(const struct DirectParams*,const ALfloat*RESTRICT,ALuint,ALuint,ALuint,ALuint);
+void MixDirect_Hrtf_Neon(const struct DirectParams*,const ALfloat*restrict,ALuint,ALuint,ALuint,ALuint);
#endif /* MIXER_DEFS_H */
diff --git a/Alc/mixer_inc.c b/Alc/mixer_inc.c
index 97266d40..17be5cde 100644
--- a/Alc/mixer_inc.c
+++ b/Alc/mixer_inc.c
@@ -18,30 +18,28 @@
#define MixDirect_Hrtf MERGE2(MixDirect_Hrtf_,SUFFIX)
-static __inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*RESTRICT Values)[2],
- const ALuint irSize,
- ALfloat (*RESTRICT Coeffs)[2],
- const ALfloat (*RESTRICT CoeffStep)[2],
- ALfloat left, ALfloat right);
-static __inline void ApplyCoeffs(ALuint Offset, ALfloat (*RESTRICT Values)[2],
- const ALuint irSize,
- ALfloat (*RESTRICT Coeffs)[2],
- ALfloat left, ALfloat right);
-
-
-void MixDirect_Hrtf(const DirectParams *params, const ALfloat *RESTRICT data, ALuint srcchan,
+static inline void ApplyCoeffsStep(const ALuint irSize,
+ ALfloat (*restrict Coeffs)[2],
+ const ALfloat (*restrict CoeffStep)[2]);
+static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
+ const ALuint irSize,
+ ALfloat (*restrict Coeffs)[2],
+ ALfloat left, ALfloat right);
+
+
+void MixDirect_Hrtf(const DirectParams *params, const ALfloat *restrict data, ALuint srcchan,
ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize)
{
- ALfloat (*RESTRICT DryBuffer)[BUFFERSIZE] = params->OutBuffer;
- ALfloat *RESTRICT ClickRemoval = params->ClickRemoval;
- ALfloat *RESTRICT PendingClicks = params->PendingClicks;
+ ALfloat (*restrict DryBuffer)[BUFFERSIZE] = params->OutBuffer;
+ ALfloat *restrict ClickRemoval = params->ClickRemoval;
+ ALfloat *restrict PendingClicks = params->PendingClicks;
const ALuint IrSize = params->Hrtf.Params.IrSize;
- const ALint *RESTRICT DelayStep = params->Hrtf.Params.DelayStep;
- const ALfloat (*RESTRICT CoeffStep)[2] = params->Hrtf.Params.CoeffStep;
- const ALfloat (*RESTRICT TargetCoeffs)[2] = params->Hrtf.Params.Coeffs[srcchan];
- const ALuint *RESTRICT TargetDelay = params->Hrtf.Params.Delay[srcchan];
- ALfloat *RESTRICT History = params->Hrtf.State->History[srcchan];
- ALfloat (*RESTRICT Values)[2] = params->Hrtf.State->Values[srcchan];
+ const ALint *restrict DelayStep = params->Hrtf.Params.DelayStep;
+ const ALfloat (*restrict CoeffStep)[2] = params->Hrtf.Params.CoeffStep;
+ const ALfloat (*restrict TargetCoeffs)[2] = params->Hrtf.Params.Coeffs[srcchan];
+ const ALuint *restrict TargetDelay = params->Hrtf.Params.Delay[srcchan];
+ ALfloat *restrict History = params->Hrtf.State->History[srcchan];
+ ALfloat (*restrict Values)[2] = params->Hrtf.State->Values[srcchan];
ALint Counter = maxu(params->Hrtf.State->Counter, OutPos) - OutPos;
ALuint Offset = params->Hrtf.State->Offset + OutPos;
ALIGN(16) ALfloat Coeffs[HRIR_LENGTH][2];
@@ -92,9 +90,10 @@ void MixDirect_Hrtf(const DirectParams *params, const ALfloat *RESTRICT data, AL
Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
Offset++;
- ApplyCoeffsStep(Offset, Values, IrSize, Coeffs, CoeffStep, left, right);
+ ApplyCoeffs(Offset, Values, IrSize, Coeffs, left, right);
DryBuffer[FrontLeft][OutPos] += Values[Offset&HRIR_MASK][0];
DryBuffer[FrontRight][OutPos] += Values[Offset&HRIR_MASK][1];
+ ApplyCoeffsStep(IrSize, Coeffs, CoeffStep);
OutPos++;
Counter--;
diff --git a/Alc/mixer_neon.c b/Alc/mixer_neon.c
index 23dc792c..571221be 100644
--- a/Alc/mixer_neon.c
+++ b/Alc/mixer_neon.c
@@ -10,27 +10,22 @@
#include "alu.h"
-static __inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*RESTRICT Values)[2],
- const ALuint IrSize,
- ALfloat (*RESTRICT Coeffs)[2],
- const ALfloat (*RESTRICT CoeffStep)[2],
- ALfloat left, ALfloat right)
+static inline void ApplyCoeffsStep(const ALuint IrSize,
+ ALfloat (*restrict Coeffs)[2],
+ const ALfloat (*restrict CoeffStep)[2])
{
ALuint c;
for(c = 0;c < IrSize;c++)
{
- const ALuint off = (Offset+c)&HRIR_MASK;
- Values[off][0] += Coeffs[c][0] * left;
- Values[off][1] += Coeffs[c][1] * right;
Coeffs[c][0] += CoeffStep[c][0];
Coeffs[c][1] += CoeffStep[c][1];
}
}
-static __inline void ApplyCoeffs(ALuint Offset, ALfloat (*RESTRICT Values)[2],
- const ALuint IrSize,
- ALfloat (*RESTRICT Coeffs)[2],
- ALfloat left, ALfloat right)
+static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
+ const ALuint IrSize,
+ ALfloat (*restrict Coeffs)[2],
+ ALfloat left, ALfloat right)
{
ALuint c;
float32x4_t leftright4;
diff --git a/Alc/mixer_sse.c b/Alc/mixer_sse.c
index 3c45fd21..719ebd23 100644
--- a/Alc/mixer_sse.c
+++ b/Alc/mixer_sse.c
@@ -1,6 +1,13 @@
#include "config.h"
#ifdef HAVE_XMMINTRIN_H
+#ifdef IN_IDE_PARSER
+/* KDevelop's parser won't recognize these defines that get added by the -msse
+ * switch used to compile this source. Without them, xmmintrin.h fails to
+ * declare anything. */
+#define __MMX__
+#define __SSE__
+#endif
#include <xmmintrin.h>
#endif
@@ -14,72 +21,26 @@
#include "mixer_defs.h"
-static __inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*RESTRICT Values)[2],
- const ALuint IrSize,
- ALfloat (*RESTRICT Coeffs)[2],
- const ALfloat (*RESTRICT CoeffStep)[2],
- ALfloat left, ALfloat right)
+static inline void ApplyCoeffsStep(const ALuint IrSize,
+ ALfloat (*restrict Coeffs)[2],
+ const ALfloat (*restrict CoeffStep)[2])
{
- const __m128 lrlr = { left, right, left, right };
- __m128 coeffs, deltas, imp0, imp1;
- __m128 vals = _mm_setzero_ps();
+ __m128 coeffs, deltas;
ALuint i;
- if((Offset&1))
+ for(i = 0;i < IrSize;i += 2)
{
- const ALuint o0 = Offset&HRIR_MASK;
- const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
-
- coeffs = _mm_load_ps(&Coeffs[0][0]);
- deltas = _mm_load_ps(&CoeffStep[0][0]);
- vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
- imp0 = _mm_mul_ps(lrlr, coeffs);
+ coeffs = _mm_load_ps(&Coeffs[i][0]);
+ deltas = _mm_load_ps(&CoeffStep[i][0]);
coeffs = _mm_add_ps(coeffs, deltas);
- vals = _mm_add_ps(imp0, vals);
- _mm_store_ps(&Coeffs[0][0], coeffs);
- _mm_storel_pi((__m64*)&Values[o0][0], vals);
- for(i = 1;i < IrSize-1;i += 2)
- {
- const ALuint o2 = (Offset+i)&HRIR_MASK;
-
- coeffs = _mm_load_ps(&Coeffs[i+1][0]);
- deltas = _mm_load_ps(&CoeffStep[i+1][0]);
- vals = _mm_load_ps(&Values[o2][0]);
- imp1 = _mm_mul_ps(lrlr, coeffs);
- coeffs = _mm_add_ps(coeffs, deltas);
- imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
- vals = _mm_add_ps(imp0, vals);
- _mm_store_ps(&Coeffs[i+1][0], coeffs);
- _mm_store_ps(&Values[o2][0], vals);
- imp0 = imp1;
- }
- vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
- imp0 = _mm_movehl_ps(imp0, imp0);
- vals = _mm_add_ps(imp0, vals);
- _mm_storel_pi((__m64*)&Values[o1][0], vals);
- }
- else
- {
- for(i = 0;i < IrSize;i += 2)
- {
- const ALuint o = (Offset + i)&HRIR_MASK;
-
- coeffs = _mm_load_ps(&Coeffs[i][0]);
- deltas = _mm_load_ps(&CoeffStep[i][0]);
- vals = _mm_load_ps(&Values[o][0]);
- imp0 = _mm_mul_ps(lrlr, coeffs);
- coeffs = _mm_add_ps(coeffs, deltas);
- vals = _mm_add_ps(imp0, vals);
- _mm_store_ps(&Coeffs[i][0], coeffs);
- _mm_store_ps(&Values[o][0], vals);
- }
+ _mm_store_ps(&Coeffs[i][0], coeffs);
}
}
-static __inline void ApplyCoeffs(ALuint Offset, ALfloat (*RESTRICT Values)[2],
- const ALuint IrSize,
- ALfloat (*RESTRICT Coeffs)[2],
- ALfloat left, ALfloat right)
+static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
+ const ALuint IrSize,
+ ALfloat (*restrict Coeffs)[2],
+ ALfloat left, ALfloat right)
{
const __m128 lrlr = { left, right, left, right };
__m128 vals = _mm_setzero_ps();
@@ -133,22 +94,21 @@ static __inline void ApplyCoeffs(ALuint Offset, ALfloat (*RESTRICT Values)[2],
#undef SUFFIX
-void MixDirect_SSE(const DirectParams *params, const ALfloat *RESTRICT data, ALuint srcchan,
+void MixDirect_SSE(const DirectParams *params, const ALfloat *restrict data, ALuint srcchan,
ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize)
{
- ALfloat (*RESTRICT DryBuffer)[BUFFERSIZE] = params->OutBuffer;
- ALfloat *RESTRICT ClickRemoval = params->ClickRemoval;
- ALfloat *RESTRICT PendingClicks = params->PendingClicks;
+ ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer;
+ ALfloat *restrict ClickRemoval = params->ClickRemoval;
+ ALfloat *restrict PendingClicks = params->PendingClicks;
ALfloat DrySend;
+ __m128 gain;
ALuint pos;
ALuint c;
for(c = 0;c < MaxChannels;c++)
{
- __m128 gain;
-
DrySend = params->Gains[srcchan][c];
- if(DrySend < 0.00001f)
+ if(!(DrySend > GAIN_SILENCE_THRESHOLD))
continue;
if(OutPos == 0)
@@ -158,12 +118,12 @@ void MixDirect_SSE(const DirectParams *params, const ALfloat *RESTRICT data, ALu
for(pos = 0;BufferSize-pos > 3;pos += 4)
{
const __m128 val4 = _mm_load_ps(&data[pos]);
- __m128 dry4 = _mm_load_ps(&DryBuffer[c][OutPos+pos]);
+ __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain));
- _mm_store_ps(&DryBuffer[c][OutPos+pos], dry4);
+ _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
}
for(;pos < BufferSize;pos++)
- DryBuffer[c][OutPos+pos] += data[pos]*DrySend;
+ OutBuffer[c][OutPos+pos] += data[pos]*DrySend;
if(OutPos+pos == SamplesToDo)
PendingClicks[c] += data[pos]*DrySend;
@@ -171,34 +131,34 @@ void MixDirect_SSE(const DirectParams *params, const ALfloat *RESTRICT data, ALu
}
-void MixSend_SSE(const SendParams *params, const ALfloat *RESTRICT data,
+void MixSend_SSE(const SendParams *params, const ALfloat *restrict data,
ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize)
{
- ALeffectslot *Slot = params->Slot;
- ALfloat (*RESTRICT WetBuffer)[BUFFERSIZE] = Slot->WetBuffer;
- ALfloat *RESTRICT WetClickRemoval = Slot->ClickRemoval;
- ALfloat *RESTRICT WetPendingClicks = Slot->PendingClicks;
- const ALfloat WetGain = params->Gain;
+ ALfloat (*restrict OutBuffer)[BUFFERSIZE] = params->OutBuffer;
+ ALfloat *restrict ClickRemoval = params->ClickRemoval;
+ ALfloat *restrict PendingClicks = params->PendingClicks;
+ ALfloat WetGain;
__m128 gain;
ALuint pos;
- if(WetGain < 0.00001f)
+ WetGain = params->Gain;
+ if(!(WetGain > GAIN_SILENCE_THRESHOLD))
return;
if(OutPos == 0)
- WetClickRemoval[0] -= data[0] * WetGain;
+ ClickRemoval[0] -= data[0] * WetGain;
gain = _mm_set1_ps(WetGain);
for(pos = 0;BufferSize-pos > 3;pos += 4)
{
const __m128 val4 = _mm_load_ps(&data[pos]);
- __m128 wet4 = _mm_load_ps(&WetBuffer[0][OutPos+pos]);
+ __m128 wet4 = _mm_load_ps(&OutBuffer[0][OutPos+pos]);
wet4 = _mm_add_ps(wet4, _mm_mul_ps(val4, gain));
- _mm_store_ps(&WetBuffer[0][OutPos+pos], wet4);
+ _mm_store_ps(&OutBuffer[0][OutPos+pos], wet4);
}
for(;pos < BufferSize;pos++)
- WetBuffer[0][OutPos+pos] += data[pos] * WetGain;
+ OutBuffer[0][OutPos+pos] += data[pos] * WetGain;
if(OutPos+pos == SamplesToDo)
- WetPendingClicks[0] += data[pos] * WetGain;
+ PendingClicks[0] += data[pos] * WetGain;
}
diff --git a/Alc/panning.c b/Alc/panning.c
index d8191d8d..30a1e571 100644
--- a/Alc/panning.c
+++ b/Alc/panning.c
@@ -31,6 +31,8 @@
#include "AL/alc.h"
#include "alu.h"
+extern inline void SetGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MaxChannels]);
+
static void SetSpeakerArrangement(const char *name, ALfloat SpeakerAngle[MaxChannels],
enum Channel Speaker2Chan[MaxChannels], ALint chans)
{
@@ -102,7 +104,7 @@ static void SetSpeakerArrangement(const char *name, ALfloat SpeakerAngle[MaxChan
{
long angle = strtol(sep, NULL, 10);
if(angle >= -180 && angle <= 180)
- SpeakerAngle[i] = angle * F_PI/180.0f;
+ SpeakerAngle[i] = DEG2RAD(angle);
else
ERR("Invalid angle for speaker \"%s\": %ld\n", confkey, angle);
break;
@@ -140,13 +142,7 @@ static void SetSpeakerArrangement(const char *name, ALfloat SpeakerAngle[MaxChan
}
-/**
- * ComputeAngleGains
- *
- * Sets channel gains based on a given source's angle and its half-width. The
- * angle and hwidth parameters are in radians.
- */
-ALvoid ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat *gains)
+void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat gains[MaxChannels])
{
ALfloat tmpgains[MaxChannels] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
enum Channel Speaker2Chan[MaxChannels];
@@ -161,9 +157,11 @@ ALvoid ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth,
SpeakerAngle[i] = device->SpeakerAngle[i];
/* Some easy special-cases first... */
- if(device->NumChan == 1 || hwidth >= F_PI)
+ if(device->NumChan <= 1 || hwidth >= F_PI)
{
/* Full coverage for all speakers. */
+ for(i = 0;i < MaxChannels;i++)
+ gains[i] = 0.0f;
for(i = 0;i < device->NumChan;i++)
{
enum Channel chan = Speaker2Chan[i];
@@ -174,6 +172,8 @@ ALvoid ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth,
if(hwidth <= 0.0f)
{
/* Infinitely small sound point. */
+ for(i = 0;i < MaxChannels;i++)
+ gains[i] = 0.0f;
for(i = 0;i < device->NumChan-1;i++)
{
if(angle >= SpeakerAngle[i] && angle < SpeakerAngle[i+1])
@@ -188,9 +188,9 @@ ALvoid ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth,
}
/* Sound is between last and first speakers */
if(angle < SpeakerAngle[0])
- angle += F_PI*2.0f;
- a = (angle-SpeakerAngle[i]) /
- (F_PI*2.0f + SpeakerAngle[0]-SpeakerAngle[i]);
+ angle += F_2PI;
+ a = (angle-SpeakerAngle[i]) /
+ (F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
gains[Speaker2Chan[i]] = sqrtf(1.0f-a) * ingain;
gains[Speaker2Chan[0]] = sqrtf( a) * ingain;
return;
@@ -203,7 +203,7 @@ ALvoid ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth,
* within -pi...+pi. */
if(angle > 0.0f)
{
- ALuint done = 0;
+ ALuint done;
ALuint i = 0;
while(i < device->NumChan && device->SpeakerAngle[i]-angle < -F_PI)
i++;
@@ -215,7 +215,7 @@ ALvoid ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth,
}
for(i = 0;done < device->NumChan;i++)
{
- SpeakerAngle[done] = device->SpeakerAngle[i]-angle + F_PI*2.0f;
+ SpeakerAngle[done] = device->SpeakerAngle[i]-angle + F_2PI;
Speaker2Chan[done] = device->Speaker2Chan[i];
done++;
}
@@ -226,7 +226,7 @@ ALvoid ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth,
* we need to handle index 0. Because the iterators are unsigned,
* they'll underflow and wrap to become 0xFFFFFFFF, which will
* break as expected. */
- ALuint done = device->NumChan-1;
+ ALuint done;
ALuint i = device->NumChan-1;
while(i < device->NumChan && device->SpeakerAngle[i]-angle > F_PI)
i--;
@@ -238,7 +238,7 @@ ALvoid ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth,
}
for(i = device->NumChan-1;done < device->NumChan;i--)
{
- SpeakerAngle[done] = device->SpeakerAngle[i]-angle - F_PI*2.0f;
+ SpeakerAngle[done] = device->SpeakerAngle[i]-angle - F_2PI;
Speaker2Chan[done] = device->Speaker2Chan[i];
done--;
}
@@ -268,14 +268,14 @@ ALvoid ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth,
}
if(SpeakerAngle[i] > rangle)
{
- a = (F_PI*2.0f + rangle-SpeakerAngle[last]) /
- (F_PI*2.0f + SpeakerAngle[i]-SpeakerAngle[last]);
+ a = (F_2PI + rangle-SpeakerAngle[last]) /
+ (F_2PI + SpeakerAngle[i]-SpeakerAngle[last]);
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
}
else if(SpeakerAngle[last] < rangle)
{
- a = (rangle-SpeakerAngle[last]) /
- (F_PI*2.0f + SpeakerAngle[i]-SpeakerAngle[last]);
+ a = (rangle-SpeakerAngle[last]) /
+ (F_2PI + SpeakerAngle[i]-SpeakerAngle[last]);
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
}
} while(0);
@@ -320,14 +320,14 @@ ALvoid ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth,
}
if(SpeakerAngle[i] < langle)
{
- a = (langle-SpeakerAngle[i]) /
- (F_PI*2.0f + SpeakerAngle[0]-SpeakerAngle[i]);
+ a = (langle-SpeakerAngle[i]) /
+ (F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
}
else if(SpeakerAngle[0] > langle)
{
- a = (F_PI*2.0f + langle-SpeakerAngle[i]) /
- (F_PI*2.0f + SpeakerAngle[0]-SpeakerAngle[i]);
+ a = (F_2PI + langle-SpeakerAngle[i]) /
+ (F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
}
} while(0);
@@ -353,7 +353,7 @@ ALvoid aluInitPanning(ALCdevice *Device)
case DevFmtMono:
Device->NumChan = 1;
Speaker2Chan[0] = FrontCenter;
- SpeakerAngle[0] = F_PI/180.0f * 0.0f;
+ SpeakerAngle[0] = DEG2RAD(0.0f);
layoutname = NULL;
break;
@@ -361,8 +361,8 @@ ALvoid aluInitPanning(ALCdevice *Device)
Device->NumChan = 2;
Speaker2Chan[0] = FrontLeft;
Speaker2Chan[1] = FrontRight;
- SpeakerAngle[0] = F_PI/180.0f * -90.0f;
- SpeakerAngle[1] = F_PI/180.0f * 90.0f;
+ SpeakerAngle[0] = DEG2RAD(-90.0f);
+ SpeakerAngle[1] = DEG2RAD( 90.0f);
layoutname = "layout_stereo";
break;
@@ -372,10 +372,10 @@ ALvoid aluInitPanning(ALCdevice *Device)
Speaker2Chan[1] = FrontLeft;
Speaker2Chan[2] = FrontRight;
Speaker2Chan[3] = BackRight;
- SpeakerAngle[0] = F_PI/180.0f * -135.0f;
- SpeakerAngle[1] = F_PI/180.0f * -45.0f;
- SpeakerAngle[2] = F_PI/180.0f * 45.0f;
- SpeakerAngle[3] = F_PI/180.0f * 135.0f;
+ SpeakerAngle[0] = DEG2RAD(-135.0f);
+ SpeakerAngle[1] = DEG2RAD( -45.0f);
+ SpeakerAngle[2] = DEG2RAD( 45.0f);
+ SpeakerAngle[3] = DEG2RAD( 135.0f);
layoutname = "layout_quad";
break;
@@ -386,11 +386,11 @@ ALvoid aluInitPanning(ALCdevice *Device)
Speaker2Chan[2] = FrontCenter;
Speaker2Chan[3] = FrontRight;
Speaker2Chan[4] = BackRight;
- SpeakerAngle[0] = F_PI/180.0f * -110.0f;
- SpeakerAngle[1] = F_PI/180.0f * -30.0f;
- SpeakerAngle[2] = F_PI/180.0f * 0.0f;
- SpeakerAngle[3] = F_PI/180.0f * 30.0f;
- SpeakerAngle[4] = F_PI/180.0f * 110.0f;
+ SpeakerAngle[0] = DEG2RAD(-110.0f);
+ SpeakerAngle[1] = DEG2RAD( -30.0f);
+ SpeakerAngle[2] = DEG2RAD( 0.0f);
+ SpeakerAngle[3] = DEG2RAD( 30.0f);
+ SpeakerAngle[4] = DEG2RAD( 110.0f);
layoutname = "layout_surround51";
break;
@@ -401,11 +401,11 @@ ALvoid aluInitPanning(ALCdevice *Device)
Speaker2Chan[2] = FrontCenter;
Speaker2Chan[3] = FrontRight;
Speaker2Chan[4] = SideRight;
- SpeakerAngle[0] = F_PI/180.0f * -90.0f;
- SpeakerAngle[1] = F_PI/180.0f * -30.0f;
- SpeakerAngle[2] = F_PI/180.0f * 0.0f;
- SpeakerAngle[3] = F_PI/180.0f * 30.0f;
- SpeakerAngle[4] = F_PI/180.0f * 90.0f;
+ SpeakerAngle[0] = DEG2RAD(-90.0f);
+ SpeakerAngle[1] = DEG2RAD(-30.0f);
+ SpeakerAngle[2] = DEG2RAD( 0.0f);
+ SpeakerAngle[3] = DEG2RAD( 30.0f);
+ SpeakerAngle[4] = DEG2RAD( 90.0f);
layoutname = "layout_side51";
break;
@@ -417,12 +417,12 @@ ALvoid aluInitPanning(ALCdevice *Device)
Speaker2Chan[3] = FrontRight;
Speaker2Chan[4] = SideRight;
Speaker2Chan[5] = BackCenter;
- SpeakerAngle[0] = F_PI/180.0f * -90.0f;
- SpeakerAngle[1] = F_PI/180.0f * -30.0f;
- SpeakerAngle[2] = F_PI/180.0f * 0.0f;
- SpeakerAngle[3] = F_PI/180.0f * 30.0f;
- SpeakerAngle[4] = F_PI/180.0f * 90.0f;
- SpeakerAngle[5] = F_PI/180.0f * 180.0f;
+ SpeakerAngle[0] = DEG2RAD(-90.0f);
+ SpeakerAngle[1] = DEG2RAD(-30.0f);
+ SpeakerAngle[2] = DEG2RAD( 0.0f);
+ SpeakerAngle[3] = DEG2RAD( 30.0f);
+ SpeakerAngle[4] = DEG2RAD( 90.0f);
+ SpeakerAngle[5] = DEG2RAD(180.0f);
layoutname = "layout_surround61";
break;
@@ -435,13 +435,13 @@ ALvoid aluInitPanning(ALCdevice *Device)
Speaker2Chan[4] = FrontRight;
Speaker2Chan[5] = SideRight;
Speaker2Chan[6] = BackRight;
- SpeakerAngle[0] = F_PI/180.0f * -150.0f;
- SpeakerAngle[1] = F_PI/180.0f * -90.0f;
- SpeakerAngle[2] = F_PI/180.0f * -30.0f;
- SpeakerAngle[3] = F_PI/180.0f * 0.0f;
- SpeakerAngle[4] = F_PI/180.0f * 30.0f;
- SpeakerAngle[5] = F_PI/180.0f * 90.0f;
- SpeakerAngle[6] = F_PI/180.0f * 150.0f;
+ SpeakerAngle[0] = DEG2RAD(-150.0f);
+ SpeakerAngle[1] = DEG2RAD( -90.0f);
+ SpeakerAngle[2] = DEG2RAD( -30.0f);
+ SpeakerAngle[3] = DEG2RAD( 0.0f);
+ SpeakerAngle[4] = DEG2RAD( 30.0f);
+ SpeakerAngle[5] = DEG2RAD( 90.0f);
+ SpeakerAngle[6] = DEG2RAD( 150.0f);
layoutname = "layout_surround71";
break;
}
diff --git a/Alc/rwlock.h b/Alc/rwlock.h
new file mode 100644
index 00000000..efbab4e8
--- /dev/null
+++ b/Alc/rwlock.h
@@ -0,0 +1,21 @@
+#ifndef AL_RWLOCK_H
+#define AL_RWLOCK_H
+
+#include "AL/al.h"
+#include "atomic.h"
+
+typedef struct {
+ volatile RefCount read_count;
+ volatile RefCount write_count;
+ volatile ALenum read_lock;
+ volatile ALenum read_entry_lock;
+ volatile ALenum write_lock;
+} RWLock;
+
+void RWLockInit(RWLock *lock);
+void ReadLock(RWLock *lock);
+void ReadUnlock(RWLock *lock);
+void WriteLock(RWLock *lock);
+void WriteUnlock(RWLock *lock);
+
+#endif /* AL_RWLOCK_H */
diff --git a/Alc/threads.c b/Alc/threads.c
new file mode 100644
index 00000000..64586ae9
--- /dev/null
+++ b/Alc/threads.c
@@ -0,0 +1,200 @@
+/**
+ * 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 "threads.h"
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "alMain.h"
+#include "alThunk.h"
+
+#define THREAD_STACK_SIZE (1*1024*1024) /* 1MB */
+
+#ifdef _WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+typedef struct althread_info {
+ ALuint (*func)(ALvoid*);
+ ALvoid *ptr;
+ HANDLE hdl;
+} althread_info;
+
+static DWORD CALLBACK StarterFunc(void *ptr)
+{
+ althread_info *inf = (althread_info*)ptr;
+ ALuint ret;
+
+ ret = inf->func(inf->ptr);
+ ExitThread((DWORD)ret);
+
+ return (DWORD)ret;
+}
+
+
+ALboolean StartThread(althread_t *thread, ALuint (*func)(ALvoid*), ALvoid *ptr)
+{
+ althread_info *info;
+ DWORD dummy;
+
+ info = malloc(sizeof(*info));
+ if(!info) return AL_FALSE;
+
+ info->func = func;
+ info->ptr = ptr;
+
+ info->hdl = CreateThread(NULL, THREAD_STACK_SIZE, StarterFunc, info, 0, &dummy);
+ if(!info->hdl)
+ {
+ free(info);
+ return AL_FALSE;
+ }
+
+ *thread = info;
+ return AL_TRUE;
+}
+
+ALuint StopThread(althread_t thread)
+{
+ DWORD ret = 0;
+
+ WaitForSingleObject(thread->hdl, INFINITE);
+ GetExitCodeThread(thread->hdl, &ret);
+ CloseHandle(thread->hdl);
+
+ free(thread);
+
+ return (ALuint)ret;
+}
+
+
+void SetThreadName(const char *name)
+{
+#if defined(_MSC_VER)
+#define MS_VC_EXCEPTION 0x406D1388
+ struct {
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+ } info;
+ info.dwType = 0x1000;
+ info.szName = name;
+ info.dwThreadID = -1;
+ info.dwFlags = 0;
+
+ __try {
+ RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info);
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION) {
+ }
+#undef MS_VC_EXCEPTION
+#else
+ TRACE("Can't set thread %04lx name to \"%s\"\n", GetCurrentThreadId(), name);
+#endif
+}
+
+#else
+
+#include <pthread.h>
+
+typedef struct althread_info {
+ ALuint (*func)(ALvoid*);
+ ALvoid *ptr;
+ ALuint ret;
+ pthread_t hdl;
+} althread_info;
+
+static void *StarterFunc(void *ptr)
+{
+ althread_info *inf = (althread_info*)ptr;
+ inf->ret = inf->func(inf->ptr);
+ return NULL;
+}
+
+
+ALboolean StartThread(althread_t *thread, ALuint (*func)(ALvoid*), ALvoid *ptr)
+{
+ pthread_attr_t attr;
+ althread_info *info;
+
+ info = malloc(sizeof(*info));
+ if(!info) return AL_FALSE;
+
+ if(pthread_attr_init(&attr) != 0)
+ {
+ free(info);
+ return AL_FALSE;
+ }
+ if(pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE) != 0)
+ {
+ pthread_attr_destroy(&attr);
+ free(info);
+ return AL_FALSE;
+ }
+
+ info->func = func;
+ info->ptr = ptr;
+ if(pthread_create(&info->hdl, &attr, StarterFunc, info) != 0)
+ {
+ pthread_attr_destroy(&attr);
+ free(info);
+ return AL_FALSE;
+ }
+ pthread_attr_destroy(&attr);
+
+ *thread = info;
+ return AL_TRUE;
+}
+
+ALuint StopThread(althread_t thread)
+{
+ ALuint ret;
+
+ pthread_join(thread->hdl, NULL);
+ ret = thread->ret;
+
+ free(thread);
+
+ return ret;
+}
+
+
+void SetThreadName(const char *name)
+{
+#if defined(HAVE_PTHREAD_SETNAME_NP)
+#if defined(__GNUC__)
+ if(pthread_setname_np(pthread_self(), name) != 0)
+#elif defined(__APPLE__)
+ if(pthread_setname_np(name) != 0)
+#endif
+ WARN("Failed to set thread name to \"%s\": %s\n", name, strerror(errno));
+#elif defined(HAVE_PTHREAD_SET_NAME_NP)
+ pthread_set_name_np(pthread_self(), name);
+#else
+ TRACE("Can't set thread name to \"%s\"\n", name);
+#endif
+}
+
+#endif
diff --git a/Alc/uintmap.h b/Alc/uintmap.h
new file mode 100644
index 00000000..2c70f161
--- /dev/null
+++ b/Alc/uintmap.h
@@ -0,0 +1,34 @@
+#ifndef AL_UINTMAP_H
+#define AL_UINTMAP_H
+
+#include "AL/al.h"
+#include "rwlock.h"
+
+typedef struct UIntMap {
+ struct {
+ ALuint key;
+ ALvoid *value;
+ } *array;
+ ALsizei size;
+ ALsizei maxsize;
+ ALsizei limit;
+ RWLock lock;
+} UIntMap;
+extern UIntMap TlsDestructor;
+
+void InitUIntMap(UIntMap *map, ALsizei limit);
+void ResetUIntMap(UIntMap *map);
+ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value);
+ALvoid *RemoveUIntMapKey(UIntMap *map, ALuint key);
+ALvoid *LookupUIntMapKey(UIntMap *map, ALuint key);
+
+inline void LockUIntMapRead(UIntMap *map)
+{ ReadLock(&map->lock); }
+inline void UnlockUIntMapRead(UIntMap *map)
+{ ReadUnlock(&map->lock); }
+inline void LockUIntMapWrite(UIntMap *map)
+{ WriteLock(&map->lock); }
+inline void UnlockUIntMapWrite(UIntMap *map)
+{ WriteUnlock(&map->lock); }
+
+#endif /* AL_UINTMAP_H */