diff options
author | Sven Gothel <[email protected]> | 2014-01-26 07:06:02 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-01-26 07:06:02 +0100 |
commit | e6f4251945c228a775649b5ccd7f11dd4519c28d (patch) | |
tree | 8454b34363358cf9bb502021a68c6985c97daac4 /Alc | |
parent | 389ae1f767bfad6116e21306fc3cdf89a4cbcc0a (diff) | |
parent | 49baa9128dd98e986639def4f24c7522d9ec6b56 (diff) |
Merge branch 'UPSTREAM'
Diffstat (limited to 'Alc')
56 files changed, 11377 insertions, 2894 deletions
@@ -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); +} @@ -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, ¶m); + param.sched_priority=param.sched_curpriority+1; + SchedSet(0, 0, SCHED_NOCHANGE, ¶m); + + 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) @@ -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 */ |