# HG changeset patch # User Robert McIntyre # Date 1319572951 25200 # Node ID f9476ff7637e712bc783424e6a37ce0adfb2c84d initial forking of open-al to create multiple listeners diff -r 000000000000 -r f9476ff7637e .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,3 @@ +syntax: glob +build* + diff -r 000000000000 -r f9476ff7637e Alc/ALc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/ALc.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,2681 @@ +/** + * 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 +#include +#include +#include +#include + +#include "alMain.h" +#include "alSource.h" +#include "AL/al.h" +#include "AL/alc.h" +#include "alThunk.h" +#include "alSource.h" +#include "alBuffer.h" +#include "alAuxEffectSlot.h" +#include "bs2b.h" +#include "alu.h" + +// RLM: try to include the file here. + + + +#define EmptyFuncs { 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 }, +#endif +#ifdef HAVE_ALSA + { "alsa", alc_alsa_init, alc_alsa_deinit, alc_alsa_probe, EmptyFuncs }, +#endif +#ifdef HAVE_COREAUDIO + { "core", 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 }, +#endif +#ifdef HAVE_SOLARIS + { "solaris", 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 }, +#endif +#ifdef HAVE_MMDEVAPI + { "mmdevapi", alcMMDevApiInit, alcMMDevApiDeinit, alcMMDevApiProbe, EmptyFuncs }, +#endif +#ifdef HAVE_DSOUND + { "dsound", alcDSoundInit, alcDSoundDeinit, alcDSoundProbe, EmptyFuncs }, +#endif +#ifdef HAVE_WINMM + { "winmm", alcWinMMInit, alcWinMMDeinit, alcWinMMProbe, EmptyFuncs }, +#endif +#ifdef HAVE_PORTAUDIO + { "port", 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 }, +#endif + + { "null", alc_null_init, alc_null_deinit, alc_null_probe, EmptyFuncs }, +#ifdef HAVE_WAVE + { "wave", alc_wave_init, alc_wave_deinit, alc_wave_probe, EmptyFuncs }, +#endif + { "send", alc_send_init, alc_send_deinit, alc_send_probe, EmptyFuncs }, + // this entry is so that the list is terminated by a "null" object, + // and helps things to tell when they've reached the end of the list. + { NULL, NULL, NULL, NULL, EmptyFuncs } +}; +static struct BackendInfo BackendLoopback = { + "loopback", alc_loopback_init, alc_loopback_deinit, alc_loopback_probe, EmptyFuncs +}; +#undef EmptyFuncs + +static struct BackendInfo PlaybackBackend; +static struct BackendInfo CaptureBackend; + +/////////////////////////////////////////////////////// +// STRING and EXTENSIONS + +typedef struct ALCfunction { + const ALCchar *funcName; + ALCvoid *address; +} ALCfunction; + +typedef struct ALCenums { + const ALCchar *enumName; + ALCenum value; +} ALCenums; + + +static const ALCfunction alcFunctions[] = { + { "alcCreateContext", (ALCvoid *) alcCreateContext }, + { "alcMakeContextCurrent", (ALCvoid *) alcMakeContextCurrent }, + { "alcProcessContext", (ALCvoid *) alcProcessContext }, + { "alcSuspendContext", (ALCvoid *) alcSuspendContext }, + { "alcDestroyContext", (ALCvoid *) alcDestroyContext }, + { "alcGetCurrentContext", (ALCvoid *) alcGetCurrentContext }, + { "alcGetContextsDevice", (ALCvoid *) alcGetContextsDevice }, + { "alcOpenDevice", (ALCvoid *) alcOpenDevice }, + { "alcCloseDevice", (ALCvoid *) alcCloseDevice }, + { "alcGetError", (ALCvoid *) alcGetError }, + { "alcIsExtensionPresent", (ALCvoid *) alcIsExtensionPresent }, + { "alcGetProcAddress", (ALCvoid *) alcGetProcAddress }, + { "alcGetEnumValue", (ALCvoid *) alcGetEnumValue }, + { "alcGetString", (ALCvoid *) alcGetString }, + { "alcGetIntegerv", (ALCvoid *) alcGetIntegerv }, + { "alcCaptureOpenDevice", (ALCvoid *) alcCaptureOpenDevice }, + { "alcCaptureCloseDevice", (ALCvoid *) alcCaptureCloseDevice }, + { "alcCaptureStart", (ALCvoid *) alcCaptureStart }, + { "alcCaptureStop", (ALCvoid *) alcCaptureStop }, + { "alcCaptureSamples", (ALCvoid *) alcCaptureSamples }, + + { "alcSetThreadContext", (ALCvoid *) alcSetThreadContext }, + { "alcGetThreadContext", (ALCvoid *) alcGetThreadContext }, + + { "alcLoopbackOpenDeviceSOFT", (ALCvoid *) alcLoopbackOpenDeviceSOFT}, + { "alcIsRenderFormatSupportedSOFT",(ALCvoid *) alcIsRenderFormatSupportedSOFT}, + { "alcRenderSamplesSOFT", (ALCvoid *) alcRenderSamplesSOFT }, + + { "alEnable", (ALCvoid *) alEnable }, + { "alDisable", (ALCvoid *) alDisable }, + { "alIsEnabled", (ALCvoid *) alIsEnabled }, + { "alGetString", (ALCvoid *) alGetString }, + { "alGetBooleanv", (ALCvoid *) alGetBooleanv }, + { "alGetIntegerv", (ALCvoid *) alGetIntegerv }, + { "alGetFloatv", (ALCvoid *) alGetFloatv }, + { "alGetDoublev", (ALCvoid *) alGetDoublev }, + { "alGetBoolean", (ALCvoid *) alGetBoolean }, + { "alGetInteger", (ALCvoid *) alGetInteger }, + { "alGetFloat", (ALCvoid *) alGetFloat }, + { "alGetDouble", (ALCvoid *) alGetDouble }, + { "alGetError", (ALCvoid *) alGetError }, + { "alIsExtensionPresent", (ALCvoid *) alIsExtensionPresent }, + { "alGetProcAddress", (ALCvoid *) alGetProcAddress }, + { "alGetEnumValue", (ALCvoid *) alGetEnumValue }, + { "alListenerf", (ALCvoid *) alListenerf }, + { "alListener3f", (ALCvoid *) alListener3f }, + { "alListenerfv", (ALCvoid *) alListenerfv }, + { "alListeneri", (ALCvoid *) alListeneri }, + { "alListener3i", (ALCvoid *) alListener3i }, + { "alListeneriv", (ALCvoid *) alListeneriv }, + { "alGetListenerf", (ALCvoid *) alGetListenerf }, + { "alGetListener3f", (ALCvoid *) alGetListener3f }, + { "alGetListenerfv", (ALCvoid *) alGetListenerfv }, + { "alGetListeneri", (ALCvoid *) alGetListeneri }, + { "alGetListener3i", (ALCvoid *) alGetListener3i }, + { "alGetListeneriv", (ALCvoid *) alGetListeneriv }, + { "alGenSources", (ALCvoid *) alGenSources }, + { "alDeleteSources", (ALCvoid *) alDeleteSources }, + { "alIsSource", (ALCvoid *) alIsSource }, + { "alSourcef", (ALCvoid *) alSourcef }, + { "alSource3f", (ALCvoid *) alSource3f }, + { "alSourcefv", (ALCvoid *) alSourcefv }, + { "alSourcei", (ALCvoid *) alSourcei }, + { "alSource3i", (ALCvoid *) alSource3i }, + { "alSourceiv", (ALCvoid *) alSourceiv }, + { "alGetSourcef", (ALCvoid *) alGetSourcef }, + { "alGetSource3f", (ALCvoid *) alGetSource3f }, + { "alGetSourcefv", (ALCvoid *) alGetSourcefv }, + { "alGetSourcei", (ALCvoid *) alGetSourcei }, + { "alGetSource3i", (ALCvoid *) alGetSource3i }, + { "alGetSourceiv", (ALCvoid *) alGetSourceiv }, + { "alSourcePlayv", (ALCvoid *) alSourcePlayv }, + { "alSourceStopv", (ALCvoid *) alSourceStopv }, + { "alSourceRewindv", (ALCvoid *) alSourceRewindv }, + { "alSourcePausev", (ALCvoid *) alSourcePausev }, + { "alSourcePlay", (ALCvoid *) alSourcePlay }, + { "alSourceStop", (ALCvoid *) alSourceStop }, + { "alSourceRewind", (ALCvoid *) alSourceRewind }, + { "alSourcePause", (ALCvoid *) alSourcePause }, + { "alSourceQueueBuffers", (ALCvoid *) alSourceQueueBuffers }, + { "alSourceUnqueueBuffers", (ALCvoid *) alSourceUnqueueBuffers }, + { "alGenBuffers", (ALCvoid *) alGenBuffers }, + { "alDeleteBuffers", (ALCvoid *) alDeleteBuffers }, + { "alIsBuffer", (ALCvoid *) alIsBuffer }, + { "alBufferData", (ALCvoid *) alBufferData }, + { "alBufferf", (ALCvoid *) alBufferf }, + { "alBuffer3f", (ALCvoid *) alBuffer3f }, + { "alBufferfv", (ALCvoid *) alBufferfv }, + { "alBufferi", (ALCvoid *) alBufferi }, + { "alBuffer3i", (ALCvoid *) alBuffer3i }, + { "alBufferiv", (ALCvoid *) alBufferiv }, + { "alGetBufferf", (ALCvoid *) alGetBufferf }, + { "alGetBuffer3f", (ALCvoid *) alGetBuffer3f }, + { "alGetBufferfv", (ALCvoid *) alGetBufferfv }, + { "alGetBufferi", (ALCvoid *) alGetBufferi }, + { "alGetBuffer3i", (ALCvoid *) alGetBuffer3i }, + { "alGetBufferiv", (ALCvoid *) alGetBufferiv }, + { "alDopplerFactor", (ALCvoid *) alDopplerFactor }, + { "alDopplerVelocity", (ALCvoid *) alDopplerVelocity }, + { "alSpeedOfSound", (ALCvoid *) alSpeedOfSound }, + { "alDistanceModel", (ALCvoid *) alDistanceModel }, + + { "alGenFilters", (ALCvoid *) alGenFilters }, + { "alDeleteFilters", (ALCvoid *) alDeleteFilters }, + { "alIsFilter", (ALCvoid *) alIsFilter }, + { "alFilteri", (ALCvoid *) alFilteri }, + { "alFilteriv", (ALCvoid *) alFilteriv }, + { "alFilterf", (ALCvoid *) alFilterf }, + { "alFilterfv", (ALCvoid *) alFilterfv }, + { "alGetFilteri", (ALCvoid *) alGetFilteri }, + { "alGetFilteriv", (ALCvoid *) alGetFilteriv }, + { "alGetFilterf", (ALCvoid *) alGetFilterf }, + { "alGetFilterfv", (ALCvoid *) alGetFilterfv }, + { "alGenEffects", (ALCvoid *) alGenEffects }, + { "alDeleteEffects", (ALCvoid *) alDeleteEffects }, + { "alIsEffect", (ALCvoid *) alIsEffect }, + { "alEffecti", (ALCvoid *) alEffecti }, + { "alEffectiv", (ALCvoid *) alEffectiv }, + { "alEffectf", (ALCvoid *) alEffectf }, + { "alEffectfv", (ALCvoid *) alEffectfv }, + { "alGetEffecti", (ALCvoid *) alGetEffecti }, + { "alGetEffectiv", (ALCvoid *) alGetEffectiv }, + { "alGetEffectf", (ALCvoid *) alGetEffectf }, + { "alGetEffectfv", (ALCvoid *) alGetEffectfv }, + { "alGenAuxiliaryEffectSlots", (ALCvoid *) alGenAuxiliaryEffectSlots}, + { "alDeleteAuxiliaryEffectSlots",(ALCvoid *) alDeleteAuxiliaryEffectSlots}, + { "alIsAuxiliaryEffectSlot", (ALCvoid *) alIsAuxiliaryEffectSlot }, + { "alAuxiliaryEffectSloti", (ALCvoid *) alAuxiliaryEffectSloti }, + { "alAuxiliaryEffectSlotiv", (ALCvoid *) alAuxiliaryEffectSlotiv }, + { "alAuxiliaryEffectSlotf", (ALCvoid *) alAuxiliaryEffectSlotf }, + { "alAuxiliaryEffectSlotfv", (ALCvoid *) alAuxiliaryEffectSlotfv }, + { "alGetAuxiliaryEffectSloti", (ALCvoid *) alGetAuxiliaryEffectSloti}, + { "alGetAuxiliaryEffectSlotiv", (ALCvoid *) alGetAuxiliaryEffectSlotiv}, + { "alGetAuxiliaryEffectSlotf", (ALCvoid *) alGetAuxiliaryEffectSlotf}, + { "alGetAuxiliaryEffectSlotfv", (ALCvoid *) alGetAuxiliaryEffectSlotfv}, + + { "alBufferSubDataSOFT", (ALCvoid *) alBufferSubDataSOFT }, + + { "alBufferSamplesSOFT", (ALCvoid *) alBufferSamplesSOFT }, + { "alBufferSubSamplesSOFT", (ALCvoid *) alBufferSubSamplesSOFT }, + { "alGetBufferSamplesSOFT", (ALCvoid *) alGetBufferSamplesSOFT }, + { "alIsBufferFormatSupportedSOFT",(ALCvoid *) alIsBufferFormatSupportedSOFT}, + + { "alDeferUpdatesSOFT", (ALCvoid *) alDeferUpdatesSOFT }, + { "alProcessUpdatesSOFT", (ALCvoid *) alProcessUpdatesSOFT }, + + { NULL, (ALCvoid *) NULL } +}; + +static const ALCenums enumeration[] = { + // Types + { "ALC_INVALID", ALC_INVALID }, + { "ALC_FALSE", ALC_FALSE }, + { "ALC_TRUE", ALC_TRUE }, + + // ALC Properties + { "ALC_MAJOR_VERSION", ALC_MAJOR_VERSION }, + { "ALC_MINOR_VERSION", ALC_MINOR_VERSION }, + { "ALC_ATTRIBUTES_SIZE", ALC_ATTRIBUTES_SIZE }, + { "ALC_ALL_ATTRIBUTES", ALC_ALL_ATTRIBUTES }, + { "ALC_DEFAULT_DEVICE_SPECIFIER", ALC_DEFAULT_DEVICE_SPECIFIER }, + { "ALC_DEVICE_SPECIFIER", ALC_DEVICE_SPECIFIER }, + { "ALC_ALL_DEVICES_SPECIFIER", ALC_ALL_DEVICES_SPECIFIER }, + { "ALC_DEFAULT_ALL_DEVICES_SPECIFIER", ALC_DEFAULT_ALL_DEVICES_SPECIFIER }, + { "ALC_EXTENSIONS", ALC_EXTENSIONS }, + { "ALC_FREQUENCY", ALC_FREQUENCY }, + { "ALC_REFRESH", ALC_REFRESH }, + { "ALC_SYNC", ALC_SYNC }, + { "ALC_MONO_SOURCES", ALC_MONO_SOURCES }, + { "ALC_STEREO_SOURCES", ALC_STEREO_SOURCES }, + { "ALC_CAPTURE_DEVICE_SPECIFIER", ALC_CAPTURE_DEVICE_SPECIFIER }, + { "ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER", ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER}, + { "ALC_CAPTURE_SAMPLES", ALC_CAPTURE_SAMPLES }, + { "ALC_CONNECTED", ALC_CONNECTED }, + + // EFX Properties + { "ALC_EFX_MAJOR_VERSION", ALC_EFX_MAJOR_VERSION }, + { "ALC_EFX_MINOR_VERSION", ALC_EFX_MINOR_VERSION }, + { "ALC_MAX_AUXILIARY_SENDS", ALC_MAX_AUXILIARY_SENDS }, + + // Loopback device Properties + { "ALC_FORMAT_CHANNELS_SOFT", ALC_FORMAT_CHANNELS_SOFT }, + { "ALC_FORMAT_TYPE_SOFT", ALC_FORMAT_TYPE_SOFT }, + + // Buffer Channel Configurations + { "ALC_MONO", ALC_MONO }, + { "ALC_STEREO", ALC_STEREO }, + { "ALC_QUAD", ALC_QUAD }, + { "ALC_5POINT1", ALC_5POINT1 }, + { "ALC_6POINT1", ALC_6POINT1 }, + { "ALC_7POINT1", ALC_7POINT1 }, + + // Buffer Sample Types + { "ALC_BYTE", ALC_BYTE }, + { "ALC_UNSIGNED_BYTE", ALC_UNSIGNED_BYTE }, + { "ALC_SHORT", ALC_SHORT }, + { "ALC_UNSIGNED_SHORT", ALC_UNSIGNED_SHORT }, + { "ALC_INT", ALC_INT }, + { "ALC_UNSIGNED_INT", ALC_UNSIGNED_INT }, + { "ALC_FLOAT", ALC_FLOAT }, + + // ALC Error Message + { "ALC_NO_ERROR", ALC_NO_ERROR }, + { "ALC_INVALID_DEVICE", ALC_INVALID_DEVICE }, + { "ALC_INVALID_CONTEXT", ALC_INVALID_CONTEXT }, + { "ALC_INVALID_ENUM", ALC_INVALID_ENUM }, + { "ALC_INVALID_VALUE", ALC_INVALID_VALUE }, + { "ALC_OUT_OF_MEMORY", ALC_OUT_OF_MEMORY }, + + { NULL, (ALCenum)0 } +}; +// Error strings +static const ALCchar alcNoError[] = "No Error"; +static const ALCchar alcErrInvalidDevice[] = "Invalid Device"; +static const ALCchar alcErrInvalidContext[] = "Invalid Context"; +static const ALCchar alcErrInvalidEnum[] = "Invalid Enum"; +static const ALCchar alcErrInvalidValue[] = "Invalid Value"; +static const ALCchar alcErrOutOfMemory[] = "Out of Memory"; + +/* Device lists. Sizes only include the first ending null character, not the + * second */ +static ALCchar *alcDeviceList; +static size_t alcDeviceListSize; +static ALCchar *alcAllDeviceList; +static size_t alcAllDeviceListSize; +static ALCchar *alcCaptureDeviceList; +static size_t alcCaptureDeviceListSize; +/* Default is always the first in the list */ +static ALCchar *alcDefaultDeviceSpecifier; +static ALCchar *alcDefaultAllDeviceSpecifier; +static ALCchar *alcCaptureDefaultDeviceSpecifier; + + +static const ALCchar alcNoDeviceExtList[] = + "ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE " + "ALC_EXT_thread_local_context ALC_SOFTX_loopback_device"; +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_SOFTX_loopback_device"; +static const ALCint alcMajorVersion = 1; +static const ALCint alcMinorVersion = 1; + +static const ALCint alcEFXMajorVersion = 1; +static const ALCint alcEFXMinorVersion = 0; + +/////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// Global Variables + +static CRITICAL_SECTION ListLock; + +/* Device List */ +static ALCdevice *g_pDeviceList = NULL; +static ALCuint g_ulDeviceCount = 0; + +// Context List +static ALCcontext *g_pContextList = NULL; +static ALCuint g_ulContextCount = 0; + +// Thread-local current context +static tls_type LocalContext; +// Process-wide current context +static ALCcontext *GlobalContext; + +// Context Error +static ALCenum g_eLastNullDeviceError = ALC_NO_ERROR; + +// Default context extensions +static const ALchar alExtList[] = + "AL_EXT_DOUBLE AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 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_SOFTX_buffer_samples AL_SOFT_buffer_sub_data " + "AL_SOFTX_deferred_updates AL_SOFT_loop_points " + "AL_SOFTX_non_virtual_channels"; + +// Mixing Priority Level +ALint RTPrioLevel; + +// Output Log File +FILE *LogFile; + +// Output Log Level +#ifdef _DEBUG +enum LogLevel LogLevel = LogWarning; +#else +enum LogLevel LogLevel = LogError; +#endif + +// Cone scalar +ALdouble ConeScale = 0.5; + +// Localized Z scalar for mono sources +ALdouble ZScale = 1.0; + +/* One-time configuration init control */ +static pthread_once_t alc_config_once = PTHREAD_ONCE_INIT; + +/////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// ALC Related helper functions +static void ReleaseALC(ALCboolean doclose); + +static void alc_initconfig(void); +#define DO_INITCONFIG() pthread_once(&alc_config_once, alc_initconfig) + +#if defined(_WIN32) +static void alc_init(void); +static void alc_deinit(void); +static void alc_deinit_safe(void); + +#ifndef AL_LIBTYPE_STATIC +BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved) +{ + // Perform actions based on the reason for calling. + switch(ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hModule); + alc_init(); + break; + + case DLL_PROCESS_DETACH: + if(!lpReserved) + alc_deinit(); + else + alc_deinit_safe(); + break; + } + return TRUE; +} +#elif defined(_MSC_VER) +#pragma section(".CRT$XCU",read) +static void alc_constructor(void); +static void alc_destructor(void); +__declspec(allocate(".CRT$XCU")) void (__cdecl* alc_constructor_)(void) = alc_constructor; + +static void alc_constructor(void) +{ + atexit(alc_destructor); + alc_init(); +} + +static void alc_destructor(void) +{ + alc_deinit(); +} +#elif defined(HAVE_GCC_DESTRUCTOR) +static void alc_init(void) __attribute__((constructor)); +static void alc_deinit(void) __attribute__((destructor)); +#else +#error "No static initialization available on this platform!" +#endif +#elif defined(HAVE_GCC_DESTRUCTOR) +static void alc_init(void) __attribute__((constructor)); +static void alc_deinit(void) __attribute__((destructor)); +#else +#error "No global initialization available on this platform!" +#endif + +static void alc_init(void) +{ + const char *str; + + LogFile = stderr; + + str = getenv("__ALSOFT_HALF_ANGLE_CONES"); + if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1)) + ConeScale = 1.0; + + str = getenv("__ALSOFT_REVERSE_Z"); + if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1)) + ZScale = -1.0; + + tls_create(&LocalContext); + InitializeCriticalSection(&ListLock); + ThunkInit(); +} + +static void alc_deinit_safe(void) +{ + ReleaseALC(ALC_FALSE); + + FreeALConfig(); + + ThunkExit(); + DeleteCriticalSection(&ListLock); + tls_delete(LocalContext); + + if(LogFile != stderr) + fclose(LogFile); + LogFile = NULL; +} + +static void alc_deinit(void) +{ + int i; + + ReleaseALC(ALC_TRUE); + + memset(&PlaybackBackend, 0, sizeof(PlaybackBackend)); + memset(&CaptureBackend, 0, sizeof(CaptureBackend)); + + for(i = 0;BackendList[i].Deinit;i++) + BackendList[i].Deinit(); + BackendLoopback.Deinit(); + + alc_deinit_safe(); +} + +static void alc_initconfig(void) +{ + printf("OPENAL-AURELLEM: alc_init_config!!\n"); + const char *devs, *str; + int i, n; + + str = getenv("ALSOFT_LOGLEVEL"); + if(str) + { + long lvl = strtol(str, NULL, 0); + if(lvl >= NoLog && lvl <= LogTrace) + LogLevel = lvl; + } + + str = getenv("ALSOFT_LOGFILE"); + if(str && str[0]) + { + FILE *logfile = fopen(str, "wat"); + if(logfile) LogFile = logfile; + else ERR("Failed to open log file '%s'\n", str); + } + + ReadALConfig(); + + InitHrtf(); + +#ifdef _WIN32 + RTPrioLevel = GetConfigValueInt(NULL, "rt-prio", 1); +#else + RTPrioLevel = GetConfigValueInt(NULL, "rt-prio", 0); +#endif + + DefaultResampler = GetConfigValueInt(NULL, "resampler", RESAMPLER_DEFAULT); + if(DefaultResampler >= RESAMPLER_MAX || DefaultResampler <= RESAMPLER_MIN) + DefaultResampler = RESAMPLER_DEFAULT; + + ReverbBoost *= aluPow(10.0f, GetConfigValueFloat("reverb", "boost", 0.0f) / + 20.0f); + EmulateEAXReverb = GetConfigValueBool("reverb", "emulate-eax", AL_FALSE); + + devs = GetConfigValue(NULL, "drivers", ""); + //printf("these are the devices: %s\n", devs); + + if(devs[0]) + { + int n; + size_t len; + const char *next = devs; + int endlist, delitem; + + i = 0; + do { + devs = next; + //printf("AURELLEM: devs is %s\n",devs); + next = strchr(devs, ','); + + delitem = (devs[0] == '-'); + if(devs[0] == '-') devs++; + + if(!devs[0] || devs[0] == ',') + { + endlist = 0; + continue; + } + endlist = 1; + + len = (next ? ((size_t)(next-devs)) : strlen(devs)); + for(n = i;BackendList[n].Init;n++) + { + //printf("AURELLEM: device-name -- %s\n",BackendList[n].name); + if(len == strlen(BackendList[n].name) && + strncmp(BackendList[n].name, devs, len) == 0) + { + if(delitem) + { + do { + BackendList[n] = BackendList[n+1]; + ++n; + } + // this relies on the last entry being terminated by an + // entry with all nulls. + while(BackendList[n].Init); + } + else + { + struct BackendInfo Bkp = BackendList[n]; + while(n > i) + { + BackendList[n] = BackendList[n-1]; + --n; + } + BackendList[n] = Bkp; + + i++; + } + break; + } + } + } while(next++); + + if(endlist) + { + BackendList[i].name = NULL; + BackendList[i].Init = NULL; + BackendList[i].Deinit = NULL; + BackendList[i].Probe = NULL; + } + } + + for(i = 0;BackendList[i].Init && (!PlaybackBackend.name || !CaptureBackend.name);i++) + { + if(!BackendList[i].Init(&BackendList[i].Funcs)) + { + WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name); + continue; + } + + TRACE("Initialized backend \"%s\"\n", BackendList[i].name); + if(BackendList[i].Funcs.OpenPlayback && !PlaybackBackend.name) + { + PlaybackBackend = BackendList[i]; + TRACE("Added \"%s\" for playback\n", PlaybackBackend.name); + } + if(BackendList[i].Funcs.OpenCapture && !CaptureBackend.name) + { + CaptureBackend = BackendList[i]; + TRACE("Added \"%s\" for capture\n", CaptureBackend.name); + } + } + BackendLoopback.Init(&BackendLoopback.Funcs); + + str = GetConfigValue(NULL, "excludefx", ""); + if(str[0]) + { + size_t len; + const char *next = str; + + do { + str = next; + next = strchr(str, ','); + + if(!str[0] || next == str) + continue; + + len = (next ? ((size_t)(next-str)) : strlen(str)); + for(n = 0;EffectList[n].name;n++) + { + if(len == strlen(EffectList[n].name) && + strncmp(EffectList[n].name, str, len) == 0) + DisabledEffects[EffectList[n].type] = AL_TRUE; + } + } while(next++); + } +} + + +static void ProbeList(ALCchar **list, size_t *listsize, enum DevProbe type) +{ + free(*list); + *list = NULL; + *listsize = 0; + + DO_INITCONFIG(); + if(type == CAPTURE_DEVICE_PROBE) + CaptureBackend.Probe(type); + else + PlaybackBackend.Probe(type); +} + +static void ProbeDeviceList(void) +{ ProbeList(&alcDeviceList, &alcDeviceListSize, DEVICE_PROBE); } +static void ProbeAllDeviceList(void) +{ ProbeList(&alcAllDeviceList, &alcAllDeviceListSize, ALL_DEVICE_PROBE); } +static void ProbeCaptureDeviceList(void) +{ ProbeList(&alcCaptureDeviceList, &alcCaptureDeviceListSize, CAPTURE_DEVICE_PROBE); } + + +static void AppendList(const ALCchar *name, ALCchar **List, size_t *ListSize) +{ + size_t len = strlen(name); + void *temp; + + if(len == 0) + return; + + temp = realloc(*List, (*ListSize) + len + 2); + if(!temp) + { + ERR("Realloc failed to add %s!\n", name); + return; + } + *List = temp; + + memcpy((*List)+(*ListSize), name, len+1); + *ListSize += len+1; + (*List)[*ListSize] = 0; +} + +#define DECL_APPEND_LIST_FUNC(type) \ +void Append##type##List(const ALCchar *name) \ +{ AppendList(name, &alc##type##List, &alc##type##ListSize); } + +DECL_APPEND_LIST_FUNC(Device) +DECL_APPEND_LIST_FUNC(AllDevice) +DECL_APPEND_LIST_FUNC(CaptureDevice) + +#undef DECL_APPEND_LIST_FUNC + + +/* Sets the default channel order used by most non-WaveFormatEx-based APIs */ +void SetDefaultChannelOrder(ALCdevice *device) +{ + switch(device->FmtChans) + { + case DevFmtX51: device->DevChannels[0] = FRONT_LEFT; + device->DevChannels[1] = FRONT_RIGHT; + device->DevChannels[2] = BACK_LEFT; + device->DevChannels[3] = BACK_RIGHT; + device->DevChannels[4] = FRONT_CENTER; + device->DevChannels[5] = LFE; + return; + + case DevFmtX71: device->DevChannels[0] = FRONT_LEFT; + device->DevChannels[1] = FRONT_RIGHT; + device->DevChannels[2] = BACK_LEFT; + device->DevChannels[3] = BACK_RIGHT; + device->DevChannels[4] = FRONT_CENTER; + device->DevChannels[5] = LFE; + device->DevChannels[6] = SIDE_LEFT; + device->DevChannels[7] = SIDE_RIGHT; + return; + + /* Same as WFX order */ + case DevFmtMono: + case DevFmtStereo: + case DevFmtQuad: + case DevFmtX51Side: + case DevFmtX61: + break; + } + SetDefaultWFXChannelOrder(device); +} +/* Sets the default order used by WaveFormatEx */ +void SetDefaultWFXChannelOrder(ALCdevice *device) +{ + switch(device->FmtChans) + { + case DevFmtMono: device->DevChannels[0] = FRONT_CENTER; break; + + case DevFmtStereo: device->DevChannels[0] = FRONT_LEFT; + device->DevChannels[1] = FRONT_RIGHT; break; + + case DevFmtQuad: device->DevChannels[0] = FRONT_LEFT; + device->DevChannels[1] = FRONT_RIGHT; + device->DevChannels[2] = BACK_LEFT; + device->DevChannels[3] = BACK_RIGHT; break; + + case DevFmtX51: device->DevChannels[0] = FRONT_LEFT; + device->DevChannels[1] = FRONT_RIGHT; + device->DevChannels[2] = FRONT_CENTER; + device->DevChannels[3] = LFE; + device->DevChannels[4] = BACK_LEFT; + device->DevChannels[5] = BACK_RIGHT; break; + + case DevFmtX51Side: device->DevChannels[0] = FRONT_LEFT; + device->DevChannels[1] = FRONT_RIGHT; + device->DevChannels[2] = FRONT_CENTER; + device->DevChannels[3] = LFE; + device->DevChannels[4] = SIDE_LEFT; + device->DevChannels[5] = SIDE_RIGHT; break; + + case DevFmtX61: device->DevChannels[0] = FRONT_LEFT; + device->DevChannels[1] = FRONT_RIGHT; + device->DevChannels[2] = FRONT_CENTER; + device->DevChannels[3] = LFE; + device->DevChannels[4] = BACK_CENTER; + device->DevChannels[5] = SIDE_LEFT; + device->DevChannels[6] = SIDE_RIGHT; break; + + case DevFmtX71: device->DevChannels[0] = FRONT_LEFT; + device->DevChannels[1] = FRONT_RIGHT; + device->DevChannels[2] = FRONT_CENTER; + device->DevChannels[3] = LFE; + device->DevChannels[4] = BACK_LEFT; + device->DevChannels[5] = BACK_RIGHT; + device->DevChannels[6] = SIDE_LEFT; + device->DevChannels[7] = SIDE_RIGHT; break; + } +} + + +const ALCchar *DevFmtTypeString(enum DevFmtType type) +{ + switch(type) + { + case DevFmtByte: return "Signed Byte"; + case DevFmtUByte: return "Unsigned Byte"; + case DevFmtShort: return "Signed Short"; + case DevFmtUShort: return "Unsigned Short"; + case DevFmtFloat: return "Float"; + } + return "(unknown type)"; +} +const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans) +{ + switch(chans) + { + case DevFmtMono: return "Mono"; + case DevFmtStereo: return "Stereo"; + case DevFmtQuad: return "Quadraphonic"; + case DevFmtX51: return "5.1 Surround"; + case DevFmtX51Side: return "5.1 Side"; + case DevFmtX61: return "6.1 Surround"; + case DevFmtX71: return "7.1 Surround"; + } + return "(unknown channels)"; +} + +ALuint BytesFromDevFmt(enum DevFmtType type) +{ + switch(type) + { + case DevFmtByte: return sizeof(ALbyte); + case DevFmtUByte: return sizeof(ALubyte); + case DevFmtShort: return sizeof(ALshort); + case DevFmtUShort: return sizeof(ALushort); + case DevFmtFloat: return sizeof(ALfloat); + } + return 0; +} +ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) +{ + switch(chans) + { + case DevFmtMono: return 1; + case DevFmtStereo: return 2; + case DevFmtQuad: return 4; + case DevFmtX51: return 6; + case DevFmtX51Side: return 6; + case DevFmtX61: return 7; + case DevFmtX71: return 8; + } + return 0; +} +ALboolean DecomposeDevFormat(ALenum format, enum DevFmtChannels *chans, + enum DevFmtType *type) +{ + switch(format) + { + case AL_FORMAT_MONO8: + *chans = DevFmtMono; + *type = DevFmtUByte; + return AL_TRUE; + case AL_FORMAT_MONO16: + *chans = DevFmtMono; + *type = DevFmtShort; + return AL_TRUE; + case AL_FORMAT_MONO_FLOAT32: + *chans = DevFmtMono; + *type = DevFmtFloat; + return AL_TRUE; + case AL_FORMAT_STEREO8: + *chans = DevFmtStereo; + *type = DevFmtUByte; + return AL_TRUE; + case AL_FORMAT_STEREO16: + *chans = DevFmtStereo; + *type = DevFmtShort; + return AL_TRUE; + case AL_FORMAT_STEREO_FLOAT32: + *chans = DevFmtStereo; + *type = DevFmtFloat; + return AL_TRUE; + case AL_FORMAT_QUAD8: + *chans = DevFmtQuad; + *type = DevFmtUByte; + return AL_TRUE; + case AL_FORMAT_QUAD16: + *chans = DevFmtQuad; + *type = DevFmtShort; + return AL_TRUE; + case AL_FORMAT_QUAD32: + *chans = DevFmtQuad; + *type = DevFmtFloat; + return AL_TRUE; + case AL_FORMAT_51CHN8: + *chans = DevFmtX51; + *type = DevFmtUByte; + return AL_TRUE; + case AL_FORMAT_51CHN16: + *chans = DevFmtX51; + *type = DevFmtShort; + return AL_TRUE; + case AL_FORMAT_51CHN32: + *chans = DevFmtX51; + *type = DevFmtFloat; + return AL_TRUE; + case AL_FORMAT_61CHN8: + *chans = DevFmtX61; + *type = DevFmtUByte; + return AL_TRUE; + case AL_FORMAT_61CHN16: + *chans = DevFmtX61; + *type = DevFmtShort; + return AL_TRUE; + case AL_FORMAT_61CHN32: + *chans = DevFmtX61; + *type = DevFmtFloat; + return AL_TRUE; + case AL_FORMAT_71CHN8: + *chans = DevFmtX71; + *type = DevFmtUByte; + return AL_TRUE; + case AL_FORMAT_71CHN16: + *chans = DevFmtX71; + *type = DevFmtShort; + return AL_TRUE; + case AL_FORMAT_71CHN32: + *chans = DevFmtX71; + *type = DevFmtFloat; + return AL_TRUE; + } + return AL_FALSE; +} + +static ALCboolean IsValidALCType(ALCenum type) +{ + switch(type) + { + case ALC_BYTE: + case ALC_UNSIGNED_BYTE: + case ALC_SHORT: + case ALC_UNSIGNED_SHORT: + case ALC_INT: + case ALC_UNSIGNED_INT: + case ALC_FLOAT: + return ALC_TRUE; + } + return ALC_FALSE; +} + +static ALCboolean IsValidALCChannels(ALCenum channels) +{ + switch(channels) + { + case ALC_MONO: + case ALC_STEREO: + case ALC_QUAD: + case ALC_5POINT1: + case ALC_6POINT1: + case ALC_7POINT1: + return ALC_TRUE; + } + return ALC_FALSE; +} + + +static void LockLists(void) +{ + EnterCriticalSection(&ListLock); +} + +static void UnlockLists(void) +{ + LeaveCriticalSection(&ListLock); +} + +/* + IsDevice + + Check pDevice is a valid Device pointer +*/ +static ALCboolean IsDevice(ALCdevice *pDevice) +{ + ALCdevice *pTempDevice; + + pTempDevice = g_pDeviceList; + while(pTempDevice && pTempDevice != pDevice) + pTempDevice = pTempDevice->next; + + return (pTempDevice ? ALC_TRUE : ALC_FALSE); +} + +/* + IsContext + + Check pContext is a valid Context pointer +*/ +static ALCboolean IsContext(ALCcontext *pContext) +{ + ALCcontext *pTempContext; + + pTempContext = g_pContextList; + while (pTempContext && pTempContext != pContext) + pTempContext = pTempContext->next; + + return (pTempContext ? ALC_TRUE : ALC_FALSE); +} + + +/* + alcSetError + + Store latest ALC Error +*/ +ALCvoid alcSetError(ALCdevice *device, ALenum errorCode) +{ + LockLists(); + if(IsDevice(device)) + device->LastError = errorCode; + else + g_eLastNullDeviceError = errorCode; + UnlockLists(); +} + + +/* UpdateDeviceParams: + * + * Updates device parameters according to the attribute list. + */ +static ALCboolean UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) +{ + ALuint i; + + // Check for attributes + if(attrList && attrList[0]) + { + ALCuint freq, numMono, numStereo, numSends; + enum DevFmtChannels schans; + enum DevFmtType stype; + ALuint attrIdx; + + // 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); + device->Flags &= ~DEVICE_RUNNING; + + freq = device->Frequency; + schans = device->FmtChans; + stype = device->FmtType; + numMono = device->NumMonoSources; + numStereo = device->NumStereoSources; + numSends = device->NumAuxSends; + + freq = GetConfigValueInt(NULL, "frequency", freq); + if(freq < 8000) freq = 8000; + + attrIdx = 0; + while(attrList[attrIdx]) + { + if(attrList[attrIdx] == ALC_FORMAT_CHANNELS_SOFT && + device->IsLoopbackDevice) + { + ALCint val = attrList[attrIdx + 1]; + if(!IsValidALCChannels(val) || !ChannelsFromDevFmt(val)) + { + alcSetError(device, ALC_INVALID_VALUE); + return ALC_FALSE; + } + schans = val; + } + + if(attrList[attrIdx] == ALC_FORMAT_TYPE_SOFT && + device->IsLoopbackDevice) + { + ALCint val = attrList[attrIdx + 1]; + if(!IsValidALCType(val) || !BytesFromDevFmt(val)) + { + alcSetError(device, ALC_INVALID_VALUE); + return ALC_FALSE; + } + stype = val; + } + + if(attrList[attrIdx] == ALC_FREQUENCY) + { + if(device->IsLoopbackDevice) + { + freq = attrList[attrIdx + 1]; + if(freq < 8000) + { + alcSetError(device, ALC_INVALID_VALUE); + return ALC_FALSE; + } + } + else if(!ConfigValueExists(NULL, "frequency")) + { + freq = attrList[attrIdx + 1]; + if(freq < 8000) freq = 8000; + device->Flags |= DEVICE_FREQUENCY_REQUEST; + } + } + + if(attrList[attrIdx] == ALC_STEREO_SOURCES) + { + numStereo = attrList[attrIdx + 1]; + if(numStereo > device->MaxNoOfSources) + numStereo = device->MaxNoOfSources; + + numMono = device->MaxNoOfSources - numStereo; + } + + if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS && + !ConfigValueExists(NULL, "sends")) + { + numSends = attrList[attrIdx + 1]; + if(numSends > MAX_SENDS) + numSends = MAX_SENDS; + } + + attrIdx += 2; + } + + device->UpdateSize = (ALuint64)device->UpdateSize * freq / + device->Frequency; + + device->Frequency = freq; + device->FmtChans = schans; + device->FmtType = stype; + device->NumMonoSources = numMono; + device->NumStereoSources = numStereo; + device->NumAuxSends = numSends; + } + + if((device->Flags&DEVICE_RUNNING)) + return ALC_TRUE; + + LockDevice(device); + TRACE("Format pre-setup: %s%s, %s, %uhz%s, %u update size x%d\n", + DevFmtChannelsString(device->FmtChans), + (device->Flags&DEVICE_CHANNELS_REQUEST)?" (requested)":"", + DevFmtTypeString(device->FmtType), device->Frequency, + (device->Flags&DEVICE_FREQUENCY_REQUEST)?" (requested)":"", + device->UpdateSize, device->NumUpdates); + if(ALCdevice_ResetPlayback(device) == ALC_FALSE) + { + UnlockDevice(device); + return ALC_FALSE; + } + device->Flags |= DEVICE_RUNNING; + TRACE("Format post-setup: %s%s, %s, %uhz%s, %u update size x%d\n", + DevFmtChannelsString(device->FmtChans), + (device->Flags&DEVICE_CHANNELS_REQUEST)?" (requested)":"", + DevFmtTypeString(device->FmtType), device->Frequency, + (device->Flags&DEVICE_FREQUENCY_REQUEST)?" (requested)":"", + device->UpdateSize, device->NumUpdates); + + aluInitPanning(device); + + for(i = 0;i < MAXCHANNELS;i++) + { + device->ClickRemoval[i] = 0.0f; + device->PendingClicks[i] = 0.0f; + } + + if(!device->IsLoopbackDevice && GetConfigValueBool(NULL, "hrtf", AL_FALSE)) + device->Flags |= DEVICE_USE_HRTF; + if((device->Flags&DEVICE_USE_HRTF) && !IsHrtfCompatible(device)) + device->Flags &= ~DEVICE_USE_HRTF; + TRACE("HRTF %s\n", (device->Flags&DEVICE_USE_HRTF)?"enabled":"disabled"); + + if(!(device->Flags&DEVICE_USE_HRTF) && device->Bs2bLevel > 0 && device->Bs2bLevel <= 6) + { + if(!device->Bs2b) + { + device->Bs2b = calloc(1, sizeof(*device->Bs2b)); + bs2b_clear(device->Bs2b); + } + bs2b_set_srate(device->Bs2b, device->Frequency); + bs2b_set_level(device->Bs2b, device->Bs2bLevel); + TRACE("BS2B level %d\n", device->Bs2bLevel); + } + else + { + free(device->Bs2b); + device->Bs2b = NULL; + TRACE("BS2B disabled\n"); + } + + device->Flags &= ~DEVICE_DUPLICATE_STEREO; + switch(device->FmtChans) + { + case DevFmtMono: + case DevFmtStereo: + break; + case DevFmtQuad: + case DevFmtX51: + case DevFmtX51Side: + case DevFmtX61: + case DevFmtX71: + if(GetConfigValueBool(NULL, "stereodup", AL_TRUE)) + device->Flags |= DEVICE_DUPLICATE_STEREO; + break; + } + TRACE("Stereo duplication %s\n", (device->Flags&DEVICE_DUPLICATE_STEREO)?"enabled":"disabled"); + + for(i = 0;i < device->NumContexts;i++) + { + ALCcontext *context = device->Contexts[i]; + ALsizei pos; + + context->UpdateSources = AL_FALSE; + for(pos = 0;pos < context->EffectSlotMap.size;pos++) + { + ALeffectslot *slot = context->EffectSlotMap.array[pos].value; + + if(ALEffect_DeviceUpdate(slot->EffectState, device) == AL_FALSE) + { + UnlockDevice(device); + ALCdevice_StopPlayback(device); + device->Flags &= ~DEVICE_RUNNING; + return ALC_FALSE; + } + slot->NeedsUpdate = AL_FALSE; + ALEffect_Update(slot->EffectState, context, slot); + } + + for(pos = 0;pos < context->SourceMap.size;pos++) + { + ALsource *source = context->SourceMap.array[pos].value; + ALuint s = device->NumAuxSends; + while(s < MAX_SENDS) + { + if(source->Send[s].Slot) + source->Send[s].Slot->refcount--; + source->Send[s].Slot = NULL; + source->Send[s].WetFilter.type = 0; + source->Send[s].WetFilter.filter = 0; + s++; + } + source->NeedsUpdate = AL_FALSE; + ALsource_Update(source, context); + } + } + UnlockDevice(device); + + return ALC_TRUE; +} + + +ALCvoid LockDevice(ALCdevice *device) +{ + EnterCriticalSection(&device->Mutex); +} + +ALCvoid UnlockDevice(ALCdevice *device) +{ + LeaveCriticalSection(&device->Mutex); +} + +/* + LockContext + + Thread-safe entry +*/ +ALCvoid LockContext(ALCcontext *context) +{ + EnterCriticalSection(&context->Device->Mutex); +} + + +/* + UnlockContext + + Thread-safe exit +*/ +ALCvoid UnlockContext(ALCcontext *context) +{ + LeaveCriticalSection(&context->Device->Mutex); +} + + +/* + GetLockedContext + + Returns the currently active Context, in a locked state +*/ +ALCcontext *GetLockedContext(void) +{ + ALCcontext *pContext = NULL; + + LockLists(); + + pContext = tls_get(LocalContext); + if(pContext && !IsContext(pContext)) + { + tls_set(LocalContext, NULL); + pContext = NULL; + } + if(!pContext) + pContext = GlobalContext; + if(pContext) + LockContext(pContext); + + UnlockLists(); + + return pContext; +} + + +/* + InitContext + + Initialize Context variables +*/ +static ALvoid InitContext(ALCcontext *pContext) +{ + //Initialise listener + pContext->Listener.Gain = 1.0f; + pContext->Listener.MetersPerUnit = 1.0f; + pContext->Listener.Position[0] = 0.0f; + pContext->Listener.Position[1] = 0.0f; + pContext->Listener.Position[2] = 0.0f; + pContext->Listener.Velocity[0] = 0.0f; + pContext->Listener.Velocity[1] = 0.0f; + pContext->Listener.Velocity[2] = 0.0f; + pContext->Listener.Forward[0] = 0.0f; + pContext->Listener.Forward[1] = 0.0f; + pContext->Listener.Forward[2] = -1.0f; + pContext->Listener.Up[0] = 0.0f; + pContext->Listener.Up[1] = 1.0f; + pContext->Listener.Up[2] = 0.0f; + + //Validate pContext + pContext->LastError = AL_NO_ERROR; + pContext->UpdateSources = AL_FALSE; + pContext->ActiveSourceCount = 0; + InitUIntMap(&pContext->SourceMap); + InitUIntMap(&pContext->EffectSlotMap); + + //Set globals + pContext->DistanceModel = AL_INVERSE_DISTANCE_CLAMPED; + pContext->SourceDistanceModel = AL_FALSE; + pContext->DopplerFactor = 1.0f; + pContext->DopplerVelocity = 1.0f; + pContext->flSpeedOfSound = SPEEDOFSOUNDMETRESPERSEC; + pContext->DeferUpdates = AL_FALSE; + + pContext->ExtensionList = alExtList; +} + + +/* + FreeContext + + Clean up Context, destroy any remaining Sources +*/ +static ALCvoid FreeContext(ALCcontext *context) +{ + //Invalidate context + memset(context, 0, sizeof(ALCcontext)); + free(context); +} + +/////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// ALC Functions calls + + +// This should probably move to another c file but for now ... +ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize) +{ + ALCdevice *device = NULL; + + DO_INITCONFIG(); + + if(!CaptureBackend.name) + { + alcSetError(NULL, ALC_INVALID_VALUE); + return NULL; + } + + if(SampleSize <= 0) + { + alcSetError(NULL, ALC_INVALID_VALUE); + return NULL; + } + + if(deviceName && (!deviceName[0] || strcasecmp(deviceName, "openal soft") == 0 || strcasecmp(deviceName, "openal-soft") == 0)) + deviceName = NULL; + + device = calloc(1, sizeof(ALCdevice)); + if(!device) + { + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + + //Validate device + device->Funcs = &CaptureBackend.Funcs; + device->Connected = ALC_TRUE; + device->IsCaptureDevice = AL_TRUE; + device->IsLoopbackDevice = AL_FALSE; + InitializeCriticalSection(&device->Mutex); + + device->szDeviceName = NULL; + + device->Flags |= DEVICE_FREQUENCY_REQUEST; + device->Frequency = frequency; + + device->Flags |= DEVICE_CHANNELS_REQUEST; + if(DecomposeDevFormat(format, &device->FmtChans, &device->FmtType) == AL_FALSE) + { + free(device); + alcSetError(NULL, ALC_INVALID_ENUM); + return NULL; + } + + device->UpdateSize = SampleSize; + device->NumUpdates = 1; + + LockLists(); + if(ALCdevice_OpenCapture(device, deviceName)) + { + device->next = g_pDeviceList; + g_pDeviceList = device; + g_ulDeviceCount++; + } + else + { + DeleteCriticalSection(&device->Mutex); + free(device); + device = NULL; + alcSetError(NULL, ALC_INVALID_VALUE); + } + UnlockLists(); + + return device; +} + +ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *pDevice) +{ + ALCdevice **list; + + LockLists(); + list = &g_pDeviceList; + while(*list && *list != pDevice) + list = &(*list)->next; + + if(!*list || !(*list)->IsCaptureDevice) + { + alcSetError(*list, ALC_INVALID_DEVICE); + UnlockLists(); + return ALC_FALSE; + } + + *list = (*list)->next; + g_ulDeviceCount--; + + UnlockLists(); + + LockDevice(pDevice); + ALCdevice_CloseCapture(pDevice); + UnlockDevice(pDevice); + + free(pDevice->szDeviceName); + pDevice->szDeviceName = NULL; + + DeleteCriticalSection(&pDevice->Mutex); + + free(pDevice); + + return ALC_TRUE; +} + +ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) +{ + LockLists(); + if(!IsDevice(device) || !device->IsCaptureDevice) + { + alcSetError(device, ALC_INVALID_DEVICE); + UnlockLists(); + return; + } + LockDevice(device); + UnlockLists(); + if(device->Connected) + ALCdevice_StartCapture(device); + UnlockDevice(device); +} + +ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) +{ + LockLists(); + if(!IsDevice(device) || !device->IsCaptureDevice) + { + alcSetError(device, ALC_INVALID_DEVICE); + UnlockLists(); + return; + } + LockDevice(device); + UnlockLists(); + if(device->Connected) + ALCdevice_StopCapture(device); + UnlockDevice(device); +} + +ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) +{ + LockLists(); + if(!IsDevice(device) || !device->IsCaptureDevice) + { + alcSetError(device, ALC_INVALID_DEVICE); + UnlockLists(); + return; + } + LockDevice(device); + UnlockLists(); + if(device->Connected) + ALCdevice_CaptureSamples(device, buffer, samples); + UnlockDevice(device); +} + +/* + alcGetError + + Return last ALC generated error code +*/ +ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) +{ + ALCenum errorCode; + + LockLists(); + if(IsDevice(device)) + { + errorCode = device->LastError; + device->LastError = ALC_NO_ERROR; + } + else + { + errorCode = g_eLastNullDeviceError; + g_eLastNullDeviceError = ALC_NO_ERROR; + } + UnlockLists(); + return errorCode; +} + + +/* + alcSuspendContext + + Not functional +*/ +ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *Context) +{ + (void)Context; +} + + +/* + alcProcessContext + + Not functional +*/ +ALC_API ALCvoid ALC_APIENTRY alcProcessContext(ALCcontext *Context) +{ + (void)Context; +} + + +/* + alcGetString + + Returns information about the Device, and error strings +*/ +ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *pDevice,ALCenum param) +{ + const ALCchar *value = NULL; + + switch(param) + { + case ALC_NO_ERROR: + value = alcNoError; + break; + + case ALC_INVALID_ENUM: + value = alcErrInvalidEnum; + break; + + case ALC_INVALID_VALUE: + value = alcErrInvalidValue; + break; + + case ALC_INVALID_DEVICE: + value = alcErrInvalidDevice; + break; + + case ALC_INVALID_CONTEXT: + value = alcErrInvalidContext; + break; + + case ALC_OUT_OF_MEMORY: + value = alcErrOutOfMemory; + break; + + case ALC_DEVICE_SPECIFIER: + LockLists(); + if(IsDevice(pDevice)) + value = pDevice->szDeviceName; + else + { + ProbeDeviceList(); + value = alcDeviceList; + } + UnlockLists(); + break; + + case ALC_ALL_DEVICES_SPECIFIER: + ProbeAllDeviceList(); + value = alcAllDeviceList; + break; + + case ALC_CAPTURE_DEVICE_SPECIFIER: + LockLists(); + if(IsDevice(pDevice)) + value = pDevice->szDeviceName; + else + { + ProbeCaptureDeviceList(); + value = alcCaptureDeviceList; + } + UnlockLists(); + break; + + /* Default devices are always first in the list */ + case ALC_DEFAULT_DEVICE_SPECIFIER: + if(!alcDeviceList) + ProbeDeviceList(); + + free(alcDefaultDeviceSpecifier); + alcDefaultDeviceSpecifier = strdup(alcDeviceList ? alcDeviceList : ""); + if(!alcDefaultDeviceSpecifier) + alcSetError(pDevice, ALC_OUT_OF_MEMORY); + value = alcDefaultDeviceSpecifier; + break; + + case ALC_DEFAULT_ALL_DEVICES_SPECIFIER: + if(!alcAllDeviceList) + ProbeAllDeviceList(); + + free(alcDefaultAllDeviceSpecifier); + alcDefaultAllDeviceSpecifier = strdup(alcAllDeviceList ? + alcAllDeviceList : ""); + if(!alcDefaultAllDeviceSpecifier) + alcSetError(pDevice, ALC_OUT_OF_MEMORY); + value = alcDefaultAllDeviceSpecifier; + break; + + case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER: + if(!alcCaptureDeviceList) + ProbeCaptureDeviceList(); + + free(alcCaptureDefaultDeviceSpecifier); + alcCaptureDefaultDeviceSpecifier = strdup(alcCaptureDeviceList ? + alcCaptureDeviceList : ""); + if(!alcCaptureDefaultDeviceSpecifier) + alcSetError(pDevice, ALC_OUT_OF_MEMORY); + value = alcCaptureDefaultDeviceSpecifier; + break; + + case ALC_EXTENSIONS: + LockLists(); + if(IsDevice(pDevice)) + value = alcExtensionList; + else + value = alcNoDeviceExtList; + UnlockLists(); + break; + + default: + alcSetError(pDevice, ALC_INVALID_ENUM); + break; + } + + return value; +} + + +/* + alcGetIntegerv + + Returns information about the Device and the version of Open AL +*/ +ALC_API ALCvoid ALC_APIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALsizei size,ALCint *data) +{ + if(size == 0 || data == NULL) + { + alcSetError(device, ALC_INVALID_VALUE); + return; + } + + LockLists(); + if(!IsDevice(device)) + { + switch(param) + { + case ALC_MAJOR_VERSION: + *data = alcMajorVersion; + break; + case ALC_MINOR_VERSION: + *data = alcMinorVersion; + break; + + case ALC_ATTRIBUTES_SIZE: + case ALC_ALL_ATTRIBUTES: + case ALC_FREQUENCY: + case ALC_REFRESH: + case ALC_SYNC: + case ALC_MONO_SOURCES: + case ALC_STEREO_SOURCES: + case ALC_CAPTURE_SAMPLES: + case ALC_FORMAT_CHANNELS_SOFT: + case ALC_FORMAT_TYPE_SOFT: + alcSetError(NULL, ALC_INVALID_DEVICE); + break; + + default: + alcSetError(NULL, ALC_INVALID_ENUM); + break; + } + } + else if(device->IsCaptureDevice) + { + switch(param) + { + case ALC_CAPTURE_SAMPLES: + LockDevice(device); + *data = ALCdevice_AvailableSamples(device); + UnlockDevice(device); + break; + + case ALC_CONNECTED: + *data = device->Connected; + break; + + default: + alcSetError(device, ALC_INVALID_ENUM); + break; + } + } + else /* render device */ + { + switch(param) + { + case ALC_MAJOR_VERSION: + *data = alcMajorVersion; + break; + + case ALC_MINOR_VERSION: + *data = alcMinorVersion; + break; + + case ALC_EFX_MAJOR_VERSION: + *data = alcEFXMajorVersion; + break; + + case ALC_EFX_MINOR_VERSION: + *data = alcEFXMinorVersion; + break; + + case ALC_ATTRIBUTES_SIZE: + *data = 13; + break; + + case ALC_ALL_ATTRIBUTES: + if(size < 13) + alcSetError(device, ALC_INVALID_VALUE); + else + { + int i = 0; + + data[i++] = ALC_FREQUENCY; + data[i++] = device->Frequency; + + if(!device->IsLoopbackDevice) + { + data[i++] = ALC_REFRESH; + data[i++] = device->Frequency / device->UpdateSize; + + data[i++] = ALC_SYNC; + data[i++] = ALC_FALSE; + } + else + { + data[i++] = ALC_FORMAT_CHANNELS_SOFT; + data[i++] = device->FmtChans; + + data[i++] = ALC_FORMAT_TYPE_SOFT; + data[i++] = device->FmtType; + } + + data[i++] = ALC_MONO_SOURCES; + data[i++] = device->NumMonoSources; + + data[i++] = ALC_STEREO_SOURCES; + data[i++] = device->NumStereoSources; + + data[i++] = ALC_MAX_AUXILIARY_SENDS; + data[i++] = device->NumAuxSends; + + data[i++] = 0; + } + break; + + case ALC_FREQUENCY: + *data = device->Frequency; + break; + + case ALC_REFRESH: + if(device->IsLoopbackDevice) + alcSetError(device, ALC_INVALID_DEVICE); + else + *data = device->Frequency / device->UpdateSize; + break; + + case ALC_SYNC: + if(device->IsLoopbackDevice) + alcSetError(device, ALC_INVALID_DEVICE); + else + *data = ALC_FALSE; + break; + + case ALC_FORMAT_CHANNELS_SOFT: + if(!device->IsLoopbackDevice) + alcSetError(device, ALC_INVALID_DEVICE); + else + *data = device->FmtChans; + break; + + case ALC_FORMAT_TYPE_SOFT: + if(!device->IsLoopbackDevice) + alcSetError(device, ALC_INVALID_DEVICE); + else + *data = device->FmtType; + break; + + case ALC_MONO_SOURCES: + *data = device->NumMonoSources; + break; + + case ALC_STEREO_SOURCES: + *data = device->NumStereoSources; + break; + + case ALC_MAX_AUXILIARY_SENDS: + *data = device->NumAuxSends; + break; + + case ALC_CONNECTED: + *data = device->Connected; + break; + + default: + alcSetError(device, ALC_INVALID_ENUM); + break; + } + } + UnlockLists(); +} + + +/* + alcIsExtensionPresent + + Determines if there is support for a particular extension +*/ +ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extName) +{ + ALCboolean bResult = ALC_FALSE; + const char *ptr; + size_t len; + + if(!extName) + { + alcSetError(device, ALC_INVALID_VALUE); + return ALC_FALSE; + } + + len = strlen(extName); + LockLists(); + ptr = (IsDevice(device) ? alcExtensionList : alcNoDeviceExtList); + UnlockLists(); + while(ptr && *ptr) + { + if(strncasecmp(ptr, extName, len) == 0 && + (ptr[len] == '\0' || isspace(ptr[len]))) + { + bResult = ALC_TRUE; + break; + } + if((ptr=strchr(ptr, ' ')) != NULL) + { + do { + ++ptr; + } while(isspace(*ptr)); + } + } + + return bResult; +} + + +/* + alcGetProcAddress + + Retrieves the function address for a particular extension function +*/ +ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName) +{ + ALsizei i = 0; + + if(!funcName) + { + alcSetError(device, ALC_INVALID_VALUE); + return NULL; + } + + while(alcFunctions[i].funcName && strcmp(alcFunctions[i].funcName,funcName) != 0) + i++; + return alcFunctions[i].address; +} + + +/* + alcGetEnumValue + + Get the value for a particular ALC Enumerated Value +*/ +ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName) +{ + ALsizei i = 0; + + if(!enumName) + { + alcSetError(device, ALC_INVALID_VALUE); + return (ALCenum)0; + } + + while(enumeration[i].enumName && strcmp(enumeration[i].enumName,enumName) != 0) + i++; + return enumeration[i].value; +} + + +/* + alcCreateContext + + Create and attach a Context to a particular Device. +*/ +ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList) +{ + ALCcontext *ALContext; + void *temp; + + LockLists(); + if(!IsDevice(device) || device->IsCaptureDevice || !device->Connected) + { + alcSetError(device, ALC_INVALID_DEVICE); + UnlockLists(); + return NULL; + } + + // Reset Context Last Error code + device->LastError = ALC_NO_ERROR; + + if(UpdateDeviceParams(device, attrList) == ALC_FALSE) + { + alcSetError(device, ALC_INVALID_DEVICE); + aluHandleDisconnect(device); + UnlockLists(); + return NULL; + } + + LockDevice(device); + ALContext = NULL; + temp = realloc(device->Contexts, (device->NumContexts+1) * sizeof(*device->Contexts)); + if(temp) + { + device->Contexts = temp; + + ALContext = calloc(1, sizeof(ALCcontext)); + if(ALContext) + { + ALContext->MaxActiveSources = 256; + ALContext->ActiveSources = malloc(sizeof(ALContext->ActiveSources[0]) * + ALContext->MaxActiveSources); + } + } + if(!ALContext || !ALContext->ActiveSources) + { + free(ALContext); + alcSetError(device, ALC_OUT_OF_MEMORY); + UnlockDevice(device); + if(device->NumContexts == 0) + { + ALCdevice_StopPlayback(device); + device->Flags &= ~DEVICE_RUNNING; + } + UnlockLists(); + return NULL; + } + + device->Contexts[device->NumContexts++] = ALContext; + ALContext->Device = device; + + InitContext(ALContext); + UnlockDevice(device); + + ALContext->next = g_pContextList; + g_pContextList = ALContext; + g_ulContextCount++; + + UnlockLists(); + + return ALContext; +} + + +/* + alcDestroyContext + + Remove a Context +*/ +ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context) +{ + ALCdevice *Device; + ALCcontext **list; + ALuint i; + + LockLists(); + list = &g_pContextList; + while(*list && *list != context) + list = &(*list)->next; + + if(!*list) + { + alcSetError(NULL, ALC_INVALID_CONTEXT); + UnlockLists(); + return; + } + + *list = (*list)->next; + g_ulContextCount--; + + if(context == tls_get(LocalContext)) + tls_set(LocalContext, NULL); + if(context == GlobalContext) + GlobalContext = NULL; + + Device = context->Device; + LockDevice(Device); + for(i = 0;i < Device->NumContexts;i++) + { + if(Device->Contexts[i] == context) + { + Device->Contexts[i] = Device->Contexts[Device->NumContexts-1]; + Device->NumContexts--; + break; + } + } + UnlockDevice(Device); + + if(Device->NumContexts == 0) + { + ALCdevice_StopPlayback(Device); + Device->Flags &= ~DEVICE_RUNNING; + } + UnlockLists(); + + if(context->SourceMap.size > 0) + { + ERR("alcDestroyContext(): deleting %d Source(s)\n", context->SourceMap.size); + ReleaseALSources(context); + } + ResetUIntMap(&context->SourceMap); + + if(context->EffectSlotMap.size > 0) + { + ERR("alcDestroyContext(): deleting %d AuxiliaryEffectSlot(s)\n", context->EffectSlotMap.size); + ReleaseALAuxiliaryEffectSlots(context); + } + ResetUIntMap(&context->EffectSlotMap); + + free(context->ActiveSources); + context->ActiveSources = NULL; + context->MaxActiveSources = 0; + context->ActiveSourceCount = 0; + + FreeContext(context); +} + + +/* + alcGetCurrentContext + + Returns the currently active Context +*/ +ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(ALCvoid) +{ + ALCcontext *Context; + + LockLists(); + Context = tls_get(LocalContext); + if(Context && !IsContext(Context)) + { + tls_set(LocalContext, NULL); + Context = NULL; + } + if(!Context) + Context = GlobalContext; + UnlockLists(); + + return Context; +} + +/* + alcGetThreadContext + + Returns the currently active thread-local Context +*/ +ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) +{ + ALCcontext *pContext = NULL; + + LockLists(); + + pContext = tls_get(LocalContext); + if(pContext && !IsContext(pContext)) + { + tls_set(LocalContext, NULL); + pContext = NULL; + } + + UnlockLists(); + + return pContext; +} + + +/* + alcGetContextsDevice + + Returns the Device that a particular Context is attached to +*/ +ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *pContext) +{ + ALCdevice *pDevice = NULL; + + LockLists(); + if(IsContext(pContext)) + pDevice = pContext->Device; + else + alcSetError(NULL, ALC_INVALID_CONTEXT); + UnlockLists(); + + return pDevice; +} + + +/* + alcMakeContextCurrent + + Makes the given Context the active Context +*/ +ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) +{ + ALboolean bReturn = AL_TRUE; + + LockLists(); + + // context must be a valid Context or NULL + if(context == NULL || IsContext(context)) + { + GlobalContext = context; + tls_set(LocalContext, NULL); + } + else + { + alcSetError(NULL, ALC_INVALID_CONTEXT); + bReturn = AL_FALSE; + } + + UnlockLists(); + + return bReturn; +} + +/* + alcSetThreadContext + + Makes the given Context the active Context for the current thread +*/ +ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) +{ + ALboolean bReturn = AL_TRUE; + + LockLists(); + + // context must be a valid Context or NULL + if(context == NULL || IsContext(context)) + tls_set(LocalContext, context); + else + { + alcSetError(NULL, ALC_INVALID_CONTEXT); + bReturn = AL_FALSE; + } + + UnlockLists(); + + return bReturn; +} + + +static void GetFormatFromString(const char *str, enum DevFmtChannels *chans, enum DevFmtType *type) +{ + if(strcasecmp(str, "AL_FORMAT_MONO32") == 0) + { + *chans = DevFmtMono; + *type = DevFmtFloat; + return; + } + if(strcasecmp(str, "AL_FORMAT_STEREO32") == 0) + { + *chans = DevFmtStereo; + *type = DevFmtFloat; + return; + } + if(strcasecmp(str, "AL_FORMAT_QUAD32") == 0) + { + *chans = DevFmtQuad; + *type = DevFmtFloat; + return; + } + if(strcasecmp(str, "AL_FORMAT_51CHN32") == 0) + { + *chans = DevFmtX51; + *type = DevFmtFloat; + return; + } + if(strcasecmp(str, "AL_FORMAT_61CHN32") == 0) + { + *chans = DevFmtX61; + *type = DevFmtFloat; + return; + } + if(strcasecmp(str, "AL_FORMAT_71CHN32") == 0) + { + *chans = DevFmtX71; + *type = DevFmtFloat; + return; + } + + if(strcasecmp(str, "AL_FORMAT_MONO16") == 0) + { + *chans = DevFmtMono; + *type = DevFmtShort; + return; + } + if(strcasecmp(str, "AL_FORMAT_STEREO16") == 0) + { + *chans = DevFmtStereo; + *type = DevFmtShort; + return; + } + if(strcasecmp(str, "AL_FORMAT_QUAD16") == 0) + { + *chans = DevFmtQuad; + *type = DevFmtShort; + return; + } + if(strcasecmp(str, "AL_FORMAT_51CHN16") == 0) + { + *chans = DevFmtX51; + *type = DevFmtShort; + return; + } + if(strcasecmp(str, "AL_FORMAT_61CHN16") == 0) + { + *chans = DevFmtX61; + *type = DevFmtShort; + return; + } + if(strcasecmp(str, "AL_FORMAT_71CHN16") == 0) + { + *chans = DevFmtX71; + *type = DevFmtShort; + return; + } + + if(strcasecmp(str, "AL_FORMAT_MONO8") == 0) + { + *chans = DevFmtMono; + *type = DevFmtByte; + return; + } + if(strcasecmp(str, "AL_FORMAT_STEREO8") == 0) + { + *chans = DevFmtStereo; + *type = DevFmtByte; + return; + } + if(strcasecmp(str, "AL_FORMAT_QUAD8") == 0) + { + *chans = DevFmtQuad; + *type = DevFmtByte; + return; + } + if(strcasecmp(str, "AL_FORMAT_51CHN8") == 0) + { + *chans = DevFmtX51; + *type = DevFmtByte; + return; + } + if(strcasecmp(str, "AL_FORMAT_61CHN8") == 0) + { + *chans = DevFmtX61; + *type = DevFmtByte; + return; + } + if(strcasecmp(str, "AL_FORMAT_71CHN8") == 0) + { + *chans = DevFmtX71; + *type = DevFmtByte; + return; + } + + ERR("Unknown format: \"%s\"\n", str); + *chans = DevFmtStereo; + *type = DevFmtShort; +} + +/* + alcOpenDevice + + Open the Device specified. +*/ +ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) +{ + const ALCchar *fmt; + ALCdevice *device; + + DO_INITCONFIG(); + + if(!PlaybackBackend.name) + { + alcSetError(NULL, ALC_INVALID_VALUE); + return NULL; + } + + if(deviceName && (!deviceName[0] || strcasecmp(deviceName, "openal soft") == 0 || strcasecmp(deviceName, "openal-soft") == 0)) + deviceName = NULL; + + device = calloc(1, sizeof(ALCdevice)); + if(!device) + { + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + + //Validate device + device->Funcs = &PlaybackBackend.Funcs; + device->Connected = ALC_TRUE; + device->IsCaptureDevice = AL_FALSE; + device->IsLoopbackDevice = AL_FALSE; + InitializeCriticalSection(&device->Mutex); + device->LastError = ALC_NO_ERROR; + + device->Flags = 0; + device->Bs2b = NULL; + device->szDeviceName = NULL; + + device->Contexts = NULL; + device->NumContexts = 0; + + InitUIntMap(&device->BufferMap); + InitUIntMap(&device->EffectMap); + InitUIntMap(&device->FilterMap); + + //Set output format + if(ConfigValueExists(NULL, "frequency")) + device->Flags |= DEVICE_FREQUENCY_REQUEST; + device->Frequency = GetConfigValueInt(NULL, "frequency", DEFAULT_OUTPUT_RATE); + if(device->Frequency < 8000) + device->Frequency = 8000; + + if(ConfigValueExists(NULL, "format")) + device->Flags |= DEVICE_CHANNELS_REQUEST; + fmt = GetConfigValue(NULL, "format", "AL_FORMAT_STEREO16"); + GetFormatFromString(fmt, &device->FmtChans, &device->FmtType); + + device->NumUpdates = GetConfigValueInt(NULL, "periods", 4); + if(device->NumUpdates < 2) + device->NumUpdates = 4; + + device->UpdateSize = GetConfigValueInt(NULL, "period_size", 1024); + if(device->UpdateSize <= 0) + device->UpdateSize = 1024; + + device->MaxNoOfSources = GetConfigValueInt(NULL, "sources", 256); + if(device->MaxNoOfSources <= 0) + device->MaxNoOfSources = 256; + + device->AuxiliaryEffectSlotMax = GetConfigValueInt(NULL, "slots", 4); + if(device->AuxiliaryEffectSlotMax <= 0) + device->AuxiliaryEffectSlotMax = 4; + + device->NumStereoSources = 1; + device->NumMonoSources = device->MaxNoOfSources - device->NumStereoSources; + + device->NumAuxSends = GetConfigValueInt(NULL, "sends", MAX_SENDS); + if(device->NumAuxSends > MAX_SENDS) + device->NumAuxSends = MAX_SENDS; + + device->Bs2bLevel = GetConfigValueInt(NULL, "cf_level", 0); + + // Find a playback device to open + LockLists(); + if(ALCdevice_OpenPlayback(device, deviceName)) + { + device->next = g_pDeviceList; + g_pDeviceList = device; + g_ulDeviceCount++; + } + else + { + // No suitable output device found + DeleteCriticalSection(&device->Mutex); + free(device); + device = NULL; + alcSetError(NULL, ALC_INVALID_VALUE); + } + UnlockLists(); + + return device; +} + + +/* + alcCloseDevice + + Close the specified Device +*/ +ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *pDevice) +{ + ALCdevice **list; + + LockLists(); + list = &g_pDeviceList; + while(*list && *list != pDevice) + list = &(*list)->next; + + if(!*list || (*list)->IsCaptureDevice) + { + alcSetError(*list, ALC_INVALID_DEVICE); + UnlockLists(); + return ALC_FALSE; + } + + *list = (*list)->next; + g_ulDeviceCount--; + + UnlockLists(); + + if(pDevice->NumContexts > 0) + { + WARN("alcCloseDevice(): destroying %u Context(s)\n", pDevice->NumContexts); + while(pDevice->NumContexts > 0) + alcDestroyContext(pDevice->Contexts[0]); + } + ALCdevice_ClosePlayback(pDevice); + + if(pDevice->BufferMap.size > 0) + { + WARN("alcCloseDevice(): deleting %d Buffer(s)\n", pDevice->BufferMap.size); + ReleaseALBuffers(pDevice); + } + ResetUIntMap(&pDevice->BufferMap); + + if(pDevice->EffectMap.size > 0) + { + WARN("alcCloseDevice(): deleting %d Effect(s)\n", pDevice->EffectMap.size); + ReleaseALEffects(pDevice); + } + ResetUIntMap(&pDevice->EffectMap); + + if(pDevice->FilterMap.size > 0) + { + WARN("alcCloseDevice(): deleting %d Filter(s)\n", pDevice->FilterMap.size); + ReleaseALFilters(pDevice); + } + ResetUIntMap(&pDevice->FilterMap); + + free(pDevice->Bs2b); + pDevice->Bs2b = NULL; + + free(pDevice->szDeviceName); + pDevice->szDeviceName = NULL; + + free(pDevice->Contexts); + pDevice->Contexts = NULL; + + DeleteCriticalSection(&pDevice->Mutex); + + //Release device structure + memset(pDevice, 0, sizeof(ALCdevice)); + free(pDevice); + + return ALC_TRUE; +} + + +ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(void) +{ + ALCdevice *device; + + DO_INITCONFIG(); + + device = calloc(1, sizeof(ALCdevice)); + if(!device) + { + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + + //Validate device + device->Funcs = &BackendLoopback.Funcs; + device->Connected = ALC_TRUE; + device->IsCaptureDevice = AL_FALSE; + device->IsLoopbackDevice = AL_TRUE; + InitializeCriticalSection(&device->Mutex); + device->LastError = ALC_NO_ERROR; + + device->Flags = 0; + device->Bs2b = NULL; + device->szDeviceName = NULL; + + device->Contexts = NULL; + device->NumContexts = 0; + + InitUIntMap(&device->BufferMap); + InitUIntMap(&device->EffectMap); + InitUIntMap(&device->FilterMap); + + //Set output format + device->Frequency = 44100; + device->FmtChans = DevFmtStereo; + device->FmtType = DevFmtShort; + + device->NumUpdates = 0; + device->UpdateSize = 0; + + device->MaxNoOfSources = GetConfigValueInt(NULL, "sources", 256); + if(device->MaxNoOfSources <= 0) + device->MaxNoOfSources = 256; + + device->AuxiliaryEffectSlotMax = GetConfigValueInt(NULL, "slots", 4); + if(device->AuxiliaryEffectSlotMax <= 0) + device->AuxiliaryEffectSlotMax = 4; + + device->NumStereoSources = 1; + device->NumMonoSources = device->MaxNoOfSources - device->NumStereoSources; + + device->NumAuxSends = GetConfigValueInt(NULL, "sends", MAX_SENDS); + if(device->NumAuxSends > MAX_SENDS) + device->NumAuxSends = MAX_SENDS; + + device->Bs2bLevel = GetConfigValueInt(NULL, "cf_level", 0); + + // Open the "backend" + LockLists(); + ALCdevice_OpenPlayback(device, "Loopback"); + + device->next = g_pDeviceList; + g_pDeviceList = device; + g_ulDeviceCount++; + UnlockLists(); + + return device; +} + +ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type) +{ + ALCboolean ret = ALC_FALSE; + + LockLists(); + if(!IsDevice(device) || !device->IsLoopbackDevice) + alcSetError(device, ALC_INVALID_DEVICE); + else if(freq <= 0) + alcSetError(device, ALC_INVALID_VALUE); + else if(!IsValidALCType(type) || !IsValidALCChannels(channels)) + alcSetError(device, ALC_INVALID_ENUM); + else + { + if(BytesFromDevFmt(type) > 0 && ChannelsFromDevFmt(channels) > 0 && + freq >= 8000) + ret = ALC_TRUE; + } + UnlockLists(); + + return ret; +} + +ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) +{ + LockLists(); + if(!IsDevice(device) || !device->IsLoopbackDevice) + alcSetError(device, ALC_INVALID_DEVICE); + else if(samples < 0 || (samples > 0 && buffer == NULL)) + alcSetError(device, ALC_INVALID_VALUE); + else + aluMixData(device, buffer, samples); + UnlockLists(); +} + + +static void ReleaseALC(ALCboolean doclose) +{ + free(alcDeviceList); alcDeviceList = NULL; + alcDeviceListSize = 0; + free(alcAllDeviceList); alcAllDeviceList = NULL; + alcAllDeviceListSize = 0; + free(alcCaptureDeviceList); alcCaptureDeviceList = NULL; + alcCaptureDeviceListSize = 0; + + free(alcDefaultDeviceSpecifier); + alcDefaultDeviceSpecifier = NULL; + free(alcDefaultAllDeviceSpecifier); + alcDefaultAllDeviceSpecifier = NULL; + free(alcCaptureDefaultDeviceSpecifier); + alcCaptureDefaultDeviceSpecifier = NULL; + + if(doclose) + { + if(g_ulDeviceCount > 0) + WARN("ReleaseALC(): closing %u Device%s\n", g_ulDeviceCount, (g_ulDeviceCount>1)?"s":""); + + while(g_pDeviceList) + { + if(g_pDeviceList->IsCaptureDevice) + alcCaptureCloseDevice(g_pDeviceList); + else + alcCloseDevice(g_pDeviceList); + } + } + else + { + if(g_ulDeviceCount > 0) + WARN("ReleaseALC(): %u Device%s not closed\n", g_ulDeviceCount, (g_ulDeviceCount>1)?"s":""); + } +} + +/////////////////////////////////////////////////////// diff -r 000000000000 -r f9476ff7637e Alc/ALu.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/ALu.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,1168 @@ +/** + * 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 +#include +#include +#include +#include + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" +#include "alSource.h" +#include "alBuffer.h" +#include "alListener.h" +#include "alAuxEffectSlot.h" +#include "alu.h" +#include "bs2b.h" + + +static __inline ALvoid aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector) +{ + outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1]; + outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2]; + outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0]; +} + +static __inline ALfloat aluDotproduct(const ALfloat *inVector1, const ALfloat *inVector2) +{ + return inVector1[0]*inVector2[0] + inVector1[1]*inVector2[1] + + inVector1[2]*inVector2[2]; +} + +static __inline ALvoid aluNormalize(ALfloat *inVector) +{ + ALfloat length, inverse_length; + + length = aluSqrt(aluDotproduct(inVector, inVector)); + if(length != 0.0f) + { + inverse_length = 1.0f/length; + inVector[0] *= inverse_length; + inVector[1] *= inverse_length; + inVector[2] *= inverse_length; + } +} + +static __inline ALvoid aluMatrixVector(ALfloat *vector,ALfloat w,ALfloat matrix[4][4]) +{ + ALfloat temp[4] = { + vector[0], vector[1], vector[2], w + }; + + vector[0] = temp[0]*matrix[0][0] + temp[1]*matrix[1][0] + temp[2]*matrix[2][0] + temp[3]*matrix[3][0]; + vector[1] = temp[0]*matrix[0][1] + temp[1]*matrix[1][1] + temp[2]*matrix[2][1] + temp[3]*matrix[3][1]; + vector[2] = temp[0]*matrix[0][2] + temp[1]*matrix[1][2] + temp[2]*matrix[2][2] + temp[3]*matrix[3][2]; +} + + +ALvoid CalcNonAttnSourceParams(ALsource *ALSource, const ALCcontext *ALContext) +{ + static const ALfloat angles_Mono[1] = { 0.0f }; + static const ALfloat angles_Stereo[2] = { -30.0f, 30.0f }; + static const ALfloat angles_Rear[2] = { -150.0f, 150.0f }; + static const ALfloat angles_Quad[4] = { -45.0f, 45.0f, -135.0f, 135.0f }; + static const ALfloat angles_X51[6] = { -30.0f, 30.0f, 0.0f, 0.0f, + -110.0f, 110.0f }; + static const ALfloat angles_X61[7] = { -30.0f, 30.0f, 0.0f, 0.0f, + 180.0f, -90.0f, 90.0f }; + static const ALfloat angles_X71[8] = { -30.0f, 30.0f, 0.0f, 0.0f, + -110.0f, 110.0f, -90.0f, 90.0f }; + + static const enum Channel chans_Mono[1] = { FRONT_CENTER }; + static const enum Channel chans_Stereo[2] = { FRONT_LEFT, FRONT_RIGHT }; + static const enum Channel chans_Rear[2] = { BACK_LEFT, BACK_RIGHT }; + static const enum Channel chans_Quad[4] = { FRONT_LEFT, FRONT_RIGHT, + BACK_LEFT, BACK_RIGHT }; + static const enum Channel chans_X51[6] = { FRONT_LEFT, FRONT_RIGHT, + FRONT_CENTER, LFE, + BACK_LEFT, BACK_RIGHT }; + static const enum Channel chans_X61[7] = { FRONT_LEFT, FRONT_RIGHT, + FRONT_CENTER, LFE, BACK_CENTER, + SIDE_LEFT, SIDE_RIGHT }; + static const enum Channel chans_X71[8] = { FRONT_LEFT, FRONT_RIGHT, + FRONT_CENTER, LFE, + BACK_LEFT, BACK_RIGHT, + SIDE_LEFT, SIDE_RIGHT }; + + ALCdevice *Device = ALContext->Device; + ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume; + ALbufferlistitem *BufferListItem; + enum DevFmtChannels DevChans; + enum FmtChannels Channels; + ALfloat (*SrcMatrix)[MAXCHANNELS]; + ALfloat DryGain, DryGainHF; + ALfloat WetGain[MAX_SENDS]; + ALfloat WetGainHF[MAX_SENDS]; + ALint NumSends, Frequency; + const ALfloat *SpeakerGain; + const ALfloat *angles = NULL; + const enum Channel *chans = NULL; + enum Resampler Resampler; + ALint num_channels = 0; + ALboolean VirtualChannels; + ALfloat Pitch; + ALfloat cw; + ALuint pos; + ALint i, c; + + /* Get device properties */ + DevChans = ALContext->Device->FmtChans; + NumSends = ALContext->Device->NumAuxSends; + Frequency = ALContext->Device->Frequency; + + /* Get listener properties */ + ListenerGain = ALContext->Listener.Gain; + + /* Get source properties */ + SourceVolume = ALSource->flGain; + MinVolume = ALSource->flMinGain; + MaxVolume = ALSource->flMaxGain; + Pitch = ALSource->flPitch; + Resampler = ALSource->Resampler; + VirtualChannels = ALSource->VirtualChannels; + + /* Calculate the stepping value */ + Channels = FmtMono; + BufferListItem = ALSource->queue; + while(BufferListItem != NULL) + { + ALbuffer *ALBuffer; + if((ALBuffer=BufferListItem->buffer) != NULL) + { + ALint maxstep = STACK_DATA_SIZE / ALSource->NumChannels / + ALSource->SampleSize; + 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<Params.Step = Pitch*FRACTIONONE; + if(ALSource->Params.Step == 0) + ALSource->Params.Step = 1; + } + + Channels = ALBuffer->FmtChannels; + + if(ALSource->VirtualChannels && (Device->Flags&DEVICE_USE_HRTF)) + ALSource->Params.DoMix = SelectHrtfMixer(ALBuffer, + (ALSource->Params.Step==FRACTIONONE) ? POINT_RESAMPLER : + Resampler); + else + ALSource->Params.DoMix = SelectMixer(ALBuffer, + (ALSource->Params.Step==FRACTIONONE) ? POINT_RESAMPLER : + Resampler); + break; + } + BufferListItem = BufferListItem->next; + } + + /* Calculate gains */ + DryGain = clampf(SourceVolume, MinVolume, MaxVolume); + DryGainHF = 1.0f; + switch(ALSource->DirectFilter.type) + { + case AL_FILTER_LOWPASS: + DryGain *= ALSource->DirectFilter.Gain; + DryGainHF *= ALSource->DirectFilter.GainHF; + break; + } + for(i = 0;i < NumSends;i++) + { + WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume); + WetGainHF[i] = 1.0f; + switch(ALSource->Send[i].WetFilter.type) + { + case AL_FILTER_LOWPASS: + WetGain[i] *= ALSource->Send[i].WetFilter.Gain; + WetGainHF[i] *= ALSource->Send[i].WetFilter.GainHF; + break; + } + } + + SrcMatrix = ALSource->Params.DryGains; + for(i = 0;i < MAXCHANNELS;i++) + { + for(c = 0;c < MAXCHANNELS;c++) + SrcMatrix[i][c] = 0.0f; + } + switch(Channels) + { + case FmtMono: + angles = angles_Mono; + chans = chans_Mono; + num_channels = 1; + break; + case FmtStereo: + if(VirtualChannels && (ALContext->Device->Flags&DEVICE_DUPLICATE_STEREO)) + { + DryGain *= aluSqrt(2.0f/4.0f); + for(c = 0;c < 2;c++) + { + pos = aluCart2LUTpos(cos(angles_Rear[c] * (M_PI/180.0)), + sin(angles_Rear[c] * (M_PI/180.0))); + SpeakerGain = Device->PanningLUT[pos]; + + for(i = 0;i < (ALint)Device->NumChan;i++) + { + enum Channel chan = Device->Speaker2Chan[i]; + SrcMatrix[c][chan] += DryGain * ListenerGain * + SpeakerGain[chan]; + } + } + } + angles = angles_Stereo; + chans = chans_Stereo; + num_channels = 2; + break; + + case FmtRear: + angles = angles_Rear; + chans = chans_Rear; + num_channels = 2; + break; + + case FmtQuad: + angles = angles_Quad; + chans = chans_Quad; + num_channels = 4; + break; + + case FmtX51: + angles = angles_X51; + chans = chans_X51; + num_channels = 6; + break; + + case FmtX61: + angles = angles_X61; + chans = chans_X61; + num_channels = 7; + break; + + case FmtX71: + angles = angles_X71; + chans = chans_X71; + num_channels = 8; + break; + } + + if(VirtualChannels == AL_FALSE) + { + for(c = 0;c < num_channels;c++) + SrcMatrix[c][chans[c]] += DryGain * ListenerGain; + } + else if((Device->Flags&DEVICE_USE_HRTF)) + { + for(c = 0;c < num_channels;c++) + { + if(chans[c] == LFE) + { + /* Skip LFE */ + ALSource->Params.HrtfDelay[c][0] = 0; + ALSource->Params.HrtfDelay[c][1] = 0; + for(i = 0;i < HRIR_LENGTH;i++) + { + ALSource->Params.HrtfCoeffs[c][i][0] = 0.0f; + ALSource->Params.HrtfCoeffs[c][i][1] = 0.0f; + } + } + else + { + /* Get the static HRIR coefficients and delays for this + * channel. */ + GetLerpedHrtfCoeffs(0.0, angles[c] * (M_PI/180.0), + DryGain*ListenerGain, + ALSource->Params.HrtfCoeffs[c], + ALSource->Params.HrtfDelay[c]); + } + ALSource->HrtfCounter = 0; + } + } + else + { + for(c = 0;c < num_channels;c++) + { + if(chans[c] == LFE) /* Special-case LFE */ + { + SrcMatrix[c][LFE] += DryGain * ListenerGain; + continue; + } + pos = aluCart2LUTpos(cos(angles[c] * (M_PI/180.0)), + sin(angles[c] * (M_PI/180.0))); + SpeakerGain = Device->PanningLUT[pos]; + + for(i = 0;i < (ALint)Device->NumChan;i++) + { + enum Channel chan = Device->Speaker2Chan[i]; + SrcMatrix[c][chan] += DryGain * ListenerGain * + SpeakerGain[chan]; + } + } + } + for(i = 0;i < NumSends;i++) + { + ALSource->Params.Send[i].Slot = ALSource->Send[i].Slot; + ALSource->Params.Send[i].WetGain = WetGain[i] * ListenerGain; + } + + /* Update filter coefficients. Calculations based on the I3DL2 + * spec. */ + cw = cos(2.0*M_PI * LOWPASSFREQCUTOFF / 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.iirFilter.coeff = lpCoeffCalc(DryGainHF, cw); + for(i = 0;i < NumSends;i++) + { + /* We use a one-pole filter, so we need to take the squared gain */ + ALfloat a = lpCoeffCalc(WetGainHF[i]*WetGainHF[i], cw); + ALSource->Params.Send[i].iirFilter.coeff = a; + } +} + +ALvoid CalcSourceParams(ALsource *ALSource, const ALCcontext *ALContext) +{ + const ALCdevice *Device = ALContext->Device; + ALfloat InnerAngle,OuterAngle,Angle,Distance,ClampedDist; + ALfloat Direction[3],Position[3],SourceToListener[3]; + ALfloat Velocity[3],ListenerVel[3]; + ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff; + ALfloat ConeVolume,ConeHF,SourceVolume,ListenerGain; + ALfloat DopplerFactor, DopplerVelocity, SpeedOfSound; + ALfloat AirAbsorptionFactor; + ALfloat RoomAirAbsorption[MAX_SENDS]; + ALbufferlistitem *BufferListItem; + ALfloat Attenuation, EffectiveDist; + ALfloat RoomAttenuation[MAX_SENDS]; + ALfloat MetersPerUnit; + ALfloat RoomRolloffBase; + ALfloat RoomRolloff[MAX_SENDS]; + ALfloat DecayDistance[MAX_SENDS]; + ALfloat DryGain; + ALfloat DryGainHF; + ALboolean DryGainHFAuto; + ALfloat WetGain[MAX_SENDS]; + ALfloat WetGainHF[MAX_SENDS]; + ALboolean WetGainAuto; + ALboolean WetGainHFAuto; + enum Resampler Resampler; + ALfloat Pitch; + ALuint Frequency; + ALint NumSends; + ALfloat cw; + ALint i; + + DryGainHF = 1.0f; + for(i = 0;i < MAX_SENDS;i++) + WetGainHF[i] = 1.0f; + + //Get context properties + DopplerFactor = ALContext->DopplerFactor * ALSource->DopplerFactor; + DopplerVelocity = ALContext->DopplerVelocity; + SpeedOfSound = ALContext->flSpeedOfSound; + NumSends = Device->NumAuxSends; + Frequency = Device->Frequency; + + //Get listener properties + ListenerGain = ALContext->Listener.Gain; + MetersPerUnit = ALContext->Listener.MetersPerUnit; + memcpy(ListenerVel, ALContext->Listener.Velocity, sizeof(ALContext->Listener.Velocity)); + + //Get source properties + SourceVolume = ALSource->flGain; + MinVolume = ALSource->flMinGain; + MaxVolume = ALSource->flMaxGain; + Pitch = ALSource->flPitch; + Resampler = ALSource->Resampler; + memcpy(Position, ALSource->vPosition, sizeof(ALSource->vPosition)); + memcpy(Direction, ALSource->vOrientation, sizeof(ALSource->vOrientation)); + memcpy(Velocity, ALSource->vVelocity, sizeof(ALSource->vVelocity)); + MinDist = ALSource->flRefDistance; + MaxDist = ALSource->flMaxDistance; + Rolloff = ALSource->flRollOffFactor; + InnerAngle = ALSource->flInnerAngle * ConeScale; + OuterAngle = ALSource->flOuterAngle * ConeScale; + AirAbsorptionFactor = ALSource->AirAbsorptionFactor; + DryGainHFAuto = ALSource->DryGainHFAuto; + WetGainAuto = ALSource->WetGainAuto; + WetGainHFAuto = ALSource->WetGainHFAuto; + RoomRolloffBase = ALSource->RoomRolloffFactor; + for(i = 0;i < NumSends;i++) + { + ALeffectslot *Slot = ALSource->Send[i].Slot; + + if(!Slot || Slot->effect.type == AL_EFFECT_NULL) + { + RoomRolloff[i] = 0.0f; + DecayDistance[i] = 0.0f; + RoomAirAbsorption[i] = 1.0f; + } + else if(Slot->AuxSendAuto) + { + RoomRolloff[i] = RoomRolloffBase; + if(IsReverbEffect(Slot->effect.type)) + { + RoomRolloff[i] += Slot->effect.Params.Reverb.RoomRolloffFactor; + DecayDistance[i] = Slot->effect.Params.Reverb.DecayTime * + SPEEDOFSOUNDMETRESPERSEC; + RoomAirAbsorption[i] = Slot->effect.Params.Reverb.AirAbsorptionGainHF; + } + else + { + DecayDistance[i] = 0.0f; + RoomAirAbsorption[i] = 1.0f; + } + } + else + { + /* If the slot's auxiliary send auto is off, the data sent to the + * effect slot is the same as the dry path, sans filter effects */ + RoomRolloff[i] = Rolloff; + DecayDistance[i] = 0.0f; + RoomAirAbsorption[i] = AIRABSORBGAINHF; + } + + ALSource->Params.Send[i].Slot = Slot; + } + + //1. Translate Listener to origin (convert to head relative) + if(ALSource->bHeadRelative == AL_FALSE) + { + ALfloat U[3],V[3],N[3]; + ALfloat Matrix[4][4]; + + // Build transform matrix + memcpy(N, ALContext->Listener.Forward, sizeof(N)); // At-vector + aluNormalize(N); // Normalized At-vector + memcpy(V, ALContext->Listener.Up, sizeof(V)); // Up-vector + aluNormalize(V); // Normalized Up-vector + aluCrossproduct(N, V, U); // Right-vector + aluNormalize(U); // Normalized Right-vector + Matrix[0][0] = U[0]; Matrix[0][1] = V[0]; Matrix[0][2] = -N[0]; Matrix[0][3] = 0.0f; + Matrix[1][0] = U[1]; Matrix[1][1] = V[1]; Matrix[1][2] = -N[1]; Matrix[1][3] = 0.0f; + Matrix[2][0] = U[2]; Matrix[2][1] = V[2]; Matrix[2][2] = -N[2]; Matrix[2][3] = 0.0f; + Matrix[3][0] = 0.0f; Matrix[3][1] = 0.0f; Matrix[3][2] = 0.0f; Matrix[3][3] = 1.0f; + + // Translate position + Position[0] -= ALContext->Listener.Position[0]; + Position[1] -= ALContext->Listener.Position[1]; + Position[2] -= ALContext->Listener.Position[2]; + + // Transform source position and direction into listener space + aluMatrixVector(Position, 1.0f, Matrix); + aluMatrixVector(Direction, 0.0f, Matrix); + // Transform source and listener velocity into listener space + aluMatrixVector(Velocity, 0.0f, Matrix); + aluMatrixVector(ListenerVel, 0.0f, Matrix); + } + else + ListenerVel[0] = ListenerVel[1] = ListenerVel[2] = 0.0f; + + SourceToListener[0] = -Position[0]; + SourceToListener[1] = -Position[1]; + SourceToListener[2] = -Position[2]; + aluNormalize(SourceToListener); + aluNormalize(Direction); + + //2. Calculate distance attenuation + Distance = aluSqrt(aluDotproduct(Position, Position)); + ClampedDist = Distance; + + Attenuation = 1.0f; + for(i = 0;i < NumSends;i++) + RoomAttenuation[i] = 1.0f; + switch(ALContext->SourceDistanceModel ? ALSource->DistanceModel : + ALContext->DistanceModel) + { + case InverseDistanceClamped: + ClampedDist = clampf(ClampedDist, MinDist, MaxDist); + if(MaxDist < MinDist) + break; + //fall-through + case InverseDistance: + if(MinDist > 0.0f) + { + if((MinDist + (Rolloff * (ClampedDist - MinDist))) > 0.0f) + Attenuation = MinDist / (MinDist + (Rolloff * (ClampedDist - MinDist))); + for(i = 0;i < NumSends;i++) + { + if((MinDist + (RoomRolloff[i] * (ClampedDist - MinDist))) > 0.0f) + RoomAttenuation[i] = MinDist / (MinDist + (RoomRolloff[i] * (ClampedDist - MinDist))); + } + } + break; + + case LinearDistanceClamped: + ClampedDist = clampf(ClampedDist, MinDist, MaxDist); + if(MaxDist < MinDist) + break; + //fall-through + case LinearDistance: + if(MaxDist != MinDist) + { + Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist)); + Attenuation = maxf(Attenuation, 0.0f); + for(i = 0;i < NumSends;i++) + { + RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist)); + RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f); + } + } + break; + + case ExponentDistanceClamped: + ClampedDist = clampf(ClampedDist, MinDist, MaxDist); + if(MaxDist < MinDist) + break; + //fall-through + case ExponentDistance: + if(ClampedDist > 0.0f && MinDist > 0.0f) + { + Attenuation = aluPow(ClampedDist/MinDist, -Rolloff); + for(i = 0;i < NumSends;i++) + RoomAttenuation[i] = aluPow(ClampedDist/MinDist, -RoomRolloff[i]); + } + break; + + case DisableDistance: + break; + } + + // Source Gain + Attenuation + DryGain = SourceVolume * Attenuation; + for(i = 0;i < NumSends;i++) + WetGain[i] = SourceVolume * RoomAttenuation[i]; + + // Distance-based air absorption + EffectiveDist = 0.0f; + if(MinDist > 0.0f && Attenuation < 1.0f) + EffectiveDist = (MinDist/Attenuation - MinDist)*MetersPerUnit; + if(AirAbsorptionFactor > 0.0f && EffectiveDist > 0.0f) + { + DryGainHF *= aluPow(AIRABSORBGAINHF, AirAbsorptionFactor*EffectiveDist); + for(i = 0;i < NumSends;i++) + WetGainHF[i] *= aluPow(RoomAirAbsorption[i], + AirAbsorptionFactor*EffectiveDist); + } + + //3. Apply directional soundcones + Angle = aluAcos(aluDotproduct(Direction,SourceToListener)) * (180.0/M_PI); + if(Angle >= InnerAngle && Angle <= OuterAngle) + { + ALfloat scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle); + ConeVolume = lerp(1.0, ALSource->flOuterGain, scale); + ConeHF = lerp(1.0, ALSource->OuterGainHF, scale); + } + else if(Angle > OuterAngle) + { + ConeVolume = ALSource->flOuterGain; + ConeHF = ALSource->OuterGainHF; + } + else + { + ConeVolume = 1.0f; + ConeHF = 1.0f; + } + + DryGain *= ConeVolume; + if(WetGainAuto) + { + for(i = 0;i < NumSends;i++) + WetGain[i] *= ConeVolume; + } + if(DryGainHFAuto) + DryGainHF *= ConeHF; + if(WetGainHFAuto) + { + for(i = 0;i < NumSends;i++) + WetGainHF[i] *= ConeHF; + } + + // Clamp to Min/Max Gain + DryGain = clampf(DryGain, MinVolume, MaxVolume); + for(i = 0;i < NumSends;i++) + WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume); + + // Apply filter gains and filters + switch(ALSource->DirectFilter.type) + { + case AL_FILTER_LOWPASS: + DryGain *= ALSource->DirectFilter.Gain; + DryGainHF *= ALSource->DirectFilter.GainHF; + break; + } + DryGain *= ListenerGain; + for(i = 0;i < NumSends;i++) + { + switch(ALSource->Send[i].WetFilter.type) + { + case AL_FILTER_LOWPASS: + WetGain[i] *= ALSource->Send[i].WetFilter.Gain; + WetGainHF[i] *= ALSource->Send[i].WetFilter.GainHF; + break; + } + WetGain[i] *= ListenerGain; + } + + if(WetGainAuto) + { + /* Apply a decay-time transformation to the wet path, based on the + * attenuation of the dry path. + * + * Using the approximate (effective) source to listener distance, the + * initial decay of the reverb effect is calculated and applied to the + * wet path. + */ + for(i = 0;i < NumSends;i++) + { + if(DecayDistance[i] > 0.0f) + WetGain[i] *= aluPow(0.001f /* -60dB */, + EffectiveDist / DecayDistance[i]); + } + } + + // Calculate Velocity + if(DopplerFactor != 0.0f) + { + ALfloat VSS, VLS; + ALfloat MaxVelocity = (SpeedOfSound*DopplerVelocity) / + DopplerFactor; + + VSS = aluDotproduct(Velocity, SourceToListener); + if(VSS >= MaxVelocity) + VSS = (MaxVelocity - 1.0f); + else if(VSS <= -MaxVelocity) + VSS = -MaxVelocity + 1.0f; + + VLS = aluDotproduct(ListenerVel, SourceToListener); + if(VLS >= MaxVelocity) + VLS = (MaxVelocity - 1.0f); + else if(VLS <= -MaxVelocity) + VLS = -MaxVelocity + 1.0f; + + Pitch *= ((SpeedOfSound*DopplerVelocity) - (DopplerFactor*VLS)) / + ((SpeedOfSound*DopplerVelocity) - (DopplerFactor*VSS)); + } + + BufferListItem = ALSource->queue; + while(BufferListItem != NULL) + { + ALbuffer *ALBuffer; + if((ALBuffer=BufferListItem->buffer) != NULL) + { + ALint maxstep = STACK_DATA_SIZE / ALSource->NumChannels / + ALSource->SampleSize; + 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<Params.Step = Pitch*FRACTIONONE; + if(ALSource->Params.Step == 0) + ALSource->Params.Step = 1; + } + + if((Device->Flags&DEVICE_USE_HRTF)) + ALSource->Params.DoMix = SelectHrtfMixer(ALBuffer, + (ALSource->Params.Step==FRACTIONONE) ? POINT_RESAMPLER : + Resampler); + else + ALSource->Params.DoMix = SelectMixer(ALBuffer, + (ALSource->Params.Step==FRACTIONONE) ? POINT_RESAMPLER : + Resampler); + break; + } + BufferListItem = BufferListItem->next; + } + + if((Device->Flags&DEVICE_USE_HRTF)) + { + // Use a binaural HRTF algorithm for stereo headphone playback + ALfloat delta, ev = 0.0f, az = 0.0f; + + if(Distance > 0.0f) + { + ALfloat invlen = 1.0f/Distance; + Position[0] *= invlen; + Position[1] *= invlen; + Position[2] *= invlen; + + // Calculate elevation and azimuth only when the source is not at + // the listener. This prevents +0 and -0 Z from producing + // inconsistent panning. + ev = asin(Position[1]); + az = atan2(Position[0], -Position[2]*ZScale); + } + + // Check to see if the HRIR is already moving. + if(ALSource->HrtfMoving) + { + // Calculate the normalized HRTF transition factor (delta). + delta = CalcHrtfDelta(ALSource->Params.HrtfGain, DryGain, + ALSource->Params.HrtfDir, Position); + // If the delta is large enough, get the moving HRIR target + // coefficients, target delays, steppping values, and counter. + if(delta > 0.001f) + { + ALSource->HrtfCounter = GetMovingHrtfCoeffs(ev, az, DryGain, + delta, ALSource->HrtfCounter, + ALSource->Params.HrtfCoeffs[0], + ALSource->Params.HrtfDelay[0], + ALSource->Params.HrtfCoeffStep, + ALSource->Params.HrtfDelayStep); + ALSource->Params.HrtfGain = DryGain; + ALSource->Params.HrtfDir[0] = Position[0]; + ALSource->Params.HrtfDir[1] = Position[1]; + ALSource->Params.HrtfDir[2] = Position[2]; + } + } + else + { + // Get the initial (static) HRIR coefficients and delays. + GetLerpedHrtfCoeffs(ev, az, DryGain, + ALSource->Params.HrtfCoeffs[0], + ALSource->Params.HrtfDelay[0]); + ALSource->HrtfCounter = 0; + ALSource->Params.HrtfGain = DryGain; + ALSource->Params.HrtfDir[0] = Position[0]; + ALSource->Params.HrtfDir[1] = Position[1]; + ALSource->Params.HrtfDir[2] = Position[2]; + } + } + else + { + // Use energy-preserving panning algorithm for multi-speaker playback + ALfloat DirGain, AmbientGain; + const ALfloat *SpeakerGain; + ALfloat length; + ALint pos; + + length = maxf(Distance, MinDist); + if(length > 0.0f) + { + ALfloat invlen = 1.0f/length; + Position[0] *= invlen; + Position[1] *= invlen; + Position[2] *= invlen; + } + + pos = aluCart2LUTpos(-Position[2]*ZScale, Position[0]); + SpeakerGain = Device->PanningLUT[pos]; + + DirGain = aluSqrt(Position[0]*Position[0] + Position[2]*Position[2]); + // elevation adjustment for directional gain. this sucks, but + // has low complexity + AmbientGain = aluSqrt(1.0/Device->NumChan); + for(i = 0;i < MAXCHANNELS;i++) + { + ALuint i2; + for(i2 = 0;i2 < MAXCHANNELS;i2++) + ALSource->Params.DryGains[i][i2] = 0.0f; + } + for(i = 0;i < (ALint)Device->NumChan;i++) + { + enum Channel chan = Device->Speaker2Chan[i]; + ALfloat gain = lerp(AmbientGain, SpeakerGain[chan], DirGain); + ALSource->Params.DryGains[0][chan] = DryGain * gain; + } + } + for(i = 0;i < NumSends;i++) + ALSource->Params.Send[i].WetGain = WetGain[i]; + + /* Update filter coefficients. */ + cw = cos(2.0*M_PI * LOWPASSFREQCUTOFF / Frequency); + + ALSource->Params.iirFilter.coeff = lpCoeffCalc(DryGainHF, cw); + for(i = 0;i < NumSends;i++) + { + ALfloat a = lpCoeffCalc(WetGainHF[i]*WetGainHF[i], cw); + ALSource->Params.Send[i].iirFilter.coeff = a; + } +} + + +static __inline ALfloat aluF2F(ALfloat val) +{ return val; } +static __inline ALshort aluF2S(ALfloat val) +{ + if(val > 1.0f) return 32767; + if(val < -1.0f) return -32768; + return (ALint)(val*32767.0f); +} +static __inline ALushort aluF2US(ALfloat val) +{ return aluF2S(val)+32768; } +static __inline ALbyte aluF2B(ALfloat val) +{ return aluF2S(val)>>8; } +static __inline ALubyte aluF2UB(ALfloat val) +{ return aluF2US(val)>>8; } + +#define DECL_TEMPLATE(T, N, func) \ +static void Write_##T##_##N(ALCdevice *device, T *RESTRICT buffer, \ + ALuint SamplesToDo) \ +{ \ + ALfloat (*RESTRICT DryBuffer)[MAXCHANNELS] = device->DryBuffer; \ + const enum Channel *ChanMap = device->DevChannels; \ + ALuint i, j; \ + \ + for(i = 0;i < SamplesToDo;i++) \ + { \ + for(j = 0;j < N;j++) \ + *(buffer++) = func(DryBuffer[i][ChanMap[j]]); \ + } \ +} + +DECL_TEMPLATE(ALfloat, 1, aluF2F) +DECL_TEMPLATE(ALfloat, 4, aluF2F) +DECL_TEMPLATE(ALfloat, 6, aluF2F) +DECL_TEMPLATE(ALfloat, 7, aluF2F) +DECL_TEMPLATE(ALfloat, 8, aluF2F) + +DECL_TEMPLATE(ALushort, 1, aluF2US) +DECL_TEMPLATE(ALushort, 4, aluF2US) +DECL_TEMPLATE(ALushort, 6, aluF2US) +DECL_TEMPLATE(ALushort, 7, aluF2US) +DECL_TEMPLATE(ALushort, 8, aluF2US) + +DECL_TEMPLATE(ALshort, 1, aluF2S) +DECL_TEMPLATE(ALshort, 4, aluF2S) +DECL_TEMPLATE(ALshort, 6, aluF2S) +DECL_TEMPLATE(ALshort, 7, aluF2S) +DECL_TEMPLATE(ALshort, 8, aluF2S) + +DECL_TEMPLATE(ALubyte, 1, aluF2UB) +DECL_TEMPLATE(ALubyte, 4, aluF2UB) +DECL_TEMPLATE(ALubyte, 6, aluF2UB) +DECL_TEMPLATE(ALubyte, 7, aluF2UB) +DECL_TEMPLATE(ALubyte, 8, aluF2UB) + +DECL_TEMPLATE(ALbyte, 1, aluF2B) +DECL_TEMPLATE(ALbyte, 4, aluF2B) +DECL_TEMPLATE(ALbyte, 6, aluF2B) +DECL_TEMPLATE(ALbyte, 7, aluF2B) +DECL_TEMPLATE(ALbyte, 8, aluF2B) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T, N, func) \ +static void Write_##T##_##N(ALCdevice *device, T *RESTRICT buffer, \ + ALuint SamplesToDo) \ +{ \ + ALfloat (*RESTRICT DryBuffer)[MAXCHANNELS] = device->DryBuffer; \ + const enum Channel *ChanMap = device->DevChannels; \ + ALuint i, j; \ + \ + if(device->Bs2b) \ + { \ + for(i = 0;i < SamplesToDo;i++) \ + { \ + float samples[2]; \ + samples[0] = DryBuffer[i][ChanMap[0]]; \ + samples[1] = DryBuffer[i][ChanMap[1]]; \ + bs2b_cross_feed(device->Bs2b, samples); \ + *(buffer++) = func(samples[0]); \ + *(buffer++) = func(samples[1]); \ + } \ + } \ + else \ + { \ + for(i = 0;i < SamplesToDo;i++) \ + { \ + for(j = 0;j < N;j++) \ + *(buffer++) = func(DryBuffer[i][ChanMap[j]]); \ + } \ + } \ +} + +DECL_TEMPLATE(ALfloat, 2, aluF2F) +DECL_TEMPLATE(ALushort, 2, aluF2US) +DECL_TEMPLATE(ALshort, 2, aluF2S) +DECL_TEMPLATE(ALubyte, 2, aluF2UB) +DECL_TEMPLATE(ALbyte, 2, aluF2B) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static void Write_##T(ALCdevice *device, T *buffer, ALuint SamplesToDo) \ +{ \ + switch(device->FmtChans) \ + { \ + case DevFmtMono: \ + Write_##T##_1(device, buffer, SamplesToDo); \ + break; \ + case DevFmtStereo: \ + Write_##T##_2(device, buffer, SamplesToDo); \ + break; \ + case DevFmtQuad: \ + Write_##T##_4(device, buffer, SamplesToDo); \ + break; \ + case DevFmtX51: \ + case DevFmtX51Side: \ + Write_##T##_6(device, buffer, SamplesToDo); \ + break; \ + case DevFmtX61: \ + Write_##T##_7(device, buffer, SamplesToDo); \ + break; \ + case DevFmtX71: \ + Write_##T##_8(device, buffer, SamplesToDo); \ + break; \ + } \ +} + +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALbyte) + +#undef DECL_TEMPLATE + +ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) +{ + ALuint SamplesToDo; + ALeffectslot *ALEffectSlot; + ALCcontext **ctx, **ctx_end; + ALsource **src, **src_end; + int fpuState; + ALuint i, c; + ALsizei e; + +#if defined(HAVE_FESETROUND) + fpuState = fegetround(); + fesetround(FE_TOWARDZERO); +#elif defined(HAVE__CONTROLFP) + fpuState = _controlfp(0, 0); + (void)_controlfp(_RC_CHOP, _MCW_RC); +#else + (void)fpuState; +#endif + + while(size > 0) + { + /* Setup variables */ + SamplesToDo = minu(size, BUFFERSIZE); + + /* Clear mixing buffer */ + memset(device->DryBuffer, 0, SamplesToDo*MAXCHANNELS*sizeof(ALfloat)); + + LockDevice(device); + ctx = device->Contexts; + ctx_end = ctx + device->NumContexts; + //printf("Contexts: %d\n", device->NumContexts); + int context_number = 0; + while(ctx != ctx_end) + { + //printf("Context %d:\n", context_number++); + ALboolean DeferUpdates = (*ctx)->DeferUpdates; + ALboolean UpdateSources = AL_FALSE; + + if(!DeferUpdates) + { + //printf("NOT deferring updates, whatever that means\n"); + UpdateSources = (*ctx)->UpdateSources; + //printf("update sources is set to %d\n", UpdateSources); + (*ctx)->UpdateSources = AL_FALSE; + } + + src = (*ctx)->ActiveSources; + src_end = src + (*ctx)->ActiveSourceCount; + //printf("number of active sources are %d\n", (*ctx)->ActiveSourceCount); + while(src != src_end) + { + + if((*src)->state != AL_PLAYING) + { + --((*ctx)->ActiveSourceCount); + *src = *(--src_end); + continue; + } + + if(!DeferUpdates && ((*src)->NeedsUpdate || UpdateSources)) + { + (*src)->NeedsUpdate = AL_FALSE; + ALsource_Update(*src, *ctx); + } + //printf("calling MixSource!\n"); + MixSource(*src, device, SamplesToDo); + src++; + } + + /* effect slot processing */ + for(e = 0;e < (*ctx)->EffectSlotMap.size;e++) + { + ALEffectSlot = (*ctx)->EffectSlotMap.array[e].value; + + for(i = 0;i < SamplesToDo;i++) + { + // RLM: remove click-removal + ALEffectSlot->WetBuffer[i] += ALEffectSlot->ClickRemoval[0]; + ALEffectSlot->ClickRemoval[0] -= ALEffectSlot->ClickRemoval[0] / 256.0f; + } + for(i = 0;i < 1;i++) + { + // RLM: remove click-removal + ALEffectSlot->ClickRemoval[i] += ALEffectSlot->PendingClicks[i]; + ALEffectSlot->PendingClicks[i] = 0.0f; + } + + if(!DeferUpdates && ALEffectSlot->NeedsUpdate) + { + ALEffectSlot->NeedsUpdate = AL_FALSE; + ALEffect_Update(ALEffectSlot->EffectState, *ctx, ALEffectSlot); + } + + ALEffect_Process(ALEffectSlot->EffectState, ALEffectSlot, + SamplesToDo, ALEffectSlot->WetBuffer, + device->DryBuffer); + + for(i = 0;i < SamplesToDo;i++) + ALEffectSlot->WetBuffer[i] = 0.0f; + } + + ctx++; + } + UnlockDevice(device); + + //Post processing loop + if(device->FmtChans == DevFmtMono) + { + for(i = 0;i < SamplesToDo;i++) + { + // RLM: remove click-removal + device->DryBuffer[i][FRONT_CENTER] += device->ClickRemoval[FRONT_CENTER]; + device->ClickRemoval[FRONT_CENTER] -= device->ClickRemoval[FRONT_CENTER] / 256.0f; + } + // RLM: remove click-removal + device->ClickRemoval[FRONT_CENTER] += device->PendingClicks[FRONT_CENTER]; + device->PendingClicks[FRONT_CENTER] = 0.0f; + } + else if(device->FmtChans == DevFmtStereo) + { + /* Assumes the first two channels are FRONT_LEFT and FRONT_RIGHT */ + for(i = 0;i < SamplesToDo;i++) + { + for(c = 0;c < 2;c++) + { + // RLM: remove click-removal + device->DryBuffer[i][c] += device->ClickRemoval[c]; + device->ClickRemoval[c] -= device->ClickRemoval[c] / 256.0f; + } + } + for(c = 0;c < 2;c++) + { + // RLM: remove click-removal + device->ClickRemoval[c] += device->PendingClicks[c]; + device->PendingClicks[c] = 0.0f; + } + } + else + { + for(i = 0;i < SamplesToDo;i++) + { + for(c = 0;c < MAXCHANNELS;c++) + { + // RLM: remove click-removal + device->DryBuffer[i][c] += device->ClickRemoval[c]; + device->ClickRemoval[c] -= device->ClickRemoval[c] / 256.0f; + } + } + for(c = 0;c < MAXCHANNELS;c++) + { + // RLM: remove click-removal + device->ClickRemoval[c] += device->PendingClicks[c]; + device->PendingClicks[c] = 0.0f; + } + } + + if(buffer) + { + switch(device->FmtType) + { + case DevFmtByte: + Write_ALbyte(device, buffer, SamplesToDo); + break; + case DevFmtUByte: + Write_ALubyte(device, buffer, SamplesToDo); + break; + case DevFmtShort: + Write_ALshort(device, buffer, SamplesToDo); + break; + case DevFmtUShort: + Write_ALushort(device, buffer, SamplesToDo); + break; + case DevFmtFloat: + Write_ALfloat(device, buffer, SamplesToDo); + break; + } + } + + size -= SamplesToDo; + } + +#if defined(HAVE_FESETROUND) + fesetround(fpuState); +#elif defined(HAVE__CONTROLFP) + _controlfp(fpuState, _MCW_RC); +#endif +} + + + + + +ALvoid aluHandleDisconnect(ALCdevice *device) +{ + ALuint i; + + LockDevice(device); + for(i = 0;i < device->NumContexts;i++) + { + ALCcontext *Context = device->Contexts[i]; + ALsource *source; + ALsizei pos; + + for(pos = 0;pos < Context->SourceMap.size;pos++) + { + source = Context->SourceMap.array[pos].value; + if(source->state == AL_PLAYING) + { + source->state = AL_STOPPED; + source->BuffersPlayed = source->BuffersInQueue; + source->position = 0; + source->position_fraction = 0; + } + } + } + + device->Connected = ALC_FALSE; + UnlockDevice(device); +} diff -r 000000000000 -r f9476ff7637e Alc/alcConfig.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/alcConfig.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,342 @@ +/** + * 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 + */ + +#ifdef _WIN32 +#ifdef __MINGW64__ +#define _WIN32_IE 0x501 +#else +#define _WIN32_IE 0x400 +#endif +#endif + +#include "config.h" + +#include +#include +#include +#include + +#include "alMain.h" + +#ifdef _WIN32_IE +#include +#endif + +typedef struct ConfigEntry { + char *key; + char *value; +} ConfigEntry; + +typedef struct ConfigBlock { + char *name; + ConfigEntry *entries; + size_t entryCount; +} ConfigBlock; + +static ConfigBlock *cfgBlocks; +static size_t cfgCount; + +static char buffer[1024]; + +static void LoadConfigFromFile(FILE *f) +{ + ConfigBlock *curBlock = cfgBlocks; + ConfigEntry *ent; + + while(fgets(buffer, sizeof(buffer), f)) + { + size_t i = 0; + + while(isspace(buffer[i])) + i++; + if(!buffer[i] || buffer[i] == '#') + continue; + + memmove(buffer, buffer+i, strlen(buffer+i)+1); + + if(buffer[0] == '[') + { + ConfigBlock *nextBlock; + + i = 1; + while(buffer[i] && buffer[i] != ']') + i++; + + if(!buffer[i]) + { + ERR("config parse error: bad line \"%s\"\n", buffer); + 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; + } + } + + if(!nextBlock) + { + 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); + } + 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] != '=') + { + 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++; + + /* Check if we already have this option set */ + ent = curBlock->entries; + while((size_t)(ent-curBlock->entries) < curBlock->entryCount) + { + if(strcasecmp(ent->key, buffer) == 0) + break; + ent++; + } + + if((size_t)(ent-curBlock->entries) >= curBlock->entryCount) + { + /* Allocate a new option entry */ + ent = realloc(curBlock->entries, (curBlock->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++; + + ent->key = strdup(buffer); + 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(isspace(buffer[i])); + buffer[++i] = 0; + + free(ent->value); + ent->value = strdup(buffer); + + TRACE("found '%s' = '%s'\n", ent->key, ent->value); + } +} + +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"); + f = fopen(buffer, "rt"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + } +#else + f = fopen("/etc/openal/alsoft.conf", "r"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + if((str=getenv("HOME")) != NULL && *str) + { + snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", str); + f = fopen(buffer, "r"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + } +#endif + if((str=getenv("ALSOFT_CONF")) != NULL && *str) + { + f = fopen(str, "r"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + } +} + +void FreeALConfig(void) +{ + size_t i; + + for(i = 0;i < cfgCount;i++) + { + size_t 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(cfgBlocks); + cfgBlocks = NULL; + cfgCount = 0; +} + +const char *GetConfigValue(const char *blockName, const char *keyName, const char *def) +{ + size_t i, j; + + if(!keyName) + return def; + + if(!blockName) + blockName = "general"; + + for(i = 0;i < cfgCount;i++) + { + if(strcasecmp(cfgBlocks[i].name, blockName) != 0) + continue; + + for(j = 0;j < cfgBlocks[i].entryCount;j++) + { + 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("Key %s:%s not found\n", blockName, keyName); + return def; +} + +int ConfigValueExists(const char *blockName, const char *keyName) +{ + const char *val = GetConfigValue(blockName, keyName, ""); + return !!val[0]; +} + +int GetConfigValueInt(const char *blockName, const char *keyName, int def) +{ + const char *val = GetConfigValue(blockName, keyName, ""); + + if(!val[0]) return def; + return strtol(val, NULL, 0); +} + +float GetConfigValueFloat(const char *blockName, const char *keyName, float def) +{ + const char *val = GetConfigValue(blockName, keyName, ""); + + if(!val[0]) return def; +#ifdef HAVE_STRTOF + return strtof(val, NULL); +#else + return (float)strtod(val, NULL); +#endif +} + +int GetConfigValueBool(const char *blockName, const char *keyName, int def) +{ + const char *val = GetConfigValue(blockName, keyName, ""); + + if(!val[0]) return !!def; + return (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 || + strcasecmp(val, "on") == 0 || atoi(val) != 0); +} diff -r 000000000000 -r f9476ff7637e Alc/alcDedicated.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/alcDedicated.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,138 @@ +/** + * 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 + +#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 DedicatedDLGUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffectslot *Slot) +{ + ALdedicatedState *state = (ALdedicatedState*)effect; + ALCdevice *device = Context->Device; + const ALfloat *SpeakerGain; + ALfloat Gain; + ALint pos; + ALsizei s; + + pos = aluCart2LUTpos(1.0f, 0.0f); + SpeakerGain = device->PanningLUT[pos]; + + Gain = Slot->Gain * Slot->effect.Params.Dedicated.Gain; + for(s = 0;s < MAXCHANNELS;s++) + state->gains[s] = SpeakerGain[s] * Gain; +} + +static ALvoid DedicatedLFEUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffectslot *Slot) +{ + ALdedicatedState *state = (ALdedicatedState*)effect; + ALfloat Gain; + ALsizei s; + (void)Context; + + Gain = Slot->Gain * Slot->effect.Params.Dedicated.Gain; + for(s = 0;s < MAXCHANNELS;s++) + state->gains[s] = 0.0f; + state->gains[LFE] = Gain; +} + +static ALvoid DedicatedProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) +{ + ALdedicatedState *state = (ALdedicatedState*)effect; + const ALfloat *gains = state->gains; + ALuint i, s; + (void)Slot; + + for(i = 0;i < SamplesToDo;i++) + { + ALfloat sample; + + sample = SamplesIn[i]; + for(s = 0;s < MAXCHANNELS;s++) + SamplesOut[i][s] = sample * gains[s]; + } +} + +ALeffectState *DedicatedDLGCreate(void) +{ + ALdedicatedState *state; + ALsizei s; + + state = malloc(sizeof(*state)); + if(!state) + return NULL; + + state->state.Destroy = DedicatedDestroy; + state->state.DeviceUpdate = DedicatedDeviceUpdate; + state->state.Update = DedicatedDLGUpdate; + state->state.Process = DedicatedProcess; + + for(s = 0;s < MAXCHANNELS;s++) + state->gains[s] = 0.0f; + + return &state->state; +} + +ALeffectState *DedicatedLFECreate(void) +{ + ALdedicatedState *state; + ALsizei s; + + state = malloc(sizeof(*state)); + if(!state) + return NULL; + + state->state.Destroy = DedicatedDestroy; + state->state.DeviceUpdate = DedicatedDeviceUpdate; + state->state.Update = DedicatedLFEUpdate; + state->state.Process = DedicatedProcess; + + for(s = 0;s < MAXCHANNELS;s++) + state->gains[s] = 0.0f; + + return &state->state; +} diff -r 000000000000 -r f9476ff7637e Alc/alcEcho.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/alcEcho.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,192 @@ +/** + * 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 +#include + +#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 LR gains for the first tap. The second tap uses the reverse + ALfloat GainL; + ALfloat GainR; + + ALfloat FeedGain; + + ALfloat Gain[MAXCHANNELS]; + + 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 = (ALuint)(AL_ECHO_MAX_DELAY * Device->Frequency) + 1; + maxlen += (ALuint)(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, ALCcontext *Context, const ALeffectslot *Slot) +{ + ALechoState *state = (ALechoState*)effect; + ALCdevice *Device = Context->Device; + ALuint frequency = Device->Frequency; + ALfloat lrpan, cw, g, gain; + ALuint i; + + state->Tap[0].delay = (ALuint)(Slot->effect.Params.Echo.Delay * frequency) + 1; + state->Tap[1].delay = (ALuint)(Slot->effect.Params.Echo.LRDelay * frequency); + state->Tap[1].delay += state->Tap[0].delay; + + lrpan = Slot->effect.Params.Echo.Spread*0.5f + 0.5f; + state->GainL = aluSqrt( lrpan); + state->GainR = aluSqrt(1.0f-lrpan); + + state->FeedGain = Slot->effect.Params.Echo.Feedback; + + cw = cos(2.0*M_PI * LOWPASSFREQCUTOFF / frequency); + g = 1.0f - Slot->effect.Params.Echo.Damping; + state->iirFilter.coeff = lpCoeffCalc(g, cw); + + gain = Slot->Gain; + for(i = 0;i < MAXCHANNELS;i++) + state->Gain[i] = 0.0f; + for(i = 0;i < Device->NumChan;i++) + { + enum Channel chan = Device->Speaker2Chan[i]; + state->Gain[chan] = gain; + } +} + +static ALvoid EchoProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) +{ + 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 samp[2], smp; + ALuint i; + (void)Slot; + + for(i = 0;i < SamplesToDo;i++,offset++) + { + // Sample first tap + smp = state->SampleBuffer[(offset-tap1) & mask]; + samp[0] = smp * state->GainL; + samp[1] = smp * state->GainR; + // Sample second tap. Reverse LR panning + smp = state->SampleBuffer[(offset-tap2) & mask]; + samp[0] += smp * state->GainR; + samp[1] += smp * state->GainL; + + // 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; + + SamplesOut[i][FRONT_LEFT] += state->Gain[FRONT_LEFT] * samp[0]; + SamplesOut[i][FRONT_RIGHT] += state->Gain[FRONT_RIGHT] * samp[1]; + SamplesOut[i][SIDE_LEFT] += state->Gain[SIDE_LEFT] * samp[0]; + SamplesOut[i][SIDE_RIGHT] += state->Gain[SIDE_RIGHT] * samp[1]; + SamplesOut[i][BACK_LEFT] += state->Gain[BACK_LEFT] * samp[0]; + SamplesOut[i][BACK_RIGHT] += state->Gain[BACK_RIGHT] * samp[1]; + } + 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->GainL = 0.0f; + state->GainR = 0.0f; + + state->iirFilter.coeff = 0.0f; + state->iirFilter.history[0] = 0.0f; + state->iirFilter.history[1] = 0.0f; + + return &state->state; +} diff -r 000000000000 -r f9476ff7637e Alc/alcModulator.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/alcModulator.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,210 @@ +/** + * 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 +#include + +#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_FRACMASK ((1<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 *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) \ +{ \ + const ALuint step = state->step; \ + ALuint index = state->index; \ + ALfloat samp; \ + ALuint i; \ + \ + for(i = 0;i < SamplesToDo;i++) \ + { \ + samp = SamplesIn[i]; \ + \ + index += step; \ + index &= WAVEFORM_FRACMASK; \ + samp *= func(index); \ + \ + samp = hpFilter1P(&state->iirFilter, 0, samp); \ + \ + SamplesOut[i][FRONT_LEFT] += state->Gain[FRONT_LEFT] * samp; \ + SamplesOut[i][FRONT_RIGHT] += state->Gain[FRONT_RIGHT] * samp; \ + SamplesOut[i][FRONT_CENTER] += state->Gain[FRONT_CENTER] * samp; \ + SamplesOut[i][SIDE_LEFT] += state->Gain[SIDE_LEFT] * samp; \ + SamplesOut[i][SIDE_RIGHT] += state->Gain[SIDE_RIGHT] * samp; \ + SamplesOut[i][BACK_LEFT] += state->Gain[BACK_LEFT] * samp; \ + SamplesOut[i][BACK_RIGHT] += state->Gain[BACK_RIGHT] * samp; \ + SamplesOut[i][BACK_CENTER] += state->Gain[BACK_CENTER] * 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, ALCcontext *Context, const ALeffectslot *Slot) +{ + ALmodulatorState *state = (ALmodulatorState*)effect; + ALCdevice *Device = Context->Device; + ALfloat gain, cw, a = 0.0f; + ALuint index; + + if(Slot->effect.Params.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID) + state->Waveform = SINUSOID; + else if(Slot->effect.Params.Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH) + state->Waveform = SAWTOOTH; + else if(Slot->effect.Params.Modulator.Waveform == AL_RING_MODULATOR_SQUARE) + state->Waveform = SQUARE; + + state->step = Slot->effect.Params.Modulator.Frequency*(1<Frequency; + if(!state->step) + state->step = 1; + + cw = cos(2.0*M_PI * Slot->effect.Params.Modulator.HighPassCutoff / + Device->Frequency); + a = (2.0f-cw) - aluSqrt(aluPow(2.0f-cw, 2.0f) - 1.0f); + state->iirFilter.coeff = a; + + 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, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) +{ + ALmodulatorState *state = (ALmodulatorState*)effect; + (void)Slot; + + 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.0f; + state->step = 1.0f; + + state->iirFilter.coeff = 0.0f; + state->iirFilter.history[0] = 0.0f; + + return &state->state; +} diff -r 000000000000 -r f9476ff7637e Alc/alcReverb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/alcReverb.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,1374 @@ +/** + * Reverb for the OpenAL cross platform audio library + * Copyright (C) 2008-2009 by Christopher Fitzgerald. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * 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 +#include +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "alMain.h" +#include "alAuxEffectSlot.h" +#include "alEffect.h" +#include "alError.h" +#include "alu.h" + +typedef struct DelayLine +{ + // The delay lines use sample lengths that are powers of 2 to allow the + // use of bit-masking instead of a modulus for wrapping. + ALuint Mask; + ALfloat *Line; +} DelayLine; + +typedef struct ALverbState { + // Must be first in all effects! + ALeffectState state; + + // 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]; + struct { + // Modulator delay line. + DelayLine Delay; + // The vibrato time is tracked with an index over a modulus-wrapped + // range (in samples). + ALuint Index; + ALuint Range; + // The depth of frequency change (also in samples) and its filter. + ALfloat Depth; + ALfloat Coeff; + ALfloat Filter; + } Mod; + // Initial effect delay. + DelayLine Delay; + // The tap points for the initial delay. First tap goes to early + // reflections, the last to late reverb. + ALuint DelayTap[2]; + struct { + // Output gain for early reflections. + ALfloat Gain; + // Early reflections are done with 4 delay lines. + ALfloat Coeff[4]; + DelayLine Delay[4]; + ALuint Offset[4]; + // The gain for each output channel based on 3D panning (only for the + // EAX path). + ALfloat PanGain[MAXCHANNELS]; + } Early; + // Decorrelator delay line. + DelayLine Decorrelator; + // There are actually 4 decorrelator taps, but the first occurs at the + // initial sample. + ALuint DecoTap[3]; + struct { + // Output gain for late reverb. + ALfloat Gain; + // Attenuation to compensate for the modal density and decay rate of + // the late lines. + ALfloat DensityGain; + // The feed-back and feed-forward all-pass coefficient. + ALfloat ApFeedCoeff; + // Mixing matrix coefficient. + ALfloat MixCoeff; + // Late reverb has 4 parallel all-pass filters. + ALfloat ApCoeff[4]; + DelayLine ApDelay[4]; + ALuint ApOffset[4]; + // In addition to 4 cyclical delay lines. + ALfloat Coeff[4]; + DelayLine Delay[4]; + ALuint Offset[4]; + // The cyclical delay lines are 1-pole low-pass filtered. + ALfloat LpCoeff[4]; + ALfloat LpSample[4]; + // The gain for each output channel based on 3D panning (only for the + // EAX path). + ALfloat PanGain[MAXCHANNELS]; + } Late; + struct { + // Attenuation to compensate for the modal density and decay rate of + // the echo line. + ALfloat DensityGain; + // Echo delay and all-pass lines. + DelayLine Delay; + DelayLine ApDelay; + ALfloat Coeff; + ALfloat ApFeedCoeff; + ALfloat ApCoeff; + ALuint Offset; + ALuint ApOffset; + // The echo line is 1-pole low-pass filtered. + ALfloat LpCoeff; + ALfloat LpSample; + // Echo mixing coefficients. + ALfloat MixCoeff[2]; + } Echo; + // The current read offset for all delay lines. + ALuint Offset; + + // The gain for each output channel (non-EAX path only; aliased from + // Late.PanGain) + ALfloat *Gain; +} ALverbState; + +/* This is a user config option for modifying the overall output of the reverb + * effect. + */ +ALfloat ReverbBoost = 1.0f; + +/* Specifies whether to use a standard reverb effect in place of EAX reverb */ +ALboolean EmulateEAXReverb = AL_FALSE; + +/* This coefficient is used to define the maximum frequency range controlled + * by the modulation depth. The current value of 0.1 will allow it to swing + * from 0.9x to 1.1x. This value must be below 1. At 1 it will cause the + * sampler to stall on the downswing, and above 1 it will cause it to sample + * backwards. + */ +static const ALfloat MODULATION_DEPTH_COEFF = 0.1f; + +/* A filter is used to avoid the terrible distortion caused by changing + * modulation time and/or depth. To be consistent across different sample + * rates, the coefficient must be raised to a constant divided by the sample + * rate: coeff^(constant / rate). + */ +static const ALfloat MODULATION_FILTER_COEFF = 0.048f; +static const ALfloat MODULATION_FILTER_CONST = 100000.0f; + +// When diffusion is above 0, an all-pass filter is used to take the edge off +// the echo effect. It uses the following line length (in seconds). +static const ALfloat ECHO_ALLPASS_LENGTH = 0.0133f; + +// Input into the late reverb is decorrelated between four channels. Their +// timings are dependent on a fraction and multiplier. See the +// UpdateDecorrelator() routine for the calculations involved. +static const ALfloat DECO_FRACTION = 0.15f; +static const ALfloat DECO_MULTIPLIER = 2.0f; + +// All delay line lengths are specified in seconds. + +// The lengths of the early delay lines. +static const ALfloat EARLY_LINE_LENGTH[4] = +{ + 0.0015f, 0.0045f, 0.0135f, 0.0405f +}; + +// The lengths of the late all-pass delay lines. +static const ALfloat ALLPASS_LINE_LENGTH[4] = +{ + 0.0151f, 0.0167f, 0.0183f, 0.0200f, +}; + +// The lengths of the late cyclical delay lines. +static const ALfloat LATE_LINE_LENGTH[4] = +{ + 0.0211f, 0.0311f, 0.0461f, 0.0680f +}; + +// The late cyclical delay lines have a variable length dependent on the +// effect's density parameter (inverted for some reason) and this multiplier. +static const ALfloat LATE_LINE_MULTIPLIER = 4.0f; + +// Calculate the length of a delay line and store its mask and offset. +static ALuint CalcLineLength(ALfloat length, ALintptrEXT offset, ALuint frequency, DelayLine *Delay) +{ + ALuint samples; + + // All line lengths are powers of 2, calculated from their lengths, with + // an additional sample in case of rounding errors. + samples = NextPowerOf2((ALuint)(length * frequency) + 1); + // All lines share a single sample buffer. + Delay->Mask = samples - 1; + Delay->Line = (ALfloat*)offset; + // Return the sample count for accumulation. + return samples; +} + +// Given the allocated sample buffer, this function updates each delay line +// offset. +static __inline ALvoid RealizeLineOffset(ALfloat * sampleBuffer, DelayLine *Delay) +{ + Delay->Line = &sampleBuffer[(ALintptrEXT)Delay->Line]; +} + +/* Calculates the delay line metrics and allocates the shared sample buffer + * for all lines given a flag indicating whether or not to allocate the EAX- + * related delays (eaxFlag) and the sample rate (frequency). If an + * allocation failure occurs, it returns AL_FALSE. + */ +static ALboolean AllocLines(ALboolean eaxFlag, ALuint frequency, ALverbState *State) +{ + ALuint totalSamples, index; + ALfloat length; + ALfloat *newBuffer = NULL; + + // All delay line lengths are calculated to accomodate the full range of + // lengths given their respective paramters. + totalSamples = 0; + if(eaxFlag) + { + /* The modulator's line length is calculated from the maximum + * modulation time and depth coefficient, and halfed for the low-to- + * high frequency swing. An additional sample is added to keep it + * stable when there is no modulation. + */ + length = (AL_EAXREVERB_MAX_MODULATION_TIME * MODULATION_DEPTH_COEFF / + 2.0f) + (1.0f / frequency); + totalSamples += CalcLineLength(length, totalSamples, frequency, + &State->Mod.Delay); + } + + // The initial delay is the sum of the reflections and late reverb + // delays. + if(eaxFlag) + length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + + AL_EAXREVERB_MAX_LATE_REVERB_DELAY; + else + length = AL_REVERB_MAX_REFLECTIONS_DELAY + + AL_REVERB_MAX_LATE_REVERB_DELAY; + totalSamples += CalcLineLength(length, totalSamples, frequency, + &State->Delay); + + // The early reflection lines. + for(index = 0;index < 4;index++) + totalSamples += CalcLineLength(EARLY_LINE_LENGTH[index], totalSamples, + frequency, &State->Early.Delay[index]); + + // The decorrelator line is calculated from the lowest reverb density (a + // parameter value of 1). + length = (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) * + LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER); + totalSamples += CalcLineLength(length, totalSamples, frequency, + &State->Decorrelator); + + // The late all-pass lines. + for(index = 0;index < 4;index++) + totalSamples += CalcLineLength(ALLPASS_LINE_LENGTH[index], totalSamples, + frequency, &State->Late.ApDelay[index]); + + // The late delay lines are calculated from the lowest reverb density. + for(index = 0;index < 4;index++) + { + length = LATE_LINE_LENGTH[index] * (1.0f + LATE_LINE_MULTIPLIER); + totalSamples += CalcLineLength(length, totalSamples, frequency, + &State->Late.Delay[index]); + } + + if(eaxFlag) + { + // The echo all-pass and delay lines. + totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples, + frequency, &State->Echo.ApDelay); + totalSamples += CalcLineLength(AL_EAXREVERB_MAX_ECHO_TIME, totalSamples, + frequency, &State->Echo.Delay); + } + + if(totalSamples != State->TotalSamples) + { + newBuffer = realloc(State->SampleBuffer, sizeof(ALfloat) * totalSamples); + if(newBuffer == NULL) + return AL_FALSE; + State->SampleBuffer = newBuffer; + State->TotalSamples = totalSamples; + } + + // Update all delays to reflect the new sample buffer. + RealizeLineOffset(State->SampleBuffer, &State->Delay); + RealizeLineOffset(State->SampleBuffer, &State->Decorrelator); + for(index = 0;index < 4;index++) + { + RealizeLineOffset(State->SampleBuffer, &State->Early.Delay[index]); + RealizeLineOffset(State->SampleBuffer, &State->Late.ApDelay[index]); + RealizeLineOffset(State->SampleBuffer, &State->Late.Delay[index]); + } + if(eaxFlag) + { + RealizeLineOffset(State->SampleBuffer, &State->Mod.Delay); + RealizeLineOffset(State->SampleBuffer, &State->Echo.ApDelay); + RealizeLineOffset(State->SampleBuffer, &State->Echo.Delay); + } + + // Clear the sample buffer. + for(index = 0;index < State->TotalSamples;index++) + State->SampleBuffer[index] = 0.0f; + + return AL_TRUE; +} + +// 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) +{ + return aluPow(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) +{ + return log10(coeff) * decayTime / -3.0f/*log10(0.001)*/; +} + +// Calculate the high frequency parameter for the I3DL2 coefficient +// calculation. +static __inline ALfloat CalcI3DL2HFreq(ALfloat hfRef, ALuint frequency) +{ + return cos(2.0f * M_PI * 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) +{ + /* 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 + * amplitude for the sample n. + * + * Decaying feedback matches exponential decay of the form Sum(a^n), + * where a is the attenuation coefficient, and n is the sample. The area + * under this decay curve can be calculated as: 1 / (1 - a). + * + * Modifying the above equation to find the squared area under the curve + * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be + * calculated by inverting the square root of this approximation, + * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2). + */ + return aluSqrt(1.0f - (a * a)); +} + +// Calculate the mixing matrix coefficients given a diffusion factor. +static __inline ALvoid CalcMatrixCoeffs(ALfloat diffusion, ALfloat *x, ALfloat *y) +{ + ALfloat n, t; + + // The matrix is of order 4, so n is sqrt (4 - 1). + n = aluSqrt(3.0f); + t = diffusion * atan(n); + + // Calculate the first mixing matrix coefficient. + *x = cos(t); + // Calculate the second mixing matrix coefficient. + *y = sin(t) / n; +} + +// Calculate the limited HF ratio for use with the late reverb low-pass +// filters. +static ALfloat CalcLimitedHfRatio(ALfloat hfRatio, ALfloat airAbsorptionGainHF, ALfloat decayTime) +{ + ALfloat limitRatio; + + /* Find the attenuation due to air absorption in dB (converting delay + * time to meters using the speed of sound). Then reversing the decay + * equation, solve for HF ratio. The delay length is cancelled out of + * the equation, so it can be calculated once for all lines. + */ + limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * + SPEEDOFSOUNDMETRESPERSEC); + /* Using the limit calculated above, apply the upper bound to the HF + * ratio. Also need to limit the result to a minimum of 0.1, just like the + * HF ratio parameter. */ + return clampf(limitRatio, 0.1f, hfRatio); +} + +// 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) +{ + ALfloat coeff, g; + + // Eventually this should boost the high frequencies when the ratio + // exceeds 1. + coeff = 0.0f; + if (hfRatio < 1.0f) + { + // Calculate the low-pass coefficient by dividing the HF decay + // coefficient by the full decay coefficient. + g = CalcDecayCoeff(length, decayTime * hfRatio) / decayCoeff; + + // Damping is done with a 1-pole filter, so g needs to be squared. + g *= g; + coeff = lpCoeffCalc(g, cw); + + // Very low decay times will produce minimal output, so apply an + // upper bound to the coefficient. + coeff = minf(coeff, 0.98f); + } + return coeff; +} + +// 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) +{ + ALfloat length; + + /* Modulation is calculated in two parts. + * + * The modulation time effects the sinus applied to the change in + * frequency. An index out of the current time range (both in samples) + * is incremented each sample. The range is bound to a reasonable + * minimum (1 sample) and when the timing changes, the index is rescaled + * to the new range (to keep the sinus consistent). + */ + length = modTime * frequency; + if (length >= 1.0f) { + State->Mod.Index = (ALuint)(State->Mod.Index * length / + State->Mod.Range); + State->Mod.Range = (ALuint)length; + } else { + State->Mod.Index = 0; + State->Mod.Range = 1; + } + + /* The modulation depth effects the amount of frequency change over the + * range of the sinus. It needs to be scaled by the modulation time so + * that a given depth produces a consistent change in frequency over all + * ranges of time. Since the depth is applied to a sinus value, it needs + * to be halfed once for the sinus range and again for the sinus swing + * in time (half of it is spent decreasing the frequency, half is spent + * increasing it). + */ + State->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f / + 2.0f * frequency; +} + +// Update the offsets for the initial effect delay line. +static ALvoid UpdateDelayLine(ALfloat earlyDelay, ALfloat lateDelay, ALuint frequency, ALverbState *State) +{ + // Calculate the initial delay taps. + State->DelayTap[0] = (ALuint)(earlyDelay * frequency); + State->DelayTap[1] = (ALuint)((earlyDelay + lateDelay) * frequency); +} + +// Update the early reflections gain and line coefficients. +static ALvoid UpdateEarlyLines(ALfloat reverbGain, ALfloat earlyGain, ALfloat lateDelay, ALverbState *State) +{ + ALuint index; + + // Calculate the early reflections gain (from the master effect gain, and + // reflections gain parameters) with a constant attenuation of 0.5. + State->Early.Gain = 0.5f * reverbGain * earlyGain; + + // Calculate the gain (coefficient) for each early delay line using the + // late delay time. This expands the early reflections to the start of + // the late reverb. + for(index = 0;index < 4;index++) + State->Early.Coeff[index] = CalcDecayCoeff(EARLY_LINE_LENGTH[index], + lateDelay); +} + +// Update the offsets for the decorrelator line. +static ALvoid UpdateDecorrelator(ALfloat density, ALuint frequency, ALverbState *State) +{ + ALuint index; + ALfloat length; + + /* The late reverb inputs are decorrelated to smooth the reverb tail and + * reduce harsh echos. The first tap occurs immediately, while the + * remaining taps are delayed by multiples of a fraction of the smallest + * cyclical delay time. + * + * offset[index] = (FRACTION (MULTIPLIER^index)) smallest_delay + */ + for(index = 0;index < 3;index++) + { + length = (DECO_FRACTION * aluPow(DECO_MULTIPLIER, (ALfloat)index)) * + LATE_LINE_LENGTH[0] * (1.0f + (density * LATE_LINE_MULTIPLIER)); + State->DecoTap[index] = (ALuint)(length * frequency); + } +} + +// 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) +{ + ALfloat length; + ALuint index; + + /* Calculate the late reverb gain (from the master effect gain, and late + * reverb gain parameters). Since the output is tapped prior to the + * application of the next delay line coefficients, this gain needs to be + * attenuated by the 'x' mixing matrix coefficient as well. + */ + State->Late.Gain = reverbGain * lateGain * xMix; + + /* To compensate for changes in modal density and decay time of the late + * reverb signal, the input is attenuated based on the maximal energy of + * the outgoing signal. This approximation is used to keep the apparent + * energy of the signal equal for all ranges of density and decay time. + * + * The average length of the cyclcical delay lines is used to calculate + * the attenuation coefficient. + */ + length = (LATE_LINE_LENGTH[0] + LATE_LINE_LENGTH[1] + + LATE_LINE_LENGTH[2] + LATE_LINE_LENGTH[3]) / 4.0f; + length *= 1.0f + (density * LATE_LINE_MULTIPLIER); + State->Late.DensityGain = CalcDensityGain(CalcDecayCoeff(length, + decayTime)); + + // Calculate the all-pass feed-back and feed-forward coefficient. + State->Late.ApFeedCoeff = 0.5f * aluPow(diffusion, 2.0f); + + for(index = 0;index < 4;index++) + { + // Calculate the gain (coefficient) for each all-pass line. + State->Late.ApCoeff[index] = CalcDecayCoeff(ALLPASS_LINE_LENGTH[index], + decayTime); + + // Calculate the length (in seconds) of each cyclical delay line. + length = LATE_LINE_LENGTH[index] * (1.0f + (density * + LATE_LINE_MULTIPLIER)); + + // Calculate the delay offset for each cyclical delay line. + State->Late.Offset[index] = (ALuint)(length * frequency); + + // Calculate the gain (coefficient) for each cyclical line. + State->Late.Coeff[index] = CalcDecayCoeff(length, decayTime); + + // Calculate the damping coefficient for each low-pass filter. + State->Late.LpCoeff[index] = + CalcDampingCoeff(hfRatio, length, decayTime, + State->Late.Coeff[index], cw); + + // Attenuate the cyclical line coefficients by the mixing coefficient + // (x). + State->Late.Coeff[index] *= 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) +{ + // Update the offset and coefficient for the echo delay line. + State->Echo.Offset = (ALuint)(echoTime * frequency); + + // Calculate the decay coefficient for the echo line. + State->Echo.Coeff = CalcDecayCoeff(echoTime, decayTime); + + // Calculate the energy-based attenuation coefficient for the echo delay + // line. + State->Echo.DensityGain = CalcDensityGain(State->Echo.Coeff); + + // Calculate the echo all-pass feed coefficient. + State->Echo.ApFeedCoeff = 0.5f * aluPow(diffusion, 2.0f); + + // Calculate the echo all-pass attenuation coefficient. + State->Echo.ApCoeff = CalcDecayCoeff(ECHO_ALLPASS_LENGTH, decayTime); + + // Calculate the damping coefficient for each low-pass filter. + State->Echo.LpCoeff = CalcDampingCoeff(hfRatio, echoTime, decayTime, + State->Echo.Coeff, cw); + + /* Calculate the echo mixing coefficients. The first is applied to the + * echo itself. The second is used to attenuate the late reverb when + * echo depth is high and diffusion is low, so the echo is slightly + * stronger than the decorrelated echos in the reverb tail. + */ + State->Echo.MixCoeff[0] = reverbGain * lateGain * echoDepth; + State->Echo.MixCoeff[1] = 1.0f - (echoDepth * 0.5f * (1.0f - diffusion)); +} + +// Update the early and late 3D panning gains. +static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALverbState *State) +{ + ALfloat earlyPan[3] = { ReflectionsPan[0], ReflectionsPan[1], + ReflectionsPan[2] }; + ALfloat latePan[3] = { LateReverbPan[0], LateReverbPan[1], + LateReverbPan[2] }; + const ALfloat *speakerGain; + ALfloat ambientGain; + ALfloat dirGain; + ALfloat length; + ALuint index; + ALint pos; + + Gain *= ReverbBoost; + + // Attenuate non-directional reverb according to the number of channels + ambientGain = aluSqrt(2.0f/Device->NumChan); + + // Calculate the 3D-panning gains for the early reflections and late + // reverb. + length = earlyPan[0]*earlyPan[0] + earlyPan[1]*earlyPan[1] + earlyPan[2]*earlyPan[2]; + if(length > 1.0f) + { + length = 1.0f / aluSqrt(length); + earlyPan[0] *= length; + earlyPan[1] *= length; + earlyPan[2] *= length; + } + length = latePan[0]*latePan[0] + latePan[1]*latePan[1] + latePan[2]*latePan[2]; + if(length > 1.0f) + { + length = 1.0f / aluSqrt(length); + latePan[0] *= length; + latePan[1] *= length; + latePan[2] *= length; + } + + /* This code applies directional reverb just like the mixer applies + * directional sources. It diffuses the sound toward all speakers as the + * magnitude of the panning vector drops, which is only a rough + * approximation of the expansion of sound across the speakers from the + * panning direction. + */ + pos = aluCart2LUTpos(earlyPan[2], earlyPan[0]); + speakerGain = Device->PanningLUT[pos]; + dirGain = aluSqrt((earlyPan[0] * earlyPan[0]) + (earlyPan[2] * earlyPan[2])); + + for(index = 0;index < MAXCHANNELS;index++) + State->Early.PanGain[index] = 0.0f; + for(index = 0;index < Device->NumChan;index++) + { + enum Channel chan = Device->Speaker2Chan[index]; + State->Early.PanGain[chan] = lerp(ambientGain, speakerGain[chan], dirGain) * Gain; + } + + + pos = aluCart2LUTpos(latePan[2], latePan[0]); + speakerGain = Device->PanningLUT[pos]; + dirGain = aluSqrt((latePan[0] * latePan[0]) + (latePan[2] * latePan[2])); + + for(index = 0;index < MAXCHANNELS;index++) + State->Late.PanGain[index] = 0.0f; + for(index = 0;index < Device->NumChan;index++) + { + enum Channel chan = Device->Speaker2Chan[index]; + State->Late.PanGain[chan] = lerp(ambientGain, speakerGain[chan], dirGain) * Gain; + } +} + +// Basic delay line input/output routines. +static __inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset) +{ + return Delay->Line[offset&Delay->Mask]; +} + +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) +{ + 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) +{ + ALfloat out, feed; + + out = DelayLineOut(Delay, outOffset); + feed = feedCoeff * in; + DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in); + + // The time-based attenuation is only applied to the delay output to + // keep it from affecting the feed-back path (which is already controlled + // by the all-pass feed coefficient). + return (coeff * out) - feed; +} + +// Given an input sample, this function produces modulation for the late +// reverb. +static __inline ALfloat EAXModulation(ALverbState *State, ALfloat in) +{ + ALfloat sinus, frac; + ALuint offset; + ALfloat out0, out1; + + // 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 - cos(2.0f * M_PI * 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 + // small parameter changes. + State->Mod.Filter = lerp(State->Mod.Filter, State->Mod.Depth, + State->Mod.Coeff); + + // Calculate the read offset and fraction between it and the next sample. + frac = (1.0f + (State->Mod.Filter * sinus)); + offset = (ALuint)frac; + frac -= offset; + + // Get the two samples crossed by the offset, and feed the delay line + // with the next input sample. + out0 = DelayLineOut(&State->Mod.Delay, State->Offset - offset); + out1 = DelayLineOut(&State->Mod.Delay, State->Offset - offset - 1); + DelayLineIn(&State->Mod.Delay, State->Offset, in); + + // Step the modulation index forward, keeping it bound to its range. + State->Mod.Index = (State->Mod.Index + 1) % State->Mod.Range; + + // The output is obtained by linearly interpolating the two samples that + // were acquired above. + return lerp(out0, out1, frac); +} + +// Delay line output routine for early reflections. +static __inline ALfloat EarlyDelayLineOut(ALverbState *State, ALuint index) +{ + return AttenuatedDelayLineOut(&State->Early.Delay[index], + State->Offset - State->Early.Offset[index], + State->Early.Coeff[index]); +} + +// Given an input sample, this function produces four-channel output for the +// early reflections. +static __inline ALvoid EarlyReflection(ALverbState *State, ALfloat in, ALfloat *out) +{ + ALfloat d[4], v, f[4]; + + // Obtain the decayed results of each early delay line. + d[0] = EarlyDelayLineOut(State, 0); + d[1] = EarlyDelayLineOut(State, 1); + d[2] = EarlyDelayLineOut(State, 2); + d[3] = EarlyDelayLineOut(State, 3); + + /* The following uses a lossless scattering junction from waveguide + * theory. It actually amounts to a householder mixing matrix, which + * will produce a maximally diffuse response, and means this can probably + * be considered a simple feed-back delay network (FDN). + * N + * --- + * \ + * v = 2/N / d_i + * --- + * i=1 + */ + v = (d[0] + d[1] + d[2] + d[3]) * 0.5f; + // The junction is loaded with the input here. + v += in; + + // Calculate the feed values for the delay lines. + f[0] = v - d[0]; + f[1] = v - d[1]; + f[2] = v - d[2]; + f[3] = v - d[3]; + + // Re-feed the delay lines. + DelayLineIn(&State->Early.Delay[0], State->Offset, f[0]); + DelayLineIn(&State->Early.Delay[1], State->Offset, f[1]); + DelayLineIn(&State->Early.Delay[2], State->Offset, f[2]); + DelayLineIn(&State->Early.Delay[3], State->Offset, f[3]); + + // Output the results of the junction for all four channels. + out[0] = State->Early.Gain * f[0]; + out[1] = State->Early.Gain * f[1]; + out[2] = State->Early.Gain * f[2]; + out[3] = State->Early.Gain * f[3]; +} + +// All-pass input/output routine for late reverb. +static __inline ALfloat LateAllPassInOut(ALverbState *State, ALuint index, ALfloat in) +{ + return AllpassInOut(&State->Late.ApDelay[index], + State->Offset - State->Late.ApOffset[index], + State->Offset, in, State->Late.ApFeedCoeff, + State->Late.ApCoeff[index]); +} + +// Delay line output routine for late reverb. +static __inline ALfloat LateDelayLineOut(ALverbState *State, ALuint index) +{ + return AttenuatedDelayLineOut(&State->Late.Delay[index], + State->Offset - State->Late.Offset[index], + State->Late.Coeff[index]); +} + +// Low-pass filter input/output routine for late reverb. +static __inline ALfloat LateLowPassInOut(ALverbState *State, ALuint index, ALfloat in) +{ + in = lerp(in, State->Late.LpSample[index], State->Late.LpCoeff[index]); + State->Late.LpSample[index] = in; + return in; +} + +// Given four decorrelated input samples, this function produces four-channel +// output for the late reverb. +static __inline ALvoid LateReverb(ALverbState *State, ALfloat *in, ALfloat *out) +{ + ALfloat d[4], f[4]; + + // Obtain the decayed results of the cyclical delay lines, and add the + // corresponding input channels. Then pass the results through the + // low-pass filters. + + // This is where the feed-back cycles from line 0 to 1 to 3 to 2 and back + // to 0. + d[0] = LateLowPassInOut(State, 2, in[2] + LateDelayLineOut(State, 2)); + d[1] = LateLowPassInOut(State, 0, in[0] + LateDelayLineOut(State, 0)); + d[2] = LateLowPassInOut(State, 3, in[3] + LateDelayLineOut(State, 3)); + d[3] = LateLowPassInOut(State, 1, in[1] + LateDelayLineOut(State, 1)); + + // To help increase diffusion, run each line through an all-pass filter. + // When there is no diffusion, the shortest all-pass filter will feed the + // shortest delay line. + d[0] = LateAllPassInOut(State, 0, d[0]); + d[1] = LateAllPassInOut(State, 1, d[1]); + d[2] = LateAllPassInOut(State, 2, d[2]); + d[3] = LateAllPassInOut(State, 3, d[3]); + + /* Late reverb is done with a modified feed-back delay network (FDN) + * topology. Four input lines are each fed through their own all-pass + * filter and then into the mixing matrix. The four outputs of the + * mixing matrix are then cycled back to the inputs. Each output feeds + * a different input to form a circlular feed cycle. + * + * The mixing matrix used is a 4D skew-symmetric rotation matrix derived + * using a single unitary rotational parameter: + * + * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2 + * [ -a, d, c, -b ] + * [ -b, -c, d, a ] + * [ -c, b, -a, d ] + * + * The rotation is constructed from the effect's diffusion parameter, + * yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y + * with differing signs, and d is the coefficient x. The matrix is thus: + * + * [ x, y, -y, y ] n = sqrt(matrix_order - 1) + * [ -y, x, y, y ] t = diffusion_parameter * atan(n) + * [ y, -y, x, y ] x = cos(t) + * [ -y, -y, -y, x ] y = sin(t) / n + * + * To reduce the number of multiplies, the x coefficient is applied with + * the cyclical delay line coefficients. Thus only the y coefficient is + * applied when mixing, and is modified to be: y / x. + */ + f[0] = d[0] + (State->Late.MixCoeff * ( d[1] + -d[2] + d[3])); + f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3])); + f[2] = d[2] + (State->Late.MixCoeff * ( d[0] + -d[1] + d[3])); + f[3] = d[3] + (State->Late.MixCoeff * (-d[0] + -d[1] + -d[2] )); + + // Output the results of the matrix for all four channels, attenuated by + // the late reverb gain (which is attenuated by the 'x' mix coefficient). + out[0] = State->Late.Gain * f[0]; + out[1] = State->Late.Gain * f[1]; + out[2] = State->Late.Gain * f[2]; + out[3] = State->Late.Gain * f[3]; + + // Re-feed the cyclical delay lines. + DelayLineIn(&State->Late.Delay[0], State->Offset, f[0]); + DelayLineIn(&State->Late.Delay[1], State->Offset, f[1]); + DelayLineIn(&State->Late.Delay[2], State->Offset, f[2]); + DelayLineIn(&State->Late.Delay[3], State->Offset, f[3]); +} + +// Given an input sample, this function mixes echo into the four-channel late +// reverb. +static __inline ALvoid EAXEcho(ALverbState *State, ALfloat in, ALfloat *late) +{ + ALfloat out, feed; + + // Get the latest attenuated echo sample for output. + feed = AttenuatedDelayLineOut(&State->Echo.Delay, + State->Offset - State->Echo.Offset, + State->Echo.Coeff); + + // Mix the output into the late reverb channels. + out = State->Echo.MixCoeff[0] * feed; + late[0] = (State->Echo.MixCoeff[1] * late[0]) + out; + late[1] = (State->Echo.MixCoeff[1] * late[1]) + out; + late[2] = (State->Echo.MixCoeff[1] * late[2]) + out; + late[3] = (State->Echo.MixCoeff[1] * late[3]) + out; + + // Mix the energy-attenuated input with the output and pass it through + // the echo low-pass filter. + feed += State->Echo.DensityGain * in; + feed = lerp(feed, State->Echo.LpSample, State->Echo.LpCoeff); + State->Echo.LpSample = feed; + + // Then the echo all-pass filter. + feed = AllpassInOut(&State->Echo.ApDelay, + State->Offset - State->Echo.ApOffset, + State->Offset, feed, State->Echo.ApFeedCoeff, + State->Echo.ApCoeff); + + // Feed the delay with the mixed and filtered sample. + DelayLineIn(&State->Echo.Delay, State->Offset, feed); +} + +// 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 *early, ALfloat *late) +{ + ALfloat feed, taps[4]; + + // Low-pass filter the incoming sample. + in = lpFilter2P(&State->LpFilter, 0, in); + + // Feed the initial delay line. + DelayLineIn(&State->Delay, State->Offset, in); + + // Calculate the early reflection from the first delay tap. + in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]); + EarlyReflection(State, in, early); + + // Feed the decorrelator from the energy-attenuated output of the second + // delay tap. + in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]); + feed = in * State->Late.DensityGain; + DelayLineIn(&State->Decorrelator, State->Offset, feed); + + // Calculate the late reverb from the decorrelator taps. + taps[0] = feed; + taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]); + taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]); + taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]); + LateReverb(State, taps, late); + + // Step all delays forward one sample. + State->Offset++; +} + +// Perform the EAX reverb pass on a given input sample, resulting in four- +// channel output. +static __inline ALvoid EAXVerbPass(ALverbState *State, ALfloat in, ALfloat *early, ALfloat *late) +{ + ALfloat feed, taps[4]; + + // Low-pass filter the incoming sample. + in = lpFilter2P(&State->LpFilter, 0, in); + + // Perform any modulation on the input. + in = EAXModulation(State, in); + + // Feed the initial delay line. + DelayLineIn(&State->Delay, State->Offset, in); + + // Calculate the early reflection from the first delay tap. + in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]); + EarlyReflection(State, in, early); + + // Feed the decorrelator from the energy-attenuated output of the second + // delay tap. + in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]); + feed = in * State->Late.DensityGain; + DelayLineIn(&State->Decorrelator, State->Offset, feed); + + // Calculate the late reverb from the decorrelator taps. + taps[0] = feed; + taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]); + taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]); + taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]); + LateReverb(State, taps, late); + + // Calculate and mix in any echo. + EAXEcho(State, in, late); + + // Step all delays forward one sample. + State->Offset++; +} + +// 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 VerbDestroy(ALeffectState *effect) +{ + ALverbState *State = (ALverbState*)effect; + if(State) + { + free(State->SampleBuffer); + State->SampleBuffer = NULL; + free(State); + } +} + +// This updates the device-dependant reverb state. This is called on +// initialization and any time the device parameters (eg. playback frequency, +// or format) have been changed. +static ALboolean VerbDeviceUpdate(ALeffectState *effect, ALCdevice *Device) +{ + ALverbState *State = (ALverbState*)effect; + ALuint frequency = Device->Frequency; + ALuint index; + + // Allocate the delay lines. + if(!AllocLines(AL_FALSE, frequency, State)) + return AL_FALSE; + + // The early reflection and late all-pass filter line lengths are static, + // so their offsets only need to be calculated once. + for(index = 0;index < 4;index++) + { + State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] * + frequency); + State->Late.ApOffset[index] = (ALuint)(ALLPASS_LINE_LENGTH[index] * + frequency); + } + + 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 EAXVerbDeviceUpdate(ALeffectState *effect, ALCdevice *Device) +{ + ALverbState *State = (ALverbState*)effect; + ALuint frequency = Device->Frequency, index; + + // Allocate the delay lines. + if(!AllocLines(AL_TRUE, frequency, State)) + return AL_FALSE; + + // Calculate the modulation filter coefficient. Notice that the exponent + // is calculated given the current sample rate. This ensures that the + // resulting filter response over time is consistent across all sample + // rates. + State->Mod.Coeff = aluPow(MODULATION_FILTER_COEFF, + MODULATION_FILTER_CONST / frequency); + + // The early reflection and late all-pass filter line lengths are static, + // so their offsets only need to be calculated once. + for(index = 0;index < 4;index++) + { + State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] * + frequency); + State->Late.ApOffset[index] = (ALuint)(ALLPASS_LINE_LENGTH[index] * + frequency); + } + + // The echo all-pass filter line length is static, so its offset only + // needs to be calculated once. + State->Echo.ApOffset = (ALuint)(ECHO_ALLPASS_LENGTH * frequency); + + return AL_TRUE; +} + +// This updates the reverb state. This is called any time the reverb effect +// is loaded into a slot. +static ALvoid VerbUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffectslot *Slot) +{ + ALverbState *State = (ALverbState*)effect; + ALCdevice *Device = Context->Device; + ALuint frequency = Device->Frequency; + ALfloat cw, x, y, hfRatio, gain; + ALuint index; + + // Calculate the master low-pass filter (from the master effect HF gain). + cw = CalcI3DL2HFreq(Slot->effect.Params.Reverb.HFReference, frequency); + // This is done with 2 chained 1-pole filters, so no need to square g. + State->LpFilter.coeff = lpCoeffCalc(Slot->effect.Params.Reverb.GainHF, cw); + + // Update the initial effect delay. + UpdateDelayLine(Slot->effect.Params.Reverb.ReflectionsDelay, + Slot->effect.Params.Reverb.LateReverbDelay, + frequency, State); + + // Update the early lines. + UpdateEarlyLines(Slot->effect.Params.Reverb.Gain, + Slot->effect.Params.Reverb.ReflectionsGain, + Slot->effect.Params.Reverb.LateReverbDelay, State); + + // Update the decorrelator. + UpdateDecorrelator(Slot->effect.Params.Reverb.Density, frequency, State); + + // Get the mixing matrix coefficients (x and y). + CalcMatrixCoeffs(Slot->effect.Params.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.Params.Reverb.DecayHFRatio; + if(Slot->effect.Params.Reverb.DecayHFLimit && + Slot->effect.Params.Reverb.AirAbsorptionGainHF < 1.0f) + hfRatio = CalcLimitedHfRatio(hfRatio, + Slot->effect.Params.Reverb.AirAbsorptionGainHF, + Slot->effect.Params.Reverb.DecayTime); + + // Update the late lines. + UpdateLateLines(Slot->effect.Params.Reverb.Gain, Slot->effect.Params.Reverb.LateReverbGain, + x, Slot->effect.Params.Reverb.Density, Slot->effect.Params.Reverb.DecayTime, + Slot->effect.Params.Reverb.Diffusion, hfRatio, cw, frequency, State); + + // Update channel gains + gain = Slot->Gain; + gain *= aluSqrt(2.0f/Device->NumChan); + gain *= 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; + } +} + +// This updates the EAX reverb state. This is called any time the EAX reverb +// effect is loaded into a slot. +static ALvoid EAXVerbUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffectslot *Slot) +{ + ALverbState *State = (ALverbState*)effect; + ALuint frequency = Context->Device->Frequency; + ALfloat cw, x, y, hfRatio; + + // Calculate the master low-pass filter (from the master effect HF gain). + cw = CalcI3DL2HFreq(Slot->effect.Params.Reverb.HFReference, frequency); + // This is done with 2 chained 1-pole filters, so no need to square g. + State->LpFilter.coeff = lpCoeffCalc(Slot->effect.Params.Reverb.GainHF, cw); + + // Update the modulator line. + UpdateModulator(Slot->effect.Params.Reverb.ModulationTime, + Slot->effect.Params.Reverb.ModulationDepth, + frequency, State); + + // Update the initial effect delay. + UpdateDelayLine(Slot->effect.Params.Reverb.ReflectionsDelay, + Slot->effect.Params.Reverb.LateReverbDelay, + frequency, State); + + // Update the early lines. + UpdateEarlyLines(Slot->effect.Params.Reverb.Gain, + Slot->effect.Params.Reverb.ReflectionsGain, + Slot->effect.Params.Reverb.LateReverbDelay, State); + + // Update the decorrelator. + UpdateDecorrelator(Slot->effect.Params.Reverb.Density, frequency, State); + + // Get the mixing matrix coefficients (x and y). + CalcMatrixCoeffs(Slot->effect.Params.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.Params.Reverb.DecayHFRatio; + if(Slot->effect.Params.Reverb.DecayHFLimit && + Slot->effect.Params.Reverb.AirAbsorptionGainHF < 1.0f) + hfRatio = CalcLimitedHfRatio(hfRatio, + Slot->effect.Params.Reverb.AirAbsorptionGainHF, + Slot->effect.Params.Reverb.DecayTime); + + // Update the late lines. + UpdateLateLines(Slot->effect.Params.Reverb.Gain, Slot->effect.Params.Reverb.LateReverbGain, + x, Slot->effect.Params.Reverb.Density, Slot->effect.Params.Reverb.DecayTime, + Slot->effect.Params.Reverb.Diffusion, hfRatio, cw, frequency, State); + + // Update the echo line. + UpdateEchoLine(Slot->effect.Params.Reverb.Gain, Slot->effect.Params.Reverb.LateReverbGain, + Slot->effect.Params.Reverb.EchoTime, Slot->effect.Params.Reverb.DecayTime, + Slot->effect.Params.Reverb.Diffusion, Slot->effect.Params.Reverb.EchoDepth, + hfRatio, cw, frequency, State); + + // Update early and late 3D panning. + Update3DPanning(Context->Device, Slot->effect.Params.Reverb.ReflectionsPan, + Slot->effect.Params.Reverb.LateReverbPan, Slot->Gain, State); +} + +// This processes the reverb state, given the input samples and an output +// buffer. +static ALvoid VerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) +{ + ALverbState *State = (ALverbState*)effect; + ALuint index; + ALfloat early[4], late[4], out[4]; + const ALfloat *panGain = State->Gain; + (void)Slot; + + for(index = 0;index < SamplesToDo;index++) + { + // Process reverb for this sample. + VerbPass(State, SamplesIn[index], early, late); + + // Mix early reflections and late reverb. + out[0] = (early[0] + late[0]); + out[1] = (early[1] + late[1]); + out[2] = (early[2] + late[2]); + out[3] = (early[3] + late[3]); + + // Output the results. + SamplesOut[index][FRONT_LEFT] += panGain[FRONT_LEFT] * out[0]; + SamplesOut[index][FRONT_RIGHT] += panGain[FRONT_RIGHT] * out[1]; + SamplesOut[index][FRONT_CENTER] += panGain[FRONT_CENTER] * out[3]; + SamplesOut[index][SIDE_LEFT] += panGain[SIDE_LEFT] * out[0]; + SamplesOut[index][SIDE_RIGHT] += panGain[SIDE_RIGHT] * out[1]; + SamplesOut[index][BACK_LEFT] += panGain[BACK_LEFT] * out[0]; + SamplesOut[index][BACK_RIGHT] += panGain[BACK_RIGHT] * out[1]; + SamplesOut[index][BACK_CENTER] += panGain[BACK_CENTER] * out[2]; + } +} + +// This processes the EAX reverb state, given the input samples and an output +// buffer. +static ALvoid EAXVerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) +{ + ALverbState *State = (ALverbState*)effect; + ALuint index; + ALfloat early[4], late[4]; + (void)Slot; + + for(index = 0;index < SamplesToDo;index++) + { + // Process reverb for this sample. + EAXVerbPass(State, SamplesIn[index], early, late); + + // Unfortunately, while the number and configuration of gains for + // panning adjust according to MAXCHANNELS, the output from the + // reverb engine is not so scalable. + SamplesOut[index][FRONT_LEFT] += + (State->Early.PanGain[FRONT_LEFT]*early[0] + + State->Late.PanGain[FRONT_LEFT]*late[0]); + SamplesOut[index][FRONT_RIGHT] += + (State->Early.PanGain[FRONT_RIGHT]*early[1] + + State->Late.PanGain[FRONT_RIGHT]*late[1]); + SamplesOut[index][FRONT_CENTER] += + (State->Early.PanGain[FRONT_CENTER]*early[3] + + State->Late.PanGain[FRONT_CENTER]*late[3]); + SamplesOut[index][SIDE_LEFT] += + (State->Early.PanGain[SIDE_LEFT]*early[0] + + State->Late.PanGain[SIDE_LEFT]*late[0]); + SamplesOut[index][SIDE_RIGHT] += + (State->Early.PanGain[SIDE_RIGHT]*early[1] + + State->Late.PanGain[SIDE_RIGHT]*late[1]); + SamplesOut[index][BACK_LEFT] += + (State->Early.PanGain[BACK_LEFT]*early[0] + + State->Late.PanGain[BACK_LEFT]*late[0]); + SamplesOut[index][BACK_RIGHT] += + (State->Early.PanGain[BACK_RIGHT]*early[1] + + State->Late.PanGain[BACK_RIGHT]*late[1]); + SamplesOut[index][BACK_CENTER] += + (State->Early.PanGain[BACK_CENTER]*early[2] + + State->Late.PanGain[BACK_CENTER]*late[2]); + } +} + +// 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 *VerbCreate(void) +{ + ALverbState *State = NULL; + ALuint index; + + State = malloc(sizeof(ALverbState)); + if(!State) + return NULL; + + State->state.Destroy = VerbDestroy; + State->state.DeviceUpdate = VerbDeviceUpdate; + State->state.Update = VerbUpdate; + State->state.Process = VerbProcess; + + State->TotalSamples = 0; + State->SampleBuffer = NULL; + + State->LpFilter.coeff = 0.0f; + State->LpFilter.history[0] = 0.0f; + State->LpFilter.history[1] = 0.0f; + + 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->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->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; + 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; + } + + for(index = 0;index < MAXCHANNELS;index++) + { + 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 &State->state; +} + +ALeffectState *EAXVerbCreate(void) +{ + ALeffectState *State = VerbCreate(); + if(State && EmulateEAXReverb == AL_FALSE) + { + State->DeviceUpdate = EAXVerbDeviceUpdate; + State->Update = EAXVerbUpdate; + State->Process = EAXVerbProcess; + } + return State; +} diff -r 000000000000 -r f9476ff7637e Alc/alcRing.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/alcRing.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,127 @@ +/** + * 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 +#include + +#include "alMain.h" + + +struct RingBuffer { + ALubyte *mem; + + ALsizei frame_size; + ALsizei length; + ALint read_pos; + ALint write_pos; + + CRITICAL_SECTION cs; +}; + + +RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length) +{ + RingBuffer *ring = calloc(1, sizeof(*ring) + ((length+1) * frame_size)); + if(ring) + { + ring->mem = (ALubyte*)(ring+1); + + ring->frame_size = frame_size; + ring->length = length+1; + ring->read_pos = 0; + ring->write_pos = 0; + + InitializeCriticalSection(&ring->cs); + } + return ring; +} + +void DestroyRingBuffer(RingBuffer *ring) +{ + if(ring) + { + DeleteCriticalSection(&ring->cs); + free(ring); + } +} + +ALsizei RingBufferSize(RingBuffer *ring) +{ + ALsizei s; + + EnterCriticalSection(&ring->cs); + s = (ring->write_pos-ring->read_pos+ring->length) % ring->length; + LeaveCriticalSection(&ring->cs); + + return s; +} + +void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len) +{ + int remain; + + EnterCriticalSection(&ring->cs); + + remain = (ring->read_pos-ring->write_pos-1+ring->length) % ring->length; + if(remain < len) len = remain; + + if(len > 0) + { + remain = ring->length - ring->write_pos; + if(remain < len) + { + memcpy(ring->mem+(ring->write_pos*ring->frame_size), data, + remain*ring->frame_size); + memcpy(ring->mem, data+(remain*ring->frame_size), + (len-remain)*ring->frame_size); + } + else + memcpy(ring->mem+(ring->write_pos*ring->frame_size), data, + len*ring->frame_size); + + ring->write_pos += len; + ring->write_pos %= ring->length; + } + + LeaveCriticalSection(&ring->cs); +} + +void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len) +{ + int remain; + + EnterCriticalSection(&ring->cs); + + remain = ring->length - ring->read_pos; + if(remain < len) + { + memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), remain*ring->frame_size); + memcpy(data+(remain*ring->frame_size), ring->mem, (len-remain)*ring->frame_size); + } + else + memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), len*ring->frame_size); + + ring->read_pos += len; + ring->read_pos %= ring->length; + + LeaveCriticalSection(&ring->cs); +} diff -r 000000000000 -r f9476ff7637e Alc/alcThread.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/alcThread.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,128 @@ +/** + * 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 + +#include "alMain.h" +#include "alThunk.h" + + +#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, 0, 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 + +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) +{ + ThreadInfo *inf = malloc(sizeof(ThreadInfo)); + if(!inf) return NULL; + + inf->func = func; + inf->ptr = ptr; + if(pthread_create(&inf->thread, NULL, StarterFunc, inf) != 0) + { + free(inf); + return NULL; + } + + 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 -r 000000000000 -r f9476ff7637e Alc/backends/alsa.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/alsa.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,1138 @@ +/** + * 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 +#include +#include + +#include "alMain.h" + +#include + + +static const ALCchar alsaDevice[] = "ALSA Default"; + + +static void *alsa_handle; +#ifdef HAVE_DYNLOAD +#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_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_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); +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); +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_wait); +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_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); +#undef MAKE_FUNC + +#define snd_strerror psnd_strerror +#define snd_pcm_open psnd_pcm_open +#define snd_pcm_close psnd_pcm_close +#define snd_pcm_nonblock psnd_pcm_nonblock +#define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes +#define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames +#define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc +#define snd_pcm_hw_params_free psnd_pcm_hw_params_free +#define snd_pcm_hw_params_any psnd_pcm_hw_params_any +#define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access +#define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format +#define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels +#define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near +#define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near +#define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate +#define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample +#define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near +#define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near +#define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near +#define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near +#define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min +#define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size +#define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size +#define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access +#define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods +#define snd_pcm_hw_params psnd_pcm_hw_params +#define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc +#define snd_pcm_sw_params_current psnd_pcm_sw_params_current +#define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min +#define snd_pcm_sw_params psnd_pcm_sw_params +#define snd_pcm_sw_params_free psnd_pcm_sw_params_free +#define snd_pcm_prepare psnd_pcm_prepare +#define snd_pcm_start psnd_pcm_start +#define snd_pcm_resume psnd_pcm_resume +#define snd_pcm_wait psnd_pcm_wait +#define snd_pcm_state psnd_pcm_state +#define snd_pcm_avail_update psnd_pcm_avail_update +#define snd_pcm_areas_silence psnd_pcm_areas_silence +#define snd_pcm_mmap_begin psnd_pcm_mmap_begin +#define snd_pcm_mmap_commit psnd_pcm_mmap_commit +#define snd_pcm_readi psnd_pcm_readi +#define snd_pcm_writei psnd_pcm_writei +#define snd_pcm_drain psnd_pcm_drain +#define snd_pcm_recover psnd_pcm_recover +#define snd_pcm_info_malloc psnd_pcm_info_malloc +#define snd_pcm_info_free psnd_pcm_info_free +#define snd_pcm_info_set_device psnd_pcm_info_set_device +#define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice +#define snd_pcm_info_set_stream psnd_pcm_info_set_stream +#define snd_pcm_info_get_name psnd_pcm_info_get_name +#define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device +#define snd_ctl_pcm_info psnd_ctl_pcm_info +#define snd_ctl_open psnd_ctl_open +#define snd_ctl_close psnd_ctl_close +#define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc +#define snd_ctl_card_info_free psnd_ctl_card_info_free +#define snd_ctl_card_info psnd_ctl_card_info +#define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name +#define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id +#define snd_card_next psnd_card_next +#endif + + +static ALCboolean alsa_load(void) +{ + if(!alsa_handle) + { +#ifdef HAVE_DYNLOAD + alsa_handle = LoadLib("libasound.so.2"); + if(!alsa_handle) + return 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; \ + } \ +} 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_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_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); + 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); + 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_wait); + 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_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); +#undef LOAD_FUNC +#else + alsa_handle = (void*)0xDEADBEEF; +#endif + } + return ALC_TRUE; +} + + +typedef struct { + snd_pcm_t *pcmHandle; + + ALvoid *buffer; + ALsizei size; + + ALboolean doCapture; + RingBuffer *ring; + + volatile int killNow; + ALvoid *thread; +} alsa_data; + +typedef struct { + ALCchar *name; + char *card; + int dev; +} DevMap; + +static DevMap *allDevNameMap; +static ALuint numDevNames; +static DevMap *allCaptureDevNameMap; +static ALuint numCaptureDevNames; + +static const char *device_prefix; +static const char *capture_prefix; + + +static DevMap *probe_devices(snd_pcm_stream_t stream, ALuint *count) +{ + snd_ctl_t *handle; + int card, err, dev, idx; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + DevMap *DevList; + char name[1024]; + + snd_ctl_card_info_malloc(&info); + snd_pcm_info_malloc(&pcminfo); + + card = -1; + if((err=snd_card_next(&card)) < 0) + ERR("Failed to find a card: %s\n", snd_strerror(err)); + + DevList = malloc(sizeof(DevMap) * 1); + DevList[0].name = strdup("ALSA Default"); + DevList[0].card = NULL; + DevList[0].dev = 0; + idx = 1; + while(card >= 0) + { + sprintf(name, "hw:%d", card); + if((err = snd_ctl_open(&handle, name, 0)) < 0) + { + ERR("control open (%i): %s\n", card, snd_strerror(err)); + goto next_card; + } + if((err = snd_ctl_card_info(handle, info)) < 0) + { + ERR("control hardware info (%i): %s\n", card, snd_strerror(err)); + snd_ctl_close(handle); + goto next_card; + } + + dev = -1; + while(1) + { + const char *cname, *dname, *cid; + void *temp; + + if(snd_ctl_pcm_next_device(handle, &dev) < 0) + ERR("snd_ctl_pcm_next_device failed\n"); + if(dev < 0) + break; + + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { + if(err != -ENOENT) + ERR("control digital audio info (%i): %s\n", card, snd_strerror(err)); + continue; + } + + temp = realloc(DevList, sizeof(DevMap) * (idx+1)); + if(temp) + { + DevList = temp; + cname = snd_ctl_card_info_get_name(info); + dname = snd_pcm_info_get_name(pcminfo); + cid = snd_ctl_card_info_get_id(info); + snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)", + cname, dname, cid, dev); + DevList[idx].name = strdup(name); + DevList[idx].card = strdup(cid); + DevList[idx].dev = dev; + idx++; + } + } + snd_ctl_close(handle); + next_card: + if(snd_card_next(&card) < 0) { + ERR("snd_card_next failed\n"); + break; + } + } + + snd_pcm_info_free(pcminfo); + snd_ctl_card_info_free(info); + + *count = idx; + return DevList; +} + + +static int xrun_recovery(snd_pcm_t *handle, int err) +{ + err = snd_pcm_recover(handle, err, 1); + if(err < 0) + ERR("recover failed: %s\n", snd_strerror(err)); + return err; +} + +static int verify_state(snd_pcm_t *handle) +{ + snd_pcm_state_t state = snd_pcm_state(handle); + if(state == SND_PCM_STATE_DISCONNECTED) + return -ENODEV; + if(state == SND_PCM_STATE_XRUN) + { + int err = xrun_recovery(handle, -EPIPE); + if(err < 0) return err; + } + else if(state == SND_PCM_STATE_SUSPENDED) + { + int err = xrun_recovery(handle, -ESTRPIPE); + if(err < 0) return err; + } + + return state; +} + + +static ALuint ALSAProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + alsa_data *data = (alsa_data*)pDevice->ExtraData; + const snd_pcm_channel_area_t *areas = NULL; + snd_pcm_sframes_t avail, commitres; + snd_pcm_uframes_t offset, frames; + char *WritePtr; + int err; + + SetRTPriority(); + + while(!data->killNow) + { + int state = verify_state(data->pcmHandle); + if(state < 0) + { + ERR("Invalid state detected: %s\n", snd_strerror(state)); + aluHandleDisconnect(pDevice); + break; + } + + avail = snd_pcm_avail_update(data->pcmHandle); + if(avail < 0) + { + ERR("available update failed: %s\n", snd_strerror(avail)); + continue; + } + + // make sure there's frames to process + if((snd_pcm_uframes_t)avail < pDevice->UpdateSize) + { + if(state != SND_PCM_STATE_RUNNING) + { + err = snd_pcm_start(data->pcmHandle); + if(err < 0) + { + ERR("start failed: %s\n", snd_strerror(err)); + continue; + } + } + if(snd_pcm_wait(data->pcmHandle, 1000) == 0) + ERR("Wait timeout... buffer size too low?\n"); + continue; + } + avail -= avail%pDevice->UpdateSize; + + // it is possible that contiguous areas are smaller, thus we use a loop + while(avail > 0) + { + frames = avail; + + err = snd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames); + if(err < 0) + { + ERR("mmap begin error: %s\n", snd_strerror(err)); + break; + } + + WritePtr = (char*)areas->addr + (offset * areas->step / 8); + aluMixData(pDevice, WritePtr, frames); + + commitres = snd_pcm_mmap_commit(data->pcmHandle, offset, frames); + if(commitres < 0 || (commitres-frames) != 0) + { + ERR("mmap commit error: %s\n", + snd_strerror(commitres >= 0 ? -EPIPE : commitres)); + break; + } + + avail -= frames; + } + } + + return 0; +} + +static ALuint ALSANoMMapProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + alsa_data *data = (alsa_data*)pDevice->ExtraData; + snd_pcm_sframes_t avail; + char *WritePtr; + + SetRTPriority(); + + while(!data->killNow) + { + int state = verify_state(data->pcmHandle); + if(state < 0) + { + ERR("Invalid state detected: %s\n", snd_strerror(state)); + aluHandleDisconnect(pDevice); + break; + } + + WritePtr = data->buffer; + avail = data->size / snd_pcm_frames_to_bytes(data->pcmHandle, 1); + aluMixData(pDevice, WritePtr, avail); + + while(avail > 0) + { + int ret = snd_pcm_writei(data->pcmHandle, WritePtr, avail); + switch (ret) + { + case -EAGAIN: + continue; + case -ESTRPIPE: + case -EPIPE: + case -EINTR: + ret = snd_pcm_recover(data->pcmHandle, ret, 1); + if(ret < 0) + avail = 0; + break; + default: + if (ret >= 0) + { + WritePtr += snd_pcm_frames_to_bytes(data->pcmHandle, ret); + avail -= ret; + } + break; + } + if (ret < 0) + { + ret = snd_pcm_prepare(data->pcmHandle); + if(ret < 0) + break; + } + } + } + + return 0; +} + +static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + alsa_data *data; + char driver[128]; + int i; + + strncpy(driver, GetConfigValue("alsa", "device", "default"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + + if(!deviceName) + deviceName = alsaDevice; + else if(strcmp(deviceName, alsaDevice) != 0) + { + size_t idx; + + if(!allDevNameMap) + allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames); + + for(idx = 0;idx < numDevNames;idx++) + { + if(allDevNameMap[idx].name && + strcmp(deviceName, allDevNameMap[idx].name) == 0) + { + if(idx > 0) + snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", device_prefix, + allDevNameMap[idx].card, allDevNameMap[idx].dev); + break; + } + } + if(idx == numDevNames) + return ALC_FALSE; + } + + data = (alsa_data*)calloc(1, sizeof(alsa_data)); + + i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + if(i >= 0) + { + i = snd_pcm_nonblock(data->pcmHandle, 0); + if(i < 0) + snd_pcm_close(data->pcmHandle); + } + if(i < 0) + { + free(data); + ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(i)); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + return ALC_TRUE; +} + +static void alsa_close_playback(ALCdevice *device) +{ + alsa_data *data = (alsa_data*)device->ExtraData; + + snd_pcm_close(data->pcmHandle); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean alsa_reset_playback(ALCdevice *device) +{ + alsa_data *data = (alsa_data*)device->ExtraData; + snd_pcm_uframes_t periodSizeInFrames; + unsigned int periodLen, bufferLen; + snd_pcm_sw_params_t *sp = NULL; + snd_pcm_hw_params_t *p = NULL; + snd_pcm_access_t access; + snd_pcm_format_t format; + unsigned int periods; + unsigned int rate; + int allowmmap; + char *err; + int i; + + + format = -1; + switch(device->FmtType) + { + case DevFmtByte: + format = SND_PCM_FORMAT_S8; + break; + case DevFmtUByte: + format = SND_PCM_FORMAT_U8; + break; + case DevFmtShort: + format = SND_PCM_FORMAT_S16; + break; + case DevFmtUShort: + format = SND_PCM_FORMAT_U16; + break; + case DevFmtFloat: + format = SND_PCM_FORMAT_FLOAT; + break; + } + + allowmmap = GetConfigValueBool("alsa", "mmap", 1); + periods = device->NumUpdates; + periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency; + bufferLen = periodLen * periods; + rate = device->Frequency; + + err = NULL; + snd_pcm_hw_params_malloc(&p); + + if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0) + err = "any"; + /* set interleaved access */ + if(i >= 0 && (!allowmmap || (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0)) + { + if(periods > 2) + { + periods--; + bufferLen = periodLen * periods; + } + if((i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + err = "set access"; + } + /* set format (implicitly sets sample bits) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0) + { + device->FmtType = DevFmtFloat; + if(format == SND_PCM_FORMAT_FLOAT || + (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_FLOAT)) < 0) + { + device->FmtType = DevFmtShort; + if(format == SND_PCM_FORMAT_S16 || + (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_S16)) < 0) + { + device->FmtType = DevFmtUByte; + if(format == SND_PCM_FORMAT_U8 || + (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_U8)) < 0) + err = "set format"; + } + } + } + /* set channels (implicitly sets frame bits) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(device->FmtChans))) < 0) + { + if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 2)) < 0) + { + if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 1)) < 0) + err = "set channels"; + else + { + if((device->Flags&DEVICE_CHANNELS_REQUEST)) + ERR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device->FmtChans)); + device->FmtChans = DevFmtMono; + } + } + else + { + if((device->Flags&DEVICE_CHANNELS_REQUEST)) + ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device->FmtChans)); + device->FmtChans = DevFmtStereo; + } + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + if(i >= 0 && (i=snd_pcm_hw_params_set_rate_resample(data->pcmHandle, p, 0)) < 0) + { + ERR("Failed to disable ALSA resampler\n"); + i = 0; + } + /* set rate (implicitly constrains period/buffer parameters) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &rate, NULL)) < 0) + err = "set rate near"; + /* set buffer time (implicitly constrains period/buffer parameters) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, p, &bufferLen, NULL)) < 0) + err = "set buffer time near"; + /* set period time in frame units (implicitly sets buffer size/bytes/time and period size/bytes) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_period_time_near(data->pcmHandle, p, &periodLen, NULL)) < 0) + err = "set period time near"; + /* install and prepare hardware configuration */ + if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0) + err = "set params"; + if(i >= 0 && (i=snd_pcm_hw_params_get_access(p, &access)) < 0) + err = "get access"; + if(i >= 0 && (i=snd_pcm_hw_params_get_period_size(p, &periodSizeInFrames, NULL)) < 0) + err = "get period size"; + if(i >= 0 && (i=snd_pcm_hw_params_get_periods(p, &periods, NULL)) < 0) + err = "get periods"; + if(i < 0) + { + ERR("%s failed: %s\n", err, snd_strerror(i)); + snd_pcm_hw_params_free(p); + return ALC_FALSE; + } + + snd_pcm_hw_params_free(p); + + err = NULL; + snd_pcm_sw_params_malloc(&sp); + + if((i=snd_pcm_sw_params_current(data->pcmHandle, sp)) != 0) + err = "sw current"; + if(i == 0 && (i=snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)) != 0) + err = "sw set avail min"; + if(i == 0 && (i=snd_pcm_sw_params(data->pcmHandle, sp)) != 0) + err = "sw set params"; + if(i != 0) + { + ERR("%s failed: %s\n", err, snd_strerror(i)); + snd_pcm_sw_params_free(sp); + return ALC_FALSE; + } + + snd_pcm_sw_params_free(sp); + + if(device->Frequency != rate) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, rate); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = rate; + } + + SetDefaultChannelOrder(device); + + data->size = snd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames); + if(access == SND_PCM_ACCESS_RW_INTERLEAVED) + { + /* Increase periods by one, since the temp buffer counts as an extra + * period */ + periods++; + data->buffer = malloc(data->size); + if(!data->buffer) + { + ERR("buffer malloc failed\n"); + return ALC_FALSE; + } + device->UpdateSize = periodSizeInFrames; + device->NumUpdates = periods; + data->thread = StartThread(ALSANoMMapProc, device); + } + else + { + i = snd_pcm_prepare(data->pcmHandle); + if(i < 0) + { + ERR("prepare error: %s\n", snd_strerror(i)); + return ALC_FALSE; + } + device->UpdateSize = periodSizeInFrames; + device->NumUpdates = periods; + data->thread = StartThread(ALSAProc, device); + } + if(data->thread == NULL) + { + ERR("Could not create playback thread\n"); + free(data->buffer); + data->buffer = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void alsa_stop_playback(ALCdevice *device) +{ + alsa_data *data = (alsa_data*)device->ExtraData; + + if(data->thread) + { + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + } + data->killNow = 0; + free(data->buffer); + data->buffer = NULL; +} + + +static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName) +{ + snd_pcm_hw_params_t *p; + snd_pcm_uframes_t bufferSizeInFrames; + snd_pcm_format_t format; + ALuint frameSize; + alsa_data *data; + char driver[128]; + char *err; + int i; + + strncpy(driver, GetConfigValue("alsa", "capture", "default"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + + if(!allCaptureDevNameMap) + allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames); + + if(!deviceName) + deviceName = allCaptureDevNameMap[0].name; + else + { + size_t idx; + + for(idx = 0;idx < numCaptureDevNames;idx++) + { + if(allCaptureDevNameMap[idx].name && + strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0) + { + if(idx > 0) + snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", capture_prefix, + allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev); + break; + } + } + if(idx == numCaptureDevNames) + return ALC_FALSE; + } + + data = (alsa_data*)calloc(1, sizeof(alsa_data)); + + i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + if(i < 0) + { + ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(i)); + free(data); + return ALC_FALSE; + } + + format = -1; + switch(pDevice->FmtType) + { + case DevFmtByte: + format = SND_PCM_FORMAT_S8; + break; + case DevFmtUByte: + format = SND_PCM_FORMAT_U8; + break; + case DevFmtShort: + format = SND_PCM_FORMAT_S16; + break; + case DevFmtUShort: + format = SND_PCM_FORMAT_U16; + break; + case DevFmtFloat: + format = SND_PCM_FORMAT_FLOAT; + break; + } + + err = NULL; + bufferSizeInFrames = pDevice->UpdateSize * pDevice->NumUpdates; + snd_pcm_hw_params_malloc(&p); + + if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0) + err = "any"; + /* set interleaved access */ + if(i >= 0 && (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + err = "set access"; + /* set format (implicitly sets sample bits) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0) + err = "set format"; + /* set channels (implicitly sets frame bits) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(pDevice->FmtChans))) < 0) + err = "set channels"; + /* set rate (implicitly constrains period/buffer parameters) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_rate(data->pcmHandle, p, pDevice->Frequency, 0)) < 0) + err = "set rate near"; + /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */ + if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames)) < 0) + err = "set buffer size near"; + /* install and prepare hardware configuration */ + if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0) + err = "set params"; + if(i < 0) + { + ERR("%s failed: %s\n", err, snd_strerror(i)); + snd_pcm_hw_params_free(p); + goto error; + } + + if((i=snd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0) + { + ERR("get size failed: %s\n", snd_strerror(i)); + snd_pcm_hw_params_free(p); + goto error; + } + + snd_pcm_hw_params_free(p); + + frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + data->ring = CreateRingBuffer(frameSize, pDevice->UpdateSize*pDevice->NumUpdates); + if(!data->ring) + { + ERR("ring buffer create failed\n"); + goto error; + } + + data->size = snd_pcm_frames_to_bytes(data->pcmHandle, bufferSizeInFrames); + data->buffer = malloc(data->size); + if(!data->buffer) + { + ERR("buffer malloc failed\n"); + goto error; + } + + pDevice->szDeviceName = strdup(deviceName); + + pDevice->ExtraData = data; + return ALC_TRUE; + +error: + free(data->buffer); + DestroyRingBuffer(data->ring); + snd_pcm_close(data->pcmHandle); + free(data); + + pDevice->ExtraData = NULL; + return ALC_FALSE; +} + +static void alsa_close_capture(ALCdevice *pDevice) +{ + alsa_data *data = (alsa_data*)pDevice->ExtraData; + + snd_pcm_close(data->pcmHandle); + DestroyRingBuffer(data->ring); + + free(data->buffer); + free(data); + pDevice->ExtraData = NULL; +} + +static void alsa_start_capture(ALCdevice *Device) +{ + alsa_data *data = (alsa_data*)Device->ExtraData; + int err; + + err = snd_pcm_start(data->pcmHandle); + if(err < 0) + { + ERR("start failed: %s\n", snd_strerror(err)); + aluHandleDisconnect(Device); + } + else + data->doCapture = AL_TRUE; +} + +static void alsa_stop_capture(ALCdevice *Device) +{ + alsa_data *data = (alsa_data*)Device->ExtraData; + snd_pcm_drain(data->pcmHandle); + data->doCapture = AL_FALSE; +} + +static ALCuint alsa_available_samples(ALCdevice *Device) +{ + alsa_data *data = (alsa_data*)Device->ExtraData; + snd_pcm_sframes_t avail; + + avail = (Device->Connected ? snd_pcm_avail_update(data->pcmHandle) : 0); + if(avail < 0) + { + ERR("avail update failed: %s\n", snd_strerror(avail)); + + if((avail=snd_pcm_recover(data->pcmHandle, avail, 1)) >= 0) + { + if(data->doCapture) + avail = snd_pcm_start(data->pcmHandle); + if(avail >= 0) + avail = snd_pcm_avail_update(data->pcmHandle); + } + if(avail < 0) + { + ERR("restore error: %s\n", snd_strerror(avail)); + aluHandleDisconnect(Device); + } + } + while(avail > 0) + { + snd_pcm_sframes_t amt; + + amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size); + if(avail < amt) amt = avail; + + amt = snd_pcm_readi(data->pcmHandle, data->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(data->doCapture) + amt = snd_pcm_start(data->pcmHandle); + if(amt >= 0) + amt = snd_pcm_avail_update(data->pcmHandle); + } + if(amt < 0) + { + ERR("restore error: %s\n", snd_strerror(amt)); + aluHandleDisconnect(Device); + break; + } + avail = amt; + continue; + } + + WriteRingBuffer(data->ring, data->buffer, amt); + avail -= amt; + } + + return RingBufferSize(data->ring); +} + +static void alsa_capture_samples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples) +{ + alsa_data *data = (alsa_data*)Device->ExtraData; + + if(Samples <= alsa_available_samples(Device)) + ReadRingBuffer(data->ring, Buffer, Samples); + else + alcSetError(Device, ALC_INVALID_VALUE); +} + + +static const BackendFuncs alsa_funcs = { + alsa_open_playback, + alsa_close_playback, + alsa_reset_playback, + alsa_stop_playback, + alsa_open_capture, + alsa_close_capture, + alsa_start_capture, + alsa_stop_capture, + alsa_capture_samples, + alsa_available_samples +}; + +ALCboolean alc_alsa_init(BackendFuncs *func_list) +{ + if(!alsa_load()) + return ALC_FALSE; + device_prefix = GetConfigValue("alsa", "device-prefix", "plughw:"); + capture_prefix = GetConfigValue("alsa", "capture-prefix", "plughw:"); + *func_list = alsa_funcs; + return ALC_TRUE; +} + +void alc_alsa_deinit(void) +{ + ALuint i; + + for(i = 0;i < numDevNames;++i) + { + free(allDevNameMap[i].name); + free(allDevNameMap[i].card); + } + free(allDevNameMap); + allDevNameMap = NULL; + numDevNames = 0; + + for(i = 0;i < numCaptureDevNames;++i) + { + free(allCaptureDevNameMap[i].name); + free(allCaptureDevNameMap[i].card); + } + free(allCaptureDevNameMap); + allCaptureDevNameMap = NULL; + numCaptureDevNames = 0; + +#ifdef HAVE_DYNLOAD + if(alsa_handle) + CloseLib(alsa_handle); + alsa_handle = NULL; +#endif +} + +void alc_alsa_probe(enum DevProbe type) +{ + ALuint i; + + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(alsaDevice); + break; + + case ALL_DEVICE_PROBE: + for(i = 0;i < numDevNames;++i) + { + free(allDevNameMap[i].name); + free(allDevNameMap[i].card); + } + + free(allDevNameMap); + allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames); + + for(i = 0;i < numDevNames;++i) + AppendAllDeviceList(allDevNameMap[i].name); + break; + + case CAPTURE_DEVICE_PROBE: + for(i = 0;i < numCaptureDevNames;++i) + { + free(allCaptureDevNameMap[i].name); + free(allCaptureDevNameMap[i].card); + } + + free(allCaptureDevNameMap); + allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames); + + for(i = 0;i < numCaptureDevNames;++i) + AppendCaptureDeviceList(allCaptureDevNameMap[i].name); + break; + } +} diff -r 000000000000 -r f9476ff7637e Alc/backends/coreaudio.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/coreaudio.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,719 @@ +/** + * 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 +#include +#include + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include +#include +#include +#include + + +typedef struct { + AudioUnit audioUnit; + + ALuint frameSize; + ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate + AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD + + AudioConverterRef audioConverter; // Sample rate converter if needed + AudioBufferList *bufferList; // Buffer for data coming from the input device + ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling + + RingBuffer *ring; +} ca_data; + +static const ALCchar ca_device[] = "CoreAudio Default"; + + +static void destroy_buffer_list(AudioBufferList* list) +{ + if(list) + { + UInt32 i; + for(i = 0;i < list->mNumberBuffers;i++) + free(list->mBuffers[i].mData); + free(list); + } +} + +static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize) +{ + AudioBufferList *list; + + list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer)); + if(list) + { + list->mNumberBuffers = 1; + + list->mBuffers[0].mNumberChannels = channelCount; + list->mBuffers[0].mDataByteSize = byteSize; + list->mBuffers[0].mData = malloc(byteSize); + if(list->mBuffers[0].mData == NULL) + { + free(list); + list = NULL; + } + } + return list; +} + +static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) +{ + ALCdevice *device = (ALCdevice*)inRefCon; + ca_data *data = (ca_data*)device->ExtraData; + + aluMixData(device, ioData->mBuffers[0].mData, + ioData->mBuffers[0].mDataByteSize / data->frameSize); + + return noErr; +} + +static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, + AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData) +{ + ALCdevice *device = (ALCdevice*)inUserData; + ca_data *data = (ca_data*)device->ExtraData; + + // Read from the ring buffer and store temporarily in a large buffer + ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets)); + + // Set the input data + ioData->mNumberBuffers = 1; + ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame; + ioData->mBuffers[0].mData = data->resampleBuffer; + ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame; + + return noErr; +} + +static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, + UInt32 inNumberFrames, AudioBufferList *ioData) +{ + ALCdevice *device = (ALCdevice*)inRefCon; + ca_data *data = (ca_data*)device->ExtraData; + AudioUnitRenderActionFlags flags = 0; + OSStatus err; + + // fill the bufferList with data from the input device + err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList); + if(err != noErr) + { + ERR("AudioUnitRender error: %d\n", err); + return err; + } + + WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames); + + return noErr; +} + +static ALCboolean ca_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + ComponentDescription desc; + Component comp; + ca_data *data; + OSStatus err; + + if(!deviceName) + deviceName = ca_device; + else if(strcmp(deviceName, ca_device) != 0) + return ALC_FALSE; + + /* open the default output unit */ + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + comp = FindNextComponent(NULL, &desc); + if(comp == NULL) + { + ERR("FindNextComponent failed\n"); + return ALC_FALSE; + } + + data = calloc(1, sizeof(*data)); + device->ExtraData = data; + + err = OpenAComponent(comp, &data->audioUnit); + if(err != noErr) + { + ERR("OpenAComponent failed\n"); + free(data); + device->ExtraData = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void ca_close_playback(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + + CloseComponent(data->audioUnit); + + free(data); + device->ExtraData = NULL; +} + +static ALCboolean ca_reset_playback(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + AudioStreamBasicDescription streamFormat; + AURenderCallbackStruct input; + OSStatus err; + UInt32 size; + + /* init and start the default audio unit... */ + err = AudioUnitInitialize(data->audioUnit); + if(err != noErr) + { + ERR("AudioUnitInitialize failed\n"); + return ALC_FALSE; + } + + err = AudioOutputUnitStart(data->audioUnit); + if(err != noErr) + { + ERR("AudioOutputUnitStart failed\n"); + return ALC_FALSE; + } + + /* retrieve default output unit's properties (output side) */ + size = sizeof(AudioStreamBasicDescription); + err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size); + if(err != noErr || size != sizeof(AudioStreamBasicDescription)) + { + ERR("AudioUnitGetProperty failed\n"); + return ALC_FALSE; + } + +#if 0 + TRACE("Output streamFormat of default output unit -\n"); + TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket); + TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame); + TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel); + TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket); + TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame); + TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate); +#endif + + /* set default output unit's input side to match output side */ + err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_FALSE; + } + + if(device->Frequency != streamFormat.mSampleRate) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("CoreAudio does not support changing sample rates (wanted %dhz, got %dhz)\n", device->Frequency, streamFormat.mSampleRate); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + + device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize * + streamFormat.mSampleRate / + device->Frequency); + device->Frequency = streamFormat.mSampleRate; + } + + /* FIXME: How to tell what channels are what in the output device, and how + * to specify what we're giving? eg, 6.0 vs 5.1 */ + switch(streamFormat.mChannelsPerFrame) + { + case 1: + if((device->Flags&DEVICE_CHANNELS_REQUEST) && + device->FmtChans != DevFmtMono) + { + ERR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + device->FmtChans = DevFmtMono; + break; + case 2: + if((device->Flags&DEVICE_CHANNELS_REQUEST) && + device->FmtChans != DevFmtStereo) + { + ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + device->FmtChans = DevFmtStereo; + break; + case 4: + if((device->Flags&DEVICE_CHANNELS_REQUEST) && + device->FmtChans != DevFmtQuad) + { + ERR("Failed to set %s, got Quad instead\n", DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + device->FmtChans = DevFmtQuad; + break; + case 6: + if((device->Flags&DEVICE_CHANNELS_REQUEST) && + device->FmtChans != DevFmtX51) + { + ERR("Failed to set %s, got 5.1 Surround instead\n", DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + device->FmtChans = DevFmtX51; + break; + case 7: + if((device->Flags&DEVICE_CHANNELS_REQUEST) && + device->FmtChans != DevFmtX61) + { + ERR("Failed to set %s, got 6.1 Surround instead\n", DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + device->FmtChans = DevFmtX61; + break; + case 8: + if((device->Flags&DEVICE_CHANNELS_REQUEST) && + device->FmtChans != DevFmtX71) + { + ERR("Failed to set %s, got 7.1 Surround instead\n", DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + device->FmtChans = DevFmtX71; + break; + default: + ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + device->FmtChans = DevFmtStereo; + streamFormat.mChannelsPerFrame = 2; + break; + } + SetDefaultWFXChannelOrder(device); + + /* use channel count and sample rate from the default output unit's current + * parameters, but reset everything else */ + streamFormat.mFramesPerPacket = 1; + switch(device->FmtType) + { + case DevFmtUByte: + device->FmtType = DevFmtByte; + /* fall-through */ + case DevFmtByte: + streamFormat.mBitsPerChannel = 8; + streamFormat.mBytesPerPacket = streamFormat.mChannelsPerFrame; + streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame; + break; + case DevFmtUShort: + case DevFmtFloat: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + streamFormat.mBitsPerChannel = 16; + streamFormat.mBytesPerPacket = 2 * streamFormat.mChannelsPerFrame; + streamFormat.mBytesPerFrame = 2 * streamFormat.mChannelsPerFrame; + break; + } + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | + kAudioFormatFlagsNativeEndian | + kLinearPCMFormatFlagIsPacked; + + err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_FALSE; + } + + /* setup callback */ + data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + input.inputProc = ca_callback; + input.inputProcRefCon = device; + + err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void ca_stop_playback(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + OSStatus err; + + AudioOutputUnitStop(data->audioUnit); + err = AudioUnitUninitialize(data->audioUnit); + if(err != noErr) + ERR("-- AudioUnitUninitialize failed.\n"); +} + +static ALCboolean ca_open_capture(ALCdevice *device, const ALCchar *deviceName) +{ + AudioStreamBasicDescription requestedFormat; // The application requested format + AudioStreamBasicDescription hardwareFormat; // The hardware format + AudioStreamBasicDescription outputFormat; // The AudioUnit output format + AURenderCallbackStruct input; + ComponentDescription desc; + AudioDeviceID inputDevice; + UInt32 outputFrameCount; + UInt32 propertySize; + UInt32 enableIO; + Component comp; + ca_data *data; + OSStatus err; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_HALOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + // Search for component with given description + comp = FindNextComponent(NULL, &desc); + if(comp == NULL) + { + ERR("FindNextComponent failed\n"); + return ALC_FALSE; + } + + data = calloc(1, sizeof(*data)); + device->ExtraData = data; + + // Open the component + err = OpenAComponent(comp, &data->audioUnit); + if(err != noErr) + { + ERR("OpenAComponent failed\n"); + goto error; + } + + // Turn off AudioUnit output + enableIO = 0; + err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + goto error; + } + + // Turn on AudioUnit input + enableIO = 1; + err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + goto error; + } + + // Get the default input device + propertySize = sizeof(AudioDeviceID); + err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize, &inputDevice); + if(err != noErr) + { + ERR("AudioHardwareGetProperty failed\n"); + goto error; + } + + if(inputDevice == kAudioDeviceUnknown) + { + ERR("No input device found\n"); + goto error; + } + + // Track the input device + err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + goto error; + } + + // set capture callback + input.inputProc = ca_capture_callback; + input.inputProcRefCon = device; + + err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + goto error; + } + + // Initialize the device + err = AudioUnitInitialize(data->audioUnit); + if(err != noErr) + { + ERR("AudioUnitInitialize failed\n"); + goto error; + } + + // Get the hardware format + propertySize = sizeof(AudioStreamBasicDescription); + err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize); + if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription)) + { + ERR("AudioUnitGetProperty failed\n"); + goto error; + } + + // Set up the requested format description + switch(device->FmtType) + { + case DevFmtUByte: + requestedFormat.mBitsPerChannel = 8; + requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked; + break; + case DevFmtShort: + requestedFormat.mBitsPerChannel = 16; + requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; + break; + case DevFmtFloat: + requestedFormat.mBitsPerChannel = 32; + requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked; + break; + case DevFmtByte: + case DevFmtUShort: + ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType)); + goto error; + } + + switch(device->FmtChans) + { + case DevFmtMono: + requestedFormat.mChannelsPerFrame = 1; + break; + case DevFmtStereo: + requestedFormat.mChannelsPerFrame = 2; + break; + + case DevFmtQuad: + case DevFmtX51: + case DevFmtX51Side: + case DevFmtX61: + case DevFmtX71: + ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans)); + goto error; + } + + requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8; + requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame; + requestedFormat.mSampleRate = device->Frequency; + requestedFormat.mFormatID = kAudioFormatLinearPCM; + requestedFormat.mReserved = 0; + requestedFormat.mFramesPerPacket = 1; + + // save requested format description for later use + data->format = requestedFormat; + data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + // Use intermediate format for sample rate conversion (outputFormat) + // Set sample rate to the same as hardware for resampling later + outputFormat = requestedFormat; + outputFormat.mSampleRate = hardwareFormat.mSampleRate; + + // Determine sample rate ratio for resampling + data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency; + + // The output format should be the requested format, but using the hardware sample rate + // This is because the AudioUnit will automatically scale other properties, except for sample rate + err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + goto error; + } + + // Set the AudioUnit output format frame count + outputFrameCount = device->UpdateSize * data->sampleRateRatio; + err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed: %d\n", err); + goto error; + } + + // Set up sample converter + err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter); + if(err != noErr) + { + ERR("AudioConverterNew failed: %d\n", err); + goto error; + } + + // Create a buffer for use in the resample callback + data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio); + + // Allocate buffer for the AudioUnit output + data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio); + if(data->bufferList == NULL) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + goto error; + } + + data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates); + if(data->ring == NULL) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + goto error; + } + + return ALC_TRUE; + +error: + DestroyRingBuffer(data->ring); + free(data->resampleBuffer); + destroy_buffer_list(data->bufferList); + + if(data->audioConverter) + AudioConverterDispose(data->audioConverter); + if(data->audioUnit) + CloseComponent(data->audioUnit); + + free(data); + device->ExtraData = NULL; + + return ALC_FALSE; +} + +static void ca_close_capture(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + + DestroyRingBuffer(data->ring); + free(data->resampleBuffer); + destroy_buffer_list(data->bufferList); + + AudioConverterDispose(data->audioConverter); + CloseComponent(data->audioUnit); + + free(data); + device->ExtraData = NULL; +} + +static void ca_start_capture(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + OSStatus err = AudioOutputUnitStart(data->audioUnit); + if(err != noErr) + ERR("AudioOutputUnitStart failed\n"); +} + +static void ca_stop_capture(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + OSStatus err = AudioOutputUnitStop(data->audioUnit); + if(err != noErr) + ERR("AudioOutputUnitStop failed\n"); +} + +static ALCuint ca_available_samples(ALCdevice *device) +{ + ca_data *data = device->ExtraData; + return RingBufferSize(data->ring) / data->sampleRateRatio; +} + +static void ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) +{ + ca_data *data = (ca_data*)device->ExtraData; + + if(samples <= ca_available_samples(device)) + { + AudioBufferList *list; + UInt32 frameCount; + OSStatus err; + + // If no samples are requested, just return + if(samples == 0) + return; + + // Allocate a temporary AudioBufferList to use as the return resamples data + list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer)); + + // Point the resampling buffer to the capture buffer + list->mNumberBuffers = 1; + list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame; + list->mBuffers[0].mDataByteSize = samples * data->frameSize; + list->mBuffers[0].mData = buffer; + + // Resample into another AudioBufferList + frameCount = samples; + err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback, device, + &frameCount, list, NULL); + if(err != noErr) + { + ERR("AudioConverterFillComplexBuffer error: %d\n", err); + alcSetError(device, ALC_INVALID_VALUE); + } + } + else + alcSetError(device, ALC_INVALID_VALUE); +} + +static const BackendFuncs ca_funcs = { + ca_open_playback, + ca_close_playback, + ca_reset_playback, + ca_stop_playback, + ca_open_capture, + ca_close_capture, + ca_start_capture, + ca_stop_capture, + ca_capture_samples, + ca_available_samples +}; + +ALCboolean alc_ca_init(BackendFuncs *func_list) +{ + *func_list = ca_funcs; + return ALC_TRUE; +} + +void alc_ca_deinit(void) +{ +} + +void alc_ca_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(ca_device); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(ca_device); + break; + case CAPTURE_DEVICE_PROBE: + AppendCaptureDeviceList(ca_device); + break; + } +} diff -r 000000000000 -r f9476ff7637e Alc/backends/dsound.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/dsound.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,614 @@ +/** + * 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" + +#define _WIN32_WINNT 0x0500 +#include +#include +#include + +#include +#include +#include +#ifndef _WAVEFORMATEXTENSIBLE_ +#include +#include +#endif + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#ifndef DSSPEAKER_5POINT1 +#define DSSPEAKER_5POINT1 6 +#endif +#ifndef DSSPEAKER_7POINT1 +#define DSSPEAKER_7POINT1 7 +#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); + + +static HMODULE ds_handle; +static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter); +static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext); + +#define DirectSoundCreate pDirectSoundCreate +#define DirectSoundEnumerateA pDirectSoundEnumerateA + + +typedef struct { + // DirectSound Playback Device + IDirectSound *lpDS; + IDirectSoundBuffer *DSpbuffer; + IDirectSoundBuffer *DSsbuffer; + IDirectSoundNotify *DSnotify; + HANDLE hNotifyEvent; + + volatile int killNow; + ALvoid *thread; +} DSoundData; + + +typedef struct { + ALCchar *name; + GUID guid; +} DevMap; + +static const ALCchar dsDevice[] = "DirectSound Default"; +static DevMap *DeviceList; +static ALuint NumDevices; + +#define MAX_UPDATES 128 + +static ALCboolean DSoundLoad(void) +{ + ALCboolean ok = ALC_TRUE; + if(!ds_handle) + { + ds_handle = LoadLibraryA("dsound.dll"); + if(ds_handle == NULL) + { + ERR("Failed to load dsound.dll\n"); + return ALC_FALSE; + } + +#define LOAD_FUNC(x) do { \ + if((p##x = (void*)GetProcAddress(ds_handle, #x)) == NULL) { \ + ERR("Could not load %s from dsound.dll\n", #x); \ + ok = ALC_FALSE; \ + } \ +} while(0) + LOAD_FUNC(DirectSoundCreate); + LOAD_FUNC(DirectSoundEnumerateA); +#undef LOAD_FUNC + + if(!ok) + { + FreeLibrary(ds_handle); + ds_handle = NULL; + } + } + return ok; +} + + +static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data) +{ + char str[1024]; + void *temp; + int count; + ALuint i; + + (void)data; + (void)drvname; + + if(!guid) + return TRUE; + + count = 0; + do { + if(count == 0) + snprintf(str, sizeof(str), "%s", desc); + else + snprintf(str, sizeof(str), "%s #%d", desc, count+1); + count++; + + for(i = 0;i < NumDevices;i++) + { + if(strcmp(str, DeviceList[i].name) == 0) + break; + } + } while(i != NumDevices); + + temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1)); + if(temp) + { + DeviceList = temp; + DeviceList[NumDevices].name = strdup(str); + DeviceList[NumDevices].guid = *guid; + NumDevices++; + } + + return TRUE; +} + + +static ALuint DSoundProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + DSoundData *pData = (DSoundData*)pDevice->ExtraData; + DSBCAPS DSBCaps; + DWORD LastCursor = 0; + DWORD PlayCursor; + VOID *WritePtr1, *WritePtr2; + DWORD WriteCnt1, WriteCnt2; + BOOL Playing = FALSE; + DWORD FrameSize; + DWORD FragSize; + DWORD avail; + HRESULT err; + + SetRTPriority(); + + memset(&DSBCaps, 0, sizeof(DSBCaps)); + DSBCaps.dwSize = sizeof(DSBCaps); + err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps); + if(FAILED(err)) + { + ERR("Failed to get buffer caps: 0x%lx\n", err); + aluHandleDisconnect(pDevice); + return 1; + } + + FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + FragSize = pDevice->UpdateSize * FrameSize; + + IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL); + while(!pData->killNow) + { + // Get current play cursor + IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL); + avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes; + + if(avail < FragSize) + { + if(!Playing) + { + err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING); + if(FAILED(err)) + { + ERR("Failed to play buffer: 0x%lx\n", err); + aluHandleDisconnect(pDevice); + return 1; + } + Playing = TRUE; + } + + avail = WaitForSingleObjectEx(pData->hNotifyEvent, 2000, FALSE); + if(avail != WAIT_OBJECT_0) + ERR("WaitForSingleObjectEx error: 0x%lx\n", avail); + continue; + } + avail -= avail%FragSize; + + // Lock output buffer + WriteCnt1 = 0; + WriteCnt2 = 0; + err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); + + // If the buffer is lost, restore it and lock + if(err == DSERR_BUFFERLOST) + { + WARN("Buffer lost, restoring...\n"); + err = IDirectSoundBuffer_Restore(pData->DSsbuffer); + if(SUCCEEDED(err)) + { + Playing = FALSE; + LastCursor = 0; + err = IDirectSoundBuffer_Lock(pData->DSsbuffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); + } + } + + // Successfully locked the output buffer + if(SUCCEEDED(err)) + { + // If we have an active context, mix data directly into output buffer otherwise fill with silence + aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize); + aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize); + + // Unlock output buffer only when successfully locked + IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2); + } + else + { + ERR("Buffer lock error: %#lx\n", err); + aluHandleDisconnect(pDevice); + return 1; + } + + // Update old write cursor location + LastCursor += WriteCnt1+WriteCnt2; + LastCursor %= DSBCaps.dwBufferBytes; + } + + return 0; +} + +static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName) +{ + DSoundData *pData = NULL; + LPGUID guid = NULL; + HRESULT hr; + + if(!deviceName) + deviceName = dsDevice; + else if(strcmp(deviceName, dsDevice) != 0) + { + ALuint i; + + if(!DeviceList) + { + hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL); + if(FAILED(hr)) + ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr); + } + + for(i = 0;i < NumDevices;i++) + { + if(strcmp(deviceName, DeviceList[i].name) == 0) + { + guid = &DeviceList[i].guid; + break; + } + } + if(i == NumDevices) + return ALC_FALSE; + } + + //Initialise requested device + pData = calloc(1, sizeof(DSoundData)); + if(!pData) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + + hr = DS_OK; + pData->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(pData->hNotifyEvent == NULL) + hr = E_FAIL; + + //DirectSound Init code + if(SUCCEEDED(hr)) + hr = DirectSoundCreate(guid, &pData->lpDS, NULL); + if(SUCCEEDED(hr)) + hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY); + if(FAILED(hr)) + { + if(pData->lpDS) + IDirectSound_Release(pData->lpDS); + if(pData->hNotifyEvent) + CloseHandle(pData->hNotifyEvent); + free(pData); + ERR("Device init failed: 0x%08lx\n", hr); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + device->ExtraData = pData; + return ALC_TRUE; +} + +static void DSoundClosePlayback(ALCdevice *device) +{ + DSoundData *pData = device->ExtraData; + + IDirectSound_Release(pData->lpDS); + CloseHandle(pData->hNotifyEvent); + free(pData); + device->ExtraData = NULL; +} + +static ALCboolean DSoundResetPlayback(ALCdevice *device) +{ + DSoundData *pData = (DSoundData*)device->ExtraData; + DSBUFFERDESC DSBDescription; + WAVEFORMATEXTENSIBLE OutputType; + DWORD speakers; + HRESULT hr; + + memset(&OutputType, 0, sizeof(OutputType)); + + switch(device->FmtType) + { + case DevFmtByte: + device->FmtType = DevFmtUByte; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + break; + case DevFmtUByte: + case DevFmtShort: + case DevFmtFloat: + break; + } + + hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers); + if(SUCCEEDED(hr)) + { + if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) + { + speakers = DSSPEAKER_CONFIG(speakers); + if(speakers == DSSPEAKER_MONO) + device->FmtChans = DevFmtMono; + else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE) + device->FmtChans = DevFmtStereo; + else if(speakers == DSSPEAKER_QUAD) + device->FmtChans = DevFmtQuad; + else if(speakers == DSSPEAKER_5POINT1) + device->FmtChans = DevFmtX51; + else if(speakers == DSSPEAKER_7POINT1) + device->FmtChans = DevFmtX71; + else + ERR("Unknown system speaker config: 0x%lx\n", speakers); + } + + switch(device->FmtChans) + { + case DevFmtMono: + OutputType.dwChannelMask = SPEAKER_FRONT_CENTER; + break; + case DevFmtStereo: + OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT; + break; + case DevFmtQuad: + OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT; + break; + case DevFmtX51: + OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT; + break; + case DevFmtX51Side: + OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT; + break; + case DevFmtX61: + OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_CENTER | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT; + break; + case DevFmtX71: + OutputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT; + break; + } + + OutputType.Format.wFormatTag = WAVE_FORMAT_PCM; + OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans); + OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8; + OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8; + OutputType.Format.nSamplesPerSec = device->Frequency; + OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign; + OutputType.Format.cbSize = 0; + } + + if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat) + { + OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + if(device->FmtType == DevFmtFloat) + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + else + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + } + else + { + if(SUCCEEDED(hr)) + { + memset(&DSBDescription,0,sizeof(DSBUFFERDESC)); + DSBDescription.dwSize=sizeof(DSBUFFERDESC); + DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER; + hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL); + } + if(SUCCEEDED(hr)) + hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format); + } + + if(SUCCEEDED(hr)) + { + if(device->NumUpdates > MAX_UPDATES) + { + device->UpdateSize = (device->UpdateSize*device->NumUpdates + + MAX_UPDATES-1) / MAX_UPDATES; + device->NumUpdates = MAX_UPDATES; + } + + memset(&DSBDescription,0,sizeof(DSBUFFERDESC)); + DSBDescription.dwSize=sizeof(DSBUFFERDESC); + DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS; + DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates * + OutputType.Format.nBlockAlign; + DSBDescription.lpwfxFormat=&OutputType.Format; + hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL); + } + + if(SUCCEEDED(hr)) + { + hr = IDirectSoundBuffer_QueryInterface(pData->DSsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&pData->DSnotify); + if(SUCCEEDED(hr)) + { + DSBPOSITIONNOTIFY notifies[MAX_UPDATES]; + ALuint i; + + for(i = 0;i < device->NumUpdates;++i) + { + notifies[i].dwOffset = i * device->UpdateSize * + OutputType.Format.nBlockAlign; + notifies[i].hEventNotify = pData->hNotifyEvent; + } + if(IDirectSoundNotify_SetNotificationPositions(pData->DSnotify, device->NumUpdates, notifies) != DS_OK) + hr = E_FAIL; + } + } + + if(SUCCEEDED(hr)) + { + ResetEvent(pData->hNotifyEvent); + SetDefaultWFXChannelOrder(device); + pData->thread = StartThread(DSoundProc, device); + if(pData->thread == NULL) + hr = E_FAIL; + } + + if(FAILED(hr)) + { + if(pData->DSnotify != NULL) + IDirectSoundNotify_Release(pData->DSnotify); + pData->DSnotify = NULL; + if(pData->DSsbuffer != NULL) + IDirectSoundBuffer_Release(pData->DSsbuffer); + pData->DSsbuffer = NULL; + if(pData->DSpbuffer != NULL) + IDirectSoundBuffer_Release(pData->DSpbuffer); + pData->DSpbuffer = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void DSoundStopPlayback(ALCdevice *device) +{ + DSoundData *pData = device->ExtraData; + + if(!pData->thread) + return; + + pData->killNow = 1; + StopThread(pData->thread); + pData->thread = NULL; + + pData->killNow = 0; + + IDirectSoundNotify_Release(pData->DSnotify); + pData->DSnotify = NULL; + IDirectSoundBuffer_Release(pData->DSsbuffer); + pData->DSsbuffer = NULL; + if(pData->DSpbuffer != NULL) + IDirectSoundBuffer_Release(pData->DSpbuffer); + pData->DSpbuffer = NULL; +} + + +static const BackendFuncs DSoundFuncs = { + DSoundOpenPlayback, + DSoundClosePlayback, + DSoundResetPlayback, + DSoundStopPlayback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + + +ALCboolean alcDSoundInit(BackendFuncs *FuncList) +{ + if(!DSoundLoad()) + return ALC_FALSE; + *FuncList = DSoundFuncs; + return ALC_TRUE; +} + +void alcDSoundDeinit(void) +{ + ALuint i; + + for(i = 0;i < NumDevices;++i) + free(DeviceList[i].name); + free(DeviceList); + DeviceList = NULL; + NumDevices = 0; + + if(ds_handle) + FreeLibrary(ds_handle); + ds_handle = NULL; +} + +void alcDSoundProbe(enum DevProbe type) +{ + HRESULT hr; + ALuint i; + + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(dsDevice); + break; + + case ALL_DEVICE_PROBE: + for(i = 0;i < NumDevices;++i) + free(DeviceList[i].name); + free(DeviceList); + DeviceList = NULL; + NumDevices = 0; + + hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL); + if(FAILED(hr)) + ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr); + else + { + for(i = 0;i < NumDevices;i++) + AppendAllDeviceList(DeviceList[i].name); + } + break; + + case CAPTURE_DEVICE_PROBE: + break; + } +} diff -r 000000000000 -r f9476ff7637e Alc/backends/loopback.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/loopback.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,77 @@ +/** + * 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 +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +static ALCboolean loopback_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + device->szDeviceName = strdup(deviceName); + return ALC_TRUE; +} + +static void loopback_close_playback(ALCdevice *device) +{ + (void)device; +} + +static ALCboolean loopback_reset_playback(ALCdevice *device) +{ + SetDefaultWFXChannelOrder(device); + return ALC_TRUE; +} + +static void loopback_stop_playback(ALCdevice *device) +{ + (void)device; +} + +static const BackendFuncs loopback_funcs = { + loopback_open_playback, + loopback_close_playback, + loopback_reset_playback, + loopback_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +ALCboolean alc_loopback_init(BackendFuncs *func_list) +{ + *func_list = loopback_funcs; + return ALC_TRUE; +} + +void alc_loopback_deinit(void) +{ +} + +void alc_loopback_probe(enum DevProbe type) +{ + (void)type; +} diff -r 000000000000 -r f9476ff7637e Alc/backends/mmdevapi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/mmdevapi.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,775 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2011 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" + +#define COBJMACROS +#define _WIN32_WINNT 0x0500 +#include +#include +#include + +#include +#include +#include +#include +#ifndef _WAVEFORMATEXTENSIBLE_ +#include +#include +#endif + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +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); + +#define MONO SPEAKER_FRONT_CENTER +#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT) +#define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT) +#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT) +#define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) +#define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) +#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) + + +typedef struct { + IMMDevice *mmdev; + IAudioClient *client; + HANDLE hNotifyEvent; + + HANDLE MsgEvent; + + volatile int killNow; + ALvoid *thread; +} MMDevApiData; + + +static const ALCchar mmDevice[] = "WASAPI Default"; + + +static HANDLE ThreadHdl; +static DWORD ThreadID; + +typedef struct { + HANDLE FinishedEvt; + HRESULT result; +} ThreadRequest; + +#define WM_USER_OpenDevice (WM_USER+0) +#define WM_USER_ResetDevice (WM_USER+1) +#define WM_USER_StopDevice (WM_USER+2) +#define WM_USER_CloseDevice (WM_USER+3) + +static HRESULT WaitForResponse(ThreadRequest *req) +{ + if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0) + return req->result; + ERR("Message response error: %lu\n", GetLastError()); + return E_FAIL; +} + + +static ALuint MMDevApiProc(ALvoid *ptr) +{ + ALCdevice *device = ptr; + MMDevApiData *data = device->ExtraData; + union { + IAudioRenderClient *iface; + void *ptr; + } render; + UINT32 written, len; + BYTE *buffer; + HRESULT hr; + + hr = CoInitialize(NULL); + if(FAILED(hr)) + { + ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr); + aluHandleDisconnect(device); + return 0; + } + + hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr); + if(FAILED(hr)) + { + ERR("Failed to get AudioRenderClient service: 0x%08lx\n", hr); + aluHandleDisconnect(device); + return 0; + } + + SetRTPriority(); + + while(!data->killNow) + { + hr = IAudioClient_GetCurrentPadding(data->client, &written); + if(FAILED(hr)) + { + ERR("Failed to get padding: 0x%08lx\n", hr); + aluHandleDisconnect(device); + break; + } + + len = device->UpdateSize*device->NumUpdates - written; + if(len < device->UpdateSize) + { + DWORD res; + res = WaitForSingleObjectEx(data->hNotifyEvent, 2000, FALSE); + if(res != WAIT_OBJECT_0) + ERR("WaitForSingleObjectEx error: 0x%lx\n", res); + continue; + } + len -= len%device->UpdateSize; + + hr = IAudioRenderClient_GetBuffer(render.iface, len, &buffer); + if(SUCCEEDED(hr)) + { + aluMixData(device, buffer, len); + hr = IAudioRenderClient_ReleaseBuffer(render.iface, len, 0); + } + if(FAILED(hr)) + { + ERR("Failed to buffer data: 0x%08lx\n", hr); + aluHandleDisconnect(device); + break; + } + } + + IAudioRenderClient_Release(render.iface); + + CoUninitialize(); + return 0; +} + + +static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) +{ + memset(out, 0, sizeof(*out)); + if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + *out = *(WAVEFORMATEXTENSIBLE*)in; + else if(in->wFormatTag == WAVE_FORMAT_PCM) + { + out->Format = *in; + out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + out->Format.cbSize = sizeof(*out) - sizeof(*in); + if(out->Format.nChannels == 1) + out->dwChannelMask = MONO; + else if(out->Format.nChannels == 2) + out->dwChannelMask = STEREO; + else + ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels); + out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + } + else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) + { + out->Format = *in; + out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + out->Format.cbSize = sizeof(*out) - sizeof(*in); + if(out->Format.nChannels == 1) + out->dwChannelMask = MONO; + else if(out->Format.nChannels == 2) + out->dwChannelMask = STEREO; + else + ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels); + out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } + else + { + ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag); + return ALC_FALSE; + } + return ALC_TRUE; +} + +static HRESULT DoReset(ALCdevice *device) +{ + MMDevApiData *data = device->ExtraData; + WAVEFORMATEXTENSIBLE OutputType; + WAVEFORMATEX *wfx = NULL; + REFERENCE_TIME min_per; + UINT32 buffer_len, min_len; + HRESULT hr; + + hr = IAudioClient_GetMixFormat(data->client, &wfx); + if(FAILED(hr)) + { + ERR("Failed to get mix format: 0x%08lx\n", hr); + return hr; + } + + if(!MakeExtensible(&OutputType, wfx)) + { + CoTaskMemFree(wfx); + return E_FAIL; + } + CoTaskMemFree(wfx); + wfx = NULL; + + if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) + device->Frequency = OutputType.Format.nSamplesPerSec; + if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) + { + if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) + device->FmtChans = DevFmtMono; + else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) + device->FmtChans = DevFmtStereo; + else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) + device->FmtChans = DevFmtQuad; + else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) + device->FmtChans = DevFmtX51; + else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) + device->FmtChans = DevFmtX51Side; + else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) + device->FmtChans = DevFmtX61; + else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1) + device->FmtChans = DevFmtX71; + else + ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask); + } + + switch(device->FmtChans) + { + case DevFmtMono: + OutputType.Format.nChannels = 1; + OutputType.dwChannelMask = MONO; + break; + case DevFmtStereo: + OutputType.Format.nChannels = 2; + OutputType.dwChannelMask = STEREO; + break; + case DevFmtQuad: + OutputType.Format.nChannels = 4; + OutputType.dwChannelMask = QUAD; + break; + case DevFmtX51: + OutputType.Format.nChannels = 6; + OutputType.dwChannelMask = X5DOT1; + break; + case DevFmtX51Side: + OutputType.Format.nChannels = 6; + OutputType.dwChannelMask = X5DOT1SIDE; + break; + case DevFmtX61: + OutputType.Format.nChannels = 7; + OutputType.dwChannelMask = X6DOT1; + break; + case DevFmtX71: + OutputType.Format.nChannels = 8; + OutputType.dwChannelMask = X7DOT1; + break; + } + switch(device->FmtType) + { + case DevFmtByte: + device->FmtType = DevFmtUByte; + /* fall-through */ + case DevFmtUByte: + OutputType.Format.wBitsPerSample = 8; + OutputType.Samples.wValidBitsPerSample = 8; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + OutputType.Format.wBitsPerSample = 16; + OutputType.Samples.wValidBitsPerSample = 16; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; + case DevFmtFloat: + OutputType.Format.wBitsPerSample = 32; + OutputType.Samples.wValidBitsPerSample = 32; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + break; + } + OutputType.Format.nSamplesPerSec = device->Frequency; + + OutputType.Format.nBlockAlign = OutputType.Format.nChannels * + OutputType.Format.wBitsPerSample / 8; + OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec * + OutputType.Format.nBlockAlign; + + hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx); + if(FAILED(hr)) + { + ERR("Failed to check format support: 0x%08lx\n", hr); + hr = IAudioClient_GetMixFormat(data->client, &wfx); + } + if(FAILED(hr)) + { + ERR("Failed to find a supported format: 0x%08lx\n", hr); + return hr; + } + + if(wfx != NULL) + { + if(!MakeExtensible(&OutputType, wfx)) + { + CoTaskMemFree(wfx); + return E_FAIL; + } + CoTaskMemFree(wfx); + wfx = NULL; + + if(device->Frequency != OutputType.Format.nSamplesPerSec) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("Failed to set %dhz, got %ldhz instead\n", device->Frequency, OutputType.Format.nSamplesPerSec); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = OutputType.Format.nSamplesPerSec; + } + + if(!((device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) || + (device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) || + (device->FmtChans == DevFmtQuad && OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) || + (device->FmtChans == DevFmtX51 && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) || + (device->FmtChans == DevFmtX51Side && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) || + (device->FmtChans == DevFmtX61 && OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) || + (device->FmtChans == DevFmtX71 && OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1))) + { + if((device->Flags&DEVICE_CHANNELS_REQUEST)) + ERR("Failed to set %s, got %d channels (0x%08lx) instead\n", DevFmtChannelsString(device->FmtChans), OutputType.Format.nChannels, OutputType.dwChannelMask); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + + if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) + device->FmtChans = DevFmtMono; + else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) + device->FmtChans = DevFmtStereo; + else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) + device->FmtChans = DevFmtQuad; + else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) + device->FmtChans = DevFmtX51; + else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) + device->FmtChans = DevFmtX51Side; + else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) + device->FmtChans = DevFmtX61; + else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1) + device->FmtChans = DevFmtX71; + else + { + ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask); + device->FmtChans = DevFmtStereo; + OutputType.Format.nChannels = 2; + OutputType.dwChannelMask = STEREO; + } + } + + if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) + { + if(OutputType.Samples.wValidBitsPerSample == 0) + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample || + !((device->FmtType == DevFmtUByte && OutputType.Format.wBitsPerSample == 8) || + (device->FmtType == DevFmtShort && OutputType.Format.wBitsPerSample == 16))) + { + ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample); + if(OutputType.Format.wBitsPerSample == 8) + device->FmtType = DevFmtUByte; + else if(OutputType.Format.wBitsPerSample == 16) + device->FmtType = DevFmtShort; + else + { + device->FmtType = DevFmtShort; + OutputType.Format.wBitsPerSample = 16; + } + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + } + } + else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + { + if(OutputType.Samples.wValidBitsPerSample == 0) + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample || + !((device->FmtType == DevFmtFloat && OutputType.Format.wBitsPerSample == 32))) + { + ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample); + if(OutputType.Format.wBitsPerSample != 32) + { + device->FmtType = DevFmtFloat; + OutputType.Format.wBitsPerSample = 32; + } + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + } + } + else + { + ERR("Unhandled format sub-type\n"); + device->FmtType = DevFmtShort; + OutputType.Format.wBitsPerSample = 16; + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + } + } + + SetDefaultWFXChannelOrder(device); + + hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL); + if(SUCCEEDED(hr)) + { + min_len = (min_per*device->Frequency + 10000000-1) / 10000000; + if(min_len < device->UpdateSize) + min_len *= (device->UpdateSize + min_len/2)/min_len; + + device->NumUpdates = (device->NumUpdates*device->UpdateSize + min_len/2) / + min_len; + device->NumUpdates = maxu(device->NumUpdates, 2); + device->UpdateSize = min_len; + + hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + ((REFERENCE_TIME)device->UpdateSize* + device->NumUpdates*10000000 + + device->Frequency-1) / device->Frequency, + 0, &OutputType.Format, NULL); + } + if(FAILED(hr)) + { + ERR("Failed to initialize audio client: 0x%08lx\n", hr); + return hr; + } + + hr = IAudioClient_GetBufferSize(data->client, &buffer_len); + if(FAILED(hr)) + { + ERR("Failed to get audio buffer info: 0x%08lx\n", hr); + return hr; + } + + device->NumUpdates = buffer_len / device->UpdateSize; + if(device->NumUpdates <= 1) + { + device->NumUpdates = 1; + ERR("Audio client returned buffer_len < period*2; expect break up\n"); + } + + ResetEvent(data->hNotifyEvent); + hr = IAudioClient_SetEventHandle(data->client, data->hNotifyEvent); + if(SUCCEEDED(hr)) + hr = IAudioClient_Start(data->client); + if(FAILED(hr)) + { + ERR("Failed to start audio client: 0x%08lx\n", hr); + return hr; + } + + data->thread = StartThread(MMDevApiProc, device); + if(!data->thread) + { + IAudioClient_Stop(data->client); + ERR("Failed to start thread\n"); + return E_FAIL; + } + + return hr; +} + + +static DWORD CALLBACK MessageProc(void *ptr) +{ + ThreadRequest *req = ptr; + IMMDeviceEnumerator *Enumerator; + MMDevApiData *data; + ALCdevice *device; + HRESULT hr; + MSG msg; + + TRACE("Starting message thread\n"); + + hr = CoInitialize(NULL); + if(FAILED(hr)) + { + WARN("Failed to initialize COM: 0x%08lx\n", hr); + req->result = hr; + SetEvent(req->FinishedEvt); + return 0; + } + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); + if(FAILED(hr)) + { + WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); + CoUninitialize(); + req->result = hr; + SetEvent(req->FinishedEvt); + return 0; + } + Enumerator = ptr; + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + + req->result = S_OK; + SetEvent(req->FinishedEvt); + + TRACE("Starting message loop\n"); + while(GetMessage(&msg, NULL, 0, 0)) + { + TRACE("Got message %u\n", msg.message); + switch(msg.message) + { + case WM_USER_OpenDevice: + req = (ThreadRequest*)msg.wParam; + device = (ALCdevice*)msg.lParam; + data = device->ExtraData; + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); + if(SUCCEEDED(hr)) + { + Enumerator = ptr; + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev); + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + } + if(SUCCEEDED(hr)) + hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr); + if(SUCCEEDED(hr)) + data->client = ptr; + + if(FAILED(hr)) + { + if(data->mmdev) + IMMDevice_Release(data->mmdev); + data->mmdev = NULL; + } + + req->result = hr; + SetEvent(req->FinishedEvt); + continue; + + case WM_USER_ResetDevice: + req = (ThreadRequest*)msg.wParam; + device = (ALCdevice*)msg.lParam; + + req->result = DoReset(device); + SetEvent(req->FinishedEvt); + continue; + + case WM_USER_StopDevice: + req = (ThreadRequest*)msg.wParam; + device = (ALCdevice*)msg.lParam; + data = device->ExtraData; + + if(data->thread) + { + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; + + IAudioClient_Stop(data->client); + } + + req->result = S_OK; + SetEvent(req->FinishedEvt); + continue; + + case WM_USER_CloseDevice: + req = (ThreadRequest*)msg.wParam; + device = (ALCdevice*)msg.lParam; + data = device->ExtraData; + + IAudioClient_Release(data->client); + data->client = NULL; + + IMMDevice_Release(data->mmdev); + data->mmdev = NULL; + + req->result = S_OK; + SetEvent(req->FinishedEvt); + continue; + + default: + ERR("Unexpected message: %u\n", msg.message); + continue; + } + } + TRACE("Message loop finished\n"); + + CoUninitialize(); + return 0; +} + + +static BOOL MMDevApiLoad(void) +{ + static HRESULT InitResult; + if(!ThreadHdl) + { + ThreadRequest req; + InitResult = E_FAIL; + + req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL); + if(req.FinishedEvt == NULL) + ERR("Failed to create event: %lu\n", GetLastError()); + else + { + ThreadHdl = CreateThread(NULL, 0, MessageProc, &req, 0, &ThreadID); + if(ThreadHdl != NULL) + InitResult = WaitForResponse(&req); + CloseHandle(req.FinishedEvt); + } + } + return SUCCEEDED(InitResult); +} + + +static ALCboolean MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName) +{ + MMDevApiData *data = NULL; + HRESULT hr; + + if(!deviceName) + deviceName = mmDevice; + else if(strcmp(deviceName, mmDevice) != 0) + return ALC_FALSE; + + //Initialise requested device + data = calloc(1, sizeof(MMDevApiData)); + if(!data) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + device->ExtraData = data; + + hr = S_OK; + data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(data->hNotifyEvent == NULL || data->MsgEvent == NULL) + hr = E_FAIL; + + if(SUCCEEDED(hr)) + { + ThreadRequest req = { data->MsgEvent, 0 }; + + hr = E_FAIL; + if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device)) + hr = WaitForResponse(&req); + } + + if(FAILED(hr)) + { + if(data->hNotifyEvent != NULL) + CloseHandle(data->hNotifyEvent); + data->hNotifyEvent = NULL; + if(data->MsgEvent != NULL) + CloseHandle(data->MsgEvent); + data->MsgEvent = NULL; + + free(data); + device->ExtraData = NULL; + + ERR("Device init failed: 0x%08lx\n", hr); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + return ALC_TRUE; +} + +static void MMDevApiClosePlayback(ALCdevice *device) +{ + MMDevApiData *data = device->ExtraData; + ThreadRequest req = { data->MsgEvent, 0 }; + + if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device)) + (void)WaitForResponse(&req); + + CloseHandle(data->MsgEvent); + data->MsgEvent = NULL; + + CloseHandle(data->hNotifyEvent); + data->hNotifyEvent = NULL; + + free(data); + device->ExtraData = NULL; +} + +static ALCboolean MMDevApiResetPlayback(ALCdevice *device) +{ + MMDevApiData *data = device->ExtraData; + ThreadRequest req = { data->MsgEvent, 0 }; + HRESULT hr = E_FAIL; + + if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device)) + hr = WaitForResponse(&req); + + return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; +} + +static void MMDevApiStopPlayback(ALCdevice *device) +{ + MMDevApiData *data = device->ExtraData; + ThreadRequest req = { data->MsgEvent, 0 }; + + if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device)) + (void)WaitForResponse(&req); +} + + +static const BackendFuncs MMDevApiFuncs = { + MMDevApiOpenPlayback, + MMDevApiClosePlayback, + MMDevApiResetPlayback, + MMDevApiStopPlayback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + + +ALCboolean alcMMDevApiInit(BackendFuncs *FuncList) +{ + if(!MMDevApiLoad()) + return ALC_FALSE; + *FuncList = MMDevApiFuncs; + return ALC_TRUE; +} + +void alcMMDevApiDeinit(void) +{ + if(ThreadHdl) + { + TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID); + PostThreadMessage(ThreadID, WM_QUIT, 0, 0); + CloseHandle(ThreadHdl); + ThreadHdl = NULL; + } +} + +void alcMMDevApiProbe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(mmDevice); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(mmDevice); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff -r 000000000000 -r f9476ff7637e Alc/backends/null.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/null.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,164 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2010 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 +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +typedef struct { + volatile int killNow; + ALvoid *thread; +} null_data; + + +static const ALCchar nullDevice[] = "No Output"; + +static ALuint NullProc(ALvoid *ptr) +{ + ALCdevice *Device = (ALCdevice*)ptr; + null_data *data = (null_data*)Device->ExtraData; + ALuint now, start; + ALuint64 avail, done; + const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 / + Device->Frequency / 2; + + done = 0; + start = timeGetTime(); + while(!data->killNow && Device->Connected) + { + now = timeGetTime(); + + avail = (ALuint64)(now-start) * Device->Frequency / 1000; + if(avail < done) + { + /* Timer wrapped. Add the remainder of the cycle to the available + * count and reset the number of samples done */ + avail += (ALuint64)0xFFFFFFFFu*Device->Frequency/1000 - done; + done = 0; + } + if(avail-done < Device->UpdateSize) + { + Sleep(restTime); + continue; + } + + while(avail-done >= Device->UpdateSize) + { + aluMixData(Device, NULL, Device->UpdateSize); + done += Device->UpdateSize; + } + } + + return 0; +} + +static ALCboolean null_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + null_data *data; + + if(!deviceName) + deviceName = nullDevice; + else if(strcmp(deviceName, nullDevice) != 0) + return ALC_FALSE; + + data = (null_data*)calloc(1, sizeof(*data)); + + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + return ALC_TRUE; +} + +static void null_close_playback(ALCdevice *device) +{ + null_data *data = (null_data*)device->ExtraData; + + free(data); + device->ExtraData = NULL; +} + +static ALCboolean null_reset_playback(ALCdevice *device) +{ + null_data *data = (null_data*)device->ExtraData; + + SetDefaultWFXChannelOrder(device); + + data->thread = StartThread(NullProc, device); + if(data->thread == NULL) + return ALC_FALSE; + + return ALC_TRUE; +} + +static void null_stop_playback(ALCdevice *device) +{ + null_data *data = (null_data*)device->ExtraData; + + if(!data->thread) + return; + + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; +} + + +static const BackendFuncs null_funcs = { + null_open_playback, + null_close_playback, + null_reset_playback, + null_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +ALCboolean alc_null_init(BackendFuncs *func_list) +{ + *func_list = null_funcs; + return ALC_TRUE; +} + +void alc_null_deinit(void) +{ +} + +void alc_null_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(nullDevice); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(nullDevice); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff -r 000000000000 -r f9476ff7637e Alc/backends/opensl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/opensl.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This is an OpenAL backend for Android using the native audio APIs based on + * OpenSL ES 1.0.1. It is based on source code for the native-audio sample app + * bundled with NDK. + */ + +#include "config.h" + +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +#include +#if 1 +#include +#else +extern SLAPIENTRY const SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + +struct SLAndroidSimpleBufferQueueItf_; +typedef const struct SLAndroidSimpleBufferQueueItf_ * const * SLAndroidSimpleBufferQueueItf; + +typedef void (*slAndroidSimpleBufferQueueCallback)(SLAndroidSimpleBufferQueueItf caller, void *pContext); + +typedef struct SLAndroidSimpleBufferQueueState_ { + SLuint32 count; + SLuint32 index; +} SLAndroidSimpleBufferQueueState; + + +struct SLAndroidSimpleBufferQueueItf_ { + SLresult (*Enqueue) ( + SLAndroidSimpleBufferQueueItf self, + const void *pBuffer, + SLuint32 size + ); + SLresult (*Clear) ( + SLAndroidSimpleBufferQueueItf self + ); + SLresult (*GetState) ( + SLAndroidSimpleBufferQueueItf self, + SLAndroidSimpleBufferQueueState *pState + ); + SLresult (*RegisterCallback) ( + SLAndroidSimpleBufferQueueItf self, + slAndroidSimpleBufferQueueCallback callback, + void* pContext + ); +}; + +#define SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE ((SLuint32) 0x800007BD) + +typedef struct SLDataLocator_AndroidSimpleBufferQueue { + SLuint32 locatorType; + SLuint32 numBuffers; +} SLDataLocator_AndroidSimpleBufferQueue; + +#endif + +/* Helper macros */ +#define SLObjectItf_Realize(a,b) ((*(a))->Realize((a),(b))) +#define SLObjectItf_GetInterface(a,b,c) ((*(a))->GetInterface((a),(b),(c))) +#define SLObjectItf_Destroy(a) ((*(a))->Destroy((a))) + +#define SLEngineItf_CreateOutputMix(a,b,c,d,e) ((*(a))->CreateOutputMix((a),(b),(c),(d),(e))) +#define SLEngineItf_CreateAudioPlayer(a,b,c,d,e,f,g) ((*(a))->CreateAudioPlayer((a),(b),(c),(d),(e),(f),(g))) + +#define SLPlayItf_SetPlayState(a,b) ((*(a))->SetPlayState((a),(b))) + + +typedef struct { + /* engine interfaces */ + SLObjectItf engineObject; + SLEngineItf engine; + + /* output mix interfaces */ + SLObjectItf outputMix; + + /* buffer queue player interfaces */ + SLObjectItf bufferQueueObject; + + void *buffer; + ALuint bufferSize; + + ALuint frameSize; +} osl_data; + + +static const ALCchar opensl_device[] = "OpenSL"; + + +static SLuint32 GetChannelMask(enum DevFmtChannels chans) +{ + switch(chans) + { + case DevFmtMono: return SL_SPEAKER_FRONT_CENTER; + case DevFmtStereo: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT; + case DevFmtQuad: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| + SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT; + case DevFmtX51: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| + SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY| + SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT; + case DevFmtX61: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| + SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY| + SL_SPEAKER_BACK_CENTER| + SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT; + case DevFmtX71: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| + SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY| + SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT| + SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT; + case DevFmtX51Side: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT| + SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY| + SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT; + } + return 0; +} + +static const char *res_str(SLresult result) +{ + switch(result) + { + case SL_RESULT_SUCCESS: return "Success"; + case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated"; + case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid"; + case SL_RESULT_MEMORY_FAILURE: return "Memory failure"; + case SL_RESULT_RESOURCE_ERROR: return "Resource error"; + case SL_RESULT_RESOURCE_LOST: return "Resource lost"; + case SL_RESULT_IO_ERROR: return "I/O error"; + case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient"; + case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted"; + case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported"; + case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found"; + case SL_RESULT_PERMISSION_DENIED: return "Permission denied"; + case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported"; + case SL_RESULT_INTERNAL_ERROR: return "Internal error"; + case SL_RESULT_UNKNOWN_ERROR: return "Unknown error"; + case SL_RESULT_OPERATION_ABORTED: return "Operation aborted"; + case SL_RESULT_CONTROL_LOST: return "Control lost"; + case SL_RESULT_READONLY: return "ReadOnly"; + case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported"; + case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible"; + } + return "Unknown error code"; +} + +#define PRINTERR(x, s) do { \ + if((x) != SL_RESULT_SUCCESS) \ + ERR("%s: %s\n", (s), res_str((x))); \ +} while(0) + +/* this callback handler is called every time a buffer finishes playing */ +static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context) +{ + ALCdevice *Device = context; + osl_data *data = Device->ExtraData; + SLresult result; + + aluMixData(Device, data->buffer, data->bufferSize/data->frameSize); + + result = (*bq)->Enqueue(bq, data->buffer, data->bufferSize); + PRINTERR(result, "bq->Enqueue"); +} + + +static ALCboolean opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName) +{ + osl_data *data = NULL; + SLresult result; + + if(!deviceName) + deviceName = opensl_device; + else if(strcmp(deviceName, opensl_device) != 0) + return ALC_FALSE; + + data = calloc(1, sizeof(*data)); + if(!data) + { + alcSetError(Device, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + + // create engine + result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL); + PRINTERR(result, "slCreateEngine"); + if(SL_RESULT_SUCCESS == result) + { + result = SLObjectItf_Realize(data->engineObject, SL_BOOLEAN_FALSE); + PRINTERR(result, "engine->Realize"); + } + if(SL_RESULT_SUCCESS == result) + { + result = SLObjectItf_GetInterface(data->engineObject, SL_IID_ENGINE, &data->engine); + PRINTERR(result, "engine->GetInterface"); + } + if(SL_RESULT_SUCCESS == result) + { + result = SLEngineItf_CreateOutputMix(data->engine, &data->outputMix, 0, NULL, NULL); + PRINTERR(result, "engine->CreateOutputMix"); + } + if(SL_RESULT_SUCCESS == result) + { + result = SLObjectItf_Realize(data->outputMix, SL_BOOLEAN_FALSE); + PRINTERR(result, "outputMix->Realize"); + } + + if(SL_RESULT_SUCCESS != result) + { + if(data->outputMix != NULL) + SLObjectItf_Destroy(data->outputMix); + data->outputMix = NULL; + + if(data->engineObject != NULL) + SLObjectItf_Destroy(data->engineObject); + data->engineObject = NULL; + data->engine = NULL; + + free(data); + return ALC_FALSE; + } + + Device->szDeviceName = strdup(deviceName); + Device->ExtraData = data; + + return ALC_TRUE; +} + + +static void opensl_close_playback(ALCdevice *Device) +{ + osl_data *data = Device->ExtraData; + + SLObjectItf_Destroy(data->outputMix); + data->outputMix = NULL; + + SLObjectItf_Destroy(data->engineObject); + data->engineObject = NULL; + data->engine = NULL; + + free(data); + Device->ExtraData = NULL; +} + +static ALCboolean opensl_reset_playback(ALCdevice *Device) +{ + osl_data *data = Device->ExtraData; + SLDataLocator_AndroidSimpleBufferQueue loc_bufq; + SLAndroidSimpleBufferQueueItf bufferQueue; + SLDataLocator_OutputMix loc_outmix; + SLDataFormat_PCM format_pcm; + SLDataSource audioSrc; + SLDataSink audioSnk; + SLPlayItf player; + SLInterfaceID id; + SLboolean req; + SLresult result; + ALuint i; + + + Device->UpdateSize = (ALuint64)Device->UpdateSize * 44100 / Device->Frequency; + Device->UpdateSize = Device->UpdateSize * Device->NumUpdates / 2; + Device->NumUpdates = 2; + + Device->Frequency = 44100; + Device->FmtChans = DevFmtStereo; + Device->FmtType = DevFmtShort; + + SetDefaultWFXChannelOrder(Device); + + + id = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + req = SL_BOOLEAN_TRUE; + + loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; + loc_bufq.numBuffers = Device->NumUpdates; + + format_pcm.formatType = SL_DATAFORMAT_PCM; + format_pcm.numChannels = ChannelsFromDevFmt(Device->FmtChans); + format_pcm.samplesPerSec = Device->Frequency * 1000; + format_pcm.bitsPerSample = BytesFromDevFmt(Device->FmtType) * 8; + format_pcm.containerSize = format_pcm.bitsPerSample; + format_pcm.channelMask = GetChannelMask(Device->FmtChans); + format_pcm.endianness = SL_BYTEORDER_NATIVE; + + audioSrc.pLocator = &loc_bufq; + audioSrc.pFormat = &format_pcm; + + loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; + loc_outmix.outputMix = data->outputMix; + audioSnk.pLocator = &loc_outmix; + audioSnk.pFormat = NULL; + + + result = SLEngineItf_CreateAudioPlayer(data->engine, &data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req); + PRINTERR(result, "engine->CreateAudioPlayer"); + if(SL_RESULT_SUCCESS == result) + { + result = SLObjectItf_Realize(data->bufferQueueObject, SL_BOOLEAN_FALSE); + PRINTERR(result, "bufferQueue->Realize"); + } + if(SL_RESULT_SUCCESS == result) + { + result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_BUFFERQUEUE, &bufferQueue); + PRINTERR(result, "bufferQueue->GetInterface"); + } + if(SL_RESULT_SUCCESS == result) + { + result = (*bufferQueue)->RegisterCallback(bufferQueue, opensl_callback, Device); + PRINTERR(result, "bufferQueue->RegisterCallback"); + } + if(SL_RESULT_SUCCESS == result) + { + data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); + data->bufferSize = Device->UpdateSize * data->frameSize; + data->buffer = calloc(1, data->bufferSize); + if(!data->buffer) + { + result = SL_RESULT_MEMORY_FAILURE; + PRINTERR(result, "calloc"); + } + } + /* enqueue the first buffer to kick off the callbacks */ + for(i = 0;i < Device->NumUpdates;i++) + { + if(SL_RESULT_SUCCESS == result) + { + result = (*bufferQueue)->Enqueue(bufferQueue, data->buffer, data->bufferSize); + PRINTERR(result, "bufferQueue->Enqueue"); + } + } + if(SL_RESULT_SUCCESS == result) + { + result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_PLAY, &player); + PRINTERR(result, "bufferQueue->GetInterface"); + } + if(SL_RESULT_SUCCESS == result) + { + result = SLPlayItf_SetPlayState(player, SL_PLAYSTATE_PLAYING); + PRINTERR(result, "player->SetPlayState"); + } + + if(SL_RESULT_SUCCESS != result) + { + if(data->bufferQueueObject != NULL) + SLObjectItf_Destroy(data->bufferQueueObject); + data->bufferQueueObject = NULL; + + free(data->buffer); + data->buffer = NULL; + data->bufferSize = 0; + + return ALC_FALSE; + } + + return ALC_TRUE; +} + + +static void opensl_stop_playback(ALCdevice *Device) +{ + osl_data *data = Device->ExtraData; + + if(data->bufferQueueObject != NULL) + SLObjectItf_Destroy(data->bufferQueueObject); + data->bufferQueueObject = NULL; + + free(data->buffer); + data->buffer = NULL; + data->bufferSize = 0; +} + + +static const BackendFuncs opensl_funcs = { + opensl_open_playback, + opensl_close_playback, + opensl_reset_playback, + opensl_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + + +ALCboolean alc_opensl_init(BackendFuncs *func_list) +{ + *func_list = opensl_funcs; + return ALC_TRUE; +} + +void alc_opensl_deinit(void) +{ +} + +void alc_opensl_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(opensl_device); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(opensl_device); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff -r 000000000000 -r f9476ff7637e Alc/backends/oss.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/oss.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,536 @@ +/** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include + +/* + * The OSS documentation talks about SOUND_MIXER_READ, but the header + * only contains MIXER_READ. Play safe. Same for WRITE. + */ +#ifndef SOUND_MIXER_READ +#define SOUND_MIXER_READ MIXER_READ +#endif +#ifndef SOUND_MIXER_WRITE +#define SOUND_MIXER_WRITE MIXER_WRITE +#endif + +static const ALCchar oss_device[] = "OSS Default"; + +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; + while (x > 1) + { + x >>= 1; + y++; + } + return y; +} + + +static ALuint OSSProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + oss_data *data = (oss_data*)pDevice->ExtraData; + ALint frameSize; + ssize_t wrote; + + SetRTPriority(); + + frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + while(!data->killNow && pDevice->Connected) + { + ALint len = data->data_size; + ALubyte *WritePtr = data->mix_data; + + aluMixData(pDevice, WritePtr, len/frameSize); + while(len > 0 && !data->killNow) + { + wrote = write(data->fd, WritePtr, len); + if(wrote < 0) + { + if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) + { + ERR("write failed: %s\n", strerror(errno)); + aluHandleDisconnect(pDevice); + break; + } + + Sleep(1); + continue; + } + + len -= wrote; + WritePtr += wrote; + } + } + + return 0; +} + +static ALuint OSSCaptureProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + oss_data *data = (oss_data*)pDevice->ExtraData; + int frameSize; + int amt; + + SetRTPriority(); + + frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + while(!data->killNow) + { + amt = read(data->fd, data->mix_data, data->data_size); + if(amt < 0) + { + ERR("read failed: %s\n", strerror(errno)); + aluHandleDisconnect(pDevice); + break; + } + if(amt == 0) + { + Sleep(1); + continue; + } + if(data->doCapture) + WriteRingBuffer(data->ring, data->mix_data, amt/frameSize); + } + + return 0; +} + +static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + char driver[64]; + oss_data *data; + + strncpy(driver, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + if(!deviceName) + deviceName = oss_device; + else if(strcmp(deviceName, oss_device) != 0) + return ALC_FALSE; + + data = (oss_data*)calloc(1, sizeof(oss_data)); + data->killNow = 0; + + data->fd = open(driver, O_WRONLY); + if(data->fd == -1) + { + free(data); + ERR("Could not open %s: %s\n", driver, strerror(errno)); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + return ALC_TRUE; +} + +static void oss_close_playback(ALCdevice *device) +{ + oss_data *data = (oss_data*)device->ExtraData; + + close(data->fd); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean oss_reset_playback(ALCdevice *device) +{ + oss_data *data = (oss_data*)device->ExtraData; + int numFragmentsLogSize; + int log2FragmentSize; + unsigned int periods; + audio_buf_info info; + ALuint frameSize; + int numChannels; + int ossFormat; + int ossSpeed; + char *err; + + switch(device->FmtType) + { + case DevFmtByte: + ossFormat = AFMT_S8; + break; + case DevFmtUByte: + ossFormat = AFMT_U8; + break; + case DevFmtUShort: + case DevFmtFloat: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + ossFormat = AFMT_S16_NE; + break; + } + + periods = device->NumUpdates; + numChannels = ChannelsFromDevFmt(device->FmtChans); + frameSize = numChannels * BytesFromDevFmt(device->FmtType); + + ossSpeed = device->Frequency; + log2FragmentSize = log2i(device->UpdateSize * frameSize); + + /* according to the OSS spec, 16 bytes are the minimum */ + if (log2FragmentSize < 4) + log2FragmentSize = 4; + /* Subtract one period since the temp mixing buffer counts as one. Still + * need at least two on the card, though. */ + if(periods > 2) periods--; + numFragmentsLogSize = (periods << 16) | log2FragmentSize; + +#define CHECKERR(func) if((func) < 0) { \ + err = #func; \ + goto err; \ +} + /* 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)); + if(0) + { + err: + ERR("%s failed: %s\n", err, strerror(errno)); + return ALC_FALSE; + } +#undef CHECKERR + + if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels) + { + ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels); + return ALC_FALSE; + } + + if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) || + (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) || + (ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort))) + { + ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat); + return ALC_FALSE; + } + + if(device->Frequency != (ALuint)ossSpeed) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, ossSpeed); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = ossSpeed; + } + device->UpdateSize = info.fragsize / frameSize; + device->NumUpdates = info.fragments + 1; + + data->data_size = device->UpdateSize * frameSize; + data->mix_data = calloc(1, data->data_size); + + SetDefaultChannelOrder(device); + + data->thread = StartThread(OSSProc, device); + if(data->thread == NULL) + { + free(data->mix_data); + data->mix_data = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void oss_stop_playback(ALCdevice *device) +{ + oss_data *data = (oss_data*)device->ExtraData; + + if(!data->thread) + return; + + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; + if(ioctl(data->fd, SNDCTL_DSP_RESET) != 0) + ERR("Error resetting device: %s\n", strerror(errno)); + + free(data->mix_data); + data->mix_data = NULL; +} + + +static ALCboolean oss_open_capture(ALCdevice *device, const ALCchar *deviceName) +{ + int numFragmentsLogSize; + int log2FragmentSize; + unsigned int periods; + audio_buf_info info; + ALuint frameSize; + int numChannels; + char driver[64]; + oss_data *data; + int ossFormat; + int ossSpeed; + char *err; + + strncpy(driver, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + if(!deviceName) + deviceName = oss_device; + else if(strcmp(deviceName, oss_device) != 0) + return ALC_FALSE; + + data = (oss_data*)calloc(1, sizeof(oss_data)); + data->killNow = 0; + + data->fd = open(driver, O_RDONLY); + if(data->fd == -1) + { + free(data); + ERR("Could not open %s: %s\n", driver, strerror(errno)); + return ALC_FALSE; + } + + switch(device->FmtType) + { + case DevFmtByte: + ossFormat = AFMT_S8; + break; + case DevFmtUByte: + ossFormat = AFMT_U8; + break; + case DevFmtShort: + ossFormat = AFMT_S16_NE; + break; + case DevFmtUShort: + case DevFmtFloat: + free(data); + ERR("%s capture samples not supported on OSS\n", DevFmtTypeString(device->FmtType)); + return ALC_FALSE; + } + + periods = 4; + numChannels = ChannelsFromDevFmt(device->FmtChans); + frameSize = numChannels * BytesFromDevFmt(device->FmtType); + ossSpeed = device->Frequency; + log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates * + frameSize / periods); + + /* according to the OSS spec, 16 bytes are the minimum */ + if (log2FragmentSize < 4) + log2FragmentSize = 4; + numFragmentsLogSize = (periods << 16) | log2FragmentSize; + +#define CHECKERR(func) if((func) < 0) { \ + 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)); + if(0) + { + err: + ERR("%s failed: %s\n", err, strerror(errno)); + close(data->fd); + free(data); + return ALC_FALSE; + } +#undef CHECKERR + + 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); + return ALC_FALSE; + } + + if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) || + (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) || + (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); + return ALC_FALSE; + } + + data->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates); + if(!data->ring) + { + ERR("Ring buffer create failed\n"); + close(data->fd); + free(data); + return ALC_FALSE; + } + + data->data_size = info.fragsize; + data->mix_data = calloc(1, data->data_size); + + device->ExtraData = data; + data->thread = StartThread(OSSCaptureProc, device); + if(data->thread == NULL) + { + device->ExtraData = NULL; + free(data->mix_data); + free(data); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + return ALC_TRUE; +} + +static void oss_close_capture(ALCdevice *device) +{ + oss_data *data = (oss_data*)device->ExtraData; + data->killNow = 1; + StopThread(data->thread); + + close(data->fd); + + DestroyRingBuffer(data->ring); + + free(data->mix_data); + free(data); + device->ExtraData = NULL; +} + +static void oss_start_capture(ALCdevice *pDevice) +{ + oss_data *data = (oss_data*)pDevice->ExtraData; + data->doCapture = 1; +} + +static void oss_stop_capture(ALCdevice *pDevice) +{ + oss_data *data = (oss_data*)pDevice->ExtraData; + data->doCapture = 0; +} + +static void oss_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) +{ + oss_data *data = (oss_data*)pDevice->ExtraData; + if(lSamples <= (ALCuint)RingBufferSize(data->ring)) + ReadRingBuffer(data->ring, pBuffer, lSamples); + else + alcSetError(pDevice, ALC_INVALID_VALUE); +} + +static ALCuint oss_available_samples(ALCdevice *pDevice) +{ + oss_data *data = (oss_data*)pDevice->ExtraData; + return RingBufferSize(data->ring); +} + + +static const BackendFuncs oss_funcs = { + oss_open_playback, + oss_close_playback, + oss_reset_playback, + oss_stop_playback, + oss_open_capture, + oss_close_capture, + oss_start_capture, + oss_stop_capture, + oss_capture_samples, + oss_available_samples +}; + +ALCboolean alc_oss_init(BackendFuncs *func_list) +{ + *func_list = oss_funcs; + return ALC_TRUE; +} + +void alc_oss_deinit(void) +{ +} + +void alc_oss_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + { +#ifdef HAVE_STAT + struct stat buf; + if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0) +#endif + AppendDeviceList(oss_device); + } + break; + + case ALL_DEVICE_PROBE: + { +#ifdef HAVE_STAT + struct stat buf; + if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0) +#endif + AppendAllDeviceList(oss_device); + } + break; + + case CAPTURE_DEVICE_PROBE: + { +#ifdef HAVE_STAT + struct stat buf; + if(stat(GetConfigValue("oss", "capture", "/dev/dsp"), &buf) == 0) +#endif + AppendCaptureDeviceList(oss_device); + } + break; + } +} diff -r 000000000000 -r f9476ff7637e Alc/backends/portaudio.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/portaudio.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,449 @@ +/** + * 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 +#include +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include + + +static const ALCchar pa_device[] = "PortAudio Default"; + + +static void *pa_handle; +#ifdef HAVE_DYNLOAD +#define MAKE_FUNC(x) static typeof(x) * p##x +MAKE_FUNC(Pa_Initialize); +MAKE_FUNC(Pa_Terminate); +MAKE_FUNC(Pa_GetErrorText); +MAKE_FUNC(Pa_StartStream); +MAKE_FUNC(Pa_StopStream); +MAKE_FUNC(Pa_OpenStream); +MAKE_FUNC(Pa_CloseStream); +MAKE_FUNC(Pa_GetDefaultOutputDevice); +MAKE_FUNC(Pa_GetStreamInfo); +#undef MAKE_FUNC + +#define Pa_Initialize pPa_Initialize +#define Pa_Terminate pPa_Terminate +#define Pa_GetErrorText pPa_GetErrorText +#define Pa_StartStream pPa_StartStream +#define Pa_StopStream pPa_StopStream +#define Pa_OpenStream pPa_OpenStream +#define Pa_CloseStream pPa_CloseStream +#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice +#define Pa_GetStreamInfo pPa_GetStreamInfo +#endif + +static ALCboolean pa_load(void) +{ + if(!pa_handle) + { + PaError err; + +#ifdef HAVE_DYNLOAD +#ifdef _WIN32 +# define PALIB "portaudio.dll" +#elif defined(__APPLE__) && defined(__MACH__) +# define PALIB "libportaudio.2.dylib" +#elif defined(__OpenBSD__) +# define PALIB "libportaudio.so" +#else +# define PALIB "libportaudio.so.2" +#endif + + pa_handle = LoadLib(PALIB); + if(!pa_handle) + return ALC_FALSE; + +#define LOAD_FUNC(f) do { \ + p##f = GetSymbol(pa_handle, #f); \ + if(p##f == NULL) \ + { \ + CloseLib(pa_handle); \ + pa_handle = NULL; \ + return ALC_FALSE; \ + } \ +} while(0) + LOAD_FUNC(Pa_Initialize); + LOAD_FUNC(Pa_Terminate); + LOAD_FUNC(Pa_GetErrorText); + LOAD_FUNC(Pa_StartStream); + LOAD_FUNC(Pa_StopStream); + LOAD_FUNC(Pa_OpenStream); + LOAD_FUNC(Pa_CloseStream); + LOAD_FUNC(Pa_GetDefaultOutputDevice); + LOAD_FUNC(Pa_GetStreamInfo); +#undef LOAD_FUNC +#else + pa_handle = (void*)0xDEADBEEF; +#endif + + if((err=Pa_Initialize()) != paNoError) + { + ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err)); + CloseLib(pa_handle); + pa_handle = NULL; + return ALC_FALSE; + } + } + return ALC_TRUE; +} + + +typedef struct { + PaStream *stream; + ALuint update_size; + + RingBuffer *ring; +} pa_data; + + +static int pa_callback(const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, + const PaStreamCallbackFlags statusFlags, void *userData) +{ + ALCdevice *device = (ALCdevice*)userData; + + (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) +{ + ALCdevice *device = (ALCdevice*)userData; + pa_data *data = (pa_data*)device->ExtraData; + + (void)outputBuffer; + (void)timeInfo; + (void)statusFlags; + + WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer); + return 0; +} + + +static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + PaStreamParameters outParams; + pa_data *data; + PaError err; + + if(!deviceName) + deviceName = pa_device; + else if(strcmp(deviceName, pa_device) != 0) + return ALC_FALSE; + + data = (pa_data*)calloc(1, sizeof(pa_data)); + data->update_size = device->UpdateSize; + + device->ExtraData = data; + + outParams.device = GetConfigValueInt("port", "device", -1); + if(outParams.device < 0) + outParams.device = Pa_GetDefaultOutputDevice(); + outParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) / + (float)device->Frequency; + outParams.hostApiSpecificStreamInfo = NULL; + + switch(device->FmtType) + { + case DevFmtByte: + outParams.sampleFormat = paInt8; + break; + case DevFmtUByte: + outParams.sampleFormat = paUInt8; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + outParams.sampleFormat = paInt16; + break; + case DevFmtFloat: + outParams.sampleFormat = paFloat32; + break; + } + outParams.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2); + + SetDefaultChannelOrder(device); + + err = Pa_OpenStream(&data->stream, NULL, &outParams, device->Frequency, + device->UpdateSize, paNoFlag, pa_callback, device); + if(err != paNoError) + { + ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err)); + device->ExtraData = NULL; + free(data); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + + if((ALuint)outParams.channelCount != ChannelsFromDevFmt(device->FmtChans)) + { + if(outParams.channelCount != 1 && outParams.channelCount != 2) + { + ERR("Unhandled channel count: %u\n", outParams.channelCount); + Pa_CloseStream(data->stream); + device->ExtraData = NULL; + free(data); + return ALC_FALSE; + } + if((device->Flags&DEVICE_CHANNELS_REQUEST)) + ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), outParams.channelCount); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + device->FmtChans = ((outParams.channelCount==1) ? DevFmtMono : DevFmtStereo); + } + + return ALC_TRUE; +} + +static void pa_close_playback(ALCdevice *device) +{ + pa_data *data = (pa_data*)device->ExtraData; + PaError err; + + err = Pa_CloseStream(data->stream); + if(err != paNoError) + ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); + + free(data); + device->ExtraData = NULL; +} + +static ALCboolean pa_reset_playback(ALCdevice *device) +{ + pa_data *data = (pa_data*)device->ExtraData; + const PaStreamInfo *streamInfo; + PaError err; + + streamInfo = Pa_GetStreamInfo(data->stream); + if(device->Frequency != streamInfo->sampleRate) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("PortAudio does not support changing sample rates (wanted %dhz, got %.1fhz)\n", device->Frequency, streamInfo->sampleRate); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = streamInfo->sampleRate; + } + device->UpdateSize = data->update_size; + + err = Pa_StartStream(data->stream); + if(err != paNoError) + { + ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err)); + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void pa_stop_playback(ALCdevice *device) +{ + pa_data *data = (pa_data*)device->ExtraData; + PaError err; + + err = Pa_StopStream(data->stream); + if(err != paNoError) + ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); +} + + +static ALCboolean pa_open_capture(ALCdevice *device, const ALCchar *deviceName) +{ + PaStreamParameters inParams; + ALuint frame_size; + pa_data *data; + PaError err; + + if(!deviceName) + deviceName = pa_device; + else if(strcmp(deviceName, pa_device) != 0) + return ALC_FALSE; + + data = (pa_data*)calloc(1, sizeof(pa_data)); + if(data == NULL) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + + frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates); + if(data->ring == NULL) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + goto error; + } + + inParams.device = GetConfigValueInt("port", "capture", -1); + if(inParams.device < 0) + inParams.device = Pa_GetDefaultOutputDevice(); + inParams.suggestedLatency = 0.0f; + inParams.hostApiSpecificStreamInfo = NULL; + + switch(device->FmtType) + { + case DevFmtByte: + inParams.sampleFormat = paInt8; + break; + case DevFmtUByte: + inParams.sampleFormat = paUInt8; + break; + case DevFmtShort: + inParams.sampleFormat = paInt16; + break; + case DevFmtFloat: + inParams.sampleFormat = paFloat32; + break; + case DevFmtUShort: + ERR("Unsigned short samples not supported\n"); + goto error; + } + inParams.channelCount = ChannelsFromDevFmt(device->FmtChans); + + err = Pa_OpenStream(&data->stream, &inParams, NULL, device->Frequency, + paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device); + if(err != paNoError) + { + ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err)); + goto error; + } + + device->szDeviceName = strdup(deviceName); + + device->ExtraData = data; + return ALC_TRUE; + +error: + DestroyRingBuffer(data->ring); + free(data); + return ALC_FALSE; +} + +static void pa_close_capture(ALCdevice *device) +{ + pa_data *data = (pa_data*)device->ExtraData; + PaError err; + + err = Pa_CloseStream(data->stream); + if(err != paNoError) + ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); + + free(data); + device->ExtraData = NULL; +} + +static void pa_start_capture(ALCdevice *device) +{ + pa_data *data = device->ExtraData; + PaError err; + + err = Pa_StartStream(data->stream); + if(err != paNoError) + ERR("Error starting stream: %s\n", Pa_GetErrorText(err)); +} + +static void pa_stop_capture(ALCdevice *device) +{ + pa_data *data = (pa_data*)device->ExtraData; + PaError err; + + err = Pa_StopStream(data->stream); + if(err != paNoError) + ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); +} + +static void pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) +{ + pa_data *data = device->ExtraData; + if(samples <= (ALCuint)RingBufferSize(data->ring)) + ReadRingBuffer(data->ring, buffer, samples); + else + alcSetError(device, ALC_INVALID_VALUE); +} + +static ALCuint pa_available_samples(ALCdevice *device) +{ + pa_data *data = device->ExtraData; + return RingBufferSize(data->ring); +} + + +static const BackendFuncs pa_funcs = { + pa_open_playback, + pa_close_playback, + pa_reset_playback, + pa_stop_playback, + pa_open_capture, + pa_close_capture, + pa_start_capture, + pa_stop_capture, + pa_capture_samples, + pa_available_samples +}; + +ALCboolean alc_pa_init(BackendFuncs *func_list) +{ + if(!pa_load()) + return ALC_FALSE; + *func_list = pa_funcs; + return ALC_TRUE; +} + +void alc_pa_deinit(void) +{ + if(pa_handle) + { + Pa_Terminate(); +#ifdef HAVE_DYNLOAD + CloseLib(pa_handle); +#endif + pa_handle = NULL; + } +} + +void alc_pa_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(pa_device); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(pa_device); + break; + case CAPTURE_DEVICE_PROBE: + AppendCaptureDeviceList(pa_device); + break; + } +} diff -r 000000000000 -r f9476ff7637e Alc/backends/pulseaudio.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/pulseaudio.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,1418 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2009 by Konstantinos Natsakis + * Copyright (C) 2010 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 "alMain.h" + +#include + +#if PA_API_VERSION == 11 +#define PA_STREAM_ADJUST_LATENCY 0x2000U +#define PA_STREAM_EARLY_REQUESTS 0x4000U +static __inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) +{ + return (x == PA_STREAM_CREATING || x == PA_STREAM_READY); +} +static __inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) +{ + return (x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING || + x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY); +} +#define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD +#define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD +#elif PA_API_VERSION != 12 +#error Invalid PulseAudio API version +#endif + +#ifndef PA_CHECK_VERSION +#define PA_CHECK_VERSION(major,minor,micro) \ + ((PA_MAJOR > (major)) || \ + (PA_MAJOR == (major) && PA_MINOR > (minor)) || \ + (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro))) +#endif + +static void *pa_handle; +#ifdef HAVE_DYNLOAD +#define MAKE_FUNC(x) static typeof(x) * p##x +MAKE_FUNC(pa_context_unref); +MAKE_FUNC(pa_sample_spec_valid); +MAKE_FUNC(pa_stream_drop); +MAKE_FUNC(pa_strerror); +MAKE_FUNC(pa_context_get_state); +MAKE_FUNC(pa_stream_get_state); +MAKE_FUNC(pa_threaded_mainloop_signal); +MAKE_FUNC(pa_stream_peek); +MAKE_FUNC(pa_threaded_mainloop_wait); +MAKE_FUNC(pa_threaded_mainloop_unlock); +MAKE_FUNC(pa_threaded_mainloop_in_thread); +MAKE_FUNC(pa_context_new); +MAKE_FUNC(pa_threaded_mainloop_stop); +MAKE_FUNC(pa_context_disconnect); +MAKE_FUNC(pa_threaded_mainloop_start); +MAKE_FUNC(pa_threaded_mainloop_get_api); +MAKE_FUNC(pa_context_set_state_callback); +MAKE_FUNC(pa_stream_write); +MAKE_FUNC(pa_xfree); +MAKE_FUNC(pa_stream_connect_record); +MAKE_FUNC(pa_stream_connect_playback); +MAKE_FUNC(pa_stream_readable_size); +MAKE_FUNC(pa_stream_writable_size); +MAKE_FUNC(pa_stream_cork); +MAKE_FUNC(pa_stream_is_suspended); +MAKE_FUNC(pa_stream_get_device_name); +MAKE_FUNC(pa_path_get_filename); +MAKE_FUNC(pa_get_binary_name); +MAKE_FUNC(pa_threaded_mainloop_free); +MAKE_FUNC(pa_context_errno); +MAKE_FUNC(pa_xmalloc); +MAKE_FUNC(pa_stream_unref); +MAKE_FUNC(pa_threaded_mainloop_accept); +MAKE_FUNC(pa_stream_set_write_callback); +MAKE_FUNC(pa_threaded_mainloop_new); +MAKE_FUNC(pa_context_connect); +MAKE_FUNC(pa_stream_set_buffer_attr); +MAKE_FUNC(pa_stream_get_buffer_attr); +MAKE_FUNC(pa_stream_get_sample_spec); +MAKE_FUNC(pa_stream_get_time); +MAKE_FUNC(pa_stream_set_read_callback); +MAKE_FUNC(pa_stream_set_state_callback); +MAKE_FUNC(pa_stream_set_moved_callback); +MAKE_FUNC(pa_stream_set_underflow_callback); +MAKE_FUNC(pa_stream_new); +MAKE_FUNC(pa_stream_disconnect); +MAKE_FUNC(pa_threaded_mainloop_lock); +MAKE_FUNC(pa_channel_map_init_auto); +MAKE_FUNC(pa_channel_map_parse); +MAKE_FUNC(pa_channel_map_snprint); +MAKE_FUNC(pa_channel_map_equal); +MAKE_FUNC(pa_context_get_server_info); +MAKE_FUNC(pa_context_get_sink_info_by_name); +MAKE_FUNC(pa_context_get_sink_info_list); +MAKE_FUNC(pa_context_get_source_info_list); +MAKE_FUNC(pa_operation_get_state); +MAKE_FUNC(pa_operation_unref); +#if PA_CHECK_VERSION(0,9,15) +MAKE_FUNC(pa_channel_map_superset); +MAKE_FUNC(pa_stream_set_buffer_attr_callback); +#endif +#if PA_CHECK_VERSION(0,9,16) +MAKE_FUNC(pa_stream_begin_write); +#endif +#undef MAKE_FUNC + +#define pa_context_unref ppa_context_unref +#define pa_sample_spec_valid ppa_sample_spec_valid +#define pa_stream_drop ppa_stream_drop +#define pa_strerror ppa_strerror +#define pa_context_get_state ppa_context_get_state +#define pa_stream_get_state ppa_stream_get_state +#define pa_threaded_mainloop_signal ppa_threaded_mainloop_signal +#define pa_stream_peek ppa_stream_peek +#define pa_threaded_mainloop_wait ppa_threaded_mainloop_wait +#define pa_threaded_mainloop_unlock ppa_threaded_mainloop_unlock +#define pa_threaded_mainloop_in_thread ppa_threaded_mainloop_in_thread +#define pa_context_new ppa_context_new +#define pa_threaded_mainloop_stop ppa_threaded_mainloop_stop +#define pa_context_disconnect ppa_context_disconnect +#define pa_threaded_mainloop_start ppa_threaded_mainloop_start +#define pa_threaded_mainloop_get_api ppa_threaded_mainloop_get_api +#define pa_context_set_state_callback ppa_context_set_state_callback +#define pa_stream_write ppa_stream_write +#define pa_xfree ppa_xfree +#define pa_stream_connect_record ppa_stream_connect_record +#define pa_stream_connect_playback ppa_stream_connect_playback +#define pa_stream_readable_size ppa_stream_readable_size +#define pa_stream_writable_size ppa_stream_writable_size +#define pa_stream_cork ppa_stream_cork +#define pa_stream_is_suspended ppa_stream_is_suspended +#define pa_stream_get_device_name ppa_stream_get_device_name +#define pa_path_get_filename ppa_path_get_filename +#define pa_get_binary_name ppa_get_binary_name +#define pa_threaded_mainloop_free ppa_threaded_mainloop_free +#define pa_context_errno ppa_context_errno +#define pa_xmalloc ppa_xmalloc +#define pa_stream_unref ppa_stream_unref +#define pa_threaded_mainloop_accept ppa_threaded_mainloop_accept +#define pa_stream_set_write_callback ppa_stream_set_write_callback +#define pa_threaded_mainloop_new ppa_threaded_mainloop_new +#define pa_context_connect ppa_context_connect +#define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr +#define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr +#define pa_stream_get_sample_spec ppa_stream_get_sample_spec +#define pa_stream_get_time ppa_stream_get_time +#define pa_stream_set_read_callback ppa_stream_set_read_callback +#define pa_stream_set_state_callback ppa_stream_set_state_callback +#define pa_stream_set_moved_callback ppa_stream_set_moved_callback +#define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback +#define pa_stream_new ppa_stream_new +#define pa_stream_disconnect ppa_stream_disconnect +#define pa_threaded_mainloop_lock ppa_threaded_mainloop_lock +#define pa_channel_map_init_auto ppa_channel_map_init_auto +#define pa_channel_map_parse ppa_channel_map_parse +#define pa_channel_map_snprint ppa_channel_map_snprint +#define pa_channel_map_equal ppa_channel_map_equal +#define pa_context_get_server_info ppa_context_get_server_info +#define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name +#define pa_context_get_sink_info_list ppa_context_get_sink_info_list +#define pa_context_get_source_info_list ppa_context_get_source_info_list +#define pa_operation_get_state ppa_operation_get_state +#define pa_operation_unref ppa_operation_unref +#if PA_CHECK_VERSION(0,9,15) +#define pa_channel_map_superset ppa_channel_map_superset +#define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback +#endif +#if PA_CHECK_VERSION(0,9,16) +#define pa_stream_begin_write ppa_stream_begin_write +#endif + +#endif + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +typedef struct { + char *device_name; + + ALCuint samples; + ALCuint frame_size; + + RingBuffer *ring; + + 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 const ALCchar pulse_device[] = "PulseAudio Default"; +static DevMap *allDevNameMap; +static ALuint numDevNames; +static DevMap *allCaptureDevNameMap; +static ALuint numCaptureDevNames; +static pa_context_flags_t pulse_ctx_flags; + + +static void context_state_callback(pa_context *context, void *pdata) +{ + pa_threaded_mainloop *loop = pdata; + pa_context_state_t state; + + state = pa_context_get_state(context); + if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state)) + pa_threaded_mainloop_signal(loop, 0); +} + +static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent) +{ + 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); + + context = pa_context_new(pa_threaded_mainloop_get_api(loop), name); + if(!context) + { + ERR("pa_context_new() failed\n"); + return NULL; + } + + 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_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 ALCboolean pulse_load(void) //{{{ +{ + ALCboolean ret = ALC_FALSE; + if(!pa_handle) + { + pa_threaded_mainloop *loop; + +#ifdef HAVE_DYNLOAD + +#ifdef _WIN32 +#define PALIB "libpulse-0.dll" +#elif defined(__APPLE__) && defined(__MACH__) +#define PALIB "libpulse.0.dylib" +#else +#define PALIB "libpulse.so.0" +#endif + pa_handle = LoadLib(PALIB); + if(!pa_handle) + return ALC_FALSE; + +#define LOAD_FUNC(x) do { \ + p##x = GetSymbol(pa_handle, #x); \ + if(!(p##x)) { \ + CloseLib(pa_handle); \ + pa_handle = NULL; \ + return ALC_FALSE; \ + } \ +} while(0) + LOAD_FUNC(pa_context_unref); + LOAD_FUNC(pa_sample_spec_valid); + LOAD_FUNC(pa_stream_drop); + LOAD_FUNC(pa_strerror); + LOAD_FUNC(pa_context_get_state); + LOAD_FUNC(pa_stream_get_state); + LOAD_FUNC(pa_threaded_mainloop_signal); + LOAD_FUNC(pa_stream_peek); + LOAD_FUNC(pa_threaded_mainloop_wait); + LOAD_FUNC(pa_threaded_mainloop_unlock); + LOAD_FUNC(pa_threaded_mainloop_in_thread); + LOAD_FUNC(pa_context_new); + LOAD_FUNC(pa_threaded_mainloop_stop); + LOAD_FUNC(pa_context_disconnect); + LOAD_FUNC(pa_threaded_mainloop_start); + LOAD_FUNC(pa_threaded_mainloop_get_api); + LOAD_FUNC(pa_context_set_state_callback); + LOAD_FUNC(pa_stream_write); + LOAD_FUNC(pa_xfree); + LOAD_FUNC(pa_stream_connect_record); + LOAD_FUNC(pa_stream_connect_playback); + LOAD_FUNC(pa_stream_readable_size); + LOAD_FUNC(pa_stream_writable_size); + LOAD_FUNC(pa_stream_cork); + LOAD_FUNC(pa_stream_is_suspended); + LOAD_FUNC(pa_stream_get_device_name); + LOAD_FUNC(pa_path_get_filename); + LOAD_FUNC(pa_get_binary_name); + LOAD_FUNC(pa_threaded_mainloop_free); + LOAD_FUNC(pa_context_errno); + LOAD_FUNC(pa_xmalloc); + LOAD_FUNC(pa_stream_unref); + LOAD_FUNC(pa_threaded_mainloop_accept); + LOAD_FUNC(pa_stream_set_write_callback); + LOAD_FUNC(pa_threaded_mainloop_new); + LOAD_FUNC(pa_context_connect); + LOAD_FUNC(pa_stream_set_buffer_attr); + LOAD_FUNC(pa_stream_get_buffer_attr); + LOAD_FUNC(pa_stream_get_sample_spec); + LOAD_FUNC(pa_stream_get_time); + LOAD_FUNC(pa_stream_set_read_callback); + LOAD_FUNC(pa_stream_set_state_callback); + LOAD_FUNC(pa_stream_set_moved_callback); + LOAD_FUNC(pa_stream_set_underflow_callback); + LOAD_FUNC(pa_stream_new); + LOAD_FUNC(pa_stream_disconnect); + LOAD_FUNC(pa_threaded_mainloop_lock); + LOAD_FUNC(pa_channel_map_init_auto); + LOAD_FUNC(pa_channel_map_parse); + LOAD_FUNC(pa_channel_map_snprint); + LOAD_FUNC(pa_channel_map_equal); + LOAD_FUNC(pa_context_get_server_info); + LOAD_FUNC(pa_context_get_sink_info_by_name); + LOAD_FUNC(pa_context_get_sink_info_list); + LOAD_FUNC(pa_context_get_source_info_list); + LOAD_FUNC(pa_operation_get_state); + LOAD_FUNC(pa_operation_unref); +#undef LOAD_FUNC +#define LOAD_OPTIONAL_FUNC(x) do { \ + p##x = GetSymbol(pa_handle, #x); \ +} while(0) +#if PA_CHECK_VERSION(0,9,15) + LOAD_OPTIONAL_FUNC(pa_channel_map_superset); + LOAD_OPTIONAL_FUNC(pa_stream_set_buffer_attr_callback); +#endif +#if PA_CHECK_VERSION(0,9,16) + LOAD_OPTIONAL_FUNC(pa_stream_begin_write); +#endif +#undef LOAD_OPTIONAL_FUNC + +#else /* HAVE_DYNLOAD */ + pa_handle = (void*)0xDEADBEEF; +#endif + + 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_TRUE); + if(context) + { + ret = ALC_TRUE; + + 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); + + if(!ret) + { +#ifdef HAVE_DYNLOAD + CloseLib(pa_handle); +#endif + pa_handle = NULL; + } + } + return ret; +} //}}} + +// PulseAudio Event Callbacks //{{{ +static void stream_state_callback(pa_stream *stream, void *pdata) //{{{ +{ + pa_threaded_mainloop *loop = pdata; + pa_stream_state_t state; + + state = pa_stream_get_state(stream); + if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state)) + pa_threaded_mainloop_signal(loop, 0); +}//}}} + +static void stream_signal_callback(pa_stream *stream, void *pdata) //{{{ +{ + ALCdevice *Device = pdata; + pulse_data *data = Device->ExtraData; + (void)stream; + + pa_threaded_mainloop_signal(data->loop, 0); +}//}}} + +static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{ +{ + ALCdevice *Device = pdata; + pulse_data *data = Device->ExtraData; + + LockDevice(Device); + + data->attr = *(pa_stream_get_buffer_attr(stream)); + Device->UpdateSize = data->attr.minreq / data->frame_size; + Device->NumUpdates = (data->attr.tlength/data->frame_size) / Device->UpdateSize; + if(Device->NumUpdates <= 1) + { + Device->NumUpdates = 1; + ERR("PulseAudio returned minreq > tlength/2; expect break up\n"); + } + + UnlockDevice(Device); +}//}}} + +static void stream_device_callback(pa_stream *stream, void *pdata) //{{{ +{ + ALCdevice *Device = pdata; + pulse_data *data = Device->ExtraData; + + free(data->device_name); + data->device_name = strdup(pa_stream_get_device_name(stream)); +}//}}} + +static void context_state_callback2(pa_context *context, void *pdata) //{{{ +{ + ALCdevice *Device = pdata; + pulse_data *data = Device->ExtraData; + + if(pa_context_get_state(context) == PA_CONTEXT_FAILED) + { + ERR("Received context failure!\n"); + aluHandleDisconnect(Device); + } + pa_threaded_mainloop_signal(data->loop, 0); +}//}}} + +static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{ +{ + ALCdevice *Device = pdata; + pulse_data *data = Device->ExtraData; + + if(pa_stream_get_state(stream) == PA_STREAM_FAILED) + { + ERR("Received stream failure!\n"); + aluHandleDisconnect(Device); + } + 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_threaded_mainloop_signal(data->loop, 0); +}//}}} + +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) + { + pa_threaded_mainloop_signal(data->loop, 0); + return; + } + + for(i = 0;chanmaps[i].str;i++) + { + pa_channel_map map; + if(!pa_channel_map_parse(&map, chanmaps[i].str)) + continue; + + 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_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 void sink_device_callback(pa_context *context, const pa_sink_info *info, int eol, void *pdata) //{{{ +{ + pa_threaded_mainloop *loop = pdata; + char str[1024]; + void *temp; + int count; + ALuint i; + + (void)context; + + if(eol) + { + pa_threaded_mainloop_signal(loop, 0); + return; + } + + count = 0; + do { + if(count == 0) + snprintf(str, sizeof(str), "%s", info->description); + else + snprintf(str, sizeof(str), "%s #%d", info->description, count+1); + count++; + + for(i = 0;i < numDevNames;i++) + { + if(strcmp(str, allDevNameMap[i].name) == 0) + break; + } + } while(i != numDevNames); + + temp = realloc(allDevNameMap, (numDevNames+1) * sizeof(*allDevNameMap)); + if(temp) + { + allDevNameMap = temp; + allDevNameMap[numDevNames].name = strdup(str); + allDevNameMap[numDevNames].device_name = strdup(info->name); + numDevNames++; + } +}//}}} + +static void source_device_callback(pa_context *context, const pa_source_info *info, int eol, void *pdata) //{{{ +{ + pa_threaded_mainloop *loop = pdata; + char str[1024]; + void *temp; + int count; + ALuint i; + + (void)context; + + if(eol) + { + pa_threaded_mainloop_signal(loop, 0); + return; + } + + count = 0; + do { + if(count == 0) + snprintf(str, sizeof(str), "%s", info->description); + else + snprintf(str, sizeof(str), "%s #%d", info->description, count+1); + count++; + + for(i = 0;i < numCaptureDevNames;i++) + { + if(strcmp(str, allCaptureDevNameMap[i].name) == 0) + break; + } + } while(i != numCaptureDevNames); + + temp = realloc(allCaptureDevNameMap, (numCaptureDevNames+1) * sizeof(*allCaptureDevNameMap)); + if(temp) + { + allCaptureDevNameMap = temp; + allCaptureDevNameMap[numCaptureDevNames].name = strdup(str); + allCaptureDevNameMap[numCaptureDevNames].device_name = strdup(info->name); + numCaptureDevNames++; + } +}//}}} +//}}} + +// PulseAudio I/O Callbacks //{{{ +static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{ +{ + ALCdevice *Device = pdata; + pulse_data *data = Device->ExtraData; + (void)stream; + (void)len; + + pa_threaded_mainloop_signal(data->loop, 0); +} //}}} +//}}} + +static ALuint PulseProc(ALvoid *param) +{ + ALCdevice *Device = param; + pulse_data *data = Device->ExtraData; + ssize_t len; + + SetRTPriority(); + + pa_threaded_mainloop_lock(data->loop); + do { + len = (Device->Connected ? pa_stream_writable_size(data->stream) : 0); + len -= len%(Device->UpdateSize*data->frame_size); + if(len == 0) + { + pa_threaded_mainloop_wait(data->loop); + continue; + } + + while(len > 0) + { + size_t newlen = len; + void *buf; + pa_free_cb_t free_func = NULL; + +#if PA_CHECK_VERSION(0,9,16) + if(!pa_stream_begin_write || + pa_stream_begin_write(data->stream, &buf, &newlen) < 0) +#endif + { + buf = pa_xmalloc(newlen); + free_func = pa_xfree; + } + pa_threaded_mainloop_unlock(data->loop); + + aluMixData(Device, buf, newlen/data->frame_size); + + pa_threaded_mainloop_lock(data->loop); + pa_stream_write(data->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); + len -= newlen; + } + } while(Device->Connected && !data->killNow); + pa_threaded_mainloop_unlock(data->loop); + + return 0; +} + +static pa_stream *connect_playback_stream(ALCdevice *device, + pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, + pa_channel_map *chanmap) +{ + pulse_data *data = device->ExtraData; + pa_stream_state_t state; + pa_stream *stream; + + stream = pa_stream_new(data->context, "Playback Stream", spec, chanmap); + if(!stream) + { + ERR("pa_stream_new() failed: %s\n", + pa_strerror(pa_context_errno(data->context))); + return NULL; + } + + pa_stream_set_state_callback(stream, stream_state_callback, data->loop); + + if(pa_stream_connect_playback(stream, data->device_name, attr, flags, NULL, NULL) < 0) + { + ERR("Stream did not connect: %s\n", + pa_strerror(pa_context_errno(data->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(data->context))); + pa_stream_unref(stream); + return NULL; + } + + pa_threaded_mainloop_wait(data->loop); + } + pa_stream_set_state_callback(stream, NULL, NULL); + + return stream; +} + +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) + o = pa_context_get_sink_info_list(context, sink_device_callback, loop); + else + o = pa_context_get_source_info_list(context, source_device_callback, loop); + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(loop); + pa_operation_unref(o); + + 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 ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{ +{ + 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); + + device->szDeviceName = strdup(device_name); + + 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_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); + + DestroyRingBuffer(data->ring); + free(data->device_name); + + device->ExtraData = NULL; + pa_xfree(data); +} //}}} +//}}} + +// OpenAL {{{ +static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{ +{ + char *pulse_name = NULL; + pa_sample_spec spec; + pulse_data *data; + + if(!allDevNameMap) + probe_devices(AL_FALSE); + + if(!device_name) + device_name = pulse_device; + else if(strcmp(device_name, pulse_device) != 0) + { + ALuint i; + + for(i = 0;i < numDevNames;i++) + { + if(strcmp(device_name, allDevNameMap[i].name) == 0) + { + pulse_name = allDevNameMap[i].device_name; + break; + } + } + if(i == numDevNames) + return ALC_FALSE; + } + + if(pulse_open(device, device_name) == ALC_FALSE) + return ALC_FALSE; + + data = device->ExtraData; + + pa_threaded_mainloop_lock(data->loop); + + spec.format = PA_SAMPLE_S16NE; + spec.rate = 44100; + spec.channels = 2; + + data->device_name = pulse_name; + pa_stream *stream = connect_playback_stream(device, 0, NULL, &spec, NULL); + if(!stream) + { + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + if(pa_stream_is_suspended(stream)) + { + ERR("Device is suspended\n"); + pa_stream_disconnect(stream); + pa_stream_unref(stream); + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + data->device_name = strdup(pa_stream_get_device_name(stream)); + + pa_stream_disconnect(stream); + pa_stream_unref(stream); + + pa_threaded_mainloop_unlock(data->loop); + + return ALC_TRUE; + +fail: + pulse_close(device); + return ALC_FALSE; +} //}}} + +static void pulse_close_playback(ALCdevice *device) //{{{ +{ + pulse_close(device); +} //}}} + +static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{ +{ + pulse_data *data = device->ExtraData; + pa_stream_flags_t flags = 0; + pa_channel_map chanmap; + + pa_threaded_mainloop_lock(data->loop); + + 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); + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(data->loop); + pa_operation_unref(o); + } + if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) + flags |= PA_STREAM_FIX_RATE; + + data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + data->attr.prebuf = -1; + data->attr.fragsize = -1; + data->attr.minreq = device->UpdateSize * data->frame_size; + data->attr.tlength = data->attr.minreq * device->NumUpdates; + if(data->attr.tlength < data->attr.minreq*2) + data->attr.tlength = data->attr.minreq*2; + data->attr.maxlength = data->attr.tlength; + flags |= PA_STREAM_EARLY_REQUESTS; + flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE; + + switch(device->FmtType) + { + case DevFmtByte: + device->FmtType = DevFmtUByte; + /* fall-through */ + case DevFmtUByte: + data->spec.format = PA_SAMPLE_U8; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + data->spec.format = PA_SAMPLE_S16NE; + break; + case DevFmtFloat: + data->spec.format = PA_SAMPLE_FLOAT32NE; + break; + } + data->spec.rate = device->Frequency; + data->spec.channels = ChannelsFromDevFmt(device->FmtChans); + + if(pa_sample_spec_valid(&data->spec) == 0) + { + ERR("Invalid sample format\n"); + pa_threaded_mainloop_unlock(data->loop); + return ALC_FALSE; + } + + if(!pa_channel_map_init_auto(&chanmap, data->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); + return ALC_FALSE; + } + SetDefaultWFXChannelOrder(device); + + data->stream = connect_playback_stream(device, flags, &data->attr, &data->spec, &chanmap); + if(!data->stream) + { + pa_threaded_mainloop_unlock(data->loop); + return ALC_FALSE; + } + + pa_stream_set_state_callback(data->stream, stream_state_callback2, device); + + data->spec = *(pa_stream_get_sample_spec(data->stream)); + if(device->Frequency != data->spec.rate) + { + pa_operation *o; + + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("Failed to set frequency %dhz, got %dhz instead\n", device->Frequency, data->spec.rate); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + + /* Server updated our playback rate, so modify the buffer attribs + * accordingly. */ + data->attr.minreq = (ALuint64)(data->attr.minreq/data->frame_size) * + data->spec.rate / device->Frequency * data->frame_size; + data->attr.tlength = data->attr.minreq * device->NumUpdates; + data->attr.maxlength = data->attr.tlength; + + o = pa_stream_set_buffer_attr(data->stream, &data->attr, + stream_success_callback, device); + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(data->loop); + pa_operation_unref(o); + + device->Frequency = data->spec.rate; + } + + stream_buffer_attr_callback(data->stream, 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); +#endif + pa_stream_set_moved_callback(data->stream, stream_device_callback, device); + pa_stream_set_write_callback(data->stream, stream_write_callback, device); + pa_stream_set_underflow_callback(data->stream, stream_signal_callback, device); + + data->thread = StartThread(PulseProc, device); + if(!data->thread) + { +#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_set_moved_callback(data->stream, NULL, NULL); + pa_stream_set_write_callback(data->stream, NULL, NULL); + pa_stream_set_underflow_callback(data->stream, NULL, NULL); + pa_stream_disconnect(data->stream); + pa_stream_unref(data->stream); + data->stream = NULL; + + pa_threaded_mainloop_unlock(data->loop); + return ALC_FALSE; + } + + pa_threaded_mainloop_unlock(data->loop); + return ALC_TRUE; +} //}}} + +static void pulse_stop_playback(ALCdevice *device) //{{{ +{ + pulse_data *data = device->ExtraData; + + if(!data->stream) + return; + + data->killNow = AL_TRUE; + if(data->thread) + { + pa_threaded_mainloop_signal(data->loop, 0); + StopThread(data->thread); + data->thread = NULL; + } + data->killNow = AL_FALSE; + + pa_threaded_mainloop_lock(data->loop); + +#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_set_moved_callback(data->stream, NULL, NULL); + pa_stream_set_write_callback(data->stream, NULL, NULL); + pa_stream_set_underflow_callback(data->stream, NULL, NULL); + pa_stream_disconnect(data->stream); + pa_stream_unref(data->stream); + data->stream = NULL; + + pa_threaded_mainloop_unlock(data->loop); +} //}}} + + +static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{ +{ + char *pulse_name = NULL; + pulse_data *data; + pa_stream_flags_t flags = 0; + pa_stream_state_t state; + pa_channel_map chanmap; + + if(!allCaptureDevNameMap) + probe_devices(AL_TRUE); + + if(!device_name) + device_name = pulse_device; + else if(strcmp(device_name, pulse_device) != 0) + { + ALuint i; + + for(i = 0;i < numCaptureDevNames;i++) + { + if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0) + { + pulse_name = allCaptureDevNameMap[i].device_name; + break; + } + } + if(i == numCaptureDevNames) + return ALC_FALSE; + } + + if(pulse_open(device, device_name) == ALC_FALSE) + return ALC_FALSE; + + data = device->ExtraData; + pa_threaded_mainloop_lock(data->loop); + + data->samples = device->UpdateSize * device->NumUpdates; + data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + if(data->samples < 100 * device->Frequency / 1000) + data->samples = 100 * device->Frequency / 1000; + + if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples))) + { + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + data->attr.minreq = -1; + data->attr.prebuf = -1; + data->attr.maxlength = data->samples * data->frame_size; + data->attr.tlength = -1; + data->attr.fragsize = minu(data->samples, 50*device->Frequency/1000) * + data->frame_size; + + data->spec.rate = device->Frequency; + data->spec.channels = ChannelsFromDevFmt(device->FmtChans); + + switch(device->FmtType) + { + case DevFmtUByte: + data->spec.format = PA_SAMPLE_U8; + break; + case DevFmtShort: + data->spec.format = PA_SAMPLE_S16NE; + break; + case DevFmtFloat: + data->spec.format = PA_SAMPLE_FLOAT32NE; + break; + case DevFmtByte: + case DevFmtUShort: + ERR("Capture format type %#x capture not supported on PulseAudio\n", device->FmtType); + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + if(pa_sample_spec_valid(&data->spec) == 0) + { + ERR("Invalid sample format\n"); + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + if(!pa_channel_map_init_auto(&chanmap, data->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); + goto fail; + } + + data->stream = pa_stream_new(data->context, "Capture Stream", &data->spec, &chanmap); + if(!data->stream) + { + ERR("pa_stream_new() failed: %s\n", + pa_strerror(pa_context_errno(data->context))); + + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + pa_stream_set_state_callback(data->stream, stream_state_callback, data->loop); + + flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY; + if(pa_stream_connect_record(data->stream, pulse_name, &data->attr, flags) < 0) + { + ERR("Stream did not connect: %s\n", + pa_strerror(pa_context_errno(data->context))); + + pa_stream_unref(data->stream); + data->stream = NULL; + + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + while((state=pa_stream_get_state(data->stream)) != PA_STREAM_READY) + { + if(!PA_STREAM_IS_GOOD(state)) + { + ERR("Stream did not get ready: %s\n", + pa_strerror(pa_context_errno(data->context))); + + pa_stream_unref(data->stream); + data->stream = NULL; + + pa_threaded_mainloop_unlock(data->loop); + goto fail; + } + + pa_threaded_mainloop_wait(data->loop); + } + pa_stream_set_state_callback(data->stream, stream_state_callback2, device); + + pa_threaded_mainloop_unlock(data->loop); + return ALC_TRUE; + +fail: + pulse_close(device); + return ALC_FALSE; +} //}}} + +static void pulse_close_capture(ALCdevice *device) //{{{ +{ + pulse_close(device); +} //}}} + +static void pulse_start_capture(ALCdevice *device) //{{{ +{ + pulse_data *data = device->ExtraData; + pa_operation *o; + + pa_threaded_mainloop_lock(data->loop); + o = pa_stream_cork(data->stream, 0, stream_success_callback, device); + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(data->loop); + pa_operation_unref(o); + pa_threaded_mainloop_unlock(data->loop); +} //}}} + +static void pulse_stop_capture(ALCdevice *device) //{{{ +{ + pulse_data *data = device->ExtraData; + pa_operation *o; + + pa_threaded_mainloop_lock(data->loop); + o = pa_stream_cork(data->stream, 1, stream_success_callback, device); + while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(data->loop); + pa_operation_unref(o); + pa_threaded_mainloop_unlock(data->loop); +} //}}} + +static ALCuint pulse_available_samples(ALCdevice *device) //{{{ +{ + pulse_data *data = device->ExtraData; + size_t samples; + + pa_threaded_mainloop_lock(data->loop); + /* Capture is done in fragment-sized chunks, so we loop until we get all + * that's available */ + samples = (device->Connected ? pa_stream_readable_size(data->stream) : 0); + while(samples > 0) + { + const void *buf; + size_t length; + + if(pa_stream_peek(data->stream, &buf, &length) < 0) + { + ERR("pa_stream_peek() failed: %s\n", + pa_strerror(pa_context_errno(data->context))); + break; + } + + WriteRingBuffer(data->ring, buf, length/data->frame_size); + samples -= length; + + pa_stream_drop(data->stream); + } + pa_threaded_mainloop_unlock(data->loop); + + return RingBufferSize(data->ring); +} //}}} + +static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{ +{ + pulse_data *data = device->ExtraData; + + if(pulse_available_samples(device) >= samples) + ReadRingBuffer(data->ring, buffer, samples); + else + alcSetError(device, ALC_INVALID_VALUE); +} //}}} + + +static const BackendFuncs pulse_funcs = { //{{{ + pulse_open_playback, + pulse_close_playback, + pulse_reset_playback, + pulse_stop_playback, + pulse_open_capture, + pulse_close_capture, + pulse_start_capture, + pulse_stop_capture, + pulse_capture_samples, + pulse_available_samples +}; //}}} + +ALCboolean alc_pulse_init(BackendFuncs *func_list) //{{{ +{ + if(!pulse_load()) + return ALC_FALSE; + + *func_list = pulse_funcs; + + pulse_ctx_flags = 0; + if(!GetConfigValueBool("pulse", "spawn-server", 0)) + pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN; + + return ALC_TRUE; +} //}}} + +void alc_pulse_deinit(void) //{{{ +{ + ALuint i; + + for(i = 0;i < numDevNames;++i) + { + free(allDevNameMap[i].name); + free(allDevNameMap[i].device_name); + } + free(allDevNameMap); + allDevNameMap = NULL; + numDevNames = 0; + + for(i = 0;i < numCaptureDevNames;++i) + { + free(allCaptureDevNameMap[i].name); + free(allCaptureDevNameMap[i].device_name); + } + free(allCaptureDevNameMap); + allCaptureDevNameMap = NULL; + numCaptureDevNames = 0; + +#ifdef HAVE_DYNLOAD + if(pa_handle) + CloseLib(pa_handle); + pa_handle = NULL; +#endif +} //}}} + +void alc_pulse_probe(enum DevProbe type) //{{{ +{ + pa_threaded_mainloop *loop; + ALuint i; + + switch(type) + { + case DEVICE_PROBE: + 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) + { + AppendDeviceList(pulse_device); + + 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); + break; + + case ALL_DEVICE_PROBE: + for(i = 0;i < numDevNames;++i) + { + free(allDevNameMap[i].name); + free(allDevNameMap[i].device_name); + } + free(allDevNameMap); + allDevNameMap = NULL; + numDevNames = 0; + + probe_devices(AL_FALSE); + + for(i = 0;i < numDevNames;i++) + AppendAllDeviceList(allDevNameMap[i].name); + break; + + case CAPTURE_DEVICE_PROBE: + for(i = 0;i < numCaptureDevNames;++i) + { + free(allCaptureDevNameMap[i].name); + free(allCaptureDevNameMap[i].device_name); + } + free(allCaptureDevNameMap); + allCaptureDevNameMap = NULL; + numCaptureDevNames = 0; + + probe_devices(AL_TRUE); + + for(i = 0;i < numCaptureDevNames;i++) + AppendCaptureDeviceList(allCaptureDevNameMap[i].name); + break; + } +} //}}} +//}}} diff -r 000000000000 -r f9476ff7637e Alc/backends/send.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/send.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,472 @@ +#include "config.h" +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" +#include "alSource.h" +#include + +//////////////////// Summary + +struct send_data; +struct context_data; + +static void addContext(ALCdevice *, ALCcontext *); +static void syncContexts(ALCcontext *master, ALCcontext *slave); +static void syncSources(ALsource *master, ALsource *slave, + ALCcontext *masterCtx, ALCcontext *slaveCtx); + +static void syncSourcei(ALuint master, ALuint slave, + ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param); +static void syncSourcef(ALuint master, ALuint slave, + ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param); +static void syncSource3f(ALuint master, ALuint slave, + ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param); + +static void swapInContext(ALCdevice *, struct context_data *); +static void saveContext(ALCdevice *, struct context_data *); +static void limitContext(ALCdevice *, ALCcontext *); +static void unLimitContext(ALCdevice *); + +static void init(ALCdevice *); +static void renderData(ALCdevice *, int samples); + +#define UNUSED(x) (void)(x) + +//////////////////// State + +typedef struct context_data { + ALfloat ClickRemoval[MAXCHANNELS]; + ALfloat PendingClicks[MAXCHANNELS]; + ALvoid *renderBuffer; + ALCcontext *ctx; +} context_data; + +typedef struct send_data { + ALuint size; + context_data **contexts; + ALuint numContexts; + ALuint maxContexts; +} send_data; + + + +//////////////////// Context Creation / Synchronization + +#define _MAKE_SYNC(NAME, INIT_EXPR, GET_EXPR, SET_EXPR) \ + void NAME (ALuint sourceID1, ALuint sourceID2, \ + ALCcontext *ctx1, ALCcontext *ctx2, \ + ALenum param){ \ + INIT_EXPR; \ + ALCcontext *current = alcGetCurrentContext(); \ + alcMakeContextCurrent(ctx1); \ + GET_EXPR; \ + alcMakeContextCurrent(ctx2); \ + SET_EXPR; \ + alcMakeContextCurrent(current); \ + } + +#define MAKE_SYNC(NAME, TYPE, GET, SET) \ + _MAKE_SYNC(NAME, \ + TYPE value, \ + GET(sourceID1, param, &value), \ + SET(sourceID2, param, value)) + +#define MAKE_SYNC3(NAME, TYPE, GET, SET) \ + _MAKE_SYNC(NAME, \ + TYPE value1; TYPE value2; TYPE value3;, \ + GET(sourceID1, param, &value1, &value2, &value3), \ + SET(sourceID2, param, value1, value2, value3)) + +MAKE_SYNC( syncSourcei, ALint, alGetSourcei, alSourcei); +MAKE_SYNC( syncSourcef, ALfloat, alGetSourcef, alSourcef); +MAKE_SYNC3(syncSource3i, ALint, alGetSource3i, alSource3i); +MAKE_SYNC3(syncSource3f, ALfloat, alGetSource3f, alSource3f); + +void syncSources(ALsource *masterSource, ALsource *slaveSource, + ALCcontext *masterCtx, ALCcontext *slaveCtx){ + ALuint master = masterSource->source; + ALuint slave = slaveSource->source; + ALCcontext *current = alcGetCurrentContext(); + + syncSourcef(master,slave,masterCtx,slaveCtx,AL_PITCH); + syncSourcef(master,slave,masterCtx,slaveCtx,AL_GAIN); + syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_DISTANCE); + syncSourcef(master,slave,masterCtx,slaveCtx,AL_ROLLOFF_FACTOR); + syncSourcef(master,slave,masterCtx,slaveCtx,AL_REFERENCE_DISTANCE); + syncSourcef(master,slave,masterCtx,slaveCtx,AL_MIN_GAIN); + syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_GAIN); + syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_GAIN); + syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_INNER_ANGLE); + syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_ANGLE); + syncSourcef(master,slave,masterCtx,slaveCtx,AL_SEC_OFFSET); + syncSourcef(master,slave,masterCtx,slaveCtx,AL_SAMPLE_OFFSET); + syncSourcef(master,slave,masterCtx,slaveCtx,AL_BYTE_OFFSET); + + syncSource3f(master,slave,masterCtx,slaveCtx,AL_POSITION); + syncSource3f(master,slave,masterCtx,slaveCtx,AL_VELOCITY); + syncSource3f(master,slave,masterCtx,slaveCtx,AL_DIRECTION); + + syncSourcei(master,slave,masterCtx,slaveCtx,AL_SOURCE_RELATIVE); + syncSourcei(master,slave,masterCtx,slaveCtx,AL_LOOPING); + + alcMakeContextCurrent(masterCtx); + ALint source_type; + alGetSourcei(master, AL_SOURCE_TYPE, &source_type); + + // Only static sources are currently synchronized! + if (AL_STATIC == source_type){ + ALint master_buffer; + ALint slave_buffer; + alGetSourcei(master, AL_BUFFER, &master_buffer); + alcMakeContextCurrent(slaveCtx); + alGetSourcei(slave, AL_BUFFER, &slave_buffer); + if (master_buffer != slave_buffer){ + alSourcei(slave, AL_BUFFER, master_buffer); + } + } + + // Synchronize the state of the two sources. + alcMakeContextCurrent(masterCtx); + ALint masterState; + ALint slaveState; + + alGetSourcei(master, AL_SOURCE_STATE, &masterState); + alcMakeContextCurrent(slaveCtx); + alGetSourcei(slave, AL_SOURCE_STATE, &slaveState); + + if (masterState != slaveState){ + switch (masterState){ + case AL_INITIAL : alSourceRewind(slave); break; + case AL_PLAYING : alSourcePlay(slave); break; + case AL_PAUSED : alSourcePause(slave); break; + case AL_STOPPED : alSourceStop(slave); break; + } + } + // Restore whatever context was previously active. + alcMakeContextCurrent(current); +} + + +void syncContexts(ALCcontext *master, ALCcontext *slave){ + /* If there aren't sufficient sources in slave to mirror + the sources in master, create them. */ + ALCcontext *current = alcGetCurrentContext(); + + UIntMap *masterSourceMap = &(master->SourceMap); + UIntMap *slaveSourceMap = &(slave->SourceMap); + ALuint numMasterSources = masterSourceMap->size; + ALuint numSlaveSources = slaveSourceMap->size; + + alcMakeContextCurrent(slave); + if (numSlaveSources < numMasterSources){ + ALuint numMissingSources = numMasterSources - numSlaveSources; + ALuint newSources[numMissingSources]; + alGenSources(numMissingSources, newSources); + } + + /* Now, slave is gauranteed to have at least as many sources + as master. Sync each source from master to the corresponding + source in slave. */ + int i; + for(i = 0; i < masterSourceMap->size; i++){ + syncSources((ALsource*)masterSourceMap->array[i].value, + (ALsource*)slaveSourceMap->array[i].value, + master, slave); + } + alcMakeContextCurrent(current); +} + +static void addContext(ALCdevice *Device, ALCcontext *context){ + send_data *data = (send_data*)Device->ExtraData; + // expand array if necessary + if (data->numContexts >= data->maxContexts){ + ALuint newMaxContexts = data->maxContexts*2 + 1; + data->contexts = realloc(data->contexts, newMaxContexts*sizeof(context_data)); + data->maxContexts = newMaxContexts; + } + // create context_data and add it to the main array + context_data *ctxData; + ctxData = (context_data*)calloc(1, sizeof(*ctxData)); + ctxData->renderBuffer = malloc(data->size); + ctxData->ctx = context; + + data->contexts[data->numContexts] = ctxData; + data->numContexts++; +} + + +//////////////////// Context Switching + +/* A device brings along with it two pieces of state + * which have to be swapped in and out with each context. + */ +static void swapInContext(ALCdevice *Device, context_data *ctxData){ + memcpy(Device->ClickRemoval, ctxData->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS); + memcpy(Device->PendingClicks, ctxData->PendingClicks, sizeof(ALfloat)*MAXCHANNELS); +} + +static void saveContext(ALCdevice *Device, context_data *ctxData){ + memcpy(ctxData->ClickRemoval, Device->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS); + memcpy(ctxData->PendingClicks, Device->PendingClicks, sizeof(ALfloat)*MAXCHANNELS); +} + +static ALCcontext **currentContext; +static ALuint currentNumContext; + +/* By default, all contexts are rendered at once for each call to aluMixData. + * This function uses the internals of the ALCdecice struct to temporarly + * cause aluMixData to only render the chosen context. + */ +static void limitContext(ALCdevice *Device, ALCcontext *ctx){ + currentContext = Device->Contexts; + currentNumContext = Device->NumContexts; + Device->Contexts = &ctx; + Device->NumContexts = 1; +} + +static void unLimitContext(ALCdevice *Device){ + Device->Contexts = currentContext; + Device->NumContexts = currentNumContext; +} + + +//////////////////// Main Device Loop + +/* Establish the LWJGL context as the main context, which will + * be synchronized to all the slave contexts + */ +static void init(ALCdevice *Device){ + ALCcontext *masterContext = alcGetCurrentContext(); + addContext(Device, masterContext); +} + + +static void renderData(ALCdevice *Device, int samples){ + if(!Device->Connected){return;} + send_data *data = (send_data*)Device->ExtraData; + ALCcontext *current = alcGetCurrentContext(); + + ALuint i; + for (i = 1; i < data->numContexts; i++){ + syncContexts(data->contexts[0]->ctx , data->contexts[i]->ctx); + } + + if ((uint) samples > Device->UpdateSize){ + printf("exceeding internal buffer size; dropping samples\n"); + printf("requested %d; available %d\n", samples, Device->UpdateSize); + samples = (int) Device->UpdateSize; + } + + for (i = 0; i < data->numContexts; i++){ + context_data *ctxData = data->contexts[i]; + ALCcontext *ctx = ctxData->ctx; + alcMakeContextCurrent(ctx); + limitContext(Device, ctx); + swapInContext(Device, ctxData); + aluMixData(Device, ctxData->renderBuffer, samples); + saveContext(Device, ctxData); + unLimitContext(Device); + } + alcMakeContextCurrent(current); +} + + +//////////////////// JNI Methods + +#include "com_aurellem_capture_AudioSend.h" + +/* + * Class: com_aurellem_capture_AudioSend + * Method: nstep + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_nstep +(JNIEnv *env, jclass clazz, jlong device, jint samples){ + UNUSED(env);UNUSED(clazz);UNUSED(device); + renderData((ALCdevice*)((intptr_t)device), samples); +} + +/* + * Class: com_aurellem_capture_AudioSend + * Method: ngetSamples + * Signature: (JLjava/nio/ByteBuffer;III)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_ngetSamples +(JNIEnv *env, jclass clazz, jlong device, jobject buffer, jint position, + jint samples, jint n){ + UNUSED(clazz); + + ALvoid *buffer_address = + ((ALbyte *)(((char*)(*env)->GetDirectBufferAddress(env, buffer)) + position)); + ALCdevice *recorder = (ALCdevice*) ((intptr_t)device); + send_data *data = (send_data*)recorder->ExtraData; + if ((ALuint)n > data->numContexts){return;} + if ((uint) samples > data->size){ + samples = (int) data->size; + } + memcpy(buffer_address, data->contexts[n]->renderBuffer, samples*sizeof(ALfloat)); +} + +/* + * Class: com_aurellem_capture_AudioSend + * Method: naddListener + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_naddListener +(JNIEnv *env, jclass clazz, jlong device){ + UNUSED(env); UNUSED(clazz); + printf("creating new context via naddListener\n"); + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); + ALCcontext *new = alcCreateContext(Device, NULL); + addContext(Device, new); +} + +/* + * Class: com_aurellem_capture_AudioSend + * Method: nsetNthListener3f + * Signature: (IFFFJI)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_nsetNthListener3f + (JNIEnv *env, jclass clazz, jint param, + jfloat v1, jfloat v2, jfloat v3, jlong device, jint contextNum){ + UNUSED(env);UNUSED(clazz); + + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); + send_data *data = (send_data*)Device->ExtraData; + + ALCcontext *current = alcGetCurrentContext(); + if ((ALuint)contextNum > data->numContexts){return;} + alcMakeContextCurrent(data->contexts[contextNum]->ctx); + alListener3f(param, v1, v2, v3); + alcMakeContextCurrent(current); +} + +/* + * Class: com_aurellem_capture_AudioSend + * Method: nsetNthListenerf + * Signature: (IFJI)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_nsetNthListenerf +(JNIEnv *env, jclass clazz, jint param, jfloat v1, jlong device, + jint contextNum){ + + UNUSED(env);UNUSED(clazz); + + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); + send_data *data = (send_data*)Device->ExtraData; + + ALCcontext *current = alcGetCurrentContext(); + if ((ALuint)contextNum > data->numContexts){return;} + alcMakeContextCurrent(data->contexts[contextNum]->ctx); + alListenerf(param, v1); + alcMakeContextCurrent(current); +} + +/* + * Class: com_aurellem_capture_AudioSend + * Method: ninitDevice + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_ninitDevice +(JNIEnv *env, jclass clazz, jlong device){ + UNUSED(env);UNUSED(clazz); + + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); + init(Device); + +} + + +//////////////////// Device Initilization / Management + +static const ALCchar sendDevice[] = "Multiple Audio Send"; + +static ALCboolean send_open_playback(ALCdevice *device, + const ALCchar *deviceName) +{ + send_data *data; + // stop any buffering for stdout, so that I can + // see the printf statements in my terminal immediatley + setbuf(stdout, NULL); + + if(!deviceName) + deviceName = sendDevice; + else if(strcmp(deviceName, sendDevice) != 0) + return ALC_FALSE; + data = (send_data*)calloc(1, sizeof(*data)); + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + return ALC_TRUE; +} + +static void send_close_playback(ALCdevice *device) +{ + send_data *data = (send_data*)device->ExtraData; + alcMakeContextCurrent(NULL); + ALuint i; + // Destroy all slave contexts. LWJGL will take care of + // its own context. + for (i = 1; i < data->numContexts; i++){ + context_data *ctxData = data->contexts[i]; + alcDestroyContext(ctxData->ctx); + free(ctxData->renderBuffer); + free(ctxData); + } + free(data); + device->ExtraData = NULL; +} + +static ALCboolean send_reset_playback(ALCdevice *device) +{ + send_data *data = (send_data*)device->ExtraData; + ALuint channels=0, bits=0; + device->FmtType = DevFmtShort; + bits = BytesFromDevFmt(device->FmtType) * 8; + channels = ChannelsFromDevFmt(device->FmtChans); + data->size = device->UpdateSize * channels * bits / 8; + + return ALC_TRUE; +} + +static void send_stop_playback(ALCdevice *Device){ + UNUSED(Device); +} + +static const BackendFuncs send_funcs = { + send_open_playback, + send_close_playback, + send_reset_playback, + send_stop_playback, + NULL, + NULL, /* These would be filled with functions to */ + NULL, /* handle capturing audio if we we into that */ + NULL, /* sort of thing... */ + NULL, + NULL +}; + +ALCboolean alc_send_init(BackendFuncs *func_list){ + *func_list = send_funcs; + return ALC_TRUE; +} + +void alc_send_deinit(void){} + +void alc_send_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(sendDevice); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(sendDevice); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} + + + diff -r 000000000000 -r f9476ff7637e Alc/backends/sndio.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/sndio.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,381 @@ +/** + * 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 +#include +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include + + +static const ALCchar sndio_device[] = "SndIO Default"; + + +static void *sndio_handle; +#ifdef HAVE_DYNLOAD +#define MAKE_FUNC(x) static typeof(x) * p##x +MAKE_FUNC(sio_initpar); +MAKE_FUNC(sio_open); +MAKE_FUNC(sio_close); +MAKE_FUNC(sio_setpar); +MAKE_FUNC(sio_getpar); +MAKE_FUNC(sio_getcap); +MAKE_FUNC(sio_onmove); +MAKE_FUNC(sio_write); +MAKE_FUNC(sio_read); +MAKE_FUNC(sio_start); +MAKE_FUNC(sio_stop); +MAKE_FUNC(sio_nfds); +MAKE_FUNC(sio_pollfd); +MAKE_FUNC(sio_revents); +MAKE_FUNC(sio_eof); +MAKE_FUNC(sio_setvol); +MAKE_FUNC(sio_onvol); + +#define sio_initpar psio_initpar +#define sio_open psio_open +#define sio_close psio_close +#define sio_setpar psio_setpar +#define sio_getpar psio_getpar +#define sio_getcap psio_getcap +#define sio_onmove psio_onmove +#define sio_write psio_write +#define sio_read psio_read +#define sio_start psio_start +#define sio_stop psio_stop +#define sio_nfds psio_nfds +#define sio_pollfd psio_pollfd +#define sio_revents psio_revents +#define sio_eof psio_eof +#define sio_setvol psio_setvol +#define sio_onvol psio_onvol +#endif + + +static ALCboolean sndio_load(void) +{ + if(!sndio_handle) + { +#ifdef HAVE_DYNLOAD + sndio_handle = LoadLib("libsndio.so"); + if(!sndio_handle) + return ALC_FALSE; + +#define LOAD_FUNC(f) do { \ + p##f = GetSymbol(sndio_handle, #f); \ + if(p##f == NULL) { \ + CloseLib(sndio_handle); \ + sndio_handle = NULL; \ + return ALC_FALSE; \ + } \ +} while(0) + LOAD_FUNC(sio_initpar); + LOAD_FUNC(sio_open); + LOAD_FUNC(sio_close); + LOAD_FUNC(sio_setpar); + LOAD_FUNC(sio_getpar); + LOAD_FUNC(sio_getcap); + LOAD_FUNC(sio_onmove); + LOAD_FUNC(sio_write); + LOAD_FUNC(sio_read); + LOAD_FUNC(sio_start); + LOAD_FUNC(sio_stop); + LOAD_FUNC(sio_nfds); + LOAD_FUNC(sio_pollfd); + LOAD_FUNC(sio_revents); + LOAD_FUNC(sio_eof); + LOAD_FUNC(sio_setvol); + LOAD_FUNC(sio_onvol); +#undef LOAD_FUNC +#else + sndio_handle = (void*)0xDEADBEEF; +#endif + } + return ALC_TRUE; +} + + +typedef struct { + struct sio_hdl *sndHandle; + + ALvoid *mix_data; + ALsizei data_size; + + volatile int killNow; + ALvoid *thread; +} sndio_data; + + +static ALuint sndio_proc(ALvoid *ptr) +{ + ALCdevice *device = ptr; + sndio_data *data = device->ExtraData; + ALsizei frameSize; + size_t wrote; + + SetRTPriority(); + + frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + while(!data->killNow && device->Connected) + { + ALsizei len = data->data_size; + ALubyte *WritePtr = data->mix_data; + + aluMixData(device, WritePtr, len/frameSize); + while(len > 0 && !data->killNow) + { + wrote = sio_write(data->sndHandle, WritePtr, len); + if(wrote == 0) + { + ERR("sio_write failed\n"); + aluHandleDisconnect(device); + break; + } + + len -= wrote; + WritePtr += wrote; + } + } + + return 0; +} + + + +static ALCboolean sndio_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + sndio_data *data; + + if(!deviceName) + deviceName = sndio_device; + else if(strcmp(deviceName, sndio_device) != 0) + return ALC_FALSE; + + data = calloc(1, sizeof(*data)); + data->killNow = 0; + + data->sndHandle = sio_open(NULL, SIO_PLAY, 0); + if(data->sndHandle == NULL) + { + free(data); + ERR("Could not open device\n"); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + + return ALC_TRUE; +} + +static void sndio_close_playback(ALCdevice *device) +{ + sndio_data *data = device->ExtraData; + + sio_close(data->sndHandle); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean sndio_reset_playback(ALCdevice *device) +{ + sndio_data *data = device->ExtraData; + struct sio_par par; + + sio_initpar(&par); + + par.rate = device->Frequency; + + par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1); + + switch(device->FmtType) + { + case DevFmtByte: + par.bits = 8; + par.sig = 1; + break; + case DevFmtUByte: + par.bits = 8; + par.sig = 0; + break; + case DevFmtFloat: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + par.bits = 16; + par.sig = 1; + break; + case DevFmtUShort: + par.bits = 16; + par.sig = 0; + break; + } + par.le = SIO_LE_NATIVE; + + par.round = device->UpdateSize; + par.appbufsz = device->UpdateSize * (device->NumUpdates-1); + if(!par.appbufsz) par.appbufsz = device->UpdateSize; + + + if(!sio_setpar(data->sndHandle, &par) || !sio_getpar(data->sndHandle, &par)) + { + ERR("Failed to set device parameters\n"); + return ALC_FALSE; + } + + if(par.rate != device->Frequency) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("Failed to set frequency %uhz, got %uhz instead\n", device->Frequency, par.rate); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = par.rate; + } + + if(par.pchan != ChannelsFromDevFmt(device->FmtChans)) + { + if(par.pchan != 1 && par.pchan != 2) + { + ERR("Unhandled channel count: %u\n", par.pchan); + return ALC_FALSE; + } + if((device->Flags&DEVICE_CHANNELS_REQUEST)) + ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), par.pchan); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo); + } + + if(par.bits != par.bps*8) + { + ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8); + return ALC_FALSE; + } + + if(par.bits == 8 && par.sig == 1) + device->FmtType = DevFmtByte; + else if(par.bits == 8 && par.sig == 0) + device->FmtType = DevFmtUByte; + else if(par.bits == 16 && par.sig == 1) + device->FmtType = DevFmtShort; + else if(par.bits == 16 && par.sig == 0) + device->FmtType = DevFmtUShort; + else + { + ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits); + return ALC_FALSE; + } + + + device->UpdateSize = par.round; + device->NumUpdates = (par.bufsz/par.round) + 1; + + SetDefaultChannelOrder(device); + + + if(!sio_start(data->sndHandle)) + { + ERR("Error starting playback\n"); + return ALC_FALSE; + } + + data->data_size = device->UpdateSize * par.bps * par.pchan; + data->mix_data = calloc(1, data->data_size); + + data->thread = StartThread(sndio_proc, device); + if(data->thread == NULL) + { + sio_stop(data->sndHandle); + free(data->mix_data); + data->mix_data = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void sndio_stop_playback(ALCdevice *device) +{ + sndio_data *data = device->ExtraData; + + if(!data->thread) + return; + + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; + if(!sio_stop(data->sndHandle)) + ERR("Error stopping device\n"); + + free(data->mix_data); + data->mix_data = NULL; +} + + +static const BackendFuncs sndio_funcs = { + sndio_open_playback, + sndio_close_playback, + sndio_reset_playback, + sndio_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +ALCboolean alc_sndio_init(BackendFuncs *func_list) +{ + if(!sndio_load()) + return ALC_FALSE; + *func_list = sndio_funcs; + return ALC_TRUE; +} + +void alc_sndio_deinit(void) +{ +#ifdef HAVE_DYNLOAD + if(sndio_handle) + CloseLib(sndio_handle); + sndio_handle = NULL; +#endif +} + +void alc_sndio_probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(sndio_device); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(sndio_device); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff -r 000000000000 -r f9476ff7637e Alc/backends/solaris.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/solaris.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,282 @@ +/** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include + + +static const ALCchar solaris_device[] = "Solaris Default"; + +typedef struct { + int fd; + volatile int killNow; + ALvoid *thread; + + ALubyte *mix_data; + int data_size; +} solaris_data; + + +static ALuint SolarisProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + solaris_data *data = (solaris_data*)pDevice->ExtraData; + ALint frameSize; + int wrote; + + SetRTPriority(); + + frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + while(!data->killNow && pDevice->Connected) + { + ALint len = data->data_size; + ALubyte *WritePtr = data->mix_data; + + aluMixData(pDevice, WritePtr, len/frameSize); + while(len > 0 && !data->killNow) + { + wrote = write(data->fd, WritePtr, len); + if(wrote < 0) + { + if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) + { + ERR("write failed: %s\n", strerror(errno)); + aluHandleDisconnect(pDevice); + break; + } + + Sleep(1); + continue; + } + + len -= wrote; + WritePtr += wrote; + } + } + + return 0; +} + + +static ALCboolean solaris_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + char driver[64]; + solaris_data *data; + + strncpy(driver, GetConfigValue("solaris", "device", "/dev/audio"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + + if(!deviceName) + deviceName = solaris_device; + else if(strcmp(deviceName, solaris_device) != 0) + return ALC_FALSE; + + data = (solaris_data*)calloc(1, sizeof(solaris_data)); + data->killNow = 0; + + data->fd = open(driver, O_WRONLY); + if(data->fd == -1) + { + free(data); + ERR("Could not open %s: %s\n", driver, strerror(errno)); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + return ALC_TRUE; +} + +static void solaris_close_playback(ALCdevice *device) +{ + solaris_data *data = (solaris_data*)device->ExtraData; + + close(data->fd); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean solaris_reset_playback(ALCdevice *device) +{ + solaris_data *data = (solaris_data*)device->ExtraData; + audio_info_t info; + ALuint frameSize; + int numChannels; + + AUDIO_INITINFO(&info); + + info.play.sample_rate = device->Frequency; + + if(device->FmtChans != DevFmtMono) + device->FmtChans = DevFmtStereo; + numChannels = ChannelsFromDevFmt(device->FmtChans); + info.play.channels = numChannels; + + switch(device->FmtType) + { + case DevFmtByte: + info.play.precision = 8; + info.play.encoding = AUDIO_ENCODING_LINEAR; + break; + case DevFmtUByte: + info.play.precision = 8; + info.play.encoding = AUDIO_ENCODING_LINEAR8; + break; + case DevFmtUShort: + case DevFmtFloat: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + info.play.precision = 16; + info.play.encoding = AUDIO_ENCODING_LINEAR; + break; + } + + frameSize = numChannels * BytesFromDevFmt(device->FmtType); + info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize; + + if(ioctl(data->fd, AUDIO_SETINFO, &info) < 0) + { + ERR("ioctl failed: %s\n", strerror(errno)); + return ALC_FALSE; + } + + if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels) + { + ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels); + return ALC_FALSE; + } + + if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && + device->FmtType == DevFmtByte) || + (info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && + device->FmtType == DevFmtUByte) || + (info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && + device->FmtType == DevFmtShort))) + { + ERR("Could not set %#x sample type, got %d (%#x)\n", + device->FmtType, info.play.precision, info.play.encoding); + return ALC_FALSE; + } + + if(device->Frequency != info.play.sample_rate) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("Failed to set requested frequency %dhz, got %dhz instead\n", device->Frequency, info.play.sample_rate); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = info.play.sample_rate; + } + device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1; + + data->data_size = device->UpdateSize * frameSize; + data->mix_data = calloc(1, data->data_size); + + SetDefaultChannelOrder(device); + + data->thread = StartThread(SolarisProc, device); + if(data->thread == NULL) + { + free(data->mix_data); + data->mix_data = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void solaris_stop_playback(ALCdevice *device) +{ + solaris_data *data = (solaris_data*)device->ExtraData; + + if(!data->thread) + return; + + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; + if(ioctl(data->fd, AUDIO_DRAIN) < 0) + ERR("Error draining device: %s\n", strerror(errno)); + + free(data->mix_data); + data->mix_data = NULL; +} + + +static const BackendFuncs solaris_funcs = { + solaris_open_playback, + solaris_close_playback, + solaris_reset_playback, + solaris_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +ALCboolean alc_solaris_init(BackendFuncs *func_list) +{ + *func_list = solaris_funcs; + return ALC_TRUE; +} + +void alc_solaris_deinit(void) +{ +} + +void alc_solaris_probe(enum DevProbe type) +{ +#ifdef HAVE_STAT + struct stat buf; + if(stat(GetConfigValue("solaris", "device", "/dev/audio"), &buf) != 0) + return; +#endif + + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(solaris_device); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(solaris_device); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff -r 000000000000 -r f9476ff7637e Alc/backends/wave.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/wave.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,357 @@ +/** + * 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 +#include +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +typedef struct { + FILE *f; + long DataStart; + + ALvoid *buffer; + ALuint size; + + volatile int killNow; + ALvoid *thread; +} wave_data; + + +static const ALCchar waveDevice[] = "Wave File Writer"; + +static const ALubyte SUBTYPE_PCM[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, + 0x00, 0x38, 0x9b, 0x71 +}; +static const ALubyte SUBTYPE_FLOAT[] = { + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, + 0x00, 0x38, 0x9b, 0x71 +}; + +static const ALuint channel_masks[] = { + 0, /* invalid */ + 0x4, /* Mono */ + 0x1 | 0x2, /* Stereo */ + 0, /* 3 channel */ + 0x1 | 0x2 | 0x10 | 0x20, /* Quad */ + 0, /* 5 channel */ + 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20, /* 5.1 */ + 0x1 | 0x2 | 0x4 | 0x8 | 0x100 | 0x200 | 0x400, /* 6.1 */ + 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x200 | 0x400, /* 7.1 */ +}; + + +static void fwrite16le(ALushort val, FILE *f) +{ + fputc(val&0xff, f); + fputc((val>>8)&0xff, f); +} + +static void fwrite32le(ALuint val, FILE *f) +{ + fputc(val&0xff, f); + fputc((val>>8)&0xff, f); + fputc((val>>16)&0xff, f); + fputc((val>>24)&0xff, f); +} + + +static ALuint WaveProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + wave_data *data = (wave_data*)pDevice->ExtraData; + ALuint frameSize; + ALuint now, start; + ALuint64 avail, done; + size_t fs; + union { + short s; + char b[sizeof(short)]; + } uSB; + const ALuint restTime = (ALuint64)pDevice->UpdateSize * 1000 / + pDevice->Frequency / 2; + + uSB.s = 1; + frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + done = 0; + start = timeGetTime(); + while(!data->killNow && pDevice->Connected) + { + now = timeGetTime(); + + avail = (ALuint64)(now-start) * pDevice->Frequency / 1000; + if(avail < done) + { + /* Timer wrapped. Add the remainder of the cycle to the available + * count and reset the number of samples done */ + avail += (ALuint64)0xFFFFFFFFu*pDevice->Frequency/1000 - done; + done = 0; + } + if(avail-done < pDevice->UpdateSize) + { + Sleep(restTime); + continue; + } + + while(avail-done >= pDevice->UpdateSize) + { + aluMixData(pDevice, data->buffer, pDevice->UpdateSize); + done += pDevice->UpdateSize; + + if(uSB.b[0] != 1) + { + ALuint bytesize = BytesFromDevFmt(pDevice->FmtType); + ALubyte *bytes = data->buffer; + ALuint i; + + if(bytesize == 1) + { + for(i = 0;i < data->size;i++) + fputc(bytes[i], data->f); + } + else if(bytesize == 2) + { + for(i = 0;i < data->size;i++) + fputc(bytes[i^1], data->f); + } + else if(bytesize == 4) + { + for(i = 0;i < data->size;i++) + fputc(bytes[i^3], data->f); + } + } + else + fs = fwrite(data->buffer, frameSize, pDevice->UpdateSize, + data->f); + if(ferror(data->f)) + { + ERR("Error writing to file\n"); + aluHandleDisconnect(pDevice); + break; + } + } + } + + return 0; +} + +static ALCboolean wave_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + wave_data *data; + const char *fname; + + fname = GetConfigValue("wave", "file", ""); + if(!fname[0]) + return ALC_FALSE; + + if(!deviceName) + deviceName = waveDevice; + else if(strcmp(deviceName, waveDevice) != 0) + return ALC_FALSE; + + data = (wave_data*)calloc(1, sizeof(wave_data)); + + data->f = fopen(fname, "wb"); + if(!data->f) + { + free(data); + ERR("Could not open file '%s': %s\n", fname, strerror(errno)); + return ALC_FALSE; + } + + device->szDeviceName = strdup(deviceName); + device->ExtraData = data; + return ALC_TRUE; +} + +static void wave_close_playback(ALCdevice *device) +{ + wave_data *data = (wave_data*)device->ExtraData; + + fclose(data->f); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean wave_reset_playback(ALCdevice *device) +{ + wave_data *data = (wave_data*)device->ExtraData; + ALuint channels=0, bits=0; + size_t val; + + fseek(data->f, 0, SEEK_SET); + clearerr(data->f); + + switch(device->FmtType) + { + case DevFmtByte: + device->FmtType = DevFmtUByte; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + break; + case DevFmtUByte: + case DevFmtShort: + case DevFmtFloat: + break; + } + bits = BytesFromDevFmt(device->FmtType) * 8; + channels = ChannelsFromDevFmt(device->FmtChans); + + fprintf(data->f, "RIFF"); + fwrite32le(0xFFFFFFFF, data->f); // 'RIFF' header len; filled in at close + + fprintf(data->f, "WAVE"); + + fprintf(data->f, "fmt "); + fwrite32le(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE + + // 16-bit val, format type id (extensible: 0xFFFE) + fwrite16le(0xFFFE, data->f); + // 16-bit val, channel count + fwrite16le(channels, data->f); + // 32-bit val, frequency + fwrite32le(device->Frequency, data->f); + // 32-bit val, bytes per second + fwrite32le(device->Frequency * channels * bits / 8, data->f); + // 16-bit val, frame size + fwrite16le(channels * bits / 8, data->f); + // 16-bit val, bits per sample + fwrite16le(bits, data->f); + // 16-bit val, extra byte count + fwrite16le(22, data->f); + // 16-bit val, valid bits per sample + fwrite16le(bits, data->f); + // 32-bit val, channel mask + fwrite32le(channel_masks[channels], data->f); + // 16 byte GUID, sub-type format + val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f); + + fprintf(data->f, "data"); + fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close + + if(ferror(data->f)) + { + ERR("Error writing header: %s\n", strerror(errno)); + return ALC_FALSE; + } + + data->DataStart = ftell(data->f); + + data->size = device->UpdateSize * channels * bits / 8; + data->buffer = malloc(data->size); + if(!data->buffer) + { + ERR("Buffer malloc failed\n"); + return ALC_FALSE; + } + + SetDefaultWFXChannelOrder(device); + + data->thread = StartThread(WaveProc, device); + if(data->thread == NULL) + { + free(data->buffer); + data->buffer = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void wave_stop_playback(ALCdevice *device) +{ + wave_data *data = (wave_data*)device->ExtraData; + ALuint dataLen; + long size; + + if(!data->thread) + return; + + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; + + free(data->buffer); + data->buffer = NULL; + + size = ftell(data->f); + if(size > 0) + { + dataLen = size - data->DataStart; + if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0) + fwrite32le(dataLen, data->f); // 'data' header len + if(fseek(data->f, 4, SEEK_SET) == 0) + fwrite32le(size-8, data->f); // 'WAVE' header len + } +} + + +static const BackendFuncs wave_funcs = { + wave_open_playback, + wave_close_playback, + wave_reset_playback, + wave_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +ALCboolean alc_wave_init(BackendFuncs *func_list) +{ + *func_list = wave_funcs; + printf("WAVE: I'm init!!\n"); + return ALC_TRUE; +} + +void alc_wave_deinit(void) +{ +} + +void alc_wave_probe(enum DevProbe type) +{ + printf("WAVE: I'm being probed :)\n"); + if(!ConfigValueExists("wave", "file")) + return; + + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(waveDevice); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(waveDevice); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff -r 000000000000 -r f9476ff7637e Alc/backends/winmm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/winmm.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,780 @@ +/** + * 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" + +#define _WIN32_WINNT 0x0500 +#include +#include +#include + +#include +#include + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +typedef struct { + // MMSYSTEM Device + volatile ALboolean bWaveShutdown; + HANDLE hWaveThreadEvent; + HANDLE hWaveThread; + DWORD ulWaveThreadID; + LONG lWaveBuffersCommitted; + WAVEHDR WaveBuffer[4]; + + union { + HWAVEIN In; + HWAVEOUT Out; + } hWaveHandle; + + ALuint Frequency; + + RingBuffer *pRing; +} WinMMData; + + +static const ALCchar woDefault[] = "WaveOut Default"; + +static ALCchar **PlaybackDeviceList; +static ALuint NumPlaybackDevices; +static ALCchar **CaptureDeviceList; +static ALuint NumCaptureDevices; + + +static void ProbePlaybackDevices(void) +{ + ALuint i; + + for(i = 0;i < NumPlaybackDevices;i++) + free(PlaybackDeviceList[i]); + + NumPlaybackDevices = waveOutGetNumDevs(); + PlaybackDeviceList = realloc(PlaybackDeviceList, sizeof(ALCchar*) * NumPlaybackDevices); + for(i = 0;i < NumPlaybackDevices;i++) + { + WAVEOUTCAPS WaveCaps; + + PlaybackDeviceList[i] = NULL; + if(waveOutGetDevCaps(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) + { + char name[1024]; + ALuint count, j; + + count = 0; + do { + if(count == 0) + snprintf(name, sizeof(name), "%s", WaveCaps.szPname); + else + snprintf(name, sizeof(name), "%s #%d", WaveCaps.szPname, count+1); + count++; + + for(j = 0;j < i;j++) + { + if(strcmp(name, PlaybackDeviceList[j]) == 0) + break; + } + } while(j != i); + + PlaybackDeviceList[i] = strdup(name); + } + } +} + +static void ProbeCaptureDevices(void) +{ + ALuint i; + + for(i = 0;i < NumCaptureDevices;i++) + free(CaptureDeviceList[i]); + + NumCaptureDevices = waveInGetNumDevs(); + CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices); + for(i = 0;i < NumCaptureDevices;i++) + { + WAVEINCAPS WaveInCaps; + + CaptureDeviceList[i] = NULL; + if(waveInGetDevCaps(i, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR) + { + char name[1024]; + ALuint count, j; + + count = 0; + do { + if(count == 0) + snprintf(name, sizeof(name), "%s", WaveInCaps.szPname); + else + snprintf(name, sizeof(name), "%s #%d", WaveInCaps.szPname, count+1); + count++; + + for(j = 0;j < i;j++) + { + if(strcmp(name, CaptureDeviceList[j]) == 0) + break; + } + } while(j != i); + + CaptureDeviceList[i] = strdup(name); + } + } +} + + +/* + WaveOutProc + + Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and + returns to the application (for more data) +*/ +static void CALLBACK WaveOutProc(HWAVEOUT hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2) +{ + ALCdevice *pDevice = (ALCdevice*)dwInstance; + WinMMData *pData = pDevice->ExtraData; + + (void)hDevice; + (void)dwParam2; + + if(uMsg != WOM_DONE) + return; + + // Decrement number of buffers in use + InterlockedDecrement(&pData->lWaveBuffersCommitted); + + if(pData->bWaveShutdown == AL_FALSE) + { + // Notify Wave Processor Thread that a Wave Header has returned + PostThreadMessage(pData->ulWaveThreadID, uMsg, 0, dwParam1); + } + else + { + if(pData->lWaveBuffersCommitted == 0) + { + // Post 'Quit' Message to WaveOut Processor Thread + PostThreadMessage(pData->ulWaveThreadID, WM_QUIT, 0, 0); + } + } +} + +/* + PlaybackThreadProc + + Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its + audio data. +*/ +static DWORD WINAPI PlaybackThreadProc(LPVOID lpParameter) +{ + ALCdevice *pDevice = (ALCdevice*)lpParameter; + WinMMData *pData = pDevice->ExtraData; + LPWAVEHDR pWaveHdr; + ALuint FrameSize; + MSG msg; + + FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + SetRTPriority(); + + while(GetMessage(&msg, NULL, 0, 0)) + { + if(msg.message != WOM_DONE || pData->bWaveShutdown) + continue; + + pWaveHdr = ((LPWAVEHDR)msg.lParam); + + aluMixData(pDevice, pWaveHdr->lpData, pWaveHdr->dwBufferLength/FrameSize); + + // Send buffer back to play more data + waveOutWrite(pData->hWaveHandle.Out, pWaveHdr, sizeof(WAVEHDR)); + InterlockedIncrement(&pData->lWaveBuffersCommitted); + } + + // Signal Wave Thread completed event + if(pData->hWaveThreadEvent) + SetEvent(pData->hWaveThreadEvent); + + ExitThread(0); + + return 0; +} + +/* + WaveInProc + + Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and + returns to the application (with more data) +*/ +static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2) +{ + ALCdevice *pDevice = (ALCdevice*)dwInstance; + WinMMData *pData = pDevice->ExtraData; + + (void)hDevice; + (void)dwParam2; + + if(uMsg != WIM_DATA) + return; + + // Decrement number of buffers in use + InterlockedDecrement(&pData->lWaveBuffersCommitted); + + if(pData->bWaveShutdown == AL_FALSE) + { + // Notify Wave Processor Thread that a Wave Header has returned + PostThreadMessage(pData->ulWaveThreadID,uMsg,0,dwParam1); + } + else + { + if(pData->lWaveBuffersCommitted == 0) + { + // Post 'Quit' Message to WaveIn Processor Thread + PostThreadMessage(pData->ulWaveThreadID,WM_QUIT,0,0); + } + } +} + +/* + CaptureThreadProc + + Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new + audio data. +*/ +static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter) +{ + ALCdevice *pDevice = (ALCdevice*)lpParameter; + WinMMData *pData = pDevice->ExtraData; + LPWAVEHDR pWaveHdr; + ALuint FrameSize; + MSG msg; + + FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); + + while(GetMessage(&msg, NULL, 0, 0)) + { + if(msg.message != WIM_DATA || pData->bWaveShutdown) + continue; + + pWaveHdr = ((LPWAVEHDR)msg.lParam); + + WriteRingBuffer(pData->pRing, (ALubyte*)pWaveHdr->lpData, + pWaveHdr->dwBytesRecorded/FrameSize); + + // Send buffer back to capture more data + waveInAddBuffer(pData->hWaveHandle.In,pWaveHdr,sizeof(WAVEHDR)); + InterlockedIncrement(&pData->lWaveBuffersCommitted); + } + + // Signal Wave Thread completed event + if(pData->hWaveThreadEvent) + SetEvent(pData->hWaveThreadEvent); + + ExitThread(0); + + return 0; +} + + +static ALCboolean WinMMOpenPlayback(ALCdevice *pDevice, const ALCchar *deviceName) +{ + WAVEFORMATEX wfexFormat; + WinMMData *pData = NULL; + UINT lDeviceID = 0; + MMRESULT res; + ALuint i = 0; + + // Find the Device ID matching the deviceName if valid + if(!deviceName || strcmp(deviceName, woDefault) == 0) + lDeviceID = WAVE_MAPPER; + else + { + if(!PlaybackDeviceList) + ProbePlaybackDevices(); + + for(i = 0;i < NumPlaybackDevices;i++) + { + if(PlaybackDeviceList[i] && + strcmp(deviceName, PlaybackDeviceList[i]) == 0) + { + lDeviceID = i; + break; + } + } + if(i == NumPlaybackDevices) + return ALC_FALSE; + } + + pData = calloc(1, sizeof(*pData)); + if(!pData) + { + alcSetError(pDevice, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + pDevice->ExtraData = pData; + + if(pDevice->FmtChans != DevFmtMono) + { + if((pDevice->Flags&DEVICE_CHANNELS_REQUEST) && + pDevice->FmtChans != DevFmtStereo) + { + ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(pDevice->FmtChans)); + pDevice->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + pDevice->FmtChans = DevFmtStereo; + } + switch(pDevice->FmtType) + { + case DevFmtByte: + pDevice->FmtType = DevFmtUByte; + break; + case DevFmtUShort: + case DevFmtFloat: + pDevice->FmtType = DevFmtShort; + break; + case DevFmtUByte: + case DevFmtShort: + break; + } + + memset(&wfexFormat, 0, sizeof(WAVEFORMATEX)); + wfexFormat.wFormatTag = WAVE_FORMAT_PCM; + wfexFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans); + wfexFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8; + wfexFormat.nBlockAlign = wfexFormat.wBitsPerSample * + wfexFormat.nChannels / 8; + wfexFormat.nSamplesPerSec = pDevice->Frequency; + wfexFormat.nAvgBytesPerSec = wfexFormat.nSamplesPerSec * + wfexFormat.nBlockAlign; + wfexFormat.cbSize = 0; + + if((res=waveOutOpen(&pData->hWaveHandle.Out, lDeviceID, &wfexFormat, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) + { + ERR("waveOutOpen failed: %u\n", res); + goto failure; + } + + pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(pData->hWaveThreadEvent == NULL) + { + ERR("CreateEvent failed: %lu\n", GetLastError()); + goto failure; + } + + pData->Frequency = pDevice->Frequency; + + pDevice->szDeviceName = strdup((lDeviceID==WAVE_MAPPER) ? woDefault : + PlaybackDeviceList[lDeviceID]); + return ALC_TRUE; + +failure: + if(pData->hWaveThreadEvent) + CloseHandle(pData->hWaveThreadEvent); + + if(pData->hWaveHandle.Out) + waveOutClose(pData->hWaveHandle.Out); + + free(pData); + pDevice->ExtraData = NULL; + return ALC_FALSE; +} + +static void WinMMClosePlayback(ALCdevice *device) +{ + WinMMData *pData = (WinMMData*)device->ExtraData; + + // Close the Wave device + CloseHandle(pData->hWaveThreadEvent); + pData->hWaveThreadEvent = 0; + + waveOutClose(pData->hWaveHandle.Out); + pData->hWaveHandle.Out = 0; + + free(pData); + device->ExtraData = NULL; +} + +static ALCboolean WinMMResetPlayback(ALCdevice *device) +{ + WinMMData *pData = (WinMMData*)device->ExtraData; + ALbyte *BufferData; + ALint lBufferSize; + ALuint i; + + pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &pData->ulWaveThreadID); + if(pData->hWaveThread == NULL) + return ALC_FALSE; + + device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize * + pData->Frequency / device->Frequency); + if(device->Frequency != pData->Frequency) + { + if((device->Flags&DEVICE_FREQUENCY_REQUEST)) + ERR("WinMM does not support changing sample rates (wanted %dhz, got %dhz)\n", device->Frequency, pData->Frequency); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + device->Frequency = pData->Frequency; + } + + SetDefaultWFXChannelOrder(device); + + pData->lWaveBuffersCommitted = 0; + + // Create 4 Buffers + lBufferSize = device->UpdateSize*device->NumUpdates / 4; + lBufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + BufferData = calloc(4, lBufferSize); + for(i = 0;i < 4;i++) + { + memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR)); + pData->WaveBuffer[i].dwBufferLength = lBufferSize; + pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData : + (pData->WaveBuffer[i-1].lpData + + pData->WaveBuffer[i-1].dwBufferLength)); + waveOutPrepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + waveOutWrite(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + InterlockedIncrement(&pData->lWaveBuffersCommitted); + } + + return ALC_TRUE; +} + +static void WinMMStopPlayback(ALCdevice *device) +{ + WinMMData *pData = (WinMMData*)device->ExtraData; + void *buffer = NULL; + int i; + + if(pData->hWaveThread == NULL) + return; + + // Set flag to stop processing headers + pData->bWaveShutdown = AL_TRUE; + + // Wait for signal that Wave Thread has been destroyed + WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE); + + CloseHandle(pData->hWaveThread); + pData->hWaveThread = 0; + + pData->bWaveShutdown = AL_FALSE; + + // Release the wave buffers + for(i = 0;i < 4;i++) + { + waveOutUnprepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + if(i == 0) buffer = pData->WaveBuffer[i].lpData; + pData->WaveBuffer[i].lpData = NULL; + } + free(buffer); +} + + +static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName) +{ + WAVEFORMATEX wfexCaptureFormat; + DWORD ulCapturedDataSize; + WinMMData *pData = NULL; + UINT lDeviceID = 0; + ALbyte *BufferData; + ALint lBufferSize; + MMRESULT res; + ALuint i; + + if(!CaptureDeviceList) + ProbeCaptureDevices(); + + // Find the Device ID matching the deviceName if valid + if(deviceName) + { + for(i = 0;i < NumCaptureDevices;i++) + { + if(CaptureDeviceList[i] && + strcmp(deviceName, CaptureDeviceList[i]) == 0) + { + lDeviceID = i; + break; + } + } + } + else + { + for(i = 0;i < NumCaptureDevices;i++) + { + if(CaptureDeviceList[i]) + { + lDeviceID = i; + break; + } + } + } + if(i == NumCaptureDevices) + return ALC_FALSE; + + pData = calloc(1, sizeof(*pData)); + if(!pData) + { + alcSetError(pDevice, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + pDevice->ExtraData = pData; + + if((pDevice->FmtChans != DevFmtMono && pDevice->FmtChans != DevFmtStereo) || + (pDevice->FmtType != DevFmtUByte && pDevice->FmtType != DevFmtShort)) + { + alcSetError(pDevice, ALC_INVALID_ENUM); + goto failure; + } + + memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX)); + wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM; + wfexCaptureFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans); + wfexCaptureFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8; + wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample * + wfexCaptureFormat.nChannels / 8; + wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency; + wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec * + wfexCaptureFormat.nBlockAlign; + wfexCaptureFormat.cbSize = 0; + + if((res=waveInOpen(&pData->hWaveHandle.In, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) + { + ERR("waveInOpen failed: %u\n", res); + goto failure; + } + + pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(pData->hWaveThreadEvent == NULL) + { + ERR("CreateEvent failed: %lu\n", GetLastError()); + goto failure; + } + + pData->Frequency = pDevice->Frequency; + + // Allocate circular memory buffer for the captured audio + ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates; + + // Make sure circular buffer is at least 100ms in size + if(ulCapturedDataSize < (wfexCaptureFormat.nSamplesPerSec / 10)) + ulCapturedDataSize = wfexCaptureFormat.nSamplesPerSec / 10; + + pData->pRing = CreateRingBuffer(wfexCaptureFormat.nBlockAlign, ulCapturedDataSize); + if(!pData->pRing) + goto failure; + + pData->lWaveBuffersCommitted = 0; + + // Create 4 Buffers of 50ms each + lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20; + lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign); + + BufferData = calloc(4, lBufferSize); + if(!BufferData) + goto failure; + + for(i = 0;i < 4;i++) + { + memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR)); + pData->WaveBuffer[i].dwBufferLength = lBufferSize; + pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData : + (pData->WaveBuffer[i-1].lpData + + pData->WaveBuffer[i-1].dwBufferLength)); + pData->WaveBuffer[i].dwFlags = 0; + pData->WaveBuffer[i].dwLoops = 0; + waveInPrepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + waveInAddBuffer(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + InterlockedIncrement(&pData->lWaveBuffersCommitted); + } + + pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveThreadID); + if (pData->hWaveThread == NULL) + goto failure; + + pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]); + return ALC_TRUE; + +failure: + if(pData->hWaveThread) + CloseHandle(pData->hWaveThread); + + for(i = 0;i < 4;i++) + { + if(pData->WaveBuffer[i].lpData) + { + waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + if(i == 0) + free(pData->WaveBuffer[i].lpData); + } + } + + if(pData->pRing) + DestroyRingBuffer(pData->pRing); + + if(pData->hWaveThreadEvent) + CloseHandle(pData->hWaveThreadEvent); + + if(pData->hWaveHandle.In) + waveInClose(pData->hWaveHandle.In); + + free(pData); + pDevice->ExtraData = NULL; + return ALC_FALSE; +} + +static void WinMMCloseCapture(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + void *buffer = NULL; + int i; + + // Call waveOutReset to shutdown wave device + pData->bWaveShutdown = AL_TRUE; + waveInReset(pData->hWaveHandle.In); + + // Wait for signal that Wave Thread has been destroyed + WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE); + + CloseHandle(pData->hWaveThread); + pData->hWaveThread = 0; + + // Release the wave buffers + for(i = 0;i < 4;i++) + { + waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + if(i == 0) buffer = pData->WaveBuffer[i].lpData; + pData->WaveBuffer[i].lpData = NULL; + } + free(buffer); + + DestroyRingBuffer(pData->pRing); + pData->pRing = NULL; + + // Close the Wave device + CloseHandle(pData->hWaveThreadEvent); + pData->hWaveThreadEvent = 0; + + waveInClose(pData->hWaveHandle.In); + pData->hWaveHandle.In = 0; + + free(pData); + pDevice->ExtraData = NULL; +} + +static void WinMMStartCapture(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + waveInStart(pData->hWaveHandle.In); +} + +static void WinMMStopCapture(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + waveInStop(pData->hWaveHandle.In); +} + +static ALCuint WinMMAvailableSamples(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + return RingBufferSize(pData->pRing); +} + +static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + + if(WinMMAvailableSamples(pDevice) >= lSamples) + ReadRingBuffer(pData->pRing, pBuffer, lSamples); + else + alcSetError(pDevice, ALC_INVALID_VALUE); +} + + +static const BackendFuncs WinMMFuncs = { + WinMMOpenPlayback, + WinMMClosePlayback, + WinMMResetPlayback, + WinMMStopPlayback, + WinMMOpenCapture, + WinMMCloseCapture, + WinMMStartCapture, + WinMMStopCapture, + WinMMCaptureSamples, + WinMMAvailableSamples +}; + +ALCboolean alcWinMMInit(BackendFuncs *FuncList) +{ + *FuncList = WinMMFuncs; + return ALC_TRUE; +} + +void alcWinMMDeinit() +{ + ALuint lLoop; + + for(lLoop = 0;lLoop < NumPlaybackDevices;lLoop++) + free(PlaybackDeviceList[lLoop]); + free(PlaybackDeviceList); + PlaybackDeviceList = NULL; + + NumPlaybackDevices = 0; + + + for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++) + free(CaptureDeviceList[lLoop]); + free(CaptureDeviceList); + CaptureDeviceList = NULL; + + NumCaptureDevices = 0; +} + +void alcWinMMProbe(enum DevProbe type) +{ + ALuint i; + + switch(type) + { + case DEVICE_PROBE: + ProbePlaybackDevices(); + if(NumPlaybackDevices > 0) + AppendDeviceList(woDefault); + break; + + case ALL_DEVICE_PROBE: + ProbePlaybackDevices(); + if(NumPlaybackDevices > 0) + AppendAllDeviceList(woDefault); + for(i = 0;i < NumPlaybackDevices;i++) + { + if(PlaybackDeviceList[i]) + AppendAllDeviceList(PlaybackDeviceList[i]); + } + break; + + case CAPTURE_DEVICE_PROBE: + ProbeCaptureDevices(); + for(i = 0;i < NumCaptureDevices;i++) + { + if(CaptureDeviceList[i]) + AppendCaptureDeviceList(CaptureDeviceList[i]); + } + break; + } +} diff -r 000000000000 -r f9476ff7637e Alc/bs2b.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/bs2b.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,201 @@ +/*- + * Copyright (c) 2005 Boris Mikhaylov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include + +#include "bs2b.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/* Single pole IIR filter. + * O[n] = a0*I[n] + a1*I[n-1] + b1*O[n-1] + */ + +/* Lowpass filter */ +#define lo_filter(in, out_1) (bs2b->a0_lo*(in) + bs2b->b1_lo*(out_1)) + +/* Highboost filter */ +#define hi_filter(in, in_1, out_1) (bs2b->a0_hi*(in) + bs2b->a1_hi*(in_1) + bs2b->b1_hi*(out_1)) + +/* Set up all data. */ +static void init(struct bs2b *bs2b) +{ + double Fc_lo, Fc_hi; + double G_lo, G_hi; + double x; + + if ((bs2b->srate > 192000) || (bs2b->srate < 2000)) + bs2b->srate = BS2B_DEFAULT_SRATE; + + switch(bs2b->level) + { + case BS2B_LOW_CLEVEL: /* Low crossfeed level */ + Fc_lo = 360.0; + Fc_hi = 501.0; + G_lo = 0.398107170553497; + G_hi = 0.205671765275719; + break; + + case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */ + Fc_lo = 500.0; + Fc_hi = 711.0; + G_lo = 0.459726988530872; + G_hi = 0.228208484414988; + break; + + case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */ + Fc_lo = 700.0; + Fc_hi = 1021.0; + G_lo = 0.530884444230988; + G_hi = 0.250105790667544; + break; + + case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */ + Fc_lo = 360.0; + Fc_hi = 494.0; + G_lo = 0.316227766016838; + G_hi = 0.168236228897329; + break; + + case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */ + Fc_lo = 500.0; + Fc_hi = 689.0; + G_lo = 0.354813389233575; + G_hi = 0.187169483835901; + break; + + default: /* High easy crossfeed level */ + bs2b->level = BS2B_HIGH_ECLEVEL; + + Fc_lo = 700.0; + Fc_hi = 975.0; + G_lo = 0.398107170553497; + G_hi = 0.205671765275719; + break; + } /* switch */ + + /* $fc = $Fc / $s; + * $d = 1 / 2 / pi / $fc; + * $x = exp(-1 / $d); + */ + + x = exp(-2.0 * M_PI * Fc_lo / bs2b->srate); + bs2b->b1_lo = x; + bs2b->a0_lo = G_lo * (1.0 - x); + + x = exp(-2.0 * M_PI * Fc_hi / bs2b->srate); + bs2b->b1_hi = x; + bs2b->a0_hi = 1.0 - G_hi * (1.0 - x); + bs2b->a1_hi = -x; + + bs2b->gain = 1.0 / (1.0 - G_hi + G_lo); +} /* init */ + +/* Exported functions. + * See descriptions in "bs2b.h" + */ + +void bs2b_set_level(struct bs2b *bs2b, int level) +{ + if(level == bs2b->level) + return; + bs2b->level = level; + init(bs2b); +} /* bs2b_set_level */ + +int bs2b_get_level(struct bs2b *bs2b) +{ + return bs2b->level; +} /* bs2b_get_level */ + +void bs2b_set_srate(struct bs2b *bs2b, int srate) +{ + if (srate == bs2b->srate) + return; + bs2b->srate = srate; + init(bs2b); +} /* bs2b_set_srate */ + +int bs2b_get_srate(struct bs2b *bs2b) +{ + return bs2b->srate; +} /* bs2b_get_srate */ + +void bs2b_clear(struct bs2b *bs2b) +{ + int loopv = sizeof(bs2b->last_sample); + + while (loopv) + { + ((char *)&bs2b->last_sample)[--loopv] = 0; + } +} /* bs2b_clear */ + +int bs2b_is_clear(struct bs2b *bs2b) +{ + int loopv = sizeof(bs2b->last_sample); + + while (loopv) + { + if (((char *)&bs2b->last_sample)[--loopv] != 0) + return 0; + } + return 1; +} /* bs2b_is_clear */ + +void bs2b_cross_feed(struct bs2b *bs2b, float *sample) +{ + /* Lowpass filter */ + bs2b->last_sample.lo[0] = lo_filter(sample[0], bs2b->last_sample.lo[0]); + bs2b->last_sample.lo[1] = lo_filter(sample[1], bs2b->last_sample.lo[1]); + + /* Highboost filter */ + bs2b->last_sample.hi[0] = hi_filter(sample[0], bs2b->last_sample.asis[0], bs2b->last_sample.hi[0]); + bs2b->last_sample.hi[1] = hi_filter(sample[1], bs2b->last_sample.asis[1], bs2b->last_sample.hi[1]); + bs2b->last_sample.asis[0] = sample[0]; + bs2b->last_sample.asis[1] = sample[1]; + + /* Crossfeed */ + sample[0] = bs2b->last_sample.hi[0] + bs2b->last_sample.lo[1]; + sample[1] = bs2b->last_sample.hi[1] + bs2b->last_sample.lo[0]; + + /* Bass boost cause allpass attenuation */ + sample[0] *= bs2b->gain; + sample[1] *= bs2b->gain; + + /* Clipping of overloaded samples */ +#if 0 + if (sample[0] > 1.0) + sample[0] = 1.0; + if (sample[0] < -1.0) + sample[0] = -1.0; + if (sample[1] > 1.0) + sample[1] = 1.0; + if (sample[1] < -1.0) + sample[1] = -1.0; +#endif +} /* bs2b_cross_feed */ diff -r 000000000000 -r f9476ff7637e Alc/helpers.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/helpers.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,349 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2011 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 +#ifdef HAVE_DLFCN_H +#include +#endif + +#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H) +#define INITGUID +#include +#ifdef HAVE_GUIDDEF_H +#include +#else +#include +#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); + +DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16); + +DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e); +DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6); +DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2); +DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2); + +#endif + +#include "alMain.h" + +#ifdef _WIN32 +void pthread_once(pthread_once_t *once, void (*callback)(void)) +{ + LONG ret; + while((ret=InterlockedExchange(once, 1)) == 1) + Sleep(0); + if(ret == 0) + callback(); + InterlockedExchange(once, 2); +} + +void *LoadLib(const char *name) +{ return LoadLibraryA(name); } +void CloseLib(void *handle) +{ FreeLibrary((HANDLE)handle); } +void *GetSymbol(void *handle, const char *name) +{ + void *ret; + + ret = (void*)GetProcAddress((HANDLE)handle, name); + if(ret == NULL) + ERR("Failed to load %s\n", name); + return ret; +} + +#else + +void InitializeCriticalSection(CRITICAL_SECTION *cs) +{ + pthread_mutexattr_t attrib; + int ret; + + ret = pthread_mutexattr_init(&attrib); + assert(ret == 0); + + ret = pthread_mutexattr_settype(&attrib, PTHREAD_MUTEX_RECURSIVE); +#ifdef HAVE_PTHREAD_NP_H + if(ret != 0) + ret = pthread_mutexattr_setkind_np(&attrib, PTHREAD_MUTEX_RECURSIVE); +#endif + assert(ret == 0); + ret = pthread_mutex_init(cs, &attrib); + assert(ret == 0); + + pthread_mutexattr_destroy(&attrib); +} +void DeleteCriticalSection(CRITICAL_SECTION *cs) +{ + int ret; + ret = pthread_mutex_destroy(cs); + assert(ret == 0); +} +void EnterCriticalSection(CRITICAL_SECTION *cs) +{ + int ret; + ret = pthread_mutex_lock(cs); + assert(ret == 0); +} +void LeaveCriticalSection(CRITICAL_SECTION *cs) +{ + int ret; + ret = pthread_mutex_unlock(cs); + assert(ret == 0); +} + +/* NOTE: This wrapper isn't quite accurate as it returns an ALuint, as opposed + * to the expected DWORD. Both are defined as unsigned 32-bit types, however. + * Additionally, Win32 is supposed to measure the time since Windows started, + * as opposed to the actual time. */ +ALuint timeGetTime(void) +{ +#if _POSIX_TIMERS > 0 + struct timespec ts; + int ret = -1; + +#if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK >= 0) +#if _POSIX_MONOTONIC_CLOCK == 0 + static int hasmono = 0; + if(hasmono > 0 || (hasmono == 0 && + (hasmono=sysconf(_SC_MONOTONIC_CLOCK)) > 0)) +#endif + ret = clock_gettime(CLOCK_MONOTONIC, &ts); +#endif + if(ret != 0) + ret = clock_gettime(CLOCK_REALTIME, &ts); + assert(ret == 0); + + return ts.tv_nsec/1000000 + ts.tv_sec*1000; +#else + struct timeval tv; + int ret; + + ret = gettimeofday(&tv, NULL); + assert(ret == 0); + + return tv.tv_usec/1000 + tv.tv_sec*1000; +#endif +} + +#ifdef HAVE_DLFCN_H + +void *LoadLib(const char *name) +{ + const char *err; + void *handle; + + dlerror(); + handle = dlopen(name, RTLD_NOW); + if((err=dlerror()) != NULL) + handle = NULL; + return handle; +} +void CloseLib(void *handle) +{ dlclose(handle); } +void *GetSymbol(void *handle, const char *name) +{ + const char *err; + void *sym; + + dlerror(); + sym = dlsym(handle, name); + if((err=dlerror()) != NULL) + { + ERR("Failed to load %s: %s\n", name, err); + sym = NULL; + } + return sym; +} + +#endif +#endif + + +void al_print(const char *fname, unsigned int line, const char *fmt, ...) +{ + const char *fn; + char str[256]; + int i; + + fn = strrchr(fname, '/'); + if(!fn) fn = strrchr(fname, '\\'); + if(!fn) fn = fname; + else fn += 1; + + i = snprintf(str, sizeof(str), "AL lib: %s:%d: ", fn, line); + if(i < (int)sizeof(str) && i > 0) + { + va_list ap; + va_start(ap, fmt); + vsnprintf(str+i, sizeof(str)-i, fmt, ap); + va_end(ap); + } + str[sizeof(str)-1] = 0; + + fprintf(LogFile, "%s", str); + fflush(LogFile); +} + + +void SetRTPriority(void) +{ + ALboolean failed; + +#ifdef _WIN32 + if(RTPrioLevel > 0) + failed = !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + else + failed = !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); +#elif defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__) + struct sched_param param; + + if(RTPrioLevel > 0) + { + /* Use the minimum real-time priority possible for now (on Linux this + * should be 1 for SCHED_RR) */ + param.sched_priority = sched_get_priority_min(SCHED_RR); + failed = !!pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); + } + else + { + param.sched_priority = 0; + failed = !!pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m); + } +#else + /* Real-time priority not available */ + failed = (RTPrioLevel>0); +#endif + if(failed) + ERR("Failed to set priority level for thread\n"); +} + + +void InitUIntMap(UIntMap *map) +{ + map->array = NULL; + map->size = 0; + map->maxsize = 0; +} + +void ResetUIntMap(UIntMap *map) +{ + free(map->array); + map->array = NULL; + map->size = 0; + map->maxsize = 0; +} + +ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value) +{ + ALsizei pos = 0; + + if(map->size > 0) + { + ALsizei low = 0; + ALsizei high = map->size - 1; + while(low < high) + { + ALsizei mid = low + (high-low)/2; + if(map->array[mid].key < key) + low = mid + 1; + else + high = mid; + } + if(map->array[low].key < key) + low++; + pos = low; + } + + if(pos == map->size || map->array[pos].key != key) + { + if(map->size == map->maxsize) + { + ALvoid *temp; + ALsizei newsize; + + newsize = (map->maxsize ? (map->maxsize<<1) : 4); + if(newsize < map->maxsize) + return AL_OUT_OF_MEMORY; + + temp = realloc(map->array, newsize*sizeof(map->array[0])); + if(!temp) return AL_OUT_OF_MEMORY; + map->array = temp; + map->maxsize = newsize; + } + + map->size++; + if(pos < map->size-1) + memmove(&map->array[pos+1], &map->array[pos], + (map->size-1-pos)*sizeof(map->array[0])); + } + map->array[pos].key = key; + map->array[pos].value = value; + + return AL_NO_ERROR; +} + +void RemoveUIntMapKey(UIntMap *map, ALuint key) +{ + if(map->size > 0) + { + ALsizei low = 0; + ALsizei high = map->size - 1; + while(low < high) + { + ALsizei mid = low + (high-low)/2; + if(map->array[mid].key < key) + low = mid + 1; + else + high = mid; + } + if(map->array[low].key == key) + { + if(low < map->size-1) + memmove(&map->array[low], &map->array[low+1], + (map->size-1-low)*sizeof(map->array[0])); + map->size--; + } + } +} + +ALvoid *LookupUIntMapKey(UIntMap *map, ALuint key) +{ + if(map->size > 0) + { + ALsizei low = 0; + ALsizei high = map->size - 1; + while(low < high) + { + ALsizei mid = low + (high-low)/2; + if(map->array[mid].key < key) + low = mid + 1; + else + high = mid; + } + if(map->array[low].key == key) + return map->array[low].value; + } + return NULL; +} diff -r 000000000000 -r f9476ff7637e Alc/hrtf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/hrtf.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,412 @@ +/** + * 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 "AL/al.h" +#include "AL/alc.h" +#include "alMain.h" +#include "alSource.h" + +/* External HRTF file format (LE byte order): + * + * ALchar magic[8] = "MinPHR00"; + * ALuint sampleRate; + * + * ALushort hrirCount; // Required value: 828 + * ALushort hrirSize; // Required value: 32 + * ALubyte evCount; // Required value: 19 + * + * ALushort evOffset[evCount]; // Required values: + * { 0, 1, 13, 37, 73, 118, 174, 234, 306, 378, 450, 522, 594, 654, 710, 755, 791, 815, 827 } + * + * ALushort coefficients[hrirCount][hrirSize]; + * ALubyte delays[hrirCount]; // Element values must not exceed 127 + */ + +static const ALchar magicMarker[8] = "MinPHR00"; + +#define HRIR_COUNT 828 +#define ELEV_COUNT 19 + +static const ALushort evOffset[ELEV_COUNT] = { 0, 1, 13, 37, 73, 118, 174, 234, 306, 378, 450, 522, 594, 654, 710, 755, 791, 815, 827 }; +static const ALubyte azCount[ELEV_COUNT] = { 1, 12, 24, 36, 45, 56, 60, 72, 72, 72, 72, 72, 60, 56, 45, 36, 24, 12, 1 }; + +static struct Hrtf { + ALuint sampleRate; + ALshort coeffs[HRIR_COUNT][HRIR_LENGTH]; + ALubyte delays[HRIR_COUNT]; +} Hrtf = { + 44100, +#include "hrtf_tables.inc" +}; + +// Calculate the elevation indices given the polar elevation in radians. +// This will return two indices between 0 and (ELEV_COUNT-1) and an +// interpolation factor between 0.0 and 1.0. +static void CalcEvIndices(ALfloat ev, ALuint *evidx, ALfloat *evmu) +{ + ev = (M_PI/2.0f + ev) * (ELEV_COUNT-1) / M_PI; + evidx[0] = (ALuint)ev; + evidx[1] = minu(evidx[0] + 1, ELEV_COUNT-1); + *evmu = ev - evidx[0]; +} + +// Calculate the azimuth indices given the polar azimuth in radians. This +// will return two indices between 0 and (azCount [ei] - 1) and an +// interpolation factor between 0.0 and 1.0. +static void CalcAzIndices(ALuint evidx, ALfloat az, ALuint *azidx, ALfloat *azmu) +{ + az = (M_PI*2.0f + az) * azCount[evidx] / (M_PI*2.0f); + azidx[0] = (ALuint)az % azCount[evidx]; + azidx[1] = (azidx[0] + 1) % azCount[evidx]; + *azmu = az - floor(az); +} + +// Calculates the normalized HRTF transition factor (delta) from the changes +// in gain and listener to source angle between updates. The result is a +// normalized delta factor than can be used to calculate moving HRIR stepping +// values. +ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3]) +{ + ALfloat gainChange, angleChange; + + // Calculate the normalized dB gain change. + newGain = maxf(newGain, 0.0001f); + oldGain = maxf(oldGain, 0.0001f); + gainChange = aluFabs(log10(newGain / oldGain) / log10(0.0001f)); + + // Calculate the normalized listener to source angle change when there is + // enough gain to notice it. + angleChange = 0.0f; + if(gainChange > 0.0001f || newGain > 0.0001f) + { + // No angle change when the directions are equal or degenerate (when + // both have zero length). + if(newdir[0]-olddir[0] || newdir[1]-olddir[1] || newdir[2]-olddir[2]) + angleChange = aluAcos(olddir[0]*newdir[0] + + olddir[1]*newdir[1] + + olddir[2]*newdir[2]) / M_PI; + + } + + // Use the largest of the two changes for the delta factor, and apply a + // significance shaping function to it. + return clampf(angleChange*2.0f, gainChange*2.0f, 1.0f); +} + +// Calculates static HRIR coefficients and delays for the given polar +// elevation and azimuth in radians. Linear interpolation is used to +// increase the apparent resolution of the HRIR dataset. The coefficients +// are also normalized and attenuated by the specified gain. +void GetLerpedHrtfCoeffs(ALfloat elevation, ALfloat azimuth, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays) +{ + ALuint evidx[2], azidx[2]; + ALfloat mu[3]; + ALuint lidx[4], ridx[4]; + ALuint i; + + // Claculate elevation indices and interpolation factor. + CalcEvIndices(elevation, evidx, &mu[2]); + + // Calculate azimuth indices and interpolation factor for the first + // elevation. + CalcAzIndices(evidx[0], azimuth, azidx, &mu[0]); + + // Calculate the first set of linear HRIR indices for left and right + // channels. + lidx[0] = evOffset[evidx[0]] + azidx[0]; + lidx[1] = evOffset[evidx[0]] + azidx[1]; + ridx[0] = evOffset[evidx[0]] + ((azCount[evidx[0]]-azidx[0]) % azCount[evidx[0]]); + ridx[1] = evOffset[evidx[0]] + ((azCount[evidx[0]]-azidx[1]) % azCount[evidx[0]]); + + // Calculate azimuth indices and interpolation factor for the second + // elevation. + CalcAzIndices(evidx[1], azimuth, azidx, &mu[1]); + + // Calculate the second set of linear HRIR indices for left and right + // channels. + lidx[2] = evOffset[evidx[1]] + azidx[0]; + lidx[3] = evOffset[evidx[1]] + azidx[1]; + ridx[2] = evOffset[evidx[1]] + ((azCount[evidx[1]]-azidx[0]) % azCount[evidx[1]]); + ridx[3] = evOffset[evidx[1]] + ((azCount[evidx[1]]-azidx[1]) % azCount[evidx[1]]); + + // Calculate the normalized and attenuated HRIR coefficients using linear + // interpolation when there is enough gain to warrant it. Zero the + // coefficients if gain is too low. + if(gain > 0.0001f) + { + ALdouble scale = gain * (1.0/32767.0); + for(i = 0;i < HRIR_LENGTH;i++) + { + coeffs[i][0] = lerp(lerp(Hrtf.coeffs[lidx[0]][i], Hrtf.coeffs[lidx[1]][i], mu[0]), + lerp(Hrtf.coeffs[lidx[2]][i], Hrtf.coeffs[lidx[3]][i], mu[1]), + mu[2]) * scale; + coeffs[i][1] = lerp(lerp(Hrtf.coeffs[ridx[0]][i], Hrtf.coeffs[ridx[1]][i], mu[0]), + lerp(Hrtf.coeffs[ridx[2]][i], Hrtf.coeffs[ridx[3]][i], mu[1]), + mu[2]) * scale; + } + } + else + { + for(i = 0;i < HRIR_LENGTH;i++) + { + coeffs[i][0] = 0.0f; + coeffs[i][1] = 0.0f; + } + } + + // Calculate the HRIR delays using linear interpolation. + delays[0] = (ALuint)(lerp(lerp(Hrtf.delays[lidx[0]], Hrtf.delays[lidx[1]], mu[0]), + lerp(Hrtf.delays[lidx[2]], Hrtf.delays[lidx[3]], mu[1]), + mu[2]) * 65536.0f); + delays[1] = (ALuint)(lerp(lerp(Hrtf.delays[ridx[0]], Hrtf.delays[ridx[1]], mu[0]), + lerp(Hrtf.delays[ridx[2]], Hrtf.delays[ridx[3]], mu[1]), + mu[2]) * 65536.0f); +} + +// Calculates the moving HRIR target coefficients, target delays, and +// stepping values for the given polar elevation and azimuth in radians. +// Linear interpolation is used to increase the apparent resolution of the +// HRIR dataset. The coefficients are also normalized and attenuated by the +// specified gain. Stepping resolution and count is determined using the +// given delta factor between 0.0 and 1.0. +ALuint GetMovingHrtfCoeffs(ALfloat elevation, ALfloat azimuth, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep) +{ + ALuint evidx[2], azidx[2]; + ALuint lidx[4], ridx[4]; + ALfloat left, right; + ALfloat mu[3]; + ALfloat step; + ALuint i; + + // Claculate elevation indices and interpolation factor. + CalcEvIndices(elevation, evidx, &mu[2]); + + // Calculate azimuth indices and interpolation factor for the first + // elevation. + CalcAzIndices(evidx[0], azimuth, azidx, &mu[0]); + + // Calculate the first set of linear HRIR indices for left and right + // channels. + lidx[0] = evOffset[evidx[0]] + azidx[0]; + lidx[1] = evOffset[evidx[0]] + azidx[1]; + ridx[0] = evOffset[evidx[0]] + ((azCount[evidx[0]]-azidx[0]) % azCount[evidx[0]]); + ridx[1] = evOffset[evidx[0]] + ((azCount[evidx[0]]-azidx[1]) % azCount[evidx[0]]); + + // Calculate azimuth indices and interpolation factor for the second + // elevation. + CalcAzIndices(evidx[1], azimuth, azidx, &mu[1]); + + // Calculate the second set of linear HRIR indices for left and right + // channels. + lidx[2] = evOffset[evidx[1]] + azidx[0]; + lidx[3] = evOffset[evidx[1]] + azidx[1]; + ridx[2] = evOffset[evidx[1]] + ((azCount[evidx[1]]-azidx[0]) % azCount[evidx[1]]); + ridx[3] = evOffset[evidx[1]] + ((azCount[evidx[1]]-azidx[1]) % azCount[evidx[1]]); + + // Calculate the stepping parameters. + delta = maxf(floor(delta*(Hrtf.sampleRate*0.015f) + 0.5), 1.0f); + step = 1.0f / delta; + + // Calculate the normalized and attenuated target HRIR coefficients using + // linear interpolation when there is enough gain to warrant it. Zero + // the target coefficients if gain is too low. Then calculate the + // coefficient stepping values using the target and previous running + // coefficients. + if(gain > 0.0001f) + { + ALdouble scale = gain * (1.0/32767.0); + for(i = 0;i < HRIR_LENGTH;i++) + { + left = coeffs[i][0] - (coeffStep[i][0] * counter); + right = coeffs[i][1] - (coeffStep[i][1] * counter); + + coeffs[i][0] = lerp(lerp(Hrtf.coeffs[lidx[0]][i], Hrtf.coeffs[lidx[1]][i], mu[0]), + lerp(Hrtf.coeffs[lidx[2]][i], Hrtf.coeffs[lidx[3]][i], mu[1]), + mu[2]) * scale; + coeffs[i][1] = lerp(lerp(Hrtf.coeffs[ridx[0]][i], Hrtf.coeffs[ridx[1]][i], mu[0]), + lerp(Hrtf.coeffs[ridx[2]][i], Hrtf.coeffs[ridx[3]][i], mu[1]), + mu[2]) * scale; + + coeffStep[i][0] = step * (coeffs[i][0] - left); + coeffStep[i][1] = step * (coeffs[i][1] - right); + } + } + else + { + for(i = 0;i < HRIR_LENGTH;i++) + { + left = coeffs[i][0] - (coeffStep[i][0] * counter); + right = coeffs[i][1] - (coeffStep[i][1] * counter); + + coeffs[i][0] = 0.0f; + coeffs[i][1] = 0.0f; + + coeffStep[i][0] = step * -left; + coeffStep[i][1] = step * -right; + } + } + + // Calculate the HRIR delays using linear interpolation. Then calculate + // the delay stepping values using the target and previous running + // delays. + left = delays[0] - (delayStep[0] * counter); + right = delays[1] - (delayStep[1] * counter); + + delays[0] = (ALuint)(lerp(lerp(Hrtf.delays[lidx[0]], Hrtf.delays[lidx[1]], mu[0]), + lerp(Hrtf.delays[lidx[2]], Hrtf.delays[lidx[3]], mu[1]), + mu[2]) * 65536.0f); + delays[1] = (ALuint)(lerp(lerp(Hrtf.delays[ridx[0]], Hrtf.delays[ridx[1]], mu[0]), + lerp(Hrtf.delays[ridx[2]], Hrtf.delays[ridx[3]], mu[1]), + mu[2]) * 65536.0f); + + delayStep[0] = (ALint)(step * (delays[0] - left)); + delayStep[1] = (ALint)(step * (delays[1] - right)); + + // The stepping count is the number of samples necessary for the HRIR to + // complete its transition. The mixer will only apply stepping for this + // many samples. + return (ALuint)delta; +} + +ALCboolean IsHrtfCompatible(ALCdevice *device) +{ + if(device->FmtChans == DevFmtStereo && device->Frequency == Hrtf.sampleRate) + return ALC_TRUE; + ERR("Incompatible HRTF format: %s %uhz (%s %uhz needed)\n", + DevFmtChannelsString(device->FmtChans), device->Frequency, + DevFmtChannelsString(DevFmtStereo), Hrtf.sampleRate); + return ALC_FALSE; +} + +void InitHrtf(void) +{ + const char *fname; + FILE *f = NULL; + + fname = GetConfigValue(NULL, "hrtf_tables", ""); + if(fname[0] != '\0') + { + f = fopen(fname, "rb"); + if(f == NULL) + ERR("Could not open %s\n", fname); + } + if(f != NULL) + { + const ALubyte maxDelay = SRC_HISTORY_LENGTH-1; + ALboolean failed = AL_FALSE; + struct Hrtf newdata; + ALchar magic[9]; + ALsizei i, j; + + if(fread(magic, 1, sizeof(magicMarker), f) != sizeof(magicMarker)) + { + ERR("Failed to read magic marker\n"); + failed = AL_TRUE; + } + else if(memcmp(magic, magicMarker, sizeof(magicMarker)) != 0) + { + magic[8] = 0; + ERR("Invalid magic marker: \"%s\"\n", magic); + failed = AL_TRUE; + } + + if(!failed) + { + ALushort hrirCount, hrirSize; + ALubyte evCount; + + newdata.sampleRate = fgetc(f); + newdata.sampleRate |= fgetc(f)<<8; + newdata.sampleRate |= fgetc(f)<<16; + newdata.sampleRate |= fgetc(f)<<24; + + hrirCount = fgetc(f); + hrirCount |= fgetc(f)<<8; + + hrirSize = fgetc(f); + hrirSize |= fgetc(f)<<8; + + evCount = fgetc(f); + + if(hrirCount != HRIR_COUNT || hrirSize != HRIR_LENGTH || evCount != ELEV_COUNT) + { + ERR("Unsupported value: hrirCount=%d (%d), hrirSize=%d (%d), evCount=%d (%d)\n", + hrirCount, HRIR_COUNT, hrirSize, HRIR_LENGTH, evCount, ELEV_COUNT); + failed = AL_TRUE; + } + } + + if(!failed) + { + for(i = 0;i < HRIR_COUNT;i++) + { + ALushort offset; + offset = fgetc(f); + offset |= fgetc(f)<<8; + if(offset != evOffset[i]) + { + ERR("Unsupported evOffset[%d] value: %d (%d)\n", i, offset, evOffset[i]); + failed = AL_TRUE; + } + } + } + + if(!failed) + { + for(i = 0;i < HRIR_COUNT;i++) + { + for(j = 0;j < HRIR_LENGTH;j++) + { + ALshort coeff; + coeff = fgetc(f); + coeff |= fgetc(f)<<8; + newdata.coeffs[i][j] = coeff; + } + } + for(i = 0;i < HRIR_COUNT;i++) + { + ALubyte delay; + delay = fgetc(f); + newdata.delays[i] = delay; + if(delay > maxDelay) + { + ERR("Invalid delay[%d]: %d (%d)\n", i, delay, maxDelay); + failed = AL_TRUE; + } + } + + if(feof(f)) + { + ERR("Premature end of data\n"); + failed = AL_TRUE; + } + } + + fclose(f); + f = NULL; + + if(!failed) + Hrtf = newdata; + else + ERR("Failed to load %s\n", fname); + } +} diff -r 000000000000 -r f9476ff7637e Alc/hrtf_tables.inc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/hrtf_tables.inc Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,839 @@ +/* This data is Copyright 1994 by the MIT Media Laboratory. It is provided free + * with no restrictions on use, provided the authors are cited when the data is + * used in any research or commercial application. */ +/* Bill Gardner and Keith Martin */ + + /* HRIR Coefficients */ + { + { +5817, +7833, +3253, +2795, +1080, +403, +948, -690, -2207, -1110, +658, -33, -481, -949, -839, -287, -146, -103, +7, -47, -269, -105, -316, -221, -304, -497, -373, -231, -114, -14, +66, -62, }, + { +6586, +8107, +2781, +3167, +1095, +99, +922, -641, -2255, -1457, +602, +170, -469, -781, -818, -773, -258, +8, -111, -125, -368, -216, -345, -265, -298, -447, -408, -158, -22, +0, +63, -96, }, + { +5986, +7570, +2713, +2756, +1080, +403, +921, -758, -2112, -1067, +565, -93, -431, -738, -604, -361, -238, +10, +63, -88, -302, -101, -306, -243, -358, -488, -341, -224, -98, -1, +79, -55, }, + { +5571, +7005, +2499, +2549, +974, +438, +959, -688, -1966, -793, +755, -35, -305, -631, -545, -165, -118, -72, +26, -36, -228, -36, -253, -158, -261, -436, -311, -197, -85, +11, +82, -47, }, + { +5472, +6865, +2460, +2524, +978, +492, +1057, -561, -1857, -706, +813, -3, -353, -739, -587, -120, -98, -94, +21, -32, -228, -31, -232, -119, -204, -399, -293, -197, -124, -45, +34, -81, }, + { +5627, +7081, +2487, +2449, +858, +396, +981, -676, -2051, -899, +820, +82, -332, -774, -643, -127, -26, -14, +72, +27, -183, -16, -234, -142, -240, -422, -289, -151, -36, +63, +108, -65, }, + { +6172, +7677, +2541, +2564, +874, +397, +966, -927, -2421, -1117, +905, +38, -545, -990, -785, -200, -66, -60, +50, +17, -244, -35, -244, -146, -246, -433, -290, -139, -42, +40, +108, -37, }, + { +6521, +8320, +2827, +2690, +909, +230, +917, -1084, -2525, -1115, +652, -122, -480, -974, -823, -289, -150, +21, +73, -58, -296, -90, -299, -198, -317, -480, -327, -206, -64, +25, +103, -47, }, + { +6956, +8505, +2738, +3040, +1131, +209, +719, -1389, -2419, -516, +530, -857, -697, -801, -443, -77, -328, -117, +128, -70, -281, -49, -356, -233, -419, -579, -344, -271, -169, -16, +76, -98, }, + { +7928, +9215, +2040, +2470, +595, +264, +1348, -1308, -2698, -258, +1302, -836, -1179, -1210, -483, +125, -403, -370, +81, -61, -416, -45, -378, -208, -426, -708, -383, -223, -150, -20, +82, -119, }, + { +8205, +9593, +2241, +2306, +197, +241, +1480, -1080, -3081, -704, +1757, -153, -1144, -1906, -912, +311, -4, -441, -129, -99, -476, -104, -467, -207, -379, -660, -412, -194, -106, -31, +48, -172, }, + { +7822, +9388, +3008, +2690, +349, +155, +1066, -976, -3351, -1396, +1366, +202, -165, -1794, -1686, +63, +209, -110, -81, -164, -375, -114, -487, -174, -394, -771, -456, -312, -109, +27, +100, -71, }, + { +7342, +9001, +2833, +2968, +859, +118, +830, -1009, -2609, -1583, +1030, +177, -566, -888, -1564, -882, +55, +78, +62, +48, -275, -224, -531, -354, -242, -534, -487, -241, -107, +31, +92, -76, }, + { +7420, +8277, +2251, +3723, +1049, -308, +945, -518, -2311, -1945, +592, +496, -502, -565, -848, -1420, -265, +189, -293, -192, -468, -339, -342, -319, -275, -379, -474, -58, +77, -10, +55, -134, }, + { +6584, +7746, +2495, +3374, +1000, -52, +897, -756, -2030, -1406, +280, +34, -398, -477, -456, -856, -571, +107, +104, -158, -435, -199, -286, -253, -345, -425, -316, -178, -107, -66, +74, -62, }, + { +6049, +7215, +2251, +2809, +1129, +395, +858, -790, -1953, -1091, +399, -101, -343, -474, -371, -537, -334, +183, +103, -155, -328, -94, -287, -281, -418, -457, -307, -226, -82, +10, +91, -45, }, + { +5611, +6654, +2026, +2614, +1122, +421, +846, -730, -1795, -787, +486, -181, -199, -331, -331, -290, -184, +57, +64, -94, -264, -59, -241, -160, -276, -381, -321, -242, -63, +33, +98, -44, }, + { +5099, +6036, +1921, +2401, +945, +508, +937, -640, -1638, -506, +755, -23, -74, -266, -268, -108, -114, -39, +41, -26, -172, +33, -185, -102, -220, -366, -254, -174, -58, +33, +94, -28, }, + { +4863, +5622, +1644, +2154, +818, +547, +1098, -307, -1280, -210, +982, +96, -111, -376, -319, -58, -43, -22, +51, -10, -188, -3, -174, -47, -165, -316, -201, -114, -14, +20, +16, -134, }, + { +4874, +5742, +1871, +2357, +961, +630, +1147, -376, -1424, -337, +863, +34, -193, -499, -332, +8, -81, -97, +36, -13, -175, +46, -137, -17, -102, -297, -226, -178, -147, -83, +1, -91, }, + { +4960, +5827, +1797, +2194, +842, +622, +1162, -376, -1492, -421, +910, +56, -246, -568, -430, +32, +120, +83, +106, +18, -182, -19, -208, -85, -152, -240, -94, -64, -84, -82, -30, -123, }, + { +5228, +6194, +1872, +2175, +707, +445, +1001, -617, -1837, -723, +933, +227, -167, -586, -451, +10, +86, +68, +127, +106, -88, +66, -146, -70, -175, -339, -208, -74, +41, +139, +139, -76, }, + { +5697, +6722, +1953, +2283, +727, +469, +969, -882, -2258, -1037, +972, +175, -413, -806, -593, -28, +130, +87, +177, +163, -115, +43, -180, -84, -142, -297, -156, -24, +4, +49, +85, -29, }, + { +6473, +7408, +1826, +2417, +711, +442, +966, -1188, -2613, -1118, +1193, +107, -644, -1020, -716, -117, +16, -26, +92, +89, -223, +39, -164, -76, -190, -362, -208, -46, +24, +86, +145, -12, }, + { +7101, +8208, +1841, +2445, +711, +357, +985, -1505, -2879, -1121, +1181, +70, -658, -1173, -814, -168, -29, +29, +47, +36, -265, -40, -293, -166, -283, -429, -229, -63, +43, +81, +160, -20, }, + { +7271, +8783, +2298, +2617, +755, +48, +900, -1532, -2806, -1083, +615, -189, -449, -1001, -807, -311, -150, +184, +122, -88, -322, -71, -273, -179, -335, -451, -278, -188, -9, +60, +138, -32, }, + { +7519, +8972, +2519, +3099, +1077, -70, +527, -1818, -2665, -568, +208, -889, -424, -657, -398, -291, -483, +67, +222, -56, -219, -19, -307, -204, -436, -529, -306, -304, -126, +27, +128, -48, }, + { +8267, +9086, +2039, +3441, +1167, -66, +449, -2175, -2478, +261, +170, -1844, -741, -537, +19, +68, -620, -86, +289, -131, -285, +19, -414, -244, -560, -647, -289, -330, -230, -5, +89, -141, }, + { +9334, +10020, +1039, +2681, +800, -36, +1253, -2153, -2906, +675, +921, -2082, -1336, -748, +118, +362, -708, -352, +305, -89, -430, +16, -461, -267, -609, -817, -292, -288, -287, -70, +134, -107, }, + { +10490, +10443, +234, +2319, +106, +215, +1880, -2162, -3146, +928, +1870, -2011, -1861, -1339, +44, +548, -846, -647, +252, -93, -597, +65, -464, -180, -581, -943, -344, -209, -200, -20, +104, -191, }, + { +11090, +10859, +176, +1929, -492, +415, +2257, -1899, -3660, +617, +2693, -1436, -2083, -2245, -339, +944, -576, -854, +49, -173, -603, +64, -607, -194, -525, -910, -310, -113, -162, +10, +55, -268, }, + { +11123, +11231, +589, +1885, -762, +272, +2193, -1677, -4087, +10, +3036, -569, -1972, -2950, -747, +1099, +20, -922, -216, -122, -721, -53, -651, -153, -474, -845, -423, -135, -107, -57, +30, -304, }, + { +10803, +11222, +1474, +2265, -883, +91, +1845, -1667, -4470, -728, +2793, +190, -972, -3366, -1480, +1091, +471, -617, -486, -175, -639, -113, -591, -51, -507, -934, -502, -309, -106, -118, +53, -177, }, + { +10246, +10868, +2402, +2554, -531, +31, +1268, -1323, -4728, -1500, +2335, +359, +174, -2966, -2559, +743, +585, -219, -198, -287, -474, -103, -696, -72, -517, -1094, -493, -391, -81, +78, +133, -87, }, + { +9638, +10459, +2554, +3136, -109, -170, +1213, -1306, -4339, -2094, +2099, +264, +202, -1798, -3312, -170, +781, +126, +63, -260, -424, +20, -759, -504, -338, -983, -510, -236, -85, +153, +125, -96, }, + { +9150, +10114, +2114, +3243, +563, -199, +731, -1335, -3018, -2131, +1610, +397, -733, -785, -2524, -1463, +491, +266, +80, +156, -304, -383, -768, -471, -118, -592, -631, -225, -95, +87, +113, -96, }, + { +8454, +9243, +2047, +3497, +881, -249, +701, -1042, -2617, -2062, +1294, +712, -942, -676, -1397, -1797, -57, +250, -231, -14, -250, -237, -521, -454, -179, -387, -649, -192, -30, +13, +161, -121, }, + { +8323, +8323, +1685, +4494, +893, -823, +1049, -315, -2397, -2598, +672, +957, -622, -284, -950, -2240, -109, +425, -567, -229, -570, -475, -300, -391, -228, -295, -579, +83, +176, -48, +44, -175, }, + { +7274, +7900, +2262, +4107, +785, -714, +918, -662, -1986, -1894, +51, +378, -372, -108, -420, -1664, -750, +303, +73, -176, -577, -344, -248, -256, -255, -331, -406, -129, -13, -101, +50, -88, }, + { +6560, +7256, +2146, +3528, +960, -9, +858, -751, -1783, -1572, +54, +101, -313, -181, -229, -1133, -648, +400, +128, -265, -444, -160, -232, -331, -449, -399, -265, -206, -153, -62, +116, -28, }, + { +5980, +6790, +1905, +2935, +1217, +364, +757, -764, -1730, -1213, +165, -24, -228, -158, -154, -831, -410, +428, +110, -247, -340, -86, -256, -338, -480, -399, -278, -238, -61, +18, +103, -30, }, + { +5480, +6206, +1692, +2736, +1300, +391, +709, -692, -1565, -861, +226, -147, -68, -24, -127, -550, -217, +281, +49, -181, -257, -43, -247, -249, -345, -298, -274, -270, -50, +56, +119, -43, }, + { +4953, +5620, +1632, +2478, +1119, +519, +800, -639, -1430, -598, +380, -73, +141, +70, -109, -295, -151, +126, +65, -105, -195, +54, -97, -104, -271, -314, -265, -208, -14, +48, +99, +5, }, + { +4358, +4972, +1588, +2310, +986, +595, +869, -526, -1223, -277, +652, +34, +216, +141, -22, -120, -122, +1, +49, -14, -101, +99, -108, -54, -180, -284, -207, -160, -31, +53, +104, -2, }, + { +4049, +4526, +1367, +2016, +774, +619, +1057, -193, -943, +8, +975, +238, +224, +73, -43, -96, -105, -16, +104, +54, -140, +64, -50, +12, -174, -276, -142, -78, +11, +66, +70, -116, }, + { +3893, +4241, +1180, +1972, +964, +806, +1165, +33, -630, +212, +995, +139, -10, -165, -98, +59, +41, +7, +5, -42, -115, +70, -77, +64, -30, -184, -140, -90, -15, -39, -72, -166, }, + { +3974, +4515, +1563, +2245, +1024, +802, +1202, -117, -913, -34, +802, +109, -1, -233, -83, +92, -89, -104, +51, +9, -107, +121, -30, +82, +5, -192, -173, -173, -183, -127, -31, -91, }, + { +4054, +4585, +1487, +2101, +966, +877, +1249, -118, -989, -177, +776, +97, -55, -258, -131, +135, +150, +93, +56, -19, -114, +43, -129, +22, -1, -45, +2, -139, -203, -156, -63, -102, }, + { +4259, +4784, +1375, +1894, +699, +649, +1161, -136, -1098, -231, +956, +281, +11, -419, -371, +109, +241, +228, +268, +110, -100, +71, -147, -93, -92, -157, -42, +95, +87, -12, -90, -205, }, + { +4579, +5216, +1470, +1933, +626, +538, +991, -498, -1568, -608, +1002, +425, +4, -387, -268, +119, +196, +143, +174, +193, +16, +138, -48, -8, -110, -248, -134, +1, +116, +214, +157, -94, }, + { +5066, +5749, +1521, +2044, +640, +538, +926, -811, -2022, -949, +1051, +371, -252, -591, -377, +160, +302, +178, +234, +199, -60, +136, -88, +2, -40, -201, -104, +47, +67, +67, +148, +54, }, + { +5824, +6357, +1417, +2188, +604, +584, +938, -1093, -2439, -1140, +1270, +283, -550, -828, -547, +25, +209, +96, +231, +241, -75, +150, -72, -37, -72, -173, -85, +78, +65, +65, +106, -26, }, + { +6705, +7034, +1139, +2353, +584, +531, +931, -1467, -2777, -1121, +1528, +169, -787, -1030, -630, -42, +104, -4, +133, +171, -212, +119, -74, -14, -133, -284, -130, +49, +82, +127, +179, +14, }, + { +7494, +7838, +930, +2406, +628, +447, +939, -1854, -3086, -1058, +1655, +110, -889, -1206, -690, -58, +26, -36, +86, +157, -264, +21, -209, -75, -225, -353, -152, +18, +88, +139, +234, +25, }, + { +7942, +8659, +1161, +2413, +549, +251, +1028, -2079, -3182, -1091, +1258, +58, -663, -1281, -862, -154, +30, +213, +47, +25, -289, -45, -300, -173, -306, -391, -167, -32, +123, +117, +202, -22, }, + { +8068, +9218, +1660, +2593, +617, -151, +900, -2038, -3036, -1021, +535, -219, -391, -1036, -790, -354, -141, +391, +141, -138, -345, -47, -238, -167, -360, -407, -230, -180, +55, +90, +173, -18, }, + { +8265, +9463, +2020, +3087, +909, -384, +486, -2292, -2858, -489, -42, -958, -218, -589, -423, -435, -531, +296, +255, -79, -195, -39, -305, -178, -439, -471, -257, -338, -66, +74, +175, -25, }, + { +8828, +9479, +1858, +3692, +1200, -444, +108, -2595, -2598, +225, -443, -1837, -211, -338, +130, -297, -878, +233, +403, -155, -160, +68, -348, -198, -618, -577, -252, -401, -165, +50, +123, -106, }, + { +9769, +9532, +1154, +4053, +1153, -433, +152, -3044, -2341, +1209, -501, -2958, -549, -180, +526, +122, -1023, +24, +484, -243, -273, +98, -497, -249, -730, -696, -208, -420, -294, +24, +104, -194, }, + { +10963, +10314, -18, +3480, +928, -378, +839, -3146, -2679, +1815, +181, -3477, -1184, -280, +767, +456, -1173, -196, +557, -284, -445, +171, -561, -316, -811, -844, -191, -379, -352, -44, +133, -183, }, + { +12437, +11094, -1445, +2785, +310, -110, +1926, -3295, -3171, +2280, +1217, -3589, -1908, -710, +881, +724, -1458, -605, +653, -147, -678, +163, -557, -237, -805, -1040, -224, -349, -355, -12, +190, -216, }, + { +13565, +11394, -2213, +2503, -423, +267, +2542, -3317, -3490, +2485, +2267, -3594, -2432, -1318, +735, +938, -1508, -888, +536, -170, -812, +241, -591, -131, -780, -1195, -241, -201, -267, -8, +131, -283, }, + { +14365, +11785, -2502, +2065, -1128, +690, +3071, -3145, -4082, +2294, +3294, -3126, -2782, -2332, +449, +1450, -1337, -1152, +323, -232, -796, +274, -707, -133, -760, -1160, -175, -77, -224, +1, +81, -344, }, + { +14713, +12188, -2245, +1714, -1545, +697, +3166, -2769, -4728, +1796, +4066, -2366, -3022, -3257, +104, +1860, -844, -1416, +119, -254, -847, +177, -909, -97, -642, -1112, -239, -41, -190, +38, +3, -445, }, + { +14645, +12610, -1772, +1687, -1818, +544, +3067, -2552, -5197, +1109, +4450, -1369, -2921, -4051, -284, +2048, -140, -1537, -208, -123, -1018, +64, -886, -45, -602, -1050, -397, -58, -122, -90, +12, -460, }, + { +14311, +12744, -823, +2013, -2087, +348, +2751, -2573, -5594, +298, +4380, -359, -2003, -4793, -1003, +2181, +482, -1298, -654, -93, -995, -9, -758, +71, -678, -1141, -461, -235, -94, -220, +50, -343, }, + { +13776, +12639, +346, +2236, -2014, +116, +2251, -2236, -6147, -504, +3963, +239, -615, -5033, -2049, +2178, +726, -876, -698, -265, -701, -87, -865, +185, -648, -1310, -522, -464, -75, -87, +92, -171, }, + { +13149, +12169, +1402, +2459, -1565, +84, +1536, -1759, -6362, -1341, +3564, +365, +555, -4522, -3382, +1810, +905, -453, -324, -409, -562, -71, -953, +103, -691, -1468, -465, -480, -26, +135, +160, -111, }, + { +12380, +11681, +1885, +3275, -1333, -131, +1419, -1701, -5961, -2232, +3308, +405, +924, -3382, -4610, +1021, +1316, -94, -127, -370, -464, +43, -1042, -366, -474, -1380, -504, -384, +12, +312, +117, -135, }, + { +11903, +11393, +1499, +3646, -589, -712, +1361, -1471, -5102, -2585, +2953, +277, +167, -1768, -4726, -478, +1438, +247, +197, -198, -524, -85, -1094, -602, -205, -1156, -495, -151, -147, +245, +126, -155, }, + { +11277, +11106, +1068, +3696, +162, -535, +662, -1668, -3438, -2767, +2450, +580, -999, -611, -3766, -1971, +1197, +402, +51, +288, -364, -586, -1018, -564, +72, -690, -805, -170, -83, +153, +125, -123, }, + { +10588, +10248, +1041, +3990, +549, -596, +503, -1508, -2940, -2531, +2104, +852, -1356, -383, -2481, -2696, +537, +474, -251, +251, -199, -459, -926, -574, +197, -420, -1013, -187, +29, +54, +181, -131, }, + { +9521, +9264, +1317, +4316, +722, -767, +764, -816, -2661, -2833, +1508, +1326, -1203, -394, -1458, -2679, +165, +492, -606, -94, -331, -324, -381, -536, -231, -294, -717, -35, +53, -16, +190, -183, }, + { +9295, +8224, +1109, +5503, +566, -1438, +1272, -36, -2542, -3436, +897, +1558, -877, +95, -1150, -3243, +276, +686, -962, -210, -683, -628, -208, -496, -150, -199, -735, +279, +265, -118, +39, -219, }, + { +7955, +7915, +2062, +4941, +392, -1429, +1053, -497, -1939, -2532, -124, +882, -416, +352, -483, -2672, -726, +593, -30, -164, -732, -489, -159, -285, -145, -245, -559, -60, +99, -150, +30, -106, }, + { +7031, +7277, +2031, +4336, +697, -646, +955, -623, -1641, -2182, -233, +535, -317, +229, -165, -1960, -826, +699, +101, -378, -580, -260, -151, -369, -398, -280, -286, -150, -143, -139, +125, -38, }, + { +6295, +6730, +1892, +3559, +1033, +63, +744, -685, -1521, -1804, -171, +306, -225, +149, +4, -1512, -648, +773, +80, -370, -396, -159, -189, -414, -539, -344, -273, -228, -102, -44, +122, +2, }, + { +5567, +6186, +1652, +2969, +1443, +344, +573, -641, -1422, -1343, -105, +121, -57, +247, +28, -1174, -348, +731, +56, -360, -317, -47, -217, -426, -532, -288, -251, -274, -18, +48, +118, -19, }, + { +4996, +5539, +1497, +2838, +1501, +319, +538, -541, -1215, -962, -91, -26, +159, +383, +48, -835, -191, +492, -4, -250, -216, -31, -205, -289, -352, -182, -273, -311, -10, +79, +133, -33, }, + { +4369, +4911, +1493, +2561, +1337, +470, +618, -484, -1046, -662, +27, +2, +405, +487, +57, -528, -160, +283, +46, -156, -163, +53, -43, -114, -258, -209, -289, -247, +29, +65, +110, +22, }, + { +3634, +4210, +1572, +2321, +1183, +650, +679, -395, -859, -352, +291, +111, +541, +545, +126, -276, -117, +132, +29, -68, -30, +169, -23, -64, -184, -195, -205, -180, +9, +60, +105, +51, }, + { +3016, +3577, +1500, +2051, +987, +707, +858, -234, -596, +114, +631, +273, +558, +609, +229, -166, -145, -29, +96, +94, -41, +113, +28, +51, -118, -204, -138, -123, -4, +88, +117, +1, }, + { +2776, +3142, +1225, +1761, +847, +791, +1067, +272, -238, +302, +902, +413, +488, +421, +138, -130, -62, +38, +115, +77, -79, +99, +64, +85, -110, -151, -69, -33, +59, +61, +9, -164, }, + { +2597, +2855, +1075, +1771, +1129, +1005, +1152, +473, +68, +492, +881, +270, +197, +161, +118, +79, +88, +32, -20, -32, -27, +115, +32, +153, +68, -66, -90, -62, +9, -70, -139, -184, }, + { +2669, +3145, +1471, +2033, +1221, +1035, +1183, +275, -243, +270, +725, +249, +159, +52, +151, +138, -51, -81, +23, -1, -18, +190, +86, +173, +118, -82, -134, -156, -186, -160, -81, -98, }, + { +2819, +3343, +1567, +2048, +1169, +1075, +1248, +244, -402, -85, +455, +306, +297, +79, +134, +124, +55, +91, +34, -77, +10, +181, -46, +108, +219, +134, -56, -289, -273, -145, -92, -88, }, + { +2900, +3386, +1384, +1773, +1008, +1084, +1261, +225, -495, +12, +783, +242, +37, -53, -9, +253, +383, +202, +102, +70, -62, +11, -40, +64, +31, +110, +190, -6, -190, -236, -126, -117, }, + { +3231, +3701, +1275, +1612, +705, +788, +1133, +122, -729, -179, +937, +566, +221, -206, -199, +166, +308, +308, +335, +202, +36, +131, -62, -37, -38, -83, +43, +191, +169, +38, -106, -235, }, + { +3635, +4205, +1339, +1662, +626, +662, +937, -299, -1254, -578, +1037, +699, +166, -176, -99, +200, +312, +211, +211, +294, +129, +195, +63, +39, -40, -148, -70, +74, +192, +288, +163, -117, }, + { +4194, +4783, +1346, +1788, +630, +642, +840, -677, -1766, -952, +1124, +635, -123, -376, -187, +304, +462, +242, +276, +259, +18, +219, +17, +71, +61, -99, -59, +121, +125, +97, +206, +113, }, + { +4979, +5362, +1202, +1940, +602, +719, +840, -1009, -2235, -1155, +1361, +510, -477, -624, -359, +169, +423, +212, +324, +400, +17, +168, -5, +20, +47, -58, +31, +174, +74, +79, +86, +27, }, + { +6045, +6030, +859, +2185, +506, +725, +848, -1395, -2650, -1218, +1701, +338, -794, -842, -492, +56, +296, +67, +259, +328, -97, +236, +42, +27, -10, -99, -27, +180, +116, +105, +139, -10, }, + { +7127, +6787, +391, +2413, +495, +620, +850, -1887, -2997, -1109, +1985, +195, -1031, -1079, -541, +28, +148, +5, +155, +227, -224, +200, +4, -1, -101, -203, -81, +123, +136, +165, +254, +85, }, + { +8041, +7641, +54, +2482, +542, +525, +873, -2340, -3303, -988, +2101, +103, -1125, -1253, -590, +7, +85, -30, +110, +254, -288, +69, -159, -33, -202, -285, -79, +104, +146, +180, +291, +49, }, + { +8670, +8596, +151, +2444, +429, +355, +1004, -2691, -3423, -1007, +1694, +127, -881, -1445, -767, -93, +121, +297, -11, +97, -290, +1, -294, -164, -296, -328, -78, +89, +211, +122, +240, -5, }, + { +8845, +9339, +619, +2533, +443, -115, +1042, -2670, -3312, -1004, +882, -48, -483, -1247, -888, -243, +10, +511, +67, -76, -337, -56, -239, -164, -351, -335, -149, -114, +174, +149, +241, -25, }, + { +8992, +9809, +1188, +2897, +625, -583, +690, -2700, -3089, -635, -11, -646, -104, -790, -618, -507, -344, +619, +168, -148, -236, -69, -262, -132, -414, -375, -196, -304, +74, +121, +206, -26, }, + { +9299, +9977, +1439, +3466, +927, -820, +224, -3016, -2800, -12, -737, -1360, +110, -354, -169, -639, -802, +553, +363, -141, -128, +0, -287, -205, -550, -443, -225, -438, -58, +99, +221, -4, }, + { +10058, +9879, +1163, +4263, +1177, -922, -219, -3311, -2434, +862, -1303, -2457, +248, -70, +493, -501, -1236, +540, +538, -275, -74, +119, -379, -193, -775, -553, -189, -513, -161, +91, +140, -128, }, + { +11144, +9761, +369, +4862, +1071, -936, -203, -3855, -2065, +1997, -1565, -3828, +38, +198, +979, -90, -1513, +341, +661, -404, -156, +165, -600, -237, -878, -660, -142, -564, -301, +70, +104, -215, }, + { +12546, +10246, -966, +4691, +927, -833, +309, -4199, -2112, +2959, -1144, -4789, -535, +243, +1329, +364, -1689, +85, +795, -505, -386, +288, -677, -309, -1025, -827, -61, -516, -419, +27, +137, -278, }, + { +14045, +11382, -2788, +3760, +580, -650, +1686, -4408, -2934, +3566, -51, -5174, -1321, +73, +1428, +705, -1911, -307, +891, -369, -599, +281, -731, -286, -1048, -1082, -16, -454, -510, -50, +248, -211, }, + { +15970, +11714, -4486, +3446, -223, -120, +2672, -4778, -3223, +4216, +1130, -5477, -2203, -468, +1756, +960, -2402, -712, +1105, -303, -935, +391, -711, -185, -1048, -1265, -82, -424, -432, +54, +241, -341, }, + { +17220, +11916, -5327, +3210, -1072, +444, +3327, -4843, -3644, +4439, +2382, -5600, -2783, -1154, +1580, +1254, -2411, -1038, +941, -324, -1057, +499, -778, -47, -1032, -1459, -61, -214, -352, +22, +162, -398, }, + { +18240, +12274, -5799, +2731, -1899, +1079, +3952, -4749, -4376, +4334, +3662, -5208, -3266, -2349, +1378, +1910, -2296, -1381, +715, -370, -1038, +545, -912, -20, -1028, -1437, +31, -67, -319, +18, +102, -464, }, + { +18833, +12668, -5683, +2184, -2439, +1329, +4171, -4328, -5232, +3901, +4824, -4546, -3741, -3476, +1105, +2561, -1914, -1779, +533, -418, -1036, +485, -1184, +14, -859, -1412, +1, +27, -315, +91, +5, -584, }, + { +19008, +13162, -5318, +1926, -2788, +1224, +4180, -3950, -5962, +3321, +5549, -3543, -3998, -4445, +848, +2902, -1193, -2106, +263, -305, -1180, +327, -1267, +112, -828, -1285, -169, +16, -197, +33, -71, -637, }, + { +18737, +13681, -4618, +1963, -3145, +1044, +4007, -3789, -6476, +2407, +5985, -2289, -3707, -5492, +344, +3198, -328, -2230, -228, -74, -1401, +254, -1150, +181, -810, -1310, -322, -25, -144, -179, +21, -629, }, + { +18348, +13822, -3468, +2221, -3496, +867, +3623, -3756, -6961, +1517, +5968, -1098, -2716, -6475, -416, +3438, +367, -2015, -778, +7, -1351, +154, -986, +288, -914, -1395, -403, -225, -85, -331, +78, -475, }, + { +17731, +13756, -2024, +2354, -3509, +578, +3074, -3309, -7685, +628, +5586, -260, -1146, -7037, -1592, +3604, +681, -1553, -946, -177, -966, +4, -1088, +448, -897, -1594, -473, -493, -35, -196, +111, -266, }, + { +16988, +13672, -858, +2219, -2857, +446, +2261, -2723, -8186, -143, +5173, +8, +354, -6856, -3083, +3505, +877, -1058, -660, -442, -746, +8, -1186, +419, -932, -1761, -398, -679, +86, +39, +115, -97, }, + { +16138, +12803, +575, +2903, -2738, +274, +1624, -2112, -8167, -1458, +4911, +239, +1463, -6034, -4971, +3030, +1336, -611, -295, -547, -554, +6, -1339, +184, -852, -1914, -351, -543, +76, +333, +176, -169, }, + { +15265, +12532, +737, +3734, -2365, -148, +1753, -2212, -7519, -2381, +4804, +154, +1395, -4336, -6360, +1992, +1942, -242, -104, -546, -503, +190, -1457, -473, -403, -1767, -438, -339, +44, +430, +96, -165, }, + { +14708, +12215, +282, +4265, -1521, -1002, +1719, -1840, -6421, -2928, +4336, +115, +445, -2293, -6587, +102, +2286, +140, +231, -302, -617, -45, -1479, -669, -78, -1505, -452, -92, -181, +382, +108, -202, }, + { +13989, +12100, -300, +4352, -584, -894, +751, -1879, -4250, -3597, +3863, +484, -1133, -527, -5742, -1882, +2289, +370, +107, +366, -557, -756, -1254, -675, +260, -958, -884, -49, -127, +270, +110, -168, }, + { +13107, +11235, -316, +4649, +46, -886, +387, -2126, -3083, -3291, +3067, +1077, -1914, +82, -4188, -3391, +1793, +547, -349, +600, -246, -893, -1266, -562, +499, -574, -1293, -81, +40, +122, +177, -141, }, + { +12123, +10207, -26, +5040, +240, -1159, +554, -1339, -3049, -3489, +2834, +1553, -2088, +86, -2702, -3839, +1078, +690, -758, +264, -223, -525, -820, -718, +257, -322, -1226, +21, +75, +12, +282, -234, }, + { +10747, +9168, +566, +5397, +351, -1371, +926, -546, -2786, -3752, +1902, +1979, -1557, -9, -1749, -3706, +668, +745, -1061, -69, -419, -453, -245, -659, -216, -192, -848, +155, +137, -64, +205, -239, }, + { +10338, +7957, +561, +6771, -2, -2128, +1652, +306, -2773, -4474, +1333, +2284, -1324, +617, -1488, -4429, +966, +918, -1497, -98, -822, -796, -54, -649, -27, -96, -951, +551, +331, -223, +48, -268, }, + { +8720, +7871, +1937, +5955, -252, -2274, +1362, -265, -1942, -3385, -195, +1599, -597, +942, -704, -3945, -437, +923, -232, -93, -938, -655, -8, -340, +19, -166, -798, +69, +242, -236, +6, -124, }, + { +7602, +7369, +1980, +5375, +188, -1525, +1271, -440, -1548, -3046, -447, +1237, -466, +757, -218, -3077, -861, +1080, +8, -496, -751, -394, -12, -415, -286, -141, -377, -40, -114, -262, +138, -57, }, + { +6659, +6622, +2031, +4526, +599, -537, +864, -488, -1252, -2664, -491, +837, -265, +650, -6, -2474, -736, +1187, -23, -536, -523, -192, -58, -543, -514, -195, -232, -228, -227, -106, +203, +4, }, + { +5900, +6258, +1847, +3648, +1138, +55, +581, -518, -1216, -2232, -426, +663, -175, +546, +190, -2072, -582, +1218, -57, -487, -330, -182, -121, -530, -618, -248, -293, -250, -52, -43, +131, +41, }, + { +5056, +5699, +1640, +3073, +1682, +223, +366, -415, -1093, -1701, -428, +436, +76, +685, +156, -1697, -250, +1101, -72, -482, -271, -35, -164, -545, -578, -155, -255, -314, +31, +57, +129, +2, }, + { +4420, +5031, +1526, +2971, +1795, +154, +317, -287, -871, -1279, -467, +257, +332, +803, +167, -1309, -82, +802, -141, -342, -151, -34, -185, -411, -382, -26, -278, -370, +32, +101, +150, -29, }, + { +3741, +4414, +1550, +2755, +1697, +240, +358, -209, -641, -963, -482, +156, +636, +955, +153, -952, -130, +526, -22, -234, -141, -29, -17, -156, -225, -73, -393, -324, +91, +81, +127, +14, }, + { +2974, +3771, +1714, +2268, +1466, +609, +473, -195, -532, -642, -208, +371, +868, +928, +168, -618, -68, +350, +7, -152, -30, +198, +164, -106, -265, -109, -239, -204, +88, +59, +101, +117, }, + { +2091, +3036, +1907, +2129, +1391, +724, +464, -41, -262, -310, +40, +365, +939, +1032, +282, -384, -88, +174, -12, -37, +129, +229, +41, -48, -98, -70, -190, -179, +36, +72, +111, +99, }, + { +1445, +2433, +1850, +1781, +1171, +805, +720, +119, +17, +232, +387, +551, +917, +1092, +388, -310, -142, -4, +121, +154, +38, +139, +164, +94, -72, -106, -103, -116, +22, +112, +121, +28, }, + { +1185, +1993, +1603, +1441, +986, +909, +956, +692, +298, +365, +753, +730, +851, +889, +295, -301, -75, +80, +163, +139, -24, +148, +231, +110, -113, -59, -17, -3, +88, +93, +26, -171, }, + { +929, +1455, +1263, +1389, +1207, +1075, +1117, +1195, +937, +623, +608, +551, +588, +631, +253, -42, +158, +103, +4, +39, +49, +61, +131, +212, +119, +81, -26, +0, +138, -59, -235, -232, }, + { +822, +1583, +1506, +1563, +1699, +1350, +1018, +900, +685, +649, +736, +432, +188, +378, +360, +160, +134, -13, -136, -75, +123, +246, +203, +257, +240, +33, -106, -86, -100, -185, -187, -131, }, + { +1055, +2056, +2052, +1866, +1431, +1170, +1157, +723, +299, +248, +365, +522, +438, +367, +368, +108, -145, -117, +92, +69, +74, +249, +238, +249, +240, +27, -128, -200, -286, -220, -88, -53, }, + { +1205, +2171, +1980, +1773, +1441, +1300, +1201, +725, +193, -127, +142, +624, +550, +387, +335, +108, +110, +159, +2, -95, +146, +227, +29, +177, +403, +337, -41, -376, -330, -171, -140, -61, }, + { +1241, +2158, +1760, +1464, +1328, +1398, +1227, +684, +95, +70, +568, +408, +149, +304, +228, +314, +526, +203, +21, +102, +15, -27, +94, +161, +134, +344, +341, -75, -343, -352, -143, -62, }, + { +1622, +2444, +1530, +1228, +926, +1008, +1151, +744, -34, -42, +764, +765, +377, -38, -176, +304, +572, +431, +399, +163, +15, +139, +7, -83, +139, +174, +132, +311, +140, -144, -288, -260, }, + { +1988, +2810, +1493, +1199, +762, +896, +984, +329, -524, -369, +974, +1088, +421, +50, +39, +109, +262, +367, +396, +394, +294, +181, +60, +66, -63, -115, +123, +273, +319, +228, -43, -271, }, + { +2526, +3457, +1538, +1318, +724, +747, +744, -154, -1107, -804, +1097, +1060, +234, +52, +56, +324, +531, +217, +162, +419, +225, +256, +263, +67, +92, -2, -93, +88, +240, +428, +251, -76, }, + { +3196, +4012, +1429, +1473, +720, +771, +686, -545, -1618, -1131, +1253, +952, -133, -206, -51, +422, +646, +293, +343, +345, +85, +287, +111, +124, +185, +14, -5, +226, +158, +71, +246, +198, }, + { +4055, +4548, +1209, +1653, +679, +876, +676, -915, -2105, -1296, +1540, +778, -521, -439, -221, +267, +627, +274, +404, +557, +82, +190, +96, +57, +173, +67, +122, +268, +75, +91, +69, +85, }, + { +5274, +5182, +788, +1972, +519, +928, +689, -1324, -2574, -1398, +1954, +556, -880, -663, -380, +149, +471, +122, +381, +459, +18, +339, +145, +8, +122, +110, +27, +301, +132, +67, +132, +6, }, + { +6463, +5753, +204, +2322, +409, +883, +685, -1827, -2887, -1237, +2299, +272, -1164, -830, -425, +53, +394, -18, +257, +435, -217, +313, +174, +118, +26, -101, +42, +280, +156, +192, +176, +19, }, + { +7727, +6595, -464, +2628, +421, +701, +721, -2439, -3227, -1064, +2569, +149, -1378, -1132, -432, +91, +148, +15, +160, +263, -252, +291, +71, -34, -78, -110, -65, +186, +193, +201, +372, +187, }, + { +8674, +7441, -887, +2687, +475, +615, +762, -2918, -3490, -885, +2619, +32, -1422, -1271, -474, +50, +151, -43, +133, +383, -345, +103, -122, +5, -192, -218, -4, +192, +197, +219, +350, +56, }, + { +9426, +8468, -909, +2618, +320, +470, +937, -3379, -3580, -906, +2153, +153, -1146, -1581, -641, -54, +224, +390, -97, +193, -307, +44, -287, -162, -284, -252, +7, +204, +290, +112, +287, +9, }, + { +9648, +9372, -545, +2609, +289, -35, +1177, -3423, -3509, -971, +1312, +111, -652, -1474, -971, -83, +171, +613, -45, +26, -344, -79, -230, -165, -334, -249, -69, -50, +303, +195, +309, -45, }, + { +9794, +9998, +39, +2769, +379, -638, +980, -3266, -3261, -859, +205, -83, -237, -1162, -748, -518, -59, +946, +29, -268, -354, +5, -131, -183, -422, -257, -155, -194, +224, +120, +248, +7, }, + { +9981, +10397, +622, +3286, +610, -1141, +509, -3403, -3000, -195, -811, -998, +359, -548, -477, -751, -558, +952, +151, -164, -95, -136, -277, -86, -487, -322, -168, -469, +127, +155, +238, -39, }, + { +10400, +10438, +745, +3939, +894, -1363, +3, -3769, -2585, +471, -1670, -1614, +581, -153, +52, -948, -1056, +930, +424, -236, -31, +17, -267, -241, -666, -379, -196, -563, -20, +129, +269, +14, }, + { +11293, +10206, +442, +4923, +1094, -1505, -530, -3996, -2130, +1419, -2437, -2806, +924, +121, +806, -871, -1571, +1001, +618, -424, +58, +151, -379, -185, -956, -481, -127, -651, -119, +140, +160, -149, }, + { +12405, +9835, -281, +5828, +885, -1582, -583, -4592, -1654, +2615, -2996, -4362, +959, +504, +1380, -564, -2020, +895, +761, -610, +81, +185, -720, -204, -1012, -549, -116, -759, -235, +116, +85, -201, }, + { +13934, +9778, -1546, +6319, +686, -1381, -382, -5271, -1119, +3944, -3172, -5767, +686, +682, +1741, +77, -2181, +507, +979, -721, -237, +318, -773, -227, -1265, -756, +92, -716, -477, +162, +134, -400, }, + { +15591, +10583, -3448, +5772, +592, -1315, +577, -5494, -1701, +4790, -2265, -6627, -93, +745, +2160, +357, -2547, +377, +1130, -881, -407, +529, -934, -349, -1319, -885, +105, -673, -507, +45, +171, -344, }, + { +17313, +11801, -5808, +4851, +227, -980, +2385, -6012, -2746, +5567, -996, -7076, -952, +586, +2154, +773, -2812, -169, +1297, -600, -709, +461, -948, -242, -1337, -1246, +226, -600, -658, -10, +354, -281, }, + { +19637, +11754, -7792, +4845, -732, -208, +3348, -6604, -2889, +6378, +241, -7587, -1944, +32, +2714, +992, -3500, -540, +1647, -590, -1131, +670, -931, -134, -1354, -1421, +144, -583, -536, +136, +307, -469, }, + { +21056, +11745, -8787, +4783, -1622, +469, +3944, -6753, -3241, +6740, +1590, -8058, -2609, -411, +2588, +1199, -3554, -896, +1543, -588, -1309, +826, -1017, +54, -1317, -1707, +140, -326, -455, +92, +212, -518, }, + { +22470, +11991, -9701, +4389, -2684, +1431, +4782, -6942, -4015, +6909, +3084, -7923, -3158, -1809, +2522, +1950, -3598, -1289, +1296, -609, -1331, +920, -1125, +160, -1425, -1754, +339, -152, -445, +42, +154, -583, }, + { +23333, +12284, -9893, +3816, -3344, +2109, +5145, -6669, -4930, +6673, +4509, -7499, -3802, -2971, +2448, +2729, -3461, -1700, +1131, -635, -1249, +918, -1341, +144, -1300, -1646, +329, -23, -418, +82, +75, -652, }, + { +23907, +12687, -9670, +3149, -3819, +2297, +5278, -6129, -6015, +6230, +5855, -6823, -4412, -4207, +2246, +3493, -3012, -2245, +989, -670, -1278, +815, -1685, +265, -1083, -1694, +279, +79, -444, +187, -65, -803, }, + { +24068, +13242, -9317, +2853, -4157, +2181, +5293, -5711, -6874, +5677, +6685, -5741, -4828, -5227, +2103, +3851, -2196, -2705, +724, -483, -1476, +639, -1724, +395, -1100, -1511, +43, +72, -276, +84, -146, -836, }, + { +23835, +13868, -8722, +2737, -4517, +2057, +5139, -5437, -7512, +4767, +7374, -4441, -4873, -6362, +1764, +4214, -1258, -3060, +266, -145, -1812, +589, -1602, +416, -1030, -1500, -192, +122, -223, -168, -12, -862, }, + { +23307, +14493, -7616, +2886, -5061, +1805, +4905, -5438, -8024, +3633, +7772, -2910, -4088, -7840, +1069, +4716, -316, -3023, -501, +151, -1943, +531, -1399, +596, -1194, -1653, -168, -131, -123, -407, +123, -804, }, + { +22971, +14195, -6115, +3007, -5341, +1777, +4268, -5238, -8637, +2925, +7500, -1787, -2907, -8792, +313, +4922, +197, -2731, -1003, +162, -1684, +337, -1204, +594, -1286, -1645, -381, -303, -37, -516, +148, -504, }, + { +22070, +14375, -4392, +2868, -5223, +1286, +3714, -4447, -9674, +1907, +7159, -860, -973, -9579, -1209, +5344, +421, -2142, -1115, -130, -1112, +96, -1456, +898, -1239, -2004, -343, -667, +59, -231, +163, -308, }, + { +21240, +14393, -3174, +2492, -4298, +1094, +2745, -3688, -10265, +1178, +6777, -618, +645, -9443, -2927, +5381, +636, -1617, -791, -478, -882, +115, -1535, +816, -1293, -2149, -223, -878, +246, +6, +122, -102, }, + { +20341, +13345, -1099, +2866, -4191, +914, +1804, -2626, -10582, -327, +6635, -287, +2111, -8876, -5214, +5209, +1109, -1120, -336, -736, -608, +24, -1715, +729, -1303, -2434, -80, -706, +170, +364, +230, -221, }, + { +19164, +13059, -468, +4105, -4326, +617, +1911, -2785, -9817, -1770, +6595, -131, +2540, -7381, -7331, +4587, +2019, -857, -311, -599, -501, +196, -1861, -41, -744, -2295, -231, -641, +296, +624, -2, -206, }, + { +18422, +13001, -808, +4597, -3422, -211, +2249, -2884, -9087, -2495, +6668, -543, +1814, -4858, -8725, +3134, +2718, -437, +54, -836, -508, +440, -2036, -713, -94, -2244, -301, -159, -24, +549, +64, -190, }, + { +17855, +12583, -1366, +5362, -2553, -1443, +2178, -2151, -7613, -3290, +5904, -270, +528, -2457, -8756, +644, +3333, -90, +363, -320, -761, -106, -1922, -663, +147, -1870, -338, +6, -294, +558, +55, -269, }, + { +16989, +12653, -2050, +5407, -1459, -1174, +828, -2193, -4917, -4430, +5563, +243, -1454, -249, -7905, -1684, +3547, +136, +95, +521, -741, -985, -1507, -732, +567, -1249, -1016, +108, -174, +393, +77, -205, }, + { +15955, +11844, -2035, +5709, -643, -1173, +284, -2723, -3123, -4229, +4384, +1201, -2654, +769, -6230, -3909, +3368, +375, -503, +1015, -370, -1372, -1549, -501, +871, -801, -1619, +110, +41, +191, +164, -153, }, + { +15076, +10787, -1651, +6161, -494, -1528, +363, -1853, -3374, -4091, +4325, +1345, -2786, +774, -4553, -4709, +2401, +708, -854, +734, -248, -874, -1363, -746, +954, -560, -1815, +201, +149, +40, +297, -250, }, + { +13619, +9790, -1075, +6480, -296, -1830, +805, -986, -3179, -4836, +3833, +2526, -3239, +844, -2857, -5293, +1983, +856, -1538, +348, -255, -582, -521, -965, +224, -130, -1436, +334, +56, -57, +433, -405, }, + { +11990, +8803, -122, +6797, -290, -2053, +1262, -151, -2986, -4897, +2471, +2765, -2059, +507, -2148, -4881, +1467, +956, -1677, +43, -545, -596, -9, -842, -196, -59, -1007, +415, +207, -148, +219, -297, }, + { +12081, +7961, -53, +8316, -2153, -3098, +1599, -1374, -4780, -3918, +6091, +1729, -1658, +3619, -1653, -4298, +1906, +240, -2274, -904, -1646, -475, -581, -1417, -48, -621, -760, +583, -70, -397, +372, +38, }, + { +10221, +7710, +913, +7396, -744, -3594, +288, -1159, -3490, -2578, +4228, +2100, -676, +2348, -1090, -3898, +283, +166, -1419, -166, -1389, -451, +154, -1605, +30, -242, -1341, +92, +30, -175, +146, +10, }, + { +8688, +6964, +1507, +6729, -314, -2703, +315, -1206, -2584, -2071, +3004, +1882, -602, +1784, -753, -3344, +27, -28, -1040, +36, -1038, -169, +101, -1356, +51, -283, -1249, -154, -166, -180, +96, -77, }, + { +7115, +6222, +2194, +6013, +21, -1813, +346, -984, -1850, -1758, +2164, +1394, -547, +1436, -539, -2734, -356, -16, -801, -44, -393, +279, +133, -1115, -80, -420, -1086, -375, -460, -339, +102, +64, }, + { +5945, +5731, +2369, +5160, +628, -1311, +172, -355, -1302, -1644, +1506, +1052, -512, +980, -248, -2308, -600, +61, -662, +216, +230, +430, +21, -924, -262, -525, -975, -595, -515, -352, +24, +58, }, + { +4951, +5289, +2362, +4475, +1066, -1021, +399, +107, -1048, -1369, +919, +662, -450, +769, -110, -1957, -639, +150, -163, +531, +305, +258, +28, -774, -421, -578, -897, -479, -486, -369, -53, -38, }, + { +4363, +4902, +2175, +3906, +1203, -661, +681, +254, -736, -1138, +362, +420, -286, +700, -78, -1616, -376, +371, +225, +620, +133, +107, -31, -735, -442, -346, -613, -527, -506, -305, -33, +11, }, + { +3887, +4410, +1997, +3432, +1166, -200, +836, +280, -488, -1038, -68, +289, -94, +640, +127, -1246, -284, +556, +413, +412, -66, +15, -158, -547, -132, -220, -649, -561, -395, -212, +21, +92, }, + { +3376, +3956, +1885, +2975, +1204, +193, +869, +327, -309, -968, -279, +153, +258, +942, +175, -1108, -255, +727, +480, +101, -239, +78, +89, -338, -137, -200, -570, -519, -230, -33, +148, +121, }, + { +2882, +3708, +1714, +2272, +1421, +628, +806, +292, -242, -915, -154, +676, +462, +684, +167, -864, -170, +750, +347, -82, -16, +193, +144, -210, -259, -192, -347, -281, -16, +141, -8, -250, }, + { +2046, +3121, +1820, +2057, +1654, +693, +650, +385, +140, -232, -150, +593, +684, +440, +155, -506, -275, +602, +454, -11, +56, +206, +86, -187, -110, +2, -156, -45, +18, -158, -271, -285, }, + { +1488, +2866, +1983, +1297, +1310, +1249, +896, +455, +406, +318, +200, +433, +517, +126, +107, -107, -188, +504, +467, +57, +7, +98, +167, +102, +54, +69, +57, -187, -324, -266, -204, -157, }, + { +1053, +2185, +1754, +1283, +1374, +1384, +1276, +1018, +579, +445, +413, +125, +270, +305, -100, -39, +315, +357, +242, +93, +19, +226, +393, +295, +143, -27, -218, -292, -266, -226, -155, -132, }, + { +1057, +1688, +1471, +1786, +1766, +1650, +1060, +783, +933, +472, +248, +183, +76, +69, +137, +183, +238, +233, +230, +317, +365, +313, +253, +237, +26, -223, -314, -259, -170, -175, -164, -190, }, + { +1484, +2348, +1611, +1846, +1758, +1472, +1102, +423, +268, +233, +403, +262, +27, +176, +309, +97, -27, +318, +563, +614, +474, +195, -31, -203, -192, -193, -175, -172, -180, -189, -226, -227, }, + { +1189, +2150, +2013, +2091, +1722, +1365, +885, +462, +260, +102, +413, +490, +320, +96, +1, +58, +212, +334, +486, +716, +577, +224, -92, -268, -289, -245, -180, -118, -126, -201, -214, -200, }, + { +1538, +2422, +1826, +1858, +1328, +1049, +982, +559, +11, +288, +803, +359, +369, +46, -142, +244, +201, +241, +677, +674, +409, +408, -39, -390, -282, -219, -225, -163, -112, -102, -144, -262, }, + { +1451, +2242, +1550, +1542, +1259, +1129, +1099, +613, +12, +373, +659, +432, +541, +134, +290, +219, +14, +256, +292, +393, +633, +888, +287, -211, -275, -432, -238, -88, -138, -127, -59, -184, }, + { +1889, +2552, +1389, +1386, +798, +799, +1037, +440, -97, +313, +902, +615, +250, +224, +305, +196, +454, +188, +213, +467, +218, +428, +693, +519, -184, -445, -344, -230, -88, -89, -79, -137, }, + { +2363, +3156, +1398, +1195, +447, +518, +860, +263, -393, +80, +1073, +455, +343, +419, -4, +307, +535, +250, +536, +315, -34, +496, +401, +321, +495, -3, -404, -292, -153, -89, -99, -128, }, + { +2825, +3421, +1231, +1466, +685, +599, +704, -461, -1098, +100, +1264, +329, +295, +180, +24, +419, +226, +386, +756, +380, +127, +240, +62, +328, +348, +131, +263, +30, -341, -335, -68, -108, }, + { +3491, +4043, +1102, +1542, +710, +589, +566, -937, -1530, +137, +1469, +122, -70, -161, -43, +468, +450, +426, +430, +401, +160, +372, +284, +90, +60, +56, +234, +293, +163, -117, -305, -271, }, + { +4318, +4870, +895, +1479, +603, +516, +626, -1326, -2005, +237, +1656, -115, -219, -418, -328, +396, +406, +377, +591, +338, -178, +469, +312, +158, +217, -234, -8, +329, +210, +69, +187, -16, }, + { +5303, +5497, +566, +1592, +649, +557, +509, -1916, -2395, +604, +1814, -332, -334, -762, -437, +361, +208, +350, +554, +119, -140, +409, +18, +313, +179, -239, +98, -40, +40, +299, +419, +154, }, + { +6339, +6214, +236, +1676, +646, +546, +369, -2580, -2680, +1136, +1943, -586, -423, -1111, -529, +411, +162, +286, +376, -36, -255, +422, +29, +77, +2, -290, -43, +175, +135, +31, +367, +299, }, + { +7425, +6991, -61, +1742, +482, +489, +257, -3260, -2805, +1599, +1929, -688, -450, -1459, -633, +395, +140, +359, +251, -272, -354, +408, -47, +55, -274, -528, -51, +174, +73, +63, +482, +141, }, + { +8275, +7928, -159, +1756, +330, +237, +226, -3790, -2846, +1890, +1754, -690, -422, -1605, -800, +248, +185, +442, +143, -377, -482, +428, -180, -145, -222, -605, -186, -18, +24, +140, +387, +163, }, + { +8779, +8969, +132, +1669, +354, -215, +82, -3976, -2837, +1984, +1471, -648, -422, -1570, -842, -60, +130, +630, +84, -403, -597, +177, +30, -221, -403, -610, -358, +87, -64, -83, +409, +63, }, + { +8644, +10014, +1089, +1453, +493, -603, -298, -3906, -2820, +1754, +1240, -552, -546, -1453, -849, -345, +44, +672, +148, -531, -682, +196, +32, -233, -584, -609, -409, -84, -103, -217, +378, +199, }, + { +8306, +10593, +2458, +1598, +488, -758, -657, -3861, -2849, +1369, +1000, -322, -680, -1443, -722, -668, -37, +677, -45, -127, -673, -156, +70, -281, -443, -635, -631, -315, -160, -54, +479, +139, }, + { +8399, +10550, +3351, +2403, +311, -794, -759, -3852, -2757, +792, +477, +47, -726, -1554, -558, -902, -349, +816, +82, -285, -484, -159, +82, -356, -623, -644, -824, -243, +7, -98, +292, +151, }, + { +8816, +10302, +3569, +3594, +382, -1029, -763, -3770, -2393, +191, -597, +339, -557, -1492, -281, -1488, -279, +1005, -143, -84, -408, -266, +120, -345, -812, -549, -772, -407, -193, -80, +533, +172, }, + { +9548, +9994, +3253, +4643, +623, -1187, -749, -3597, -1800, -143, -2200, +304, -135, -1804, +254, -1488, -709, +1187, -140, -38, -318, -470, +133, -281, -894, -381, -819, -627, -170, -88, +384, +19, }, + { +10751, +9616, +2488, +5649, +553, -1161, -620, -3463, -848, -358, -3996, +121, +130, -2080, +871, -1563, -750, +1416, -465, -119, -39, -417, +70, -275, -1074, -309, -632, -645, -410, -224, +390, -12, }, + { +12014, +9312, +1365, +6755, +300, -1330, -113, -3321, +199, -72, -5935, -749, +987, -2491, +1095, -1087, -1039, +1478, -609, -197, +278, -296, -103, -265, -1088, -288, -651, -818, -474, -102, +420, -351, }, + { +13420, +9342, -277, +7865, +178, -2087, +567, -3307, +1525, +754, -7969, -2060, +1804, -2289, +1011, -875, -1158, +1512, -570, -399, +356, -20, -88, -202, -1219, -576, -573, -671, -574, -252, +223, -435, }, + { +14807, +9961, -2295, +8150, +567, -2864, +1147, -3390, +2627, +2008, -9297, -3776, +2200, -1711, +807, -518, -1276, +1360, -541, -609, +222, +438, +75, -495, -1315, -653, -426, -658, -1054, -304, +558, -512, }, + { +16799, +11079, -4722, +7037, +966, -2929, +1994, -3796, +2704, +3553, -9595, -5358, +1866, -912, +909, -200, -1483, +979, -455, -806, +207, +669, +68, -725, -1243, -745, -665, -630, -1077, -219, +691, -658, }, + { +18748, +11700, -6587, +6122, +943, -2914, +2664, -4426, +2804, +5554, -10085, -6616, +1486, -372, +1328, -285, -1517, +471, -396, -837, +75, +587, +104, -751, -1461, -967, -816, -312, -988, -264, +684, -677, }, + { +20863, +12866, -8572, +4385, +266, -1602, +3832, -5716, +2704, +6971, -9876, -6842, +1023, -479, +1689, -35, -1641, +123, -340, -939, -107, +662, +89, -932, -1675, -1081, -768, +37, -888, -310, +796, -570, }, + { +22765, +13783, -10202, +2941, -746, -81, +4256, -6846, +2898, +7803, -8917, -6979, +238, -972, +2079, +576, -1912, -370, -306, -915, -363, +634, -131, -948, -1776, -1303, -718, +235, -798, -279, +1030, -691, }, + { +24585, +14272, -11287, +1831, -1784, +1157, +4160, -7450, +3079, +8440, -7630, -7341, -269, -1716, +2654, +1114, -2449, -581, -181, -975, -680, +550, -325, -686, -1945, -1634, -616, +402, -659, -135, +1021, -867, }, + { +25869, +14987, -12073, +934, -2610, +1696, +3918, -7361, +2532, +9076, -6029, -7754, -590, -2620, +3019, +1624, -2920, -652, -46, -1264, -966, +600, -472, -467, -2153, -1908, -503, +517, -451, -126, +848, -860, }, + { +26886, +15454, -12309, +371, -3390, +1840, +3592, -7055, +1669, +9594, -4128, -8018, -1017, -3459, +3063, +2189, -3137, -722, +61, -1655, -1121, +760, -729, -326, -2158, -2099, -462, +648, -404, -166, +686, -817, }, + { +27684, +15653, -11980, +24, -4099, +1713, +3246, -6955, +802, +10113, -2356, -7981, -1587, -4226, +3032, +2573, -2987, -885, +150, -1954, -1167, +794, -988, -168, -2014, -2167, -434, +666, -569, -144, +540, -855, }, + { +27741, +16216, -11243, -193, -4856, +1352, +3238, -7293, -125, +10598, -852, -7518, -2157, -4853, +2670, +2868, -2470, -1300, +205, -1901, -1416, +672, -1092, +16, -1804, -2095, -591, +514, -688, -235, +476, -797, }, + { +27272, +16910, -10056, -159, -5917, +966, +3706, -8117, -996, +10790, +336, -6433, -2511, -5475, +1946, +3049, -1685, -1717, +71, -1629, -1727, +569, -1061, +184, -1403, -2279, -872, +450, -920, -127, +585, -861, }, + { +26292, +17598, -8440, +25, -7051, +509, +4314, -9076, -1994, +10815, +1175, -4930, -2295, -6533, +1145, +3192, -1111, -1763, -328, -1487, -1778, +538, -867, +358, -1413, -2301, -1193, +94, -600, -21, +592, -845, }, + { +24908, +18211, -6510, +265, -7874, +12, +4724, -9478, -3509, +10716, +2192, -3778, -1219, -7880, +248, +3471, -999, -1541, -792, -1628, -1421, +841, -1035, +457, -1363, -2591, -1213, -67, -308, +27, +657, -759, }, + { +23173, +18778, -4279, +420, -8200, -952, +4606, -8583, -5614, +10055, +3892, -3343, +694, -8935, -1196, +3813, -1288, -1146, -860, -1990, -933, +977, -1325, +807, -1645, -2747, -714, -417, -75, +218, +548, -805, }, + { +22000, +18365, -2003, +631, -8453, -1561, +3477, -6891, -7183, +8672, +5645, -3122, +2529, -8996, -3276, +3955, -1630, -956, -275, -2341, -1164, +1321, -1481, +629, -1309, -2933, -330, -280, -173, +221, +483, -718, }, + { +21112, +17584, -502, +1334, -8158, -2273, +2292, -5787, -7662, +7007, +7216, -2614, +3372, -7775, -5664, +3835, -1591, -1170, +244, -2374, -1158, +1090, -1550, +627, -758, -3019, +93, -274, -410, +438, +348, -800, }, + { +20632, +17088, -637, +2364, -7070, -3739, +1695, -4935, -8217, +5848, +8673, -2173, +3453, -6082, -7678, +3590, -1152, -1978, +580, -2279, -1275, +1124, -1750, +530, -17, -2783, -114, -166, -296, +284, +223, -679, }, + { +20783, +15919, -1263, +4036, -6965, -4754, +1892, -4346, -8539, +4539, +9671, -1610, +3670, -4797, -8663, +2624, -468, -2009, +28, -2355, -1460, +1576, -1563, +142, +565, -2599, -176, -95, -480, +333, +227, -736, }, + { +19898, +14834, -883, +5611, -7741, -5058, +2778, -4286, -8109, +2022, +9952, -268, +3452, -2598, -9652, +1195, +498, -1859, -458, -2733, -1925, +1990, -998, -244, +1156, -2794, -697, +541, -558, +197, +87, -769, }, + { +18353, +13734, -294, +6773, -7182, -5247, +2895, -3546, -8000, -248, +9510, +1071, +3101, -432, -9279, -621, +1397, -1978, -692, -2518, -2682, +1633, -584, +363, +1428, -3524, -1018, +1348, -563, -124, +38, -581, }, + { +17879, +12019, -1720, +8332, -4976, -5136, +2334, -3753, -7617, -1708, +9554, +1908, +1367, +1443, -7575, -1992, +1783, -1645, -1254, -2425, -2861, +887, -188, +457, +1489, -3426, -1450, +1731, -415, -317, +8, -369, }, + { +16652, +10730, -1605, +9069, -4100, -5114, +2342, -2722, -7212, -3184, +9140, +2114, -706, +3732, -5117, -4057, +2505, -1340, -2078, -1590, -2693, +151, -496, -93, +1733, -2286, -1942, +1173, +71, -202, +59, -356, }, + { +15156, +9682, -934, +8954, -3661, -4339, +2035, -2043, -6405, -3988, +8467, +1700, -1521, +4564, -3502, -4488, +2506, -991, -2469, -1109, -2320, -118, -899, -916, +1437, -1210, -1567, +612, -176, -55, +492, -295, }, + { +13768, +8771, -687, +8850, -3021, -3693, +1828, -1815, -5404, -4221, +7310, +1847, -1818, +4247, -2573, -4214, +2383, -697, -2275, -1002, -2080, -223, -830, -1342, +608, -827, -917, +655, -473, -208, +715, -175, }, + { +14260, +8470, -4402, +9252, +637, -4904, +113, -702, -4629, -3590, +7432, -364, -3180, +3406, -815, -2084, +3326, +846, -1128, +174, -1402, -361, -2087, -2634, -411, -921, -1460, -69, -168, +123, +619, -314, }, + { +12203, +8042, -2103, +8153, +36, -4209, +166, -837, -3973, -3102, +5672, +328, -1860, +2823, -972, -1947, +2514, +629, -644, +18, -1399, -247, -1603, -2116, -289, -1032, -1262, -81, -282, -111, +472, -81, }, + { +9978, +7761, -851, +7167, +518, -3404, +616, -905, -3552, -2875, +4478, +661, -1312, +2289, -665, -1823, +1788, +811, -693, -46, -1135, -436, -1105, -1539, -267, -710, -1155, -219, -156, -293, +133, -138, }, + { +8252, +7011, +325, +6475, +571, -2476, +1105, -925, -3370, -2437, +3577, +876, -1052, +1795, -250, -1885, +1251, +954, -652, -129, -973, -426, -862, -997, +85, -618, -1083, -201, -130, -385, -107, -293, }, + { +6990, +6408, +794, +5771, +1007, -1732, +1092, -813, -2841, -2294, +2966, +1185, -1180, +1392, +18, -1807, +929, +855, -550, -195, -860, -345, -413, -570, +52, -503, -888, -152, -282, -552, -210, -339, }, + { +6315, +5794, +807, +5067, +1552, -954, +511, -614, -2056, -2084, +2442, +1235, -1125, +916, +7, -1376, +711, +581, -457, -233, -563, +16, -83, -537, -90, -310, -669, -307, -465, -561, -130, -413, }, + { +5495, +5067, +1235, +4594, +1625, -330, +383, -413, -1595, -1679, +1966, +934, -895, +616, -4, -1020, +465, +389, -292, +92, -212, +95, -14, -517, -157, -268, -599, -430, -445, -406, -157, -409, }, + { +4664, +4577, +1540, +4071, +1781, +42, +302, -196, -1100, -1341, +1290, +763, -650, +297, +59, -796, +256, +526, +78, +88, -122, +237, -54, -604, -266, -281, -530, -328, -364, -356, -110, -313, }, + { +3851, +4119, +1861, +3768, +1681, +225, +590, +32, -804, -1149, +865, +588, -638, +296, +218, -632, +437, +631, +5, +112, +96, +233, -258, -721, -220, -100, -390, -319, -297, -172, -5, -185, }, + { +3336, +3750, +1919, +3488, +1574, +321, +813, +276, -570, -1058, +527, +383, -558, +511, +605, -444, +230, +571, +103, +183, +137, -15, -469, -450, -1, -70, -317, -262, -110, +31, -67, -427, }, + { +3049, +3510, +1714, +3215, +1488, +383, +1012, +442, -405, -1074, +260, +303, -38, +921, +427, -524, +201, +517, +216, +135, -99, -116, -221, -179, +46, -34, -244, -78, -50, -204, -303, -502, }, + { +2844, +3234, +1490, +2937, +1418, +496, +1224, +481, -359, -986, +230, +762, +253, +708, +333, -527, +155, +538, +90, +24, +59, +6, -45, -70, +130, +103, -256, -256, -271, -304, -297, -325, }, + { +2647, +2909, +1294, +2675, +1363, +698, +1331, +460, -184, -366, +335, +522, +431, +628, +210, -521, -47, +509, +303, +218, +56, +23, +150, +170, +182, -200, -553, -341, -197, -165, -211, -392, }, + { +2534, +2823, +1022, +1970, +1429, +1221, +1392, +703, +196, -392, +319, +726, +304, +316, -41, -517, +273, +763, +274, +109, +137, +366, +348, -105, -308, -391, -404, -192, -101, -238, -327, -356, }, + { +1989, +2468, +1081, +1801, +1546, +1331, +1957, +1126, +247, -246, -18, +528, +333, +129, -70, -149, +414, +761, +386, +169, +318, +372, +30, -395, -334, -284, -295, -143, -195, -279, -332, -341, }, + { +1620, +2490, +1471, +1655, +2013, +1585, +1301, +1138, +603, -161, -181, -90, -24, +524, +502, +148, +414, +612, +427, +246, +105, +30, -174, -325, -240, -291, -355, -203, -136, -260, -309, -267, }, + { +1263, +2410, +2075, +1764, +1891, +1970, +1489, +641, +109, -195, -152, +183, +214, +240, +589, +646, +460, +466, +334, +34, -111, -86, -179, -264, -241, -257, -328, -326, -184, -161, -161, -211, }, + { +1659, +2476, +1676, +2229, +1972, +1422, +1669, +708, -448, -272, +296, +223, +206, +469, +501, +694, +819, +331, -104, -205, -133, -45, -56, -253, -357, -286, -277, -167, -214, -215, -128, -196, }, + { +1539, +2551, +1891, +1969, +1762, +1252, +1228, +862, +110, -20, +303, +230, +166, +358, +647, +849, +752, +128, -158, -145, -243, -82, -52, -123, -279, -382, -292, -181, -163, -170, -155, -213, }, + { +1700, +2807, +1619, +1270, +1372, +1179, +1161, +1231, +504, +52, +504, +136, +92, +640, +736, +453, +675, +669, -252, -473, -51, +20, -170, -152, -154, -240, -345, -332, -109, -89, -138, -173, }, + { +1694, +2657, +1452, +968, +771, +1023, +1613, +1199, +359, +667, +624, -76, +382, +536, +594, +753, +564, +532, +330, -84, -457, -257, +116, +14, -260, -224, -186, -331, -270, -123, -32, -136, }, + { +1868, +2708, +1232, +929, +844, +827, +1033, +736, +424, +738, +823, +481, +179, +246, +857, +667, +444, +613, +612, +119, -110, -152, -501, -78, +116, -202, -290, -173, -262, -254, -131, -141, }, + { +2010, +2795, +1111, +875, +1109, +1106, +857, -4, -401, +861, +1319, +394, +265, +408, +467, +655, +737, +455, +536, +705, +304, -158, -205, -280, -395, +24, +44, -233, -199, -207, -233, -158, }, + { +2330, +2991, +964, +1027, +1208, +1180, +792, -487, -765, +646, +1124, +466, +294, +78, +501, +769, +452, +561, +675, +354, +514, +630, -115, -307, -312, -411, -176, +29, -28, -209, -223, -273, }, + { +2824, +3565, +965, +943, +1112, +1083, +667, -717, -987, +738, +1121, +2, +110, +106, +412, +576, +458, +606, +513, +519, +535, +409, +396, +221, -406, -363, -190, -297, -93, +42, -95, -282, }, + { +3349, +3983, +872, +1076, +1173, +1134, +531, -1288, -1216, +1017, +1171, -131, -155, -369, +349, +670, +365, +458, +453, +375, +399, +660, +331, +266, +204, -128, -309, -306, -200, -192, -42, -94, }, + { +3961, +4442, +707, +1254, +1277, +1123, +361, -1769, -1366, +1301, +1136, -328, -227, -538, +131, +409, +403, +542, +281, +118, +332, +560, +285, +456, +66, +169, +247, -283, -261, -198, -228, -289, }, + { +4686, +5052, +536, +1343, +1245, +1005, +207, -2188, -1265, +1645, +926, -588, -346, -640, +177, +285, +106, +548, +311, +27, +151, +303, +255, +325, +75, +299, +196, +155, +118, -260, -236, -300, }, + { +5534, +5563, +318, +1536, +1252, +806, -127, -2646, -995, +2082, +649, -787, -525, -824, +252, +141, +115, +500, +81, -14, +111, +227, +28, +135, -85, +201, +321, +171, +203, +214, -19, -500, }, + { +6386, +6175, +93, +1727, +1236, +541, -519, -3046, -559, +2421, +322, -926, -609, -965, +269, -57, +33, +671, -21, -197, +21, +165, +4, +40, -436, +106, +232, +49, +425, +144, +66, +0, }, + { +7130, +6925, -88, +1879, +1277, +209, -950, -3419, -58, +2693, -41, -998, -710, -991, +321, -320, -82, +770, -64, -189, -88, -19, +13, -38, -523, -42, -30, +68, +150, +89, +365, -23, }, + { +7792, +7702, -133, +1956, +1357, -128, -1415, -3772, +482, +2874, -375, -1048, -862, -936, +402, -512, -233, +760, -129, -149, -81, -155, -78, -100, -630, -36, -59, -442, +95, +280, +185, +75, }, + { +8084, +8648, +147, +1800, +1588, -465, -1801, -4057, +739, +3060, -585, -1055, -1095, -889, +484, -597, -294, +651, -254, -145, -54, -182, -110, -282, -564, -209, -393, -202, +61, +14, +171, +1, }, + { +7950, +9589, +964, +1429, +1845, -635, -2157, -4183, +602, +3177, -602, -1039, -1301, -965, +529, -614, -333, +613, -355, -295, -22, -253, -53, -255, -948, -322, -205, -347, -6, +31, +59, -71, }, + { +7608, +10215, +2256, +1215, +1866, -509, -2499, -4150, +252, +3102, -462, -1007, -1377, -1131, +543, -620, -361, +628, -344, -407, -176, -143, -39, -636, -843, -265, -361, -355, -99, +51, +143, +45, }, + { +7478, +10350, +3382, +1641, +1562, -364, -2671, -4182, +82, +2795, -422, -952, -1415, -1234, +496, -705, -369, +637, -288, -460, -139, -421, -273, -301, -1004, -427, -393, -387, -99, +121, +207, -128, }, + { +7601, +10277, +3938, +2546, +1409, -546, -2604, -4195, -39, +2430, -446, -937, -1542, -1235, +449, -844, -397, +623, -239, -176, -592, -583, -41, -478, -953, -587, -540, -343, +32, +110, -10, -156, }, + { +7999, +10060, +3982, +3415, +1699, -807, -2609, -3807, -190, +1946, -508, -758, -1860, -1306, +494, -1071, -466, +696, -160, -448, -342, -561, -316, -440, -936, -533, -717, -285, -33, -87, +138, +52, }, + { +8868, +9667, +3696, +4220, +1884, -786, -2974, -3169, -25, +1022, -611, -333, -2076, -1587, +587, -1486, -187, +551, -545, +152, -427, -718, -238, -557, -971, -356, -742, -544, -186, +78, +286, -144, }, + { +10008, +9228, +3019, +5173, +1730, -514, -3173, -2654, +674, -266, -982, +303, -2059, -1915, +478, -1617, -384, +327, -296, +313, -485, -639, -255, -549, -996, -197, -986, -652, +62, -23, +48, -97, }, + { +11234, +8968, +1867, +6284, +1449, -576, -2850, -2320, +1671, -1457, -1966, +1061, -1777, -2251, +610, -2387, -502, +717, -908, +856, -362, -807, -179, -457, -950, -356, -981, -565, +70, -205, +52, -184, }, + { +12424, +9043, +307, +7301, +1477, -1225, -2226, -1811, +2733, -2240, -3502, +1512, -1095, -2186, +52, -2767, -416, +476, -1043, +1163, -163, -967, -182, -310, -939, -309, -1033, -648, +172, -197, -66, -313, }, + { +13850, +9410, -1577, +7710, +1903, -2007, -1727, -1126, +3784, -2445, -5373, +1368, +274, -2547, -214, -2859, -931, +830, -1404, +1283, +192, -1142, -131, -304, -939, -130, -968, -870, +367, -211, -179, -446, }, + { +15689, +10650, -4191, +6439, +2855, -1819, -1216, -1122, +4356, -1735, -7082, +796, +1337, -2613, -354, -3165, -1020, +960, -1511, +1143, +338, -957, -254, -411, -1003, +93, -787, -961, +276, -120, -242, -590, }, + { +17568, +11974, -6732, +4560, +3824, -1109, -754, -1349, +4606, -843, -8100, -287, +1911, -2056, -658, -3260, -1157, +899, -1412, +975, +487, -915, -443, -511, -909, +161, -686, -992, +148, +70, -223, -830, }, + { +19484, +13084, -9012, +2638, +4382, +24, -354, -1619, +4661, +92, -8630, -1472, +1971, -1185, -834, -3443, -1007, +624, -1325, +1001, +434, -935, -492, -574, -862, +109, -756, -815, +84, +193, -327, -1033, }, + { +21524, +13678, -10956, +1222, +4385, +1297, -139, -2069, +5017, +1156, -9307, -2582, +2096, -601, -528, -3661, -796, +152, -1389, +1321, +257, -1041, -461, -535, -1092, +104, -751, -637, +26, +127, -265, -1109, }, + { +23605, +13718, -12574, +568, +3860, +2238, -103, -2760, +6167, +2019, -10424, -3226, +2099, -412, +299, -3787, -992, -294, -1323, +1597, -108, -1067, -318, -600, -1468, +200, -723, -642, -34, +135, -184, -1205, }, + { +25071, +14148, -13932, +471, +3008, +2768, -76, -3611, +7895, +2700, -11593, -3451, +1646, -185, +1383, -3748, -1496, -431, -1263, +1534, -312, -957, -121, -752, -1802, +171, -636, -694, -65, +159, -201, -1248, }, + { +26816, +13743, -14786, +862, +1921, +2946, -342, -4385, +10015, +2577, -12171, -3354, +671, +309, +2238, -3598, -1940, -465, -1278, +1149, -315, -796, +17, -1000, -2051, +76, -546, -729, -117, +138, -282, -1090, }, + { +27951, +14104, -15590, +1273, +860, +2927, -633, -4986, +11529, +2744, -11887, -3827, +2, +498, +2997, -3184, -2478, -171, -1514, +556, -142, -469, -72, -1148, -2207, -3, -362, -847, -143, +170, -197, -1176, }, + { +28803, +14580, -16123, +1472, -187, +3073, -1250, -5325, +12182, +3270, -11190, -4406, -641, +391, +3646, -2789, -2835, +205, -1933, -111, +62, -112, -330, -1167, -2390, +48, -212, -1112, -86, +341, -303, -1353, }, + { +30323, +13889, -15910, +1407, -1171, +3553, -2383, -5267, +12463, +3540, -10099, -4882, -1197, +116, +4331, -2625, -2785, +375, -2274, -723, +301, +72, -494, -1132, -2662, +359, -286, -1255, +88, +360, -528, -1375, }, + { +30968, +14210, -15671, +1048, -1781, +3667, -3019, -5566, +12074, +4371, -8903, -5378, -1555, -380, +4891, -2272, -2787, +479, -2516, -1197, +410, +143, -580, -995, -2832, +409, -254, -1218, +75, +188, -590, -1406, }, + { +30975, +15165, -15312, +546, -2218, +3398, -2954, -6444, +11217, +5687, -7852, -5572, -1909, -880, +5215, -1819, -2692, +359, -2645, -1443, +355, +103, -656, -709, -2922, +164, -25, -1239, -84, +72, -690, -1273, }, + { +30424, +16368, -14573, +92, -2674, +2748, -2205, -7930, +10058, +7372, -7052, -5344, -2312, -1403, +5238, -1260, -2430, -1, -2723, -1529, +312, -177, -560, -464, -2980, -55, -69, -1190, -231, -99, -594, -932, }, + { +29455, +17526, -13363, -127, -3235, +1964, -1140, -9860, +8781, +9245, -6389, -4845, -2498, -2034, +4995, -530, -2084, -491, -2825, -1345, +221, -559, -398, -274, -2983, -380, -142, -1168, -371, +10, -384, -724, }, + { +28118, +18400, -11563, -157, -4012, +1308, -199, -11792, +7222, +10997, -5639, -4233, -2301, -2934, +4504, +364, -1845, -1024, -2737, -1299, +240, -966, -422, -53, -3194, -483, -435, -1176, -132, +127, -258, -452, }, + { +26552, +19152, -9368, -160, -4867, +986, +473, -13162, +5114, +12399, -4354, -3860, -1375, -4191, +3967, +1296, -1787, -1124, -2703, -1337, +286, -1074, -1007, +244, -3127, -928, -436, -993, +94, +254, +31, -338, }, + { +24559, +20217, -7367, -220, -5053, +163, +912, -13440, +2241, +12957, -1945, -4140, -3, -5230, +2779, +2633, -1975, -895, -2652, -1685, +399, -1133, -1787, +451, -3100, -1099, -141, -1148, +476, +550, +59, -556, }, + { +23258, +20668, -6394, +41, -4001, -1343, +259, -12280, -447, +12376, +814, -4410, +990, -5553, +1277, +3778, -1842, -823, -2296, -2428, +66, -675, -2623, +69, -2488, -1174, +108, -1212, +763, +685, -33, -620, }, + { +23142, +19464, -5694, +1043, -3149, -2286, -1288, -10731, -2354, +11827, +2425, -4171, +1875, -6154, +693, +4142, -1325, -613, -2234, -3206, -20, -772, -3178, +251, -2328, -913, +289, -1277, +940, +736, -150, -671, }, + { +22552, +18662, -5393, +2289, -1879, -3683, -2436, -8894, -4473, +11069, +4563, -5019, +3166, -6153, -527, +5142, -1047, -592, -2109, -3335, -857, -775, -3407, +489, -1798, -1280, +535, -1129, +1014, +574, -236, -416, }, + { +22401, +17758, -5842, +3364, -812, -4784, -3176, -6717, -5712, +8976, +6974, -6176, +3397, -4371, -2591, +6388, -734, -1317, -1243, -3549, -1586, -824, -3469, +452, -886, -1990, +679, -751, +619, +748, -301, -324, }, + { +22309, +16537, -6181, +4660, -360, -5405, -3571, -5406, -6202, +6988, +8656, -6084, +2058, -2265, -3682, +6212, +772, -2272, -843, -3239, -2023, -881, -3727, +370, -424, -2103, +394, -539, +433, +863, -135, -441, }, + { +21791, +15541, -6192, +5616, -505, -5546, -3072, -4658, -6616, +5207, +9841, -5865, +904, -1208, -3643, +5823, +1664, -2499, -798, -2591, -2431, -478, -3918, -635, -47, -1868, +308, -596, +226, +890, +22, -361, }, + { +21324, +14642, -6583, +6799, -1051, -6105, -1417, -3904, -7362, +3483, +10991, -5815, -496, +549, -4041, +4979, +2787, -2309, -896, -2206, -2499, +60, -3674, -2156, -266, -1362, +446, -246, -422, +573, +555, -41, }, + { +20752, +13927, -7556, +7887, -383, -7140, -472, -2508, -7640, +1050, +11820, -4742, -2329, +2225, -4014, +3077, +4488, -1969, -1094, -1484, -3038, +789, -3176, -3637, -596, -1084, +354, +499, -803, -174, +1114, +269, }, + { +19965, +13135, -8141, +8473, +760, -7513, -787, -971, -6960, -1942, +12233, -3129, -3764, +3112, -3442, +1359, +4791, -618, -1183, -1102, -2895, +601, -2684, -3990, -954, -1401, +153, +1055, -625, -482, +888, +405, }, + { +19143, +11685, -8033, +9384, +754, -7209, -418, -807, -6484, -2771, +11238, -2375, -3772, +3580, -3133, +167, +4612, -12, -608, -927, -2580, +196, -2728, -3474, -1079, -1676, -444, +1107, -287, -167, +508, -54, }, + { +17857, +10290, -7109, +9878, +454, -6436, -46, -1046, -5905, -3006, +10009, -1912, -3542, +3960, -2520, -1113, +4554, +356, -838, +40, -2468, -172, -2487, -3471, -559, -1557, -1179, +703, -47, +127, +577, -318, }, + { +16296, +9009, -5714, +9900, +226, -5547, +70, -922, -5384, -3238, +8823, -1282, -3406, +3868, -1537, -1923, +3996, +793, -1145, +249, -1720, -492, -2325, -3083, -467, -1071, -1564, +201, -55, +203, +765, -336, }, + { +15841, +6997, -6669, +11869, +1968, -7696, -859, +385, -4403, -1539, +6206, -3126, -2033, +3213, -1498, -707, +2441, -260, -30, +640, -527, +1221, -934, -1381, +294, -1105, -1226, -270, -1487, -1152, -350, -611, }, + { +13568, +6667, -4985, +10337, +1992, -6022, -629, +40, -3621, -1574, +5221, -2329, -1990, +2774, -1185, -903, +2184, +6, -131, +493, -301, +1057, -963, -1225, +288, -1053, -961, -56, -1078, -893, -506, -522, }, + { +11497, +6501, -3490, +8587, +2327, -4433, -803, -59, -2956, -1671, +4442, -1603, -1990, +2273, -678, -964, +1803, +277, -100, +341, -258, +879, -733, -1205, +106, -817, -829, +182, -726, -774, -439, -507, }, + { +9657, +6295, -2357, +7055, +2839, -3195, -922, +87, -2426, -1717, +3756, -1022, -1971, +1799, -285, -852, +1503, +322, +17, +329, -329, +745, -562, -1092, +21, -498, -600, +37, -450, -530, -354, -499, }, + { +8080, +5958, -1527, +5929, +3206, -2314, -807, +279, -2098, -1713, +3210, -663, -1953, +1446, +14, -682, +1140, +403, +168, +150, -283, +600, -477, -850, +175, -366, -595, +17, -334, -374, -332, -390, }, + { +6742, +5563, -934, +5119, +3472, -1654, -579, +468, -1830, -1693, +2733, -440, -1854, +1199, +270, -615, +927, +597, +39, +58, -146, +541, -282, -642, +203, -260, -523, -66, -324, -342, -189, -288, }, + { +5645, +5082, -470, +4564, +3569, -1149, -280, +632, -1648, -1657, +2279, -195, -1747, +997, +500, -550, +854, +535, -56, +205, +54, +502, -244, -453, +284, -250, -567, -159, -320, -231, -40, -147, }, + { +4739, +4548, -15, +4161, +3424, -612, +92, +576, -1502, -1483, +1923, -133, -1573, +1049, +513, -481, +827, +418, +204, +407, -41, +456, -23, -319, +105, -295, -566, -165, -125, -78, -45, -303, }, + { +4000, +4116, +315, +3739, +3361, -78, +141, +471, -1088, -1423, +1428, +104, -1192, +778, +494, -118, +776, +531, +352, +301, +46, +496, -16, -403, -75, -250, -372, +7, -75, -290, -272, -334, }, + { +3315, +3731, +744, +3252, +3208, +505, +104, +326, -724, -1258, +1022, +253, -920, +621, +766, +320, +638, +429, +479, +312, +62, +342, -149, -425, +78, -14, -324, -163, -364, -342, -177, -191, }, + { +2721, +3507, +1076, +2691, +3219, +926, -155, +334, -235, -1266, +533, +565, -478, +790, +1052, +241, +481, +577, +434, +197, -68, +166, +67, -116, +62, -279, -535, -276, -242, -195, -120, -171, }, + { +2164, +3272, +1472, +2240, +2998, +1160, -8, +380, -347, -1061, +577, +981, +39, +670, +997, +273, +443, +502, +183, +101, +156, +367, +108, -320, -359, -368, -337, -186, -156, -185, -219, -252, }, + { +1934, +3190, +1503, +1841, +2991, +1228, -234, +344, -22, -479, +868, +985, +160, +811, +840, +155, +222, +329, +446, +340, +153, +57, -306, -454, -236, -196, -253, -198, -267, -270, -212, -218, }, + { +1850, +2863, +1570, +2109, +2264, +770, +377, +952, +272, -269, +977, +887, +259, +859, +502, -89, +465, +710, +360, -49, -274, -104, -140, -299, -180, -223, -354, -327, -223, -222, -195, -175, }, + { +1880, +2606, +1259, +1967, +2141, +1272, +1090, +983, +171, -143, +1137, +1038, +78, +386, +659, +433, +428, +299, -214, -286, +0, +94, -163, -340, -274, -342, -349, -267, -185, -155, -161, -219, }, + { +1397, +2225, +1498, +2218, +2597, +1375, +970, +1179, +446, +55, +983, +782, +92, +766, +779, +21, -117, -30, -43, +45, +38, -51, -292, -398, -291, -331, -321, -187, -130, -199, -179, -163, }, + { +1635, +3062, +2249, +1880, +1916, +1343, +1114, +965, +534, +333, +634, +1026, +601, -88, -96, -36, +19, +140, +79, -54, -187, -223, -327, -363, -331, -232, -228, -189, -124, -136, -165, -244, }, + { +1621, +2982, +2331, +1859, +1672, +1505, +1317, +1168, +916, +338, +486, +1012, +283, -527, -167, +103, +151, +173, -30, -202, -207, -185, -337, -340, -290, -264, -241, -183, -115, -121, -178, -226, }, + { +1602, +2643, +1775, +1564, +1563, +1546, +1646, +1829, +1322, +708, +618, +118, +151, +107, -304, -55, +284, +102, -151, -125, -138, -202, -309, -317, -234, -259, -302, -184, -120, -109, -170, -206, }, + { +1689, +2725, +1687, +1342, +1018, +1221, +1839, +1792, +1379, +895, +468, +478, +768, +67, -215, -9, -169, +55, +46, -227, -303, -177, -209, -261, -200, -191, -162, -205, -213, -154, -174, -239, }, + { +1884, +2685, +1171, +1190, +1175, +880, +977, +1283, +1525, +1401, +1097, +684, +686, +975, +274, -642, -241, -46, -48, +8, -211, -255, -203, -172, -169, -156, -217, -131, -186, -249, -160, -234, }, + { +1711, +2523, +1308, +1090, +1055, +1174, +1022, +299, +477, +1301, +1459, +1119, +863, +804, +1042, +601, -340, -501, -204, -29, -21, -94, -220, -196, -156, -184, -116, -170, -201, -183, -255, -271, }, + { +1817, +2536, +946, +809, +944, +1085, +1157, +217, -175, +767, +1098, +779, +1147, +1424, +1313, +1026, +631, +198, -302, -492, -270, -64, -131, -189, -253, -238, -160, -88, -70, -213, -184, -209, }, + { +2191, +2839, +854, +984, +1168, +1002, +768, -219, -19, +1063, +561, +60, +636, +861, +1348, +1294, +885, +891, +475, -73, -432, -506, -288, -67, -118, -220, -240, -103, -74, -138, -148, -245, }, + { +2451, +2833, +673, +1459, +1780, +1173, +305, -947, -35, +1449, +504, -211, +108, +410, +948, +640, +824, +1192, +884, +675, +272, -177, -565, -472, -224, -108, -84, -125, -187, -164, -89, -188, }, + { +3086, +3702, +648, +1075, +1515, +1169, +441, -1098, -143, +1309, +271, -221, +106, +208, +526, +378, +428, +792, +941, +752, +488, +344, +62, -247, -504, -376, -181, +22, -47, -250, -217, -168, }, + { +3569, +4097, +532, +1316, +1840, +1093, -29, -1514, +254, +1604, -129, -469, -58, +189, +685, +94, +33, +566, +399, +418, +683, +566, +398, +298, -51, -324, -429, -122, -77, -175, -112, -284, }, + { +4125, +4461, +378, +1609, +2191, +994, -587, -1865, +674, +1689, -401, -500, -250, +88, +680, -19, +36, +317, +78, +227, +154, +381, +584, +347, +348, +259, -144, -157, -249, -295, -182, -189, }, + { +4867, +5214, +256, +1466, +2150, +866, -894, -1915, +1051, +1495, -779, -414, -262, +45, +633, -205, +3, +311, -89, +0, -11, +125, +199, +237, +418, +383, +270, +312, -107, -370, -261, -364, }, + { +5316, +5480, +203, +2110, +2686, +411, -1842, -1931, +1799, +1361, -1123, -406, -380, +95, +608, -401, +32, +304, -272, -6, -184, -72, +198, -153, +69, +403, +277, +536, +348, -42, -176, -397, }, + { +5892, +6233, +160, +2100, +2860, +142, -2433, -1800, +2326, +963, -1427, -273, -439, +158, +610, -591, -43, +281, -390, -16, -246, -165, +68, -360, -9, +156, +82, +484, +326, +166, +189, -170, }, + { +6296, +6975, +282, +2083, +3088, -133, -3093, -1657, +2878, +590, -1820, -143, -472, +190, +638, -719, -106, +233, -510, -92, -328, -237, +113, -522, -223, +109, -26, +254, +179, +32, +200, +156, }, + { +6592, +7845, +648, +1757, +3189, -161, -3619, -1575, +3255, +370, -2151, -143, -426, +168, +727, -779, -195, +244, -572, -180, -428, -336, +66, -505, -282, -26, -117, +217, +77, -98, +16, -149, }, + { +6774, +8708, +1082, +1524, +3240, -264, -3922, -1540, +3434, +295, -2349, -304, -372, +149, +756, -675, -383, +223, -505, -236, -520, -494, -9, -552, -315, -62, -148, +121, +173, -299, -365, -62, }, + { +6908, +9422, +1625, +1424, +3190, -389, -4004, -1611, +3471, +335, -2543, -391, -396, +68, +811, -604, -534, +182, -441, -272, -536, -643, -94, -634, -428, -131, -91, +267, -223, -392, -234, -275, }, + { +7138, +10189, +2126, +1133, +2968, -382, -3722, -1762, +3018, +580, -2559, -606, -255, -134, +669, -325, -681, -12, -293, -317, -621, -648, -141, -715, -656, -53, -111, -126, -38, -347, -470, -236, }, + { +7313, +10455, +2943, +1070, +2390, +272, -3497, -2310, +2731, +755, -2380, -670, -474, -246, +602, -255, -558, -320, -482, -158, -477, -799, -319, -604, -503, -410, -501, +108, -160, -398, -359, -347, }, + { +7826, +10161, +3173, +1967, +2130, +423, -3555, -2463, +2451, +634, -1952, -839, -706, -181, +512, -312, -450, -587, -636, +17, -569, -882, -377, -371, -655, -672, -473, -170, -109, -345, -438, -377, }, + { +8467, +9477, +3103, +3430, +2024, +440, -3655, -2401, +2236, -26, -1277, -712, -1114, -26, +375, -495, -221, -726, -826, +29, -851, -620, -323, -809, -370, -647, -794, -102, -163, -412, -424, -337, }, + { +9286, +8516, +2179, +5700, +2660, -483, -3558, -1721, +1619, -1074, -311, -359, -1649, +39, +247, -528, -41, -857, -1059, -36, -827, -638, -733, -625, -208, -781, -693, -299, -176, -287, -276, -526, }, + { +10487, +8297, +724, +7001, +3276, -1546, -2976, -375, +714, -2874, +551, +663, -2103, -61, +34, -604, +320, -1061, -1370, +301, -1334, -957, -278, -1039, -89, -630, -732, -236, -218, -112, -316, -769, }, + { +11694, +8483, -1085, +7688, +4377, -2639, -2748, +1388, +290, -5170, +695, +2234, -2233, -376, -167, -879, +703, -1107, -1401, -21, -1755, -792, -436, -1099, +19, -701, -676, +56, -177, -420, -299, -499, }, + { +12813, +8986, -2960, +7720, +5822, -3441, -2888, +3186, +503, -7344, -143, +3835, -1619, -861, -365, -1277, +915, -643, -1945, -184, -1723, -1371, -165, -1036, -149, -608, -623, +361, -273, -675, -81, -360, }, + { +14282, +9854, -5144, +6666, +7543, -3247, -3573, +4303, +1159, -8673, -1552, +4734, -435, -1066, -737, -1669, +1323, -711, -2400, +154, -2059, -1735, -117, -896, -79, -641, -571, +431, -255, -662, -37, -376, }, + { +15850, +10514, -7059, +5564, +8654, -2773, -4154, +5402, +1737, -9668, -3061, +5015, +1018, -866, -1440, -1886, +1527, -978, -2388, +41, -2052, -1988, -323, -629, -13, -650, -699, +444, -203, -546, -140, -354, }, + { +17808, +11057, -9134, +4383, +9339, -1778, -4546, +6062, +2149, -10167, -4257, +4712, +2487, -456, -1988, -2239, +1739, -1173, -2546, +134, -2148, -2055, -395, -540, +91, -587, -935, +488, -19, -574, -174, -369, }, + { +20383, +11132, -11423, +3225, +9645, -449, -4728, +6097, +2297, -10259, -4895, +4011, +3369, +308, -2432, -2790, +2050, -1452, -2764, +266, -2368, -1811, -610, -479, +220, -714, -1063, +583, +89, -687, -231, -413, }, + { +22418, +11798, -13840, +2373, +9532, +812, -4649, +5776, +2876, -10183, -5672, +3197, +3848, +1355, -2692, -3324, +2269, -1738, -2840, +474, -2481, -1785, -640, -534, +229, -841, -1060, +727, -62, -819, -169, -447, }, + { +24894, +11396, -15645, +2370, +8933, +1292, -4281, +5629, +3954, -10488, -6364, +2682, +3969, +2394, -2882, -3405, +2175, -2174, -2424, +697, -2794, -1803, -440, -660, +159, -906, -842, +819, -413, -760, -126, -429, }, + { +26219, +12293, -17420, +2685, +7926, +1207, -3924, +5306, +5940, -10486, -7929, +2418, +4103, +2810, -2438, -3395, +1620, -2289, -1991, +723, -2963, -1849, -215, -902, +78, -722, -742, +870, -694, -718, -23, -517, }, + { +28416, +11175, -18030, +3639, +6517, +524, -3997, +5626, +7964, -10885, -9328, +2435, +4025, +2769, -1736, -3204, +870, -2252, -1569, +634, -3242, -1541, -240, -1235, +91, -529, -623, +850, -941, -678, +99, -756, }, + { +29176, +11689, -18551, +4399, +5252, -160, -4585, +5761, +10465, -11051, -10563, +2051, +3915, +2524, -384, -3448, +237, -1642, -1718, +678, -3401, -1225, -371, -1551, +5, -226, -521, +663, -896, -768, +144, -1146, }, + { +29021, +13077, -18841, +4810, +4427, -776, -5593, +5564, +12643, -10486, -11302, +1156, +3758, +2201, +1006, -3601, -405, -695, -2158, +611, -3242, -1002, -587, -1809, -96, +32, -433, +355, -533, -1081, -66, -1282, }, + { +30639, +12013, -18276, +5087, +3533, -892, -7519, +6359, +13438, -9871, -11200, +130, +3528, +2142, +1859, -3611, -530, -415, -2173, +244, -2952, -752, -1000, -1830, -214, +226, -496, +424, -528, -1428, -247, -1261, }, + { +31129, +12133, -17589, +4753, +3258, -1123, -8951, +6078, +13955, -8365, -10965, -1246, +3253, +2362, +2106, -3221, -543, -421, -2041, -9, -2564, -798, -1213, -1842, -312, +214, -362, +364, -897, -1374, -502, -1281, }, + { +30719, +13121, -16780, +4200, +3198, -1555, -9649, +4676, +14307, -6216, -10726, -2675, +2883, +2776, +1878, -2607, -273, -769, -1833, +57, -2369, -947, -1301, -1967, -305, +248, -529, +122, -1019, -1330, -719, -996, }, + { +29697, +14733, -15818, +3462, +3061, -1780, -9577, +2144, +14501, -3727, -10382, -3699, +2209, +3218, +1410, -1818, +264, -1375, -1409, +257, -2249, -1183, -1411, -1971, -160, -60, -974, +340, -1284, -1182, -516, -1030, }, + { +28002, +16846, -14478, +2412, +2676, -1519, -8873, -1372, +14440, -1063, -9859, -4131, +1096, +3328, +1195, -1203, +1073, -1927, -1076, +655, -2259, -1480, -1460, -1775, -599, -341, -1181, +468, -1245, -932, -470, -1164, }, + { +25869, +18455, -12266, +1637, +1978, -1262, -8280, -4934, +14128, +1672, -9443, -4041, -152, +2529, +1995, -1259, +1882, -1726, -1545, +1491, -2530, -1653, -1433, -2120, -1079, -408, -1374, +717, -856, -1223, -178, -1188, }, + { +24864, +17912, -9358, +1641, +576, -357, -8615, -7257, +13351, +3934, -9073, -3482, -838, +841, +3250, -1322, +2217, -796, -2111, +1754, -2153, -2158, -1467, -2434, -1624, -253, -1302, +874, -576, -1341, -96, -1263, }, + { +23420, +17860, -6501, +1165, -717, +588, -8140, -9637, +11644, +6449, -8639, -3126, -461, -1536, +4057, -378, +1614, +434, -2303, +1248, -1439, -2499, -1782, -2484, -2002, -134, -998, +651, -243, -1476, -235, -998, }, + { +22006, +18635, -5893, +1609, -295, -1121, -6611, -10352, +8438, +8556, -7366, -3845, +1086, -3469, +3525, +1549, +597, +1088, -1819, +227, -740, -2416, -2356, -1773, -2500, -256, -642, +230, -150, -1358, -351, -903, }, + { +22224, +17778, -6386, +3076, +518, -3282, -6409, -9035, +5543, +9128, -5573, -4587, +2339, -4194, +2628, +2481, +473, +849, -1362, -75, -999, -1551, -2497, -1277, -2514, -1057, -402, -20, +22, -1408, -555, -809, }, + { +22103, +17015, -6884, +4461, +1233, -5006, -6070, -7906, +2832, +9189, -3231, -5657, +3433, -4282, +1463, +3242, -717, +1493, -1177, -711, -254, -881, -2411, -866, -2491, -1884, -465, -353, +374, -1387, -1115, -592, }, + { +22560, +15777, -7251, +5550, +797, -5686, -5009, -7186, +427, +9113, -1133, -6601, +3854, -2859, -422, +3276, -587, +291, -113, -529, +173, -114, -2517, -809, -1886, -2312, -1324, -304, +246, -1137, -1252, -701, }, + { +22731, +15350, -9222, +6868, +2173, -7663, -4747, -4975, -1211, +7055, +1768, -6971, +2930, -1113, -1589, +2833, -506, -636, +510, +745, -151, +413, -2257, -1147, -1254, -2415, -1827, -858, -136, -928, -699, -812, }, + { +23304, +14017, -11279, +9072, +3099, -9814, -4178, -2550, -2580, +4834, +3988, -6788, +1843, +177, -2381, +2443, -295, -988, +882, +1070, +364, +564, -2129, -1074, -1171, -2223, -1782, -1409, -1115, -600, -62, -717, }, + { +23618, +12255, -12665, +11454, +3209, -11235, -3282, -912, -3248, +2468, +5358, -5978, +1078, +518, -3093, +2872, +91, -1576, +1462, +574, +484, +1620, -2554, -835, -966, -2532, -1116, -1629, -2136, -622, +243, -448, }, + { +23507, +10176, -12976, +13613, +2604, -11794, -2177, +221, -4379, +941, +6866, -5844, +60, +1571, -3421, +2227, +1420, -1610, +744, +868, +160, +1913, -1757, -1375, -527, -2462, -1299, -900, -2681, -1245, +284, -163, }, + { +22437, +8395, -11784, +14554, +1462, -10835, -1161, -107, -4923, +595, +7185, -5944, -419, +2664, -3581, +1327, +2496, -1354, +156, +805, -57, +1910, -1336, -1388, -199, -2269, -1525, -558, -2342, -1654, -242, -88, }, + { +20519, +7379, -9918, +14135, +1130, -9407, -1215, -80, -4674, -389, +7077, -5070, -1127, +3004, -2779, +575, +2442, -923, +375, +352, -401, +2124, -1583, -1128, +325, -2275, -1384, -459, -2045, -1486, -593, -444, }, + { +18163, +7171, -8342, +13120, +1703, -8708, -1203, +485, -4645, -1323, +6947, -3981, -1909, +3255, -1914, -231, +2372, -512, +246, +503, -605, +1638, -1102, -1353, +431, -1611, -1452, -256, -1866, -1326, -460, -644, }, + { +14734, +5366, -4938, +13261, +853, -8371, +138, +318, -3336, +134, +2204, -3293, +1697, +2006, -2237, -277, +1106, -520, -774, +253, -99, +68, -412, +780, +809, +219, +346, -370, -906, -336, -779, -1298, }, + { +12591, +5345, -3597, +11397, +1333, -6737, -45, +46, -2876, +145, +2069, -2657, +1229, +1805, -1739, -516, +791, -312, -657, +44, -12, +385, -291, +437, +830, +261, +90, -313, -700, -424, -611, -816, }, + { +10805, +5339, -2604, +9725, +1921, -5525, -310, +43, -2429, +58, +1959, -2032, +690, +1674, -1216, -766, +650, -159, -575, +29, +105, +445, -262, +423, +590, +102, +242, -169, -667, -426, -434, -541, }, + { +9340, +5201, -1982, +8478, +2304, -4592, -320, +86, -2009, +7, +1880, -1679, +320, +1616, -995, -817, +572, -17, -496, +83, +280, +281, -168, +371, +343, +122, +380, -5, -602, -322, -317, -483, }, + { +8188, +4843, -1495, +7540, +2395, -3667, -312, +176, -1589, -77, +1770, -1446, +114, +1442, -804, -782, +564, +82, -423, +254, +211, +181, -62, +208, +366, +208, +389, +140, -452, -279, -247, -426, }, + { +7139, +4447, -1005, +6621, +2495, -2780, -305, +313, -1320, -50, +1606, -1337, +16, +1312, -654, -711, +624, +129, -237, +218, +117, +290, -83, +217, +421, +287, +411, +153, -317, -192, -175, -342, }, + { +6157, +4147, -661, +5795, +2719, -2091, -278, +447, -1015, -108, +1345, -1106, -12, +1133, -544, -455, +616, +156, -87, +162, +222, +352, -56, +280, +494, +294, +391, +184, -187, +6, -183, -528, }, + { +5279, +3795, -302, +5095, +2792, -1423, -200, +524, -834, -167, +1181, -897, -46, +946, -299, -307, +520, +329, -7, +248, +337, +305, +68, +311, +476, +300, +386, +295, -101, -189, -428, -616, }, + { +4525, +3459, -28, +4546, +2834, -885, -89, +549, -694, -192, +1043, -644, -76, +759, -74, -153, +593, +505, +119, +343, +347, +334, +158, +340, +453, +359, +386, +40, -308, -328, -435, -568, }, + { +3930, +3246, +221, +4012, +2721, -518, +143, +558, -699, -125, +1073, -572, -271, +859, +240, +26, +775, +548, +199, +453, +346, +378, +240, +360, +327, +89, +121, -123, -319, -354, -529, -642, }, + { +3660, +3270, +329, +3382, +2495, -259, +336, +625, -667, -168, +978, -390, -146, +1158, +544, +64, +837, +706, +404, +422, +317, +415, +105, -19, +35, +69, +43, -172, -504, -539, -442, -551, }, + { +3142, +2802, +557, +3432, +2389, -71, +540, +456, -686, +42, +1063, -160, +119, +1196, +649, +468, +1015, +684, +339, +353, +244, +13, -220, -126, +48, +81, -160, -332, -500, -382, -372, -555, }, + { +2901, +2760, +686, +3156, +2256, -27, +369, +564, -227, +172, +1095, +54, +380, +1501, +986, +535, +850, +558, +60, -34, -144, -62, -229, -185, +27, -136, -229, -282, -406, -348, -382, -501, }, + { +2508, +2594, +1005, +2823, +1909, +401, +624, +677, +80, +149, +1195, +622, +765, +1450, +899, +441, +488, -3, -301, -63, -155, -197, -378, -249, +15, -114, -216, -325, -386, -326, -435, -465, }, + { +2206, +2342, +933, +2683, +2333, +903, +669, +761, +492, +499, +1258, +984, +954, +1127, +508, -78, +61, +24, -139, -253, -442, -276, -171, -30, -76, -244, -225, -191, -331, -440, -399, -321, }, + { +1974, +2815, +1553, +2323, +2233, +1046, +992, +1282, +753, +707, +1323, +599, +390, +690, +158, -204, -61, -149, -350, -307, -284, -187, -98, -47, -143, -282, -239, -272, -323, -315, -319, -272, }, + { +1654, +2776, +1836, +2401, +3022, +1959, +967, +1094, +948, +444, +652, +267, +140, +546, +166, -270, -194, -277, -387, -222, -184, -102, -101, -213, -211, -214, -174, -207, -336, -339, -249, -198, }, + { +1869, +3354, +2494, +2832, +2716, +1654, +1450, +1127, +173, -258, +546, +432, -146, +288, +167, -560, -266, -9, -321, -261, -193, -155, -101, -190, -238, -205, -240, -223, -233, -279, -241, -223, }, + { +2251, +4370, +3095, +2273, +2143, +1471, +1134, +828, +318, +171, +218, -108, -7, +202, -108, -329, -135, -186, -250, -123, -231, -173, -170, -206, -184, -240, -217, -223, -224, -243, -163, -183, }, + { +2659, +4415, +2723, +1899, +1660, +1369, +1422, +927, +331, +708, +366, -215, -222, -87, +228, -79, -456, -205, +38, -181, -201, -158, -281, -171, -233, -218, -175, -209, -188, -206, -158, -146, }, + { +2110, +3925, +2617, +1488, +1521, +1321, +1000, +1375, +1006, +577, +1002, +281, -242, +99, -57, -249, -82, -20, -257, -120, -41, -272, -333, -251, -182, -210, -176, -126, -157, -179, -176, -194, }, + { +1815, +3251, +2187, +1550, +1381, +941, +1057, +773, +515, +1445, +1370, +807, +715, +171, +116, +49, -344, -256, +25, -69, -365, -300, -115, -199, -312, -112, -74, -117, -199, -216, -192, -267, }, + { +1453, +2017, +1172, +1714, +1993, +1501, +661, -199, +441, +1198, +925, +1207, +1374, +1313, +1201, +202, -18, +29, -479, -298, -15, -198, -383, -148, -84, -172, -178, -112, -77, -224, -241, -216, }, + { +1882, +2639, +981, +885, +1287, +1149, +657, -264, +342, +1130, +630, +742, +1292, +1636, +1472, +886, +568, +182, -94, -158, -412, -373, +6, -144, -402, -52, +23, -169, -270, -217, -171, -242, }, + { +2138, +2960, +1101, +1064, +1446, +1136, +291, -438, +715, +1105, +64, +265, +594, +767, +1266, +1060, +858, +969, +438, -42, -101, -297, -344, -218, -121, -114, -231, -67, -73, -307, -272, -133, }, + { +2460, +3291, +1078, +1222, +1735, +1211, -80, -710, +954, +1086, -141, +205, +328, +431, +693, +253, +688, +1146, +655, +645, +331, -174, -61, -188, -361, -99, -16, -187, -277, -187, -156, -227, }, + { +2893, +3811, +1105, +1203, +1818, +1171, -395, -714, +1243, +818, -421, +245, +254, +371, +579, -124, +129, +613, +507, +652, +517, +400, +268, -71, -95, -9, -228, -164, -144, -359, -299, -148, }, + { +3308, +4221, +1114, +1356, +2071, +1044, -891, -658, +1576, +583, -594, +223, +142, +308, +585, -177, -43, +346, +34, +199, +317, +405, +505, +368, +236, +145, +52, -93, -322, -363, -224, -252, }, + { +3900, +4867, +998, +1260, +2220, +839, -1162, -422, +1621, +246, -664, +254, +85, +242, +512, -240, -75, +270, -146, -7, -102, +10, +325, +287, +509, +621, +248, +30, -87, -358, -365, -226, }, + { +4379, +5375, +1025, +1329, +2414, +620, -1641, -92, +1872, -196, -752, +327, -63, +215, +556, -301, -147, +211, -180, -88, -262, -176, -67, -67, +404, +602, +521, +379, +47, -155, -101, -270, }, + { +4917, +5923, +933, +1451, +2663, +215, -2061, +412, +1970, -661, -792, +379, -89, +99, +514, -309, -198, +208, -222, -155, -360, -296, -176, -258, +109, +365, +340, +336, +360, +108, +10, -101, }, + { +5325, +6387, +1044, +1617, +2857, -114, -2569, +894, +2226, -1191, -880, +471, -93, +10, +443, -288, -328, +216, -163, -257, -444, -325, -267, -361, +2, +132, +152, +154, +168, +115, +205, +89, }, + { +5680, +6946, +1250, +1595, +2986, -178, -3103, +1203, +2516, -1694, -900, +579, -179, -49, +355, -219, -348, +53, -131, -293, -494, -294, -363, -474, -96, +59, +126, -24, -77, -107, +65, +185, }, + { +6420, +7593, +839, +1808, +3146, -725, -3065, +1505, +2329, -1963, -819, +612, -243, -161, +340, -128, -458, -62, -108, -328, -544, -296, -472, -531, -38, -40, +40, -115, -239, -173, -70, -95, }, + { +6890, +8125, +798, +2033, +3239, -1037, -3175, +1581, +2439, -2196, -937, +706, -276, -254, +344, -74, -579, -163, -59, -291, -673, -428, -436, -541, -74, -59, -108, -153, -248, -283, -175, -228, }, + { +7112, +8504, +1011, +2420, +3353, -1361, -3255, +1549, +2556, -2243, -1200, +741, -167, -319, +199, +28, -584, -316, +19, -391, -797, -406, -435, -548, -248, -164, +60, -169, -402, -374, -175, -285, }, + { +7285, +8820, +1447, +2597, +3239, -1169, -3229, +1126, +2635, -2125, -1498, +885, -99, -519, +165, +47, -347, -428, -288, -299, -829, -481, -398, -641, -383, -145, +82, -141, -416, -423, -333, -478, }, + { +7520, +8869, +2021, +2814, +2876, -582, -3127, +494, +2536, -1844, -1576, +724, +34, -625, -7, +239, -155, -630, -485, -225, -859, -553, -623, -612, -241, -315, +42, -51, -367, -610, -501, -479, }, + { +7879, +8478, +2398, +3646, +2683, -425, -3110, +367, +2156, -1723, -1287, +263, +38, -359, -259, +217, +107, -737, -613, -82, -1068, -775, -685, -547, -228, -402, +0, +62, -472, -776, -425, -752, }, + { +8205, +7639, +2366, +5376, +3131, -1245, -3272, +1332, +1408, -2244, -393, -194, -320, +234, -603, -6, +519, -774, -631, -35, -1347, -848, -738, -677, -167, -469, +8, -127, -364, -733, -690, -706, }, + { +9071, +7121, +1359, +7044, +3754, -2464, -2808, +2381, +60, -2776, +793, -372, -1036, +563, -545, -275, +838, -803, -718, +19, -1374, -976, -1066, -680, +3, -628, -230, -55, -414, -742, -545, -882, }, + { +10148, +7203, -207, +7831, +4875, -3407, -2535, +3529, -1225, -3617, +2016, -129, -1945, +583, -98, -688, +1031, -623, -939, +116, -1314, -1178, -1259, -633, -72, -818, -379, -21, -446, -673, -502, -1013, }, + { +11044, +7769, -1943, +7882, +6483, -3989, -2591, +4784, -2149, -4901, +3054, +635, -2734, +50, +522, -825, +921, -244, -1180, +190, -1284, -1180, -1357, -904, -121, -897, -516, -101, -271, -733, -400, -911, }, + { +11990, +8411, -3500, +7279, +8002, -4096, -2789, +6028, -2844, -6395, +3677, +1736, -3160, -867, +846, -555, +671, +81, -1262, +34, -1076, -1221, -1513, -1089, -353, -964, -456, -261, -256, -672, -256, -770, }, + { +13335, +8653, -4769, +6560, +8870, -3919, -2715, +7255, -3621, -7665, +3846, +2827, -3088, -1854, +732, -91, +613, +191, -1151, -200, -889, -1355, -1616, -1202, -620, -945, -484, -248, -378, -527, -120, -768, }, + { +14852, +8670, -5913, +6073, +9001, -3654, -2195, +8358, -4463, -8616, +3590, +3721, -2575, -2699, +236, +272, +868, +133, -945, -394, -1059, -1238, -1780, -1341, -641, -1048, -561, -27, -440, -570, -19, -740, }, + { +16451, +8693, -7064, +5832, +8573, -3576, -1333, +9514, -5301, -9295, +3094, +4207, -1784, -3244, -399, +318, +1449, +54, -659, -662, -1314, -1021, -2073, -1203, -803, -1212, -404, +103, -451, -722, -56, -638, }, + { +18423, +8132, -8075, +6128, +7681, -4035, +138, +10573, -6216, -9800, +2747, +4350, -1192, -3295, -1042, +104, +2016, +289, -589, -954, -1505, -917, -2153, -1097, -1071, -1185, -298, +285, -530, -966, -69, -449, }, + { +20202, +8717, -10102, +5865, +7549, -3841, +390, +11057, -6161, -10160, +2346, +4324, -998, -3105, -1402, -423, +2652, +520, -1022, -905, -1505, -1084, -2016, -1271, -995, -1072, -334, +432, -570, -1222, +2, -286, }, + { +22529, +8456, -11824, +5892, +7303, -4055, +930, +11197, -5866, -10497, +2199, +4298, -1350, -2705, -1409, -1011, +2979, +873, -1590, -561, -1535, -1262, -1879, -1318, -879, -952, -350, +491, -720, -1269, +134, -272, }, + { +23974, +9134, -13513, +5844, +7038, -4057, +615, +11611, -4790, -11317, +1983, +4325, -1634, -2352, -1084, -1939, +3123, +1383, -1969, -242, -1583, -1428, -1611, -1435, -861, -839, -383, +423, -787, -1337, +408, -512, }, + { +25044, +9817, -14614, +5833, +6675, -4269, -71, +12212, -3614, -11890, +1484, +4440, -1786, -1990, -859, -2687, +2944, +1877, -2005, -175, -1440, -1495, -1418, -1531, -909, -716, -428, +289, -816, -1306, +466, -753, }, + { +26536, +9314, -14645, +5829, +6191, -4884, -796, +12908, -2788, -12074, +1081, +4219, -1691, -1613, -960, -3033, +2556, +2030, -1647, -335, -1214, -1268, -1603, -1435, -985, -580, -535, +122, -796, -1260, +177, -737, }, + { +27183, +9606, -14376, +5620, +5875, -5416, -2052, +13586, -1628, -12058, +571, +3654, -1110, -1358, -1213, -3155, +2048, +1957, -895, -643, -907, -799, -1977, -1199, -984, -549, -700, +25, -836, -1353, +35, -751, }, + { +27148, +10286, -13645, +5373, +5534, -6103, -3440, +14036, -378, -11705, -177, +2878, -226, -1089, -1747, -3125, +1710, +1453, +80, -816, -863, -89, -2314, -1012, -907, -744, -724, -326, -1037, -1054, -249, -864, }, + { +26584, +11121, -12486, +5232, +5105, -6806, -4740, +13839, +1078, -11022, -1066, +2034, +722, -813, -2405, -2897, +1499, +637, +937, -473, -1208, +661, -2238, -1145, -718, -867, -1215, -685, -932, -951, -292, -941, }, + { +25805, +11950, -11123, +5119, +4637, -7115, -6017, +12797, +2771, -10082, -1948, +1322, +1341, -452, -2821, -2640, +1355, -222, +1382, +402, -1584, +1029, -1649, -1467, -434, -1354, -1712, -685, -1153, -608, -115, -1081, }, + { +24754, +12672, -9499, +5075, +3967, -6831, -7500, +11069, +4769, -9261, -2671, +911, +1238, +136, -2811, -2628, +1336, -1074, +1459, +1369, -1523, +816, -817, -1570, -995, -1254, -2045, -954, -1214, -280, +201, -1144, }, + { +23625, +13743, -8416, +5130, +3177, -5952, -8511, +8195, +6835, -8201, -3226, +701, +799, +463, -2132, -2701, +1161, -1431, +1042, +2182, -1162, +681, -474, -1699, -1321, -1011, -2142, -1212, -1223, -47, +480, -1192, }, + { +22165, +15115, -7785, +5546, +2568, -5733, -8429, +4589, +8489, -6649, -4078, +663, +623, +133, -953, -2723, +670, -1020, -128, +2751, -525, +300, -532, -1412, -1518, -711, -1869, -1605, -920, -465, +529, -917, }, + { +22262, +14424, -6906, +6480, +1678, -6039, -7848, +2265, +8205, -4418, -5059, +773, +1015, -648, +127, -2327, +238, -660, -1047, +2455, +302, -278, -464, -941, -1372, -464, -1422, -1493, -1408, -881, +595, -495, }, + { +21426, +14579, -5798, +6478, +1251, -5676, -7497, -360, +8046, -2301, -6017, +896, +1618, -1474, +1197, -1714, -587, -84, -1747, +1143, +1103, -253, -569, -193, -1153, -2, -1077, -1593, -1678, -1403, +210, -122, }, + { +19944, +15360, -4754, +5956, +1176, -5025, -7238, -2954, +7860, -653, -6260, +621, +2153, -1709, +1912, -1547, -1249, +742, -2688, +57, +1079, +401, -432, +447, -710, -64, -484, -1682, -1600, -2206, -540, +94, }, + { +20147, +14018, -3775, +6372, +90, -4104, -7066, -4495, +7159, +825, -6523, +620, +3112, -2199, +2066, -1581, -1430, +939, -2947, -332, +348, +613, +1047, +312, -702, +618, -378, -1616, -1651, -2440, -1434, -218, }, + { +19984, +13391, -3853, +7072, -294, -4065, -6337, -5631, +5959, +2227, -6012, -65, +4276, -2601, +1149, -793, -2128, +1041, -2372, -946, +540, +152, +1289, +1452, -1002, +964, +82, -1959, -1450, -2309, -2165, -747, }, + { +20430, +12454, -5495, +8998, +65, -5776, -5336, -5251, +4459, +2909, -5003, -707, +4833, -2860, -19, +113, -2229, +469, -1412, -634, +320, -217, +361, +2809, -484, +184, +1137, -2340, -1770, -1410, -2528, -1555, }, + { +21692, +10456, -7798, +12335, +68, -8763, -3715, -3473, +2378, +2906, -3129, -2190, +5102, -2356, -1660, +915, -1539, -297, -715, +276, -315, -459, +27, +2282, +814, +228, +658, -1653, -2043, -1236, -2088, -2094, }, + { +22414, +8463, -8975, +14734, -856, -9903, -2019, -2434, +827, +2536, -1717, -3048, +4909, -1972, -2551, +1285, -549, -637, -715, +674, -143, -626, -627, +1991, +1177, +722, +464, -1119, -1865, -1815, -1391, -1829, }, + { +22530, +6646, -9511, +16483, -1322, -11024, -644, -496, -1479, +1598, +531, -3996, +3822, -711, -2705, +845, +188, -390, -681, +346, -68, -65, -1383, +1684, +1452, +284, +1178, -797, -2128, -1411, -1375, -1644, }, + { +21322, +5400, -8796, +17260, -1500, -11305, +474, +113, -2907, +1438, +1455, -4669, +3322, +481, -2925, +742, +471, -590, -202, +63, -265, +281, -1317, +1179, +1229, +431, +1123, -441, -2004, -992, -1149, -2020, }, + { +19175, +5030, -7462, +16622, -929, -10530, +416, +343, -3120, +766, +1767, -4343, +2822, +1079, -2638, +519, +570, -563, -289, +165, -315, +82, -803, +997, +763, +599, +901, -468, -1286, -794, -1066, -1838, }, + { +16819, +5384, -6370, +14965, +290, -9700, +208, +612, -3392, +183, +2183, -3797, +2080, +1803, -2493, -16, +1023, -550, -707, +329, -153, -121, -550, +1037, +727, +224, +746, -397, -1126, -361, -1019, -1629, }, + { +14912, +6709, -4328, +10591, -498, -7109, +111, -741, -770, +328, -733, -960, +2191, +30, -1474, -311, +350, -380, -1280, +108, +235, +180, +74, +223, +54, +624, +423, -99, +35, -79, -285, -105, }, + { +12603, +6186, -2629, +9885, -405, -6060, +108, -976, -537, +418, -737, -896, +2184, +171, -1454, -339, +212, -324, -1080, +111, +88, +93, +170, +250, +211, +621, +320, -131, +116, -1, -403, -106, }, + { +10408, +5748, -848, +8724, -167, -4613, -400, -1217, -70, +524, -929, -678, +2111, +202, -1239, -402, +223, -337, -970, +153, +57, +16, +164, +314, +375, +524, +121, +6, +131, -34, -146, -32, }, + { +8810, +5752, -110, +7195, +525, -3549, -713, -1201, +150, +589, -802, -524, +1697, +366, -971, -531, +153, -232, -820, +124, +74, +31, +92, +425, +518, +160, +114, +336, +59, -121, +192, +176, }, + { +7707, +5602, +35, +6275, +1122, -3180, -685, -740, +80, +415, -374, -403, +1135, +566, -689, -718, +117, -57, -675, +45, +161, +35, +47, +597, +362, +73, +358, +355, +34, +128, +414, +95, }, + { +7097, +5535, -192, +5388, +1340, -2724, -316, -506, -92, +415, +68, -445, +787, +694, -670, -663, +194, +20, -606, +105, +221, -83, +318, +654, +165, +211, +501, +464, +163, +305, +282, -143, }, + { +6452, +5181, -188, +4851, +1397, -2250, -47, -387, -147, +524, +273, -528, +602, +655, -502, -522, +157, +53, -489, +203, +143, +158, +611, +321, +242, +571, +633, +422, +125, +234, -5, -218, }, + { +5779, +4631, +28, +4565, +1363, -1816, +67, -196, +8, +488, +200, -488, +649, +572, -444, -381, +184, +6, -287, +343, +218, +465, +530, +350, +563, +605, +532, +190, +1, +83, -90, -218, }, + { +5208, +4384, +145, +3924, +1403, -1285, +290, -147, -75, +540, +306, -396, +492, +507, -283, -289, +166, +176, +48, +344, +475, +764, +468, +481, +475, +299, +347, +79, -73, +8, -92, -335, }, + { +4512, +3969, +359, +3651, +1556, -960, +277, -42, +99, +522, +205, -303, +469, +444, -138, -122, +272, +416, +283, +588, +781, +710, +302, +187, +175, +216, +260, +77, -143, -145, -168, -400, }, + { +3801, +3629, +701, +3369, +1505, -562, +487, -64, +147, +580, +155, -276, +520, +581, +54, +7, +535, +858, +532, +711, +669, +180, +6, +159, +92, +196, +192, -93, -202, -135, -272, -498, }, + { +3442, +3530, +703, +2812, +1660, -44, +424, -101, +187, +562, +336, -54, +523, +647, +416, +546, +884, +888, +356, +303, +242, +72, +10, +71, +14, +72, +66, -163, -241, -221, -397, -501, }, + { +2690, +3000, +1279, +2876, +1515, +205, +493, -52, +442, +658, +264, +192, +1000, +1052, +635, +643, +744, +413, +42, +208, +138, +112, -80, -143, -63, -30, +7, -171, -300, -327, -429, -392, }, + { +2478, +3269, +1270, +2133, +1692, +521, +531, +45, +514, +1060, +829, +686, +1118, +1030, +491, +45, +183, +420, +250, +132, -125, -159, -155, -121, -114, -108, -174, -265, -205, -328, -363, -352, }, + { +1870, +2687, +1568, +2459, +1988, +651, +612, +481, +1093, +1477, +1114, +836, +591, +278, +48, +164, +470, +379, -76, -182, +38, -195, -357, -228, -185, -143, -188, -137, -211, -282, -321, -361, }, + { +2002, +3298, +2072, +1782, +1264, +1387, +2004, +1233, +1029, +1042, +338, -15, +176, +284, +423, +207, -57, +116, -13, -210, -269, -453, -350, -132, -143, -177, -143, -97, -213, -359, -341, -306, }, + { +2146, +3307, +2355, +2752, +2119, +1859, +2021, +710, +97, +234, +110, +124, +235, +191, +340, -11, -121, +97, -245, -337, -346, -487, -277, -43, -78, -145, -151, -149, -204, -284, -366, -347, }, + { +2369, +3977, +3947, +3602, +1697, +1277, +778, -51, +139, +274, +157, +44, -14, +65, +388, -56, -226, -108, -386, -368, -349, -275, -166, -135, -161, -68, -48, -184, -269, -324, -303, -300, }, + { +2991, +5611, +4893, +2413, +32, +530, +1206, +716, -81, -249, -72, +167, +231, -189, +96, +62, -289, -321, -341, -226, -287, -249, -289, -147, -48, -120, -84, -169, -256, -385, -287, -211, }, + { +3480, +5919, +3215, +1230, +711, +1536, +1856, +445, -348, +146, +147, -235, +202, -60, -65, -10, -330, -145, -168, -321, -230, -233, -366, -152, -34, -139, -131, -161, -196, -267, -285, -233, }, + { +2004, +3234, +1971, +1898, +2099, +2208, +2155, +993, +558, +802, +440, -18, -153, -3, +129, -234, -279, -55, -99, -109, -245, -275, -242, -234, -224, -178, -205, -148, -89, -176, -206, -280, }, + { +1279, +2318, +1457, +1126, +1665, +2069, +1778, +1033, +1441, +1718, +1054, +741, +475, +244, +79, -171, -166, -218, -231, -69, -145, -125, -126, -248, -153, -180, -287, -235, -255, -196, -120, -109, }, + { +1453, +2492, +1452, +1154, +1435, +1406, +842, +362, +1287, +1720, +1252, +1179, +1008, +731, +566, +64, -227, -101, -154, -275, -230, -121, -97, -71, -157, -175, -212, -268, -250, -286, -220, -166, }, + { +1879, +2842, +1125, +1084, +1653, +1253, +390, -18, +973, +1151, +667, +1023, +1287, +1158, +966, +422, +174, +136, -200, -125, -209, -336, -116, -116, -96, -37, -181, -264, -245, -282, -257, -233, }, + { +2149, +3242, +1318, +1052, +1535, +1017, +118, +168, +1096, +645, +127, +653, +733, +907, +1209, +756, +503, +444, +179, +76, -160, -223, -173, -251, -109, -40, -121, -158, -228, -282, -247, -243, }, + { +2317, +3435, +1467, +1216, +1790, +944, -476, +147, +1327, +465, +25, +491, +329, +437, +651, +458, +687, +794, +439, +296, +209, +11, -156, -194, -142, -157, -160, -145, -173, -218, -241, -271, }, + { +2632, +3865, +1576, +1194, +1850, +818, -747, +351, +1336, +91, -24, +570, +326, +220, +377, +87, +100, +501, +607, +596, +401, +255, +179, -41, -117, -38, -173, -278, -200, -177, -186, -241, }, + { +2913, +4307, +1690, +1174, +1997, +721, -1042, +507, +1361, -158, -88, +596, +317, +156, +293, -13, -74, +80, +115, +344, +456, +579, +399, +152, +182, +78, -95, -162, -227, -292, -160, -212, }, + { +3419, +4851, +1663, +1035, +2035, +628, -1177, +737, +1236, -536, -64, +690, +334, +51, +172, -13, -135, -42, -10, -2, -68, +347, +513, +426, +347, +211, +168, -24, -193, -236, -186, -272, }, + { +3767, +5304, +1782, +1016, +2102, +463, -1378, +1048, +1239, -936, -179, +746, +362, +62, +89, -105, -187, -70, -33, -113, -245, -31, +98, +248, +442, +396, +305, +172, +12, -165, -166, -183, }, + { +4285, +5774, +1650, +1174, +2192, +50, -1405, +1467, +1118, -1334, -217, +829, +333, +12, +50, -124, -225, -140, -22, -146, -307, -135, -82, -31, +86, +236, +387, +312, +118, +25, +51, -123, }, + { +4848, +6188, +1491, +1445, +2261, -490, -1401, +1942, +965, -1671, -300, +836, +385, -47, +8, -178, -306, -200, +51, -186, -351, -153, -292, -30, +25, -103, +13, +150, +219, +188, +112, +17, }, + { +5454, +6649, +1314, +1669, +2321, -943, -1366, +2334, +803, -1890, -325, +781, +385, -83, +115, -228, -488, -225, +76, -126, -377, -187, -365, -53, +30, -174, -150, -173, -42, +89, +185, +106, }, + { +5926, +7107, +1339, +1747, +2383, -1252, -1530, +2755, +780, -2246, -303, +735, +323, -85, +73, -145, -589, -384, +135, -187, -469, -43, -386, -191, +46, -215, -180, -236, -326, -211, +7, +83, }, + { +6333, +7526, +1384, +2014, +2467, -1782, -1590, +3195, +714, -2494, -349, +719, +280, -145, +55, -69, -636, -504, +150, -221, -574, -43, -314, -228, -37, -138, -204, -369, -416, -299, -97, -205, }, + { +6696, +8017, +1498, +2006, +2624, -2005, -1798, +3417, +748, -2614, -432, +706, +173, -173, +53, -56, -573, -718, +132, -67, -717, -164, -301, -196, -38, -159, -197, -465, -469, -327, -185, -358, }, + { +6930, +8687, +1750, +1599, +2863, -1765, -2210, +3311, +900, -2491, -605, +596, +191, -265, +91, -35, -551, -808, +27, +34, -661, -338, -391, -71, -59, -282, -167, -426, -499, -386, -346, -349, }, + { +7032, +9112, +2294, +1319, +2928, -1295, -2725, +3058, +1141, -2349, -736, +409, +218, -319, +37, -5, -474, -896, -105, +108, -719, -327, -414, -210, -8, -342, -280, -290, -610, -529, -264, -398, }, + { +7290, +9148, +2788, +1517, +2890, -1004, -3145, +2936, +1182, -2207, -713, +170, +226, -310, -137, +66, -236, -1052, -288, +218, -703, -396, -407, -298, -61, -288, -332, -407, -637, -624, -99, -427, }, + { +8295, +8777, +2246, +2812, +2858, -1707, -2793, +3041, +460, -1834, -416, -196, +229, -361, -255, +143, -68, -1152, -427, +236, -539, -557, -489, -199, -168, -280, -301, -623, -729, -417, -190, -475, }, + { +8813, +8385, +2070, +3791, +3408, -2249, -3089, +3581, -338, -1829, +311, -486, -198, -209, -270, -126, +417, -1313, -692, +394, -483, -630, -627, -124, -246, -308, -391, -642, -737, -489, -253, -480, }, + { +9480, +7764, +1344, +5665, +3938, -3530, -2582, +4359, -1779, -1905, +1349, -610, -594, -360, -91, -401, +691, -1075, -1088, +435, -349, -619, -660, -124, -373, -324, -465, -444, -823, -782, -95, -634, }, + { +10219, +7759, +108, +6635, +4903, -4328, -2227, +5026, -3212, -2275, +2513, -596, -1046, -594, +196, -690, +587, -537, -1473, +302, -113, -638, -684, -100, -439, -459, -480, -472, -766, -889, -314, -724, }, + { +11359, +8080, -1537, +6725, +6202, -4589, -2055, +5443, -4331, -2746, +3483, -358, -1535, -803, +500, -787, +107, +0, -1473, -127, +202, -644, -687, +80, -605, -513, -488, -741, -549, -859, -768, -615, }, + { +12561, +8515, -3181, +6443, +7273, -4422, -1771, +5642, -5238, -3343, +4113, +235, -1988, -1143, +783, -757, -401, +173, -1070, -614, +288, -431, -703, +306, -751, -608, -594, -870, -585, -862, -824, -695, }, + { +13637, +9078, -4327, +5307, +8230, -3683, -1700, +6016, -6052, -3983, +4365, +989, -2192, -1647, +1018, -629, -785, -62, -507, -850, +26, -33, -720, +529, -810, -802, -657, -1006, -784, -809, -610, -865, }, + { +14620, +9720, -5196, +3985, +8621, -2703, -1406, +6384, -6867, -4537, +4323, +1689, -2163, -2167, +1076, -438, -1009, -536, -136, -784, -345, +300, -530, +532, -827, -847, -766, -1246, -914, -773, -428, -804, }, + { +16072, +9930, -6183, +3360, +8255, -2295, -119, +6470, -7878, -4616, +4153, +2063, -2029, -2471, +1020, -386, -950, -1040, -67, -497, -520, +425, -98, +296, -886, -669, -1038, -1392, -1030, -835, -225, -497, }, + { +17538, +10139, -7275, +3167, +7496, -2312, +1507, +6655, -8887, -4746, +4122, +2161, -1895, -2506, +749, -326, -908, -1248, -345, -284, -377, +510, +186, +31, -841, -583, -1261, -1430, -1259, -849, -50, -134, }, + { +18795, +10599, -8283, +2946, +6835, -2703, +2875, +7278, -9742, -5079, +4325, +2043, -1829, -2306, +376, -514, -590, -1442, -695, -362, +63, +785, +55, -22, -629, -692, -1264, -1504, -1464, -809, +17, +195, }, + { +19828, +11378, -9267, +2629, +6374, -3347, +3612, +8402, -10339, -5537, +4515, +1859, -1884, -1929, -100, -800, -262, -1561, -976, -577, +410, +1421, -419, +34, -250, -981, -1080, -1605, -1632, -802, +11, +380, }, + { +21206, +11505, -9851, +2389, +6035, -4347, +4232, +9433, -10759, -5759, +4577, +1581, -1877, -1583, -452, -1192, +46, -1648, -1025, -873, +537, +2263, -927, +52, +224, -1290, -882, -1564, -1935, -752, -159, +454, }, + { +21951, +12180, -10180, +1892, +5955, -5226, +3946, +10828, -10791, -5984, +4342, +1308, -1588, -1445, -711, -1558, +306, -1741, -844, -1199, +362, +3030, -1015, -204, +814, -1397, -873, -1354, -2329, -849, -416, +417, }, + { +22506, +12779, -10294, +1612, +5688, -5970, +3299, +11834, -10475, -5915, +3858, +872, -1070, -1438, -1116, -1691, +435, -1836, -594, -1442, -11, +3401, -605, -538, +1220, -1085, -1193, -1113, -2738, -1072, -644, +153, }, + { +23186, +13097, -10391, +2031, +4838, -6522, +2825, +12019, -9671, -5661, +3239, +340, -378, -1439, -1649, -1540, +599, -1995, -278, -1529, -462, +3340, +152, -492, +1137, -401, -1605, -1222, -2820, -1325, -987, -68, }, + { +22882, +14119, -9689, +1389, +4734, -6999, +1524, +12257, -8386, -5521, +2330, +28, +188, -1505, -1990, -1510, +944, -2230, -172, -1187, -1194, +2809, +1343, -197, +605, +402, -2099, -1365, -2620, -1824, -1038, -503, }, + { +21870, +15422, -8022, +32, +4928, -6961, -638, +12423, -6481, -5741, +1679, -358, +537, -1263, -2316, -1338, +1170, -2350, -127, -788, -1648, +1934, +2335, +603, -234, +775, -1895, -1692, -2402, -2007, -1134, -816, }, + { +21377, +15848, -6611, -135, +4095, -6589, -2340, +11613, -4347, -6089, +1401, -892, +589, -722, -2599, -1204, +1299, -2460, -64, -474, -1793, +1309, +2278, +1468, -710, +729, -1381, -1948, -2200, -1989, -1059, -1104, }, + { +21825, +15338, -6288, +1723, +2245, -6807, -2394, +9743, -2760, -5631, +785, -1266, +822, -455, -2395, -1291, +1244, -2162, -287, +63, -1716, +759, +1340, +2033, -170, +136, -645, -1936, -2205, -1413, -1181, -1687, }, + { +22088, +14986, -6294, +3940, +475, -7388, -1931, +7563, -1485, -4751, -302, -1228, +1282, -606, -1733, -1685, +1011, -1322, -834, +679, -1560, -371, +652, +2168, +634, +122, -548, -1338, -1967, -1463, -1230, -1785, }, + { +21218, +15763, -6154, +5244, -207, -8203, -1741, +5343, +1, -4070, -1324, -1042, +1940, -1042, -1041, -1719, +173, +234, -1546, +421, -980, -1263, -189, +2163, +1362, +537, -295, -894, -1836, -1649, -862, -1860, }, + { +20943, +15351, -5193, +5824, -681, -8421, -2182, +3733, +912, -3280, -2125, -629, +2393, -1338, -793, -1272, -318, +619, -1449, -542, -613, -929, -1049, +1622, +1869, +1834, -606, -1014, -899, -1858, -1052, -1583, }, + { +20467, +15036, -4299, +6074, -972, -8163, -2993, +2103, +1953, -2525, -2906, +84, +2478, -1496, -381, -1209, -661, +522, -1431, -1137, -494, -29, -1724, +1410, +1857, +2078, +198, -1222, -492, -1845, -1016, -1459, }, + { +19997, +14492, -3439, +6231, -1292, -7499, -3870, +428, +3016, -1672, -3589, +505, +3009, -1682, -424, -1271, -888, +168, -1633, -640, -998, +512, -655, +572, +1038, +2022, +1564, -1436, -837, -970, -1064, -1590, }, + { +19121, +13891, -1956, +5864, -1769, -5712, -5524, -1209, +4728, -1224, -4384, +1529, +2886, -1835, -216, -2037, -717, +368, -2459, +62, -470, -20, +501, +397, +27, +1794, +1472, -406, -774, -1035, -530, -1651, }, + { +18795, +12787, -866, +5679, -2785, -3778, -6009, -3416, +6104, -148, -5120, +1968, +2942, -2232, -361, -2334, -318, +255, -2810, +772, +147, -722, +931, +1048, -622, +926, +926, +464, +8, -1388, -195, -1234, }, + { +19769, +11583, -2448, +6778, -3321, -3974, -5178, -3690, +5661, +410, -4626, +1587, +2861, -2687, -704, -1965, -264, +354, -2735, +728, +431, -372, +846, +890, -556, +313, +642, +248, +534, -654, -743, -572, }, + { +20742, +9918, -4333, +9177, -3673, -5562, -3412, -3017, +4558, +903, -4422, +932, +3308, -2821, -1440, -1193, +107, +341, -2476, +352, +665, +234, +482, +681, -223, -107, +450, +161, +378, +311, -786, -592, }, + { +20393, +8513, -5551, +11591, -3341, -7618, -1960, -1344, +2974, +225, -3410, +284, +3332, -2616, -1768, -458, +23, +66, -1947, +502, +187, +334, +642, +553, -324, +98, +28, -212, +831, +137, -320, -307, }, + { +19863, +7195, -5891, +13080, -3237, -8975, -179, -735, +1159, +597, -2780, -566, +3377, -1932, -1835, -26, +57, -431, -1301, +563, -286, +265, +803, +515, -423, +180, +122, -246, +644, +165, -191, +77, }, + { +18658, +6850, -5751, +12388, -2703, -7823, +255, -1504, +610, +770, -2248, -983, +3161, -1268, -1738, +128, -21, -478, -1161, +338, -70, +251, +391, +591, -323, +20, +528, -36, +54, +115, -36, +34, }, + { +17076, +6736, -5238, +11355, -1525, -7217, -40, -1078, -147, +458, -1371, -990, +2637, -628, -1439, -189, +204, -339, -1390, +203, +285, +214, +63, +453, -132, +311, +558, +23, -66, -152, +62, -43, }, + { +13623, +7663, -736, +5443, -2593, -2244, -1530, -1649, +1729, -32, -2266, +999, +1978, -1230, -677, -1107, +219, -259, -1521, +393, -253, -340, +340, +425, +603, +1120, -212, -683, -191, -585, -148, +387, }, + { +11820, +7583, -426, +4965, -1356, -2529, -1167, -1245, +983, +124, -1778, +794, +1769, -842, -700, -980, +259, -378, -1380, +412, -355, -376, +513, +422, +537, +830, -258, -264, -158, -658, +7, +383, }, + { +10321, +7312, -105, +4864, -813, -2658, -650, -1140, +594, +154, -1345, +747, +1470, -560, -589, -773, +109, -428, -1061, +264, -409, -228, +464, +450, +557, +455, -229, +107, -87, -614, +63, +459, }, + { +8832, +6788, +754, +4529, -737, -2058, -537, -1141, +593, +102, -1073, +689, +1331, -427, -481, -535, -57, -390, -871, +136, -280, -172, +355, +496, +482, +211, +7, +299, +18, -389, +32, +568, }, + { +7264, +6341, +1804, +3830, -547, -1150, -702, -1198, +746, +193, -1033, +629, +1243, -340, -243, -463, -211, -272, -673, +38, -232, -89, +255, +491, +349, +237, +305, +440, +181, -306, +96, +481, }, + { +5764, +5793, +2760, +3310, -250, -431, -863, -1206, +942, +277, -1025, +490, +1124, +11, -135, -654, -133, +3, -652, -89, -115, -120, +324, +473, +214, +489, +656, +578, +94, -300, +162, +271, }, + { +4877, +5432, +2934, +2887, +9, -98, -665, -1051, +773, +328, -754, +397, +892, +204, -54, -717, +22, +137, -707, -136, -8, -16, +384, +457, +358, +804, +826, +406, -152, -241, +83, +160, }, + { +4273, +4974, +2996, +2688, +19, +236, -317, -1082, +645, +529, -499, +171, +725, +487, -28, -702, +171, +109, -663, +33, +8, +168, +685, +509, +520, +907, +639, +123, -222, -249, +142, +242, }, + { +3881, +4471, +2801, +2721, +214, +232, -276, -784, +726, +443, -495, +187, +790, +514, -89, -624, +292, +140, -590, +200, +269, +545, +770, +316, +532, +788, +265, -82, -82, -100, +41, +29, }, + { +3766, +4300, +2408, +2391, +333, +347, -106, -602, +651, +407, -345, +364, +720, +250, +52, -284, +220, +186, -143, +547, +529, +560, +390, +206, +482, +471, +188, -7, -119, -170, +39, -9, }, + { +3436, +3912, +2322, +2385, +450, +371, -77, -417, +729, +455, -307, +332, +707, +317, +188, -91, +487, +565, +170, +463, +431, +296, +60, +230, +424, +421, +106, -107, -113, -167, +28, -121, }, + { +3183, +3685, +2212, +2271, +537, +324, +19, -108, +778, +330, -270, +582, +667, +327, +729, +405, +569, +537, +26, +242, +252, -24, +148, +398, +218, +251, +64, -101, -137, -167, -61, -180, }, + { +2975, +3602, +2238, +1946, +368, +615, +350, -208, +659, +572, -66, +604, +1024, +904, +959, +291, +202, +264, -144, +133, +221, +2, +109, +243, +252, +133, -64, -129, -180, -137, -144, -273, }, + { +2802, +3321, +2156, +2179, +535, +317, +283, +375, +791, +445, +493, +1260, +1224, +697, +585, -84, -52, +130, -53, +215, +5, -34, +157, +233, +121, -41, -96, -197, -124, -177, -272, -304, }, + { +2567, +3351, +2539, +1843, +97, +744, +689, +452, +1235, +1224, +767, +951, +804, +213, +156, -203, +95, +139, -182, +93, +76, +25, +66, +145, -99, -130, -124, -168, -143, -326, -266, -289, }, + { +2718, +3449, +2308, +1797, +208, +761, +1522, +1644, +1223, +789, +460, +314, +454, -11, +113, +21, -170, +13, +13, +85, +59, -34, -70, +29, -183, -196, -119, -202, -174, -262, -320, -243, }, + { +2540, +3343, +2399, +2066, +1062, +1501, +1604, +1736, +1082, -140, -197, +490, +498, -164, -13, -43, -88, +38, +21, +50, -52, -74, -79, -137, -262, -173, -156, -121, -156, -298, -206, -189, }, + { +2234, +3727, +3392, +2599, +1296, +1388, +1575, +990, +120, +142, -3, +141, +343, -173, +83, -53, -176, +87, -65, -57, -14, -108, -214, -261, -285, -100, -90, -157, -152, -234, -209, -204, }, + { +2985, +5564, +3761, +1458, +757, +1364, +784, +451, +634, -7, -242, +3, +522, +51, -283, +33, -103, -187, +6, +14, -99, -195, -315, -245, -150, -121, -75, -132, -198, -215, -225, -243, }, + { +3536, +5654, +3321, +1733, +414, +394, +1272, +1090, -2, -98, +317, +30, +224, +141, -65, -151, -228, -176, -28, +9, -112, -143, -326, -205, -121, -146, -154, -155, -169, -220, -257, -233, }, + { +2423, +4490, +3856, +2307, +306, +542, +1485, +1042, +272, +121, +367, +345, +305, +76, +54, -98, -197, -211, -235, -76, +30, -102, -249, -110, -163, -144, -218, -256, -176, -211, -237, -223, }, + { +2207, +4508, +3055, +1310, +930, +958, +1117, +900, +798, +352, +193, +679, +614, +197, +54, +57, -131, -330, -215, -106, -182, -79, -43, -101, -76, -110, -233, -306, -312, -262, -203, -241, }, + { +2155, +4216, +2568, +1005, +1213, +966, +459, +898, +1067, +401, +559, +752, +475, +636, +473, -23, -19, +1, -327, -285, -98, -96, -180, -15, +129, -115, -171, -209, -363, -372, -286, -209, }, + { +2070, +3883, +2353, +1160, +1380, +728, +99, +912, +928, +215, +528, +873, +808, +593, +419, +448, +247, -17, -66, -137, -244, -204, -83, -38, -42, +64, -18, -242, -235, -315, -412, -310, }, + { +2125, +3872, +2333, +1117, +1382, +652, -82, +932, +929, -32, +165, +810, +760, +591, +704, +380, +171, +402, +223, -173, -55, +18, -230, -138, +84, +9, -127, -88, -152, -290, -286, -341, }, + { +2204, +3829, +2303, +1430, +1528, +266, -259, +1194, +944, -279, +37, +705, +496, +385, +633, +421, +252, +297, +317, +192, +43, +66, +57, +9, -58, -33, -24, -181, -247, -150, -214, -284, }, + { +2377, +4116, +2337, +1342, +1538, +248, -324, +1198, +907, -340, -9, +552, +446, +300, +373, +259, +71, +202, +429, +213, +40, +308, +228, +122, +212, +69, -122, -224, -230, -240, -255, -174, }, + { +2547, +4334, +2430, +1433, +1566, +87, -418, +1351, +878, -486, -33, +547, +363, +146, +403, +264, -240, -131, +304, +205, +66, +265, +312, +373, +311, +248, +138, -166, -373, -314, -205, -258, }, + { +2830, +4723, +2442, +1353, +1596, +57, -445, +1370, +794, -551, +26, +483, +302, +84, +326, +353, -232, -336, -50, +55, -20, +99, +386, +443, +376, +430, +300, -2, -200, -321, -318, -255, }, + { +3101, +5094, +2484, +1317, +1620, -48, -486, +1479, +713, -731, +26, +531, +246, -26, +207, +395, -120, -435, -199, -118, -261, -82, +201, +404, +445, +420, +397, +148, -80, -175, -230, -288, }, + { +3513, +5583, +2388, +1169, +1692, -102, -510, +1532, +632, -825, +39, +484, +162, -19, +107, +357, -62, -406, -271, -220, -305, -288, -44, +301, +381, +318, +417, +216, -33, -33, -125, -197, }, + { +3971, +6063, +2297, +1049, +1754, -235, -496, +1678, +453, -941, +114, +521, +25, -141, +100, +323, -30, -393, -314, -245, -341, -357, -157, +129, +280, +188, +259, +194, -7, -19, -69, -59, }, + { +4462, +6601, +2162, +929, +1825, -406, -449, +1847, +245, -1033, +198, +531, -55, -243, +34, +274, -46, -317, -280, -344, -378, -331, -147, -22, +200, +127, +86, +97, -172, -74, +29, -12, }, + { +4929, +7134, +2067, +832, +1905, -671, -420, +2107, +13, -1176, +285, +566, -115, -373, -56, +231, -88, -317, -242, -369, -432, -373, -60, -90, +32, +212, -22, -102, -274, -226, -80, -18, }, + { +5380, +7722, +2007, +689, +2018, -933, -447, +2383, -171, -1301, +369, +562, -128, -427, -220, +187, -78, -332, -233, -409, -419, -408, -34, +5, -132, +185, +51, -252, -327, -342, -282, -98, }, + { +5728, +8332, +2054, +461, +2152, -1109, -551, +2612, -297, -1395, +446, +591, -229, -430, -303, +22, -78, -336, -214, -416, -470, -471, -25, +180, -147, -54, +113, -193, -423, -380, -487, -230, }, + { +6113, +8894, +2133, +278, +2235, -1274, -667, +2769, -471, -1368, +504, +562, -301, -464, -281, -179, -193, -277, -163, -442, -520, -538, -26, +230, -9, -150, -82, -60, -486, -442, -508, -419, }, + { +6822, +9262, +1874, +539, +2127, -1579, -524, +2787, -765, -1217, +595, +512, -360, -580, -126, -403, -358, -224, -134, -389, -601, -579, +2, +211, +8, +23, -240, -219, -412, -487, -530, -498, }, + { +7515, +9472, +1708, +950, +1940, -1744, -475, +2685, -890, -1100, +601, +523, -322, -777, -22, -467, -558, -250, -104, -316, -558, -673, -18, +285, -56, +161, -229, -389, -420, -494, -565, -489, }, + { +7940, +9637, +1858, +1251, +1898, -1722, -763, +2646, -912, -1044, +619, +459, -176, -902, -97, -399, -684, -474, -18, -221, -503, -687, -201, +491, -97, +79, -100, -413, -500, -584, -564, -465, }, + { +8437, +9625, +1884, +1715, +2059, -1879, -1105, +2725, -1064, -1040, +741, +338, -154, -826, -248, -438, -648, -774, -57, -33, -524, -628, -313, +497, +49, -120, -60, -285, -620, -701, -513, -517, }, + { +9220, +9383, +1442, +2663, +2411, -2496, -1180, +2907, -1426, -1017, +899, +285, -271, -686, -239, -660, -556, -895, -297, +126, -442, -632, -203, +342, +145, -93, -144, -242, -638, -729, -500, -582, }, + { +9906, +8999, +886, +4046, +2822, -3537, -956, +3303, -2122, -1097, +1212, +287, -466, -677, -82, -821, -584, -919, -622, +120, -110, -798, -101, +317, +50, +139, -325, -314, -494, -726, -682, -494, }, + { +10165, +8622, +635, +5498, +3208, -4703, -409, +3974, -3274, -1343, +1812, +349, -758, -791, +168, -846, -676, -904, -937, -105, +296, -819, -186, +443, -101, +357, -353, -431, -390, -698, -845, -370, }, + { +10778, +9125, -461, +5499, +4480, -4885, -762, +4363, -3869, -1600, +2274, +309, -1021, -799, +442, -989, -823, -718, -1054, -519, +402, -449, -376, +548, -68, +372, -259, -484, -358, -718, -919, -405, }, + { +11808, +9598, -1631, +4810, +5752, -4539, -1177, +4391, -4229, -1790, +2570, +358, -1408, -786, +715, -1102, -1041, -581, -992, -885, +191, -44, -368, +438, +142, +323, -208, -398, -399, -804, -937, -537, }, + { +12864, +10102, -2814, +4041, +6509, -3716, -1366, +4112, -4371, -1965, +2757, +499, -1803, -807, +909, -1173, -1234, -559, -856, -1084, -225, +261, -140, +202, +383, +376, -191, -207, -486, -953, -891, -645, }, + { +13770, +10842, -3762, +2948, +6944, -2626, -1423, +3706, -4498, -2054, +2738, +733, -2151, -912, +1063, -1203, -1347, -631, -679, -1132, -639, +290, +229, +18, +464, +552, -134, -53, -593, -1082, -910, -725, }, + { +14673, +11578, -4475, +1691, +7096, -1713, -1082, +3280, -4814, -1919, +2563, +861, -2306, -1111, +1114, -1240, -1354, -818, -528, -1086, -897, +19, +528, +85, +343, +790, +8, -1, -705, -1107, -997, -856, }, + { +15287, +12615, -5054, +450, +7031, -997, -525, +3136, -5350, -1613, +2407, +806, -2180, -1353, +994, -1181, -1315, -1000, -400, -980, -938, -358, +637, +322, +261, +1020, +234, -8, -727, -1060, -1088, -991, }, + { +16341, +13198, -5831, -183, +6631, -925, +450, +3048, -6132, -1100, +2328, +438, -1973, -1456, +665, -1224, -1147, -1229, -302, -819, -969, -597, +502, +511, +258, +1295, +390, -116, -684, -1059, -1180, -1113, }, + { +17889, +13289, -7003, +251, +5613, -1102, +1630, +2856, -6736, -544, +2351, -201, -1577, -1545, +280, -1291, -936, -1371, -191, -616, -996, -677, +307, +583, +290, +1693, +557, -380, -523, -1129, -1279, -1128, }, + { +19421, +13294, -8130, +984, +4304, -1338, +2588, +2708, -7031, -194, +2313, -845, -1057, -1787, -127, -1222, -940, -1254, -198, -479, -881, -843, +201, +539, +228, +2168, +760, -747, -401, -1194, -1459, -1011, }, + { +20606, +13470, -8907, +1610, +3084, -1671, +3262, +2674, -6933, -54, +2126, -1222, -615, -2058, -501, -1073, -920, -1108, -226, -364, -678, -985, +112, +592, +23, +2550, +1067, -1061, -439, -1160, -1596, -844, }, + { +21678, +13397, -9105, +1972, +1964, -2147, +3689, +2647, -6634, +29, +1710, -1374, -333, -2266, -901, -865, -791, -1224, -108, -330, -517, -988, -65, +848, -297, +2505, +1436, -1115, -771, -1020, -1548, -824, }, + { +22043, +13828, -8808, +1968, +1087, -2691, +3735, +2813, -6153, -63, +1327, -1523, -22, -2413, -1231, -625, -620, -1517, +116, -296, -490, -687, -330, +1228, -654, +1883, +1897, -817, -1166, -848, -1353, -842, }, + { +21862, +14553, -8083, +1733, +298, -3191, +3378, +3080, -5574, -270, +1014, -1790, +389, -2456, -1466, -480, -483, -1820, +297, -178, -667, -4, -656, +1283, -726, +917, +2011, -106, -1245, -999, -903, -756, }, + { +21352, +15281, -6885, +1395, -334, -3665, +2670, +3546, -5077, -445, +803, -2146, +977, -2334, -1692, -440, -333, -2064, +440, -23, -774, +810, -1201, +1101, -364, -194, +1769, +892, -1065, -1132, -267, -995, }, + { +20861, +15614, -5482, +1081, -870, -4341, +1898, +3930, -4851, -435, +471, -2413, +1653, -2264, -1871, -605, -244, -2109, +276, +437, -945, +994, -1311, +557, +83, -748, +1003, +1546, -293, -1147, -222, -1064, }, + { +20341, +15837, -4243, +1301, -1450, -5105, +1347, +3961, -4522, -413, +325, -2497, +2316, -2115, -2138, -652, -297, -1870, +211, +677, -1276, +1047, -1153, -118, +720, -740, -74, +2033, +582, -1274, -145, -714, }, + { +19636, +16119, -3188, +1683, -1865, -5991, +823, +3722, -4115, -322, +190, -2340, +2786, -2019, -2280, -876, -206, -1208, -519, +732, -1211, +541, -669, -258, +512, +140, -566, +1174, +1263, -677, -170, -802, }, + { +18858, +16285, -2143, +1991, -1980, -6784, +50, +3403, -3485, -245, +189, -2095, +3079, -1823, -2721, -438, -196, -1206, -875, +432, -1124, +431, -376, -252, +414, +1193, -856, -296, +1960, -172, -317, -664, }, + { +18488, +15830, -1291, +2547, -2229, -7386, -730, +2796, -2460, -104, -268, -1138, +2825, -2015, -2015, -709, -496, -920, -1536, +112, -516, +216, -578, +707, +252, +875, +136, -1180, +1044, +302, +130, -464, }, + { +18032, +15462, -771, +3171, -2446, -7862, -1764, +2636, -1567, +5, -496, -701, +2816, -1760, -1991, -874, -746, -1167, -1739, +343, -701, +364, +15, +532, +203, +973, +810, -1570, -221, +371, +695, -210, }, + { +18183, +14237, -213, +3919, -3116, -7974, -2292, +2020, -153, +42, -1423, +627, +2562, -2113, -1333, -1554, -1186, -770, -1762, -91, -66, +691, -288, +385, +793, +1048, +304, -885, -866, -584, +1104, +190, }, + { +17916, +13240, +200, +4684, -3796, -7718, -2760, +1604, +850, +225, -1927, +1081, +2619, -2191, -1377, -2216, -746, -653, -2263, +733, +68, +56, +169, +482, +600, +1299, +31, -544, -723, -1663, +902, +562, }, + { +17044, +12267, +1399, +4803, -4676, -5966, -3841, +281, +3320, -114, -3303, +2279, +2401, -2496, -1283, -2365, -671, -464, -1900, +808, -230, -25, +729, +96, +303, +1800, +66, -835, -309, -1837, -28, +731, }, + { +17131, +11616, +1113, +3708, -4510, -4182, -4413, -204, +4141, -354, -3431, +2353, +1909, -2319, -1067, -2887, -24, -22, -2046, +700, -301, +199, +609, -123, +449, +1846, +102, -822, -495, -1668, -259, +595, }, + { +17804, +10827, -833, +4161, -4620, -3463, -2984, -1411, +4085, +122, -3689, +2063, +1883, -2404, -1128, -2081, +2, -158, -1877, +784, -113, -307, +389, +403, +575, +1373, +163, -658, -728, -1403, -144, +238, }, + { +17770, +9675, -2120, +5437, -4251, -3736, -2292, -788, +3134, -434, -2948, +1807, +1772, -2590, -638, -1520, -124, -311, -1778, +1073, -328, -581, +430, +559, +654, +1248, -5, -604, -701, -1222, +85, +89, }, + { +16983, +8744, -2777, +6694, -3792, -4443, -901, -815, +1755, -54, -2575, +1415, +1766, -2182, -536, -1198, -147, -428, -1414, +749, -461, -344, +355, +328, +855, +1312, -290, -626, -512, -1025, +51, +365, }, + { +15530, +7980, -1979, +6512, -3528, -3269, -860, -1658, +1879, -8, -2551, +1210, +1950, -1724, -587, -1052, -96, -226, -1441, +427, -306, -235, +212, +345, +877, +1182, -243, -745, -344, -674, -107, +390, }, + { +13644, +7839, +340, +4237, -4066, -2089, +181, -157, -98, -716, -1664, +1617, +1169, -1811, +342, -1102, -326, +22, -1305, +431, -226, -614, +436, +265, -325, +880, +370, -95, +206, -791, -194, -105, }, + { +11477, +7542, +1372, +3307, -3045, -1070, -418, -498, +213, -531, -1761, +1305, +1366, -1451, +150, -939, -219, -56, -1110, +275, -259, -524, +318, +167, -195, +840, +316, +6, -31, -753, +66, +40, }, + { +9185, +7569, +2375, +2356, -1914, -448, -695, -709, +367, -353, -1571, +1152, +1173, -975, +111, -735, -206, -120, -786, +213, -329, -455, +368, +102, -13, +690, +287, +199, -74, -549, +265, +253, }, + { +7687, +7172, +2505, +2384, -1188, -623, -434, -481, +255, -398, -1198, +1009, +1022, -762, +89, -484, -279, -47, -579, +42, -268, -226, +195, +11, +172, +581, +417, +303, -47, -184, +406, +81, }, + { +6794, +6483, +2410, +2627, -840, -796, -81, -127, -26, -355, -786, +799, +900, -672, +188, -249, -386, -6, -336, -8, -247, -119, +38, +96, +339, +606, +665, +312, -3, +47, +278, -125, }, + { +5907, +5826, +2525, +2670, -555, -752, +41, +149, -99, -234, -613, +533, +888, -428, +180, -214, -321, +180, -187, -225, -103, +27, -39, +368, +489, +694, +754, +175, -13, +28, +125, -216, }, + { +5068, +5227, +2668, +2678, -280, -715, +86, +472, -115, -212, -504, +385, +890, -299, +204, -116, -199, +274, -206, -202, +165, +190, +121, +464, +488, +765, +530, -95, +33, +85, +32, -285, }, + { +4430, +4893, +2607, +2422, +45, -558, +143, +594, -112, -70, -407, +181, +893, +9, +138, -80, -17, +293, -18, +126, +441, +150, +103, +549, +369, +433, +333, -10, +90, +43, -106, -245, }, + { +3711, +4345, +2802, +2585, +142, -598, +212, +846, -12, -130, -349, +202, +915, +136, +144, +26, +282, +585, +264, +261, +271, +98, +65, +247, +259, +366, +292, +2, +95, +26, -184, -171, }, + { +3270, +4161, +2763, +2345, +192, -419, +378, +857, +49, -39, -256, +254, +934, +196, +366, +592, +573, +461, +232, +248, -23, -159, +76, +241, +206, +248, +207, +95, +139, -106, -191, -197, }, + { +2943, +3918, +2802, +2227, +112, -304, +629, +924, +29, +150, -154, +182, +1323, +752, +534, +526, +437, +413, +22, -194, -72, +85, -38, +83, +224, +251, +213, +42, +65, -167, -226, -198, }, + { +2772, +3851, +2788, +1975, +65, -69, +845, +1077, -57, +182, +530, +759, +1311, +513, +472, +465, +64, +67, -3, -148, -49, +2, -85, +278, +204, -7, +177, +109, -60, -207, -187, -266, }, + { +2741, +3962, +2753, +1658, -41, +420, +934, +1003, +697, +759, +554, +442, +1100, +376, +43, +199, +55, +12, -47, -241, +16, +219, -40, +38, -37, +66, +191, +16, -90, -266, -234, -204, }, + { +3100, +4428, +2177, +1205, +75, +902, +1974, +1358, +427, +424, +567, +206, +484, +165, +130, +95, -110, -9, -43, +93, +142, -69, -204, -13, -9, +23, +198, -26, -264, -205, -104, -146, }, + { +3210, +4553, +1862, +1097, +1209, +1702, +1662, +1004, +308, +184, +225, +17, +388, +79, +50, +47, -163, +200, +252, -95, -147, -45, -164, -179, -57, +145, +35, -75, -120, -139, -145, -187, }, + { +2744, +4295, +2964, +2006, +764, +1468, +1817, +756, +4, +41, +245, +14, +163, +49, +70, +100, +168, +231, -53, -108, -40, -137, -327, -125, -50, -25, +121, +79, -87, -207, -216, -224, }, + { +3644, +5506, +2291, +880, +1111, +1504, +1276, +605, -113, -2, +218, +64, +212, -104, +192, +519, +136, -208, +41, +48, -219, -274, -268, -122, -133, +102, +215, +67, -186, -291, -229, -287, }, + { +3729, +5991, +1991, +263, +1099, +1310, +1186, +802, +62, -222, +346, +155, +106, +141, +330, +491, +26, -42, +16, -23, -226, -214, -280, -265, -52, +117, +214, +32, -128, -310, -288, -270, }, + { +2626, +5352, +3385, +581, +252, +872, +1262, +1271, +175, -237, +438, +384, +61, +61, +469, +546, +232, -19, -21, +71, -83, -296, -233, -189, -195, +42, +177, +99, -95, -207, -255, -295, }, + { +2086, +4619, +3769, +1305, +139, +491, +966, +1193, +432, -210, +327, +607, +212, +43, +405, +422, +412, +221, +64, +98, -131, -143, -94, -220, -249, -9, +178, +14, -136, -131, -163, -256, }, + { +2139, +4433, +3497, +1555, +347, +189, +878, +1324, +318, -496, +231, +753, +258, +26, +394, +493, +349, +186, +248, +240, -44, -89, -49, -142, -57, -56, +21, +118, -122, -273, -226, -136, }, + { +2311, +4528, +3281, +1515, +485, +155, +824, +1324, +265, -492, +160, +527, +142, +7, +422, +391, +265, +431, +202, +86, +149, +36, +38, -14, +14, +37, +95, +88, -115, -263, -276, -244, }, + { +2483, +4762, +3179, +1400, +563, +131, +828, +1336, +202, -523, +128, +527, +103, -226, +171, +342, +142, +412, +338, +45, +92, -94, +172, +187, +6, +205, +202, +105, -77, -196, -299, -324, }, + { +2726, +5137, +3097, +1180, +674, +184, +755, +1346, +184, -531, +98, +477, +195, -246, -36, +193, -87, +205, +506, +62, -30, -76, +59, +215, +91, +296, +329, +186, +97, -139, -324, -248, }, + { +3045, +5527, +3012, +1062, +756, +111, +709, +1423, +99, -641, +145, +477, +146, -204, -123, +100, -177, -119, +444, +91, -147, -88, -72, +196, +100, +177, +408, +293, +113, +30, -141, -297, }, + { +3436, +6020, +2910, +889, +868, +57, +655, +1504, +22, -713, +161, +469, +124, -165, -125, -15, -195, -258, +287, +219, -316, -262, -35, +129, +77, +61, +409, +298, +70, +141, -16, -144, }, + { +3894, +6586, +2783, +668, +954, -13, +635, +1573, -146, -783, +246, +445, +2, -172, -63, -91, -294, -318, +133, +279, -237, -506, -107, +75, +8, +47, +226, +248, +95, +53, -4, -57, }, + { +4405, +7200, +2656, +443, +1039, -128, +662, +1690, -398, -841, +393, +450, -138, -226, +1, -146, -386, -359, +40, +223, -89, -563, -227, +109, -110, -3, +147, +88, +56, +29, -5, -116, }, + { +4876, +7873, +2612, +205, +1070, -269, +712, +1849, -697, -944, +597, +475, -291, -315, +82, -190, -515, -429, -4, +168, -34, -473, -351, +121, -57, -127, +140, -117, -82, +81, -67, -139, }, + { +5345, +8532, +2640, -20, +1042, -466, +776, +2045, -1057, -1078, +828, +511, -446, -439, +123, -190, -656, -517, -117, +119, +9, -405, -323, -39, +71, -105, -9, -130, -314, -1, -54, -197, }, + { +5755, +9172, +2804, -162, +957, -687, +828, +2288, -1359, -1285, +1083, +619, -595, -549, +140, -166, -730, -627, -255, +75, +66, -414, -171, -94, -6, +93, -105, -228, -284, -205, -114, -134, }, + { +6053, +9828, +3094, -338, +874, -821, +750, +2423, -1504, -1522, +1217, +797, -715, -693, +145, -172, -790, -680, -456, -66, +242, -494, -151, +117, -189, +150, +39, -395, -358, -190, -293, -70, }, + { +6365, +10439, +3396, -513, +841, -814, +596, +2360, -1502, -1631, +1210, +940, -723, -805, +124, -197, -871, -667, -563, -359, +324, -281, -332, +308, -121, -25, +252, -378, -573, -197, -328, -115, }, + { +6691, +10976, +3599, -570, +917, -864, +376, +2212, -1463, -1679, +1089, +1036, -703, -886, +72, -213, -1017, -664, -582, -629, +140, +41, -340, +170, +189, -205, +228, -137, -721, -390, -282, -214, }, + { +7655, +11301, +2983, -165, +1190, -1185, +265, +2137, -1680, -1486, +973, +1045, -655, -1003, +145, -320, -1056, -774, -554, -718, -142, +148, -1, -9, +263, -7, +1, +12, -602, -637, -348, -173, }, + { +8725, +11372, +2035, +795, +1645, -1977, +191, +2335, -2001, -1434, +933, +1003, -589, -1155, +213, -455, -1024, -893, -641, -701, -356, -16, +344, +128, +51, +231, -87, -31, -405, -710, -562, -171, }, + { +9575, +11154, +1237, +2394, +2028, -3285, +418, +2886, -2551, -1615, +1186, +934, -650, -1197, +283, -624, -1011, -875, -759, -706, -442, -240, +477, +441, -54, +319, -30, -127, -305, -565, -677, -372, }, + { +10190, +10640, +774, +4347, +2002, -4744, +1042, +3458, -3410, -1873, +1669, +748, -934, -1066, +314, -877, -1066, -778, -875, -839, -432, -417, +363, +615, +59, +388, -7, -218, -333, -323, -683, -569, }, + { +11656, +10948, -887, +4545, +3052, -4722, +604, +3331, -3530, -1765, +1917, +460, -1170, -858, +331, -1145, -1038, -801, -770, -934, -391, -415, +118, +836, +219, +336, +68, -117, -415, -229, -603, -466, }, + { +13296, +11164, -2472, +4349, +3911, -4234, +37, +3058, -3561, -1609, +2035, +238, -1451, -733, +380, -1485, -961, -904, -594, -975, -451, -230, -189, +825, +484, +313, +29, -4, -374, -223, -531, -267, }, + { +14547, +11879, -3970, +3666, +4639, -3257, -708, +2677, -3398, -1566, +2096, +56, -1592, -844, +432, -1787, -948, -1000, -471, -858, -654, +26, -371, +599, +668, +477, -138, +127, -242, -320, -341, -194, }, + { +16244, +12044, -5188, +3051, +4787, -2203, -1047, +2078, -3288, -1206, +1927, -153, -1529, -1122, +410, -1934, -1051, -951, -400, -689, -749, +91, -214, +211, +759, +692, -292, +255, -104, -432, -82, -239, }, + { +17280, +12976, -6370, +2340, +4641, -1173, -1098, +1398, -2972, -1077, +1787, -398, -1249, -1610, +388, -1966, -1197, -775, -431, -518, -766, +52, -3, +3, +536, +1033, -423, +244, +74, -499, +32, -201, }, + { +18875, +13104, -7286, +2064, +3976, -486, -755, +780, -2840, -753, +1487, -511, -1136, -1879, +201, -1926, -1197, -654, -442, -429, -635, -89, +257, -23, +237, +1321, -511, +32, +268, -506, +24, +2, }, + { +20130, +13417, -7954, +1823, +3146, -160, -302, +339, -2656, -706, +1344, -730, -995, -2122, -28, -1814, -1115, -678, -496, -246, -631, -73, +307, +224, -38, +1338, -479, -375, +351, -455, +133, +122, }, + { +20900, +14050, -8284, +1502, +2221, -215, +187, +146, -2582, -775, +1230, -1057, -723, -2335, -281, -1549, -1106, -814, -527, -46, -662, -26, +311, +623, -314, +1032, -275, -849, +139, -141, +262, +150, }, + { +21506, +14520, -8163, +1322, +1076, -434, +702, +70, -2585, -746, +944, -1321, -256, -2471, -490, -1259, -1129, -1001, -507, +204, -799, +188, +407, +732, -431, +631, -175, -1085, -90, -20, +511, +450, }, + { +22045, +14638, -7551, +1339, -401, -605, +1069, +20, -2608, -672, +522, -1429, +425, -2597, -667, -990, -1249, -1112, -381, +216, -660, +423, +180, +808, -430, +149, -101, -901, -420, -144, +988, +646, }, + { +22374, +14605, -6584, +1379, -2012, -818, +1269, -52, -2715, -579, +90, -1306, +1138, -2771, -762, -975, -1284, -1081, -478, +502, -689, +317, +67, +770, -529, -3, +37, -932, -233, -321, +645, +1063, }, + { +21971, +14982, -5381, +1465, -3401, -1307, +1361, -157, -2826, -434, -42, -1263, +1888, -2842, -1101, -790, -1377, -899, -394, +320, -814, +398, -273, +728, -246, -176, +85, -530, -151, -691, +222, +1349, }, + { +21578, +15032, -3953, +1480, -4478, -2119, +1347, -259, -2912, +19, -196, -1128, +2559, -2963, -1432, -703, -946, -870, -888, +371, -914, +216, -287, +839, -198, -30, +401, -611, -215, -354, -239, +533, }, + { +20935, +14892, -2350, +1522, -5169, -3135, +939, +47, -2856, +331, -223, -886, +3031, -3198, -1627, +82, -1303, -1105, -934, +12, -879, +350, -351, +790, +317, +101, -59, -207, +109, -776, -140, -183, }, + { +19989, +14916, -1024, +1608, -5475, -4473, +295, +1065, -2834, +220, +84, -593, +2619, -2455, -1492, -396, -1160, -1373, -1255, +21, -642, +54, -86, +1220, -111, +160, +248, -322, +230, -981, +131, -465, }, + { +19442, +14440, -244, +2209, -5944, -5867, +441, +1708, -2969, +837, -323, -569, +3451, -2723, -1540, -344, -1757, -1134, -1142, -251, -519, +531, -17, +686, +62, +600, -73, -568, +824, -1013, -326, +77, }, + { +19215, +13282, +497, +2823, -6861, -6114, +314, +2076, -2340, +432, -449, +228, +3074, -2653, -1482, -1155, -1421, -644, -1745, -8, +375, -76, -205, +1100, -38, +316, +113, -497, +839, -778, -681, +216, }, + { +18399, +12398, +1273, +2931, -6997, -6116, +179, +2120, -1724, +857, -1704, +1041, +3221, -3315, -1375, -1169, -1113, -795, -1352, +352, -102, -102, +206, +701, -238, +511, +174, -405, +646, -548, -516, -399, }, + { +17103, +12076, +1628, +2442, -6317, -5062, -1021, +2253, +17, -617, -1910, +2032, +2182, -3218, -758, -1593, -918, +124, -1676, +57, +262, -74, -107, +549, -37, +456, +200, -287, +594, -479, -420, -621, }, + { +17339, +11281, -244, +2822, -5940, -4177, +36, +1140, -56, -183, -2196, +2015, +1763, -3155, -461, -1392, -347, -94, -1944, +612, +306, -660, +4, +659, -17, +432, +20, -3, +720, -783, -494, -283, }, + { +16567, +9681, +78, +3330, -6088, -2392, -403, +490, +834, -1104, -2339, +2535, +1400, -3259, +240, -1123, -378, -183, -1760, +840, -40, -771, +203, +636, -208, +524, +221, -45, +609, -787, -453, -228, }, + { +15277, +8488, +278, +3885, -5037, -2468, -191, +698, +82, -1238, -1833, +2336, +911, -2516, +590, -1228, -341, -135, -1481, +650, -204, -726, +413, +395, -341, +823, +281, -147, +494, -733, -476, -140, }, + { +14508, +10388, +2390, +614, -9568, -3171, +2824, +3138, -360, -1806, -3125, +365, +1587, -1832, +1166, -336, -464, +624, -617, +527, +1, -567, -177, -592, -325, +573, -499, -34, +977, -258, +261, -46, }, + { +12204, +10145, +3137, +1381, -8141, -4350, +1999, +2595, +500, -763, -2949, -365, +1128, -983, +987, -357, -350, +719, -509, +347, +303, -394, -148, -488, -492, +295, -634, +121, +1110, -75, +436, -20, }, + { +10618, +9407, +3284, +1743, -6720, -3899, +1478, +1813, +596, -356, -2413, -640, +1096, -622, +604, -220, -317, +679, -315, +187, +316, -151, -180, -467, -394, +122, -486, +261, +994, +154, +417, -67, }, + { +8737, +8566, +3830, +1870, -5294, -3295, +668, +1407, +736, -98, -1874, -761, +1017, -457, +347, -65, -252, +598, -129, +108, +374, +35, -238, -389, -303, +101, -170, +351, +816, +261, +307, -90, }, + { +7322, +7990, +4204, +1759, -4233, -2859, +204, +1225, +706, +89, -1428, -779, +812, -371, +310, +13, -255, +576, +48, +68, +394, +93, -230, -185, -105, +97, -62, +325, +596, +234, +208, -140, }, + { +5934, +7313, +4453, +1864, -3250, -2602, -73, +1073, +737, +266, -1100, -695, +649, -339, +267, +163, -183, +531, +195, +93, +388, +271, +42, +55, -87, -115, -13, +235, +333, +198, +167, -56, }, + { +5000, +6676, +4573, +2086, -2672, -2459, -188, +999, +803, +304, -865, -518, +450, -355, +364, +251, -120, +548, +192, +279, +680, +344, +32, +73, -196, -341, +1, +192, +208, +250, +135, -143, }, + { +4456, +6275, +4433, +1984, -2322, -2108, -127, +872, +815, +394, -642, -454, +355, -269, +459, +303, -78, +714, +511, +458, +618, +237, -33, -56, -404, -289, +129, +56, +240, +141, -2, +127, }, + { +3914, +5714, +4351, +2080, -2013, -1928, -97, +902, +872, +413, -420, -323, +306, -177, +447, +601, +422, +769, +448, +347, +442, +17, -200, -32, -412, -202, +177, -120, +89, +302, +138, +79, }, + { +3687, +5529, +4104, +1830, -1878, -1601, +73, +886, +904, +439, -190, -238, +213, +104, +832, +826, +315, +493, +327, +220, +233, -30, -126, -120, -318, -150, -96, +60, +301, +96, +30, +144, }, + { +3580, +5372, +3781, +1521, -1776, -1190, +327, +859, +987, +532, -67, +9, +629, +331, +636, +568, +95, +278, +193, +191, +184, -23, -203, -172, -324, -16, +176, -130, +89, +51, +115, +194, }, + { +3492, +5329, +3471, +1350, -1561, -940, +597, +949, +959, +721, +441, +128, +352, +175, +389, +425, +9, +204, +280, +156, +63, -22, -262, -158, +108, -5, -216, -107, +108, +31, +78, +239, }, + { +3432, +5245, +3183, +1194, -1128, -596, +840, +1170, +1253, +888, +199, -114, +53, +17, +302, +263, +132, +181, +140, +117, +66, -50, -59, +236, -176, -301, -113, -66, -72, -13, +269, +19, }, + { +3390, +5248, +2963, +1119, -596, -247, +1125, +1471, +935, +484, +120, -201, +1, -69, +296, +276, +64, +116, +141, +246, -26, +224, +239, -232, -240, -143, -165, -208, -22, +53, +4, +96, }, + { +3459, +5308, +2829, +1096, -226, +235, +1196, +1070, +638, +314, +33, -149, +26, -184, +259, +284, -110, +166, +359, +85, +180, +500, -207, -255, -125, -253, -294, -112, -3, -243, +82, +267, }, + { +3444, +5608, +3052, +1048, -153, +152, +952, +951, +500, +280, +174, -297, -12, -51, +82, +247, +11, +278, +212, +303, +571, -23, -220, +24, -291, -435, -178, -82, -257, -114, +197, +358, }, + { +3631, +5987, +3171, +840, -377, -261, +737, +1132, +564, +264, +192, -346, -126, +1, +155, +186, +152, +235, +119, +688, +415, -209, -47, -46, -307, -433, -160, -284, -252, +134, +257, +168, }, + { +3555, +6368, +3416, +601, -552, -585, +576, +1098, +540, +380, +255, -412, -120, +84, +90, +257, +276, +51, +139, +958, +303, -229, +70, -105, -327, -291, -183, -450, -241, +244, +389, -20, }, + { +3304, +6445, +3877, +542, -780, -691, +604, +1149, +403, +125, +220, -232, -86, +58, +247, +261, +126, +140, +91, +859, +535, -186, +15, -49, -295, -232, -192, -427, -225, +175, +356, +33, }, + { +3095, +6398, +4320, +718, -1060, -852, +617, +1319, +551, -131, -94, -318, -227, +167, +484, +239, +130, +167, -116, +734, +786, -134, -76, +64, -201, -204, -176, -362, -225, +158, +343, -8, }, + { +3043, +6443, +4566, +841, -1213, -1017, +635, +1471, +644, -187, -192, -362, -461, -72, +479, +445, +148, +181, -13, +421, +845, +87, -217, -18, -79, -55, -112, -366, -227, +211, +309, +49, }, + { +3132, +6650, +4683, +897, -1394, -1203, +748, +1560, +723, -251, -284, -272, -531, -243, +278, +347, +138, +203, +193, +288, +760, +331, -284, -258, -57, +43, +11, -256, -228, +216, +280, +136, }, + { +3367, +6988, +4849, +842, -1674, -1375, +807, +1795, +719, -420, -285, -270, -512, -280, +138, +254, -47, -57, +304, +307, +642, +594, -125, -511, -291, +9, +125, -134, -237, +289, +287, +139, }, + { +3586, +7407, +5080, +656, -1907, -1495, +823, +2057, +683, -633, -291, -228, -494, -258, +64, +194, -36, -286, +173, +318, +479, +623, +210, -460, -540, -128, +140, -101, -240, +315, +252, +163, }, + { +3913, +8036, +5294, +392, -2186, -1751, +902, +2457, +567, -915, -352, -187, -458, -275, +94, +106, -67, -364, +29, +257, +307, +466, +372, -273, -572, -259, +20, -46, -279, +200, +193, +90, }, + { +4032, +8688, +5666, +129, -2445, -1998, +927, +2764, +585, -1205, -445, -136, -465, -297, +145, +30, -86, -398, -75, +234, +196, +315, +365, -111, -602, -339, +4, +2, -305, +120, +111, -149, }, + { +4592, +9563, +5846, -404, -2900, -2105, +1130, +3189, +362, -1558, -421, -109, -448, -336, +218, +21, -218, -346, -123, +178, +175, +130, +357, -19, -545, -388, -95, -2, -115, +87, -12, -264, }, + { +5108, +10372, +5960, -878, -3340, -2212, +1378, +3487, +158, -1928, -397, -33, -497, -372, +269, +15, -353, -346, -123, +153, +121, -36, +345, +17, -453, -394, -151, -80, -129, +237, -86, -306, }, + { +5903, +11205, +5822, -1373, -3825, -2282, +1849, +3676, -196, -2204, -349, +96, -588, -398, +334, +40, -480, -394, -80, +155, +103, -202, +283, +76, -376, -305, -191, -130, -153, +273, -64, -333, }, + { +6463, +12077, +6105, -1736, -4427, -2497, +2172, +3937, -457, -2637, -316, +269, -650, -515, +328, +128, -574, -464, -145, +165, +112, -359, +102, +133, -284, -189, -158, -224, -158, +175, +22, -412, }, + { +6842, +12772, +6370, -1805, -4912, -2802, +2482, +4054, -577, -3009, -381, +505, -711, -614, +320, +143, -632, -456, -212, +124, +133, -429, -89, +129, -159, -163, +4, -302, -109, +98, -9, -351, }, + { +7495, +13436, +6402, -1641, -5389, -3222, +2902, +4096, -819, -3320, -508, +760, -709, -801, +367, +144, -733, -443, -242, +90, +109, -405, -353, -3, +96, -160, +135, -243, -147, +159, -109, -342, }, + { +8302, +13982, +6007, -1545, -5551, -3449, +3272, +3931, -1108, -3384, -659, +902, -637, -984, +381, +171, -829, -414, -266, +67, +40, -321, -455, -318, +312, -35, +128, -72, -215, +189, -55, -445, }, + { +9896, +14670, +4570, -1730, -5262, -3249, +3511, +3414, -1572, -3150, -826, +851, -414, -1191, +310, +159, -852, -430, -228, +45, -97, -288, -414, -643, +337, +260, +1, +181, -141, +21, +120, -543, }, + { +11175, +14744, +3448, -993, -4976, -3811, +3696, +3441, -2038, -3160, -852, +834, -325, -1343, +248, +102, -877, -401, -118, -49, -199, -292, -342, -788, +115, +605, -66, +267, +58, -136, +206, -540, }, + { +12433, +14644, +2289, -11, -4664, -4428, +3847, +3503, -2546, -3196, -721, +719, -321, -1413, +201, -27, -944, -298, +67, -214, -293, -242, -375, -807, -141, +822, +24, +219, +276, -175, +133, -469, }, + { +14253, +15077, +91, +194, -3679, -4227, +3554, +3173, -2873, -3182, -505, +510, -236, -1531, +106, -141, -941, -242, +293, -223, -457, -200, -494, -638, -319, +772, +318, +229, +335, -59, -21, -432, }, + { +16147, +15220, -1962, +114, -2901, -3609, +3134, +2739, -3044, -3270, -314, +358, -218, -1704, +85, -332, -895, -157, +414, -203, -648, -134, -650, -486, -341, +596, +483, +347, +229, -1, -55, -568, }, + { +17862, +15961, -4207, -422, -1951, -2655, +2484, +2264, -2914, -3618, -117, +208, -161, -1924, +25, -473, -800, -35, +478, -79, -937, -17, -769, -367, -225, +425, +413, +582, +113, -133, +87, -734, }, + { +19449, +16392, -5859, -1112, -1427, -1629, +1942, +1918, -2765, -3978, -8, +84, -134, -2062, -13, -566, -654, +37, +477, +32, -1157, +83, -816, -331, +13, +340, +96, +768, +67, -341, +213, -694, }, + { +21050, +17015, -7348, -2198, -1140, -508, +1499, +1662, -2610, -4451, +6, -138, -34, -2121, -29, -596, -503, +48, +407, +189, -1351, +208, -923, -200, +286, +156, -272, +696, +143, -514, +278, -464, }, + { +22180, +17691, -8318, -3153, -1264, +222, +1444, +1546, -2552, -4822, -136, -360, +172, -2054, -32, -523, -483, -7, +341, +375, -1419, +217, -913, -27, +273, +51, -545, +431, +314, -476, +252, -284, }, + { +23145, +18305, -8899, -3912, -1872, +754, +1676, +1457, -2542, -5140, -436, -571, +593, -1943, -18, -391, -590, -105, +360, +600, -1486, +265, -834, -64, +157, -1, -808, +207, +535, -343, +188, -148, }, + { +24201, +18791, -9169, -4533, -3193, +1197, +2201, +1353, -2588, -5477, -898, -646, +1316, -1907, +112, -361, -830, -56, +459, +703, -1301, +324, -1128, -106, +17, -169, -828, +111, +625, -123, +274, -146, }, + { +25039, +18854, -8960, -4922, -4675, +1488, +2597, +1309, -2785, -5629, -1226, -537, +1894, -1872, +177, -569, -818, +47, +414, +930, -1141, -66, -1306, -147, -197, -117, -643, -26, +588, +205, +269, -377, }, + { +25483, +18997, -8159, -5303, -6464, +1415, +3101, +1247, -3048, -5328, -1549, -448, +2627, -1908, -26, -525, -721, +144, +628, +830, -1334, -263, -1576, -203, -94, -5, -521, -41, +604, +221, +328, -313, }, + { +25310, +19394, -7240, -5364, -8082, +861, +3602, +1021, -2853, -4841, -1925, -274, +3034, -1996, -184, -315, -585, +480, +190, +564, -1199, -604, -1602, -1, -64, +5, -267, -39, +307, +300, +702, -524, }, + { +25187, +19126, -5747, -5306, -10048, +5, +3816, +1259, -2439, -4328, -2144, -285, +3285, -2132, -193, +313, -577, +11, -100, +308, -1123, -638, -1463, +80, -40, +174, -302, -152, +368, +273, +817, -512, }, + { +24584, +19059, -4370, -5103, -11540, -1206, +4149, +1948, -2239, -3792, -2260, -463, +3232, -1933, +310, +264, -1049, -39, -461, +305, -807, -683, -1310, +216, -69, -19, -134, -7, +199, +192, +1012, -495, }, + { +24229, +18498, -3219, -4673, -13052, -2239, +4802, +2529, -2152, -3029, -2527, -945, +3759, -1608, +81, +180, -1445, -83, -369, +279, -585, -589, -996, -15, -262, +176, +17, -164, +214, +234, +1010, -493, }, + { +23479, +17761, -2169, -4054, -14542, -2764, +5166, +3024, -1593, -2674, -3100, -489, +3855, -1932, +199, -475, -1408, +376, -646, +385, -160, -507, -1201, -125, -73, +89, +2, -134, +277, +184, +889, -456, }, + { +22902, +16611, -1241, -3921, -14659, -3126, +5151, +3820, -1738, -2209, -3026, -680, +3843, -2076, -205, -376, -1050, +290, -561, +675, -171, -734, -967, -54, -279, +107, +127, -143, +289, +105, +906, -440, }, + { +21520, +15491, -466, -3170, -14169, -3623, +5060, +3934, -933, -1908, -3770, -160, +3317, -2389, +45, -293, -930, +570, -302, +294, -117, -531, -816, -259, -393, +309, +177, -338, +355, +325, +623, -455, }, + { +20196, +14258, -26, -2087, -13378, -3959, +4522, +4579, -753, -2187, -3650, -282, +3062, -2403, +220, -314, -596, +900, -834, +333, +193, -640, -766, -353, -377, +474, -67, -401, +722, +187, +352, -286, }, + { +18562, +12495, +606, -395, -12713, -4067, +4718, +3983, -631, -1865, -4106, +267, +2676, -2530, +544, -90, -270, +517, -885, +629, +178, -816, -539, -358, -375, +420, -176, -225, +814, +58, +205, -176, }, + { +16507, +11335, +1670, +425, -11680, -3415, +3753, +3482, -140, -2175, -3676, +452, +2142, -2340, +899, +31, -525, +540, -719, +620, +74, -768, -288, -458, -444, +527, -237, -258, +969, -83, +155, -100, }, + { +15749, +11088, +540, -1465, -10587, +5, +4768, +1482, -1271, -1294, -2408, -204, +2109, -2180, +700, +142, -524, +394, -343, +524, -78, -510, -510, -119, -176, +83, +45, -8, -121, -132, +846, -57, }, + { +12780, +11168, +3101, -713, -10375, -2419, +3165, +2486, +741, -926, -2637, -901, +1193, -1380, +1171, +201, -695, +683, -287, +203, +326, -265, -402, +216, -760, -316, +56, -240, +385, +349, +800, +38, }, + { +10481, +10174, +3731, +12, -8189, -2627, +2287, +2162, +639, -620, -1980, -947, +827, -877, +947, +162, -591, +622, -179, +124, +414, -151, -214, +218, -687, -257, +155, -179, +273, +413, +653, +58, }, + { +8668, +9236, +4001, +664, -6447, -2669, +1688, +1709, +604, -245, -1509, -943, +653, -570, +711, +170, -456, +560, -83, +116, +460, +30, -144, +259, -390, -222, +102, -244, +172, +370, +519, +108, }, + { +7283, +8384, +4111, +1067, -5156, -2491, +1257, +1309, +661, +29, -1138, -850, +514, -376, +522, +237, -336, +496, +41, +153, +499, +232, +59, +319, -295, -329, -17, -275, -28, +319, +573, +102, }, + { +6272, +7691, +4060, +1222, -4187, -2162, +939, +1051, +723, +183, -806, -720, +436, -270, +454, +290, -208, +496, +100, +362, +696, +335, +52, +222, -281, -423, -148, -305, -83, +332, +591, +108, }, + { +5580, +7158, +3875, +1174, -3460, -1834, +755, +904, +761, +294, -524, -552, +331, -184, +477, +333, -134, +601, +379, +449, +583, +227, -20, +147, -340, -469, -157, -333, -172, +385, +542, +14, }, + { +5099, +6769, +3611, +1012, -2865, -1522, +671, +853, +823, +385, -298, -392, +284, -75, +496, +488, +66, +592, +366, +331, +409, +167, -75, +79, -239, -485, -266, -327, -145, +317, +622, +200, }, + { +4774, +6505, +3318, +801, -2367, -1254, +708, +915, +878, +465, -124, -221, +265, +118, +634, +457, -4, +374, +332, +279, +241, +173, -17, +50, -250, -474, -271, -392, +13, +518, +389, +79, }, + { +4533, +6373, +3052, +547, -1931, -979, +831, +1054, +874, +581, -34, -52, +431, +69, +479, +349, -93, +183, +314, +308, +170, +203, +2, -44, -122, -409, -440, -16, +135, +84, +235, +185, }, + { +4382, +6335, +2815, +384, -1572, -712, +951, +1234, +846, +634, +201, -204, +344, +11, +311, +320, -174, +46, +261, +434, +123, +92, +101, +62, -245, -261, -49, -321, -116, +108, +125, +189, }, + { +4310, +6316, +2697, +377, -1265, -560, +1000, +1283, +976, +592, +177, -342, +94, +124, +234, +277, -211, -42, +322, +339, +136, +155, +187, -53, -7, +128, -468, -481, -77, +89, +78, +112, }, + { +4220, +6370, +2803, +455, -1043, -593, +884, +1257, +898, +647, +131, -407, +17, -24, +259, +268, -143, -71, +248, +315, +54, +318, +56, +134, +452, -358, -580, -280, -245, +17, +207, +32, }, + { +4238, +6578, +2947, +418, -979, -561, +705, +1063, +778, +509, +327, -311, -141, -2, +117, +252, +15, +34, +169, +147, +205, +145, +62, +724, +153, -621, -342, -314, -253, +63, +216, -198, }, + { +4312, +6878, +3119, +203, -1024, -566, +664, +1052, +477, +317, +296, -326, +41, -20, +78, +256, -64, +129, +238, +109, +104, +11, +281, +812, -119, -527, -289, -348, -126, +151, +43, -331, }, + { +4243, +7275, +3432, -18, -1154, -711, +722, +1244, +373, +111, +88, -498, -48, +145, +283, +267, -58, +72, +167, +174, +186, -87, +222, +896, -239, -530, -150, -376, +16, +248, -104, -319, }, + { +4240, +7629, +3820, -212, -1418, -875, +841, +1446, +395, +35, -59, -651, -337, +57, +348, +394, +138, +74, +125, +94, +212, -51, +148, +896, -296, -557, -109, -348, +139, +310, -89, -337, }, + { +4267, +8079, +4161, -358, -1708, -1137, +1013, +1624, +419, -13, -148, -667, -486, -153, +260, +306, +109, +213, +291, +100, +122, +47, -84, +791, +19, -750, -253, -252, +190, +325, -24, -265, }, + { +4428, +8579, +4533, -649, -2161, -1157, +1152, +1818, +471, -285, -99, -625, -629, -167, +110, +208, -67, +58, +397, +149, +182, +241, -312, +521, +338, -764, -423, -220, +99, +352, +41, -277, }, + { +4663, +9147, +5017, -957, -2713, -1191, +1270, +2062, +456, -561, -64, -490, -728, -151, +48, +95, -93, -149, +333, +236, +4, +377, -80, +107, +501, -359, -693, -415, +113, +346, +3, -240, }, + { +4794, +9798, +5715, -1328, -3293, -1331, +1372, +2391, +376, -861, -165, -318, -751, -218, +113, -43, -176, -257, +208, +326, -101, +123, +104, -60, +376, +187, -610, -661, -80, +210, +60, -231, }, + { +4706, +10404, +6857, -1587, -4043, -1501, +1416, +2768, +428, -1274, -318, -83, -773, -321, +190, -68, -276, -333, +89, +301, +70, -126, -66, -7, +48, +426, +52, -740, -438, +212, -62, -314, }, + { +5492, +11533, +6802, -2410, -4639, -1493, +1942, +3028, +51, -1662, -327, +15, -748, -355, +194, -55, -404, -364, +59, +221, +123, -131, -285, -124, -44, +334, +429, -330, -642, -6, -1, -451, }, + { +5983, +12434, +7309, -2945, -5475, -1428, +2301, +3203, -66, -2148, -439, +316, -869, -393, +284, -166, -434, -371, -45, +257, +65, -121, -270, -467, -42, +274, +467, +199, -506, -380, +16, -415, }, + { +6553, +13345, +7759, -3488, -6279, -1393, +2735, +3371, -353, -2547, -584, +610, -877, -578, +354, -136, -577, -381, +10, +115, +137, -230, -229, -570, -338, +370, +460, +362, -28, -508, -363, -242, }, + { +7471, +14327, +7633, -4021, -6946, -1337, +3324, +3391, -814, -2875, -657, +809, -860, -751, +399, -191, -586, -460, +66, +94, +17, -214, -308, -558, -507, +258, +589, +343, +231, -149, -748, -423, }, + { +8279, +15118, +7553, -4198, -7540, -1469, +3893, +3301, -1178, -3117, -875, +1005, -790, -1000, +413, -197, -660, -436, +72, +94, -104, -258, -361, -556, -505, +12, +646, +436, +181, +263, -660, -907, }, + { +9124, +15874, +7171, -4175, -7769, -1782, +4384, +3236, -1562, -3240, -1066, +1125, -708, -1207, +337, -154, -693, -392, +105, +118, -192, -387, -322, -628, -341, -94, +398, +659, +143, +329, -199, -1184, }, + { +10600, +16582, +5950, -4368, -7541, -1867, +4737, +3086, -2067, -3335, -1044, +1123, -740, -1306, +237, -176, -627, -343, +172, +164, -330, -475, -330, -675, -210, +76, +40, +658, +284, +168, +174, -1087, }, + { +12376, +17175, +4171, -4522, -7036, -1733, +4785, +2866, -2442, -3529, -868, +972, -750, -1432, +142, -233, -536, -280, +203, +260, -566, -441, -421, -666, -125, +321, -234, +424, +421, +32, +318, -810, }, + { +14090, +17881, +2149, -4729, -6335, -1510, +4556, +2689, -2622, -3826, -676, +683, -672, -1544, +43, -207, -493, -179, +161, +384, -803, -320, -525, -640, -11, +437, -358, +48, +480, -16, +337, -499, }, + { +16479, +18100, -251, -5038, -5617, -895, +4158, +2494, -2872, -4087, -506, +369, -589, -1544, +53, -264, -351, -200, +196, +385, -925, -184, -637, -545, +96, +400, -519, -169, +313, +46, +383, -236, }, + { +18719, +18354, -2535, -5382, -5077, -235, +3760, +2374, -3095, -4427, -427, +22, -354, -1411, +7, -243, -275, -312, +263, +406, -941, -99, -688, -428, +90, +202, -566, -359, +122, +172, +472, -78, }, + { +20632, +18711, -4603, -5686, -4881, +473, +3484, +2242, -3308, -4836, -393, -315, +167, -1349, -10, -178, -419, -353, +354, +511, -957, -1, -751, -448, -19, +72, -604, -458, +57, +275, +400, +168, }, + { +22360, +19050, -6299, -6014, -5124, +1226, +3414, +2007, -3540, -5278, -311, -473, +850, -1407, +137, -306, -606, -202, +424, +682, -953, +71, -1092, -494, -36, -56, -597, -263, -49, +181, +480, +340, }, + { +23853, +19254, -7494, -6434, -5724, +1855, +3501, +1649, -3877, -5514, -94, -559, +1541, -1479, +150, -510, -585, -3, +419, +893, -1016, -259, -1392, -389, -165, +23, -418, -178, -252, +244, +451, +239, }, + { +25329, +18892, -8176, -6707, -6565, +2362, +3636, +1174, -4256, -5023, -220, -362, +2101, -1684, +93, -508, -345, +44, +530, +734, -1282, -474, -1490, -397, +61, +190, -464, -83, -222, +71, +299, +421, }, + { +26132, +18615, -8557, -6606, -7720, +2662, +3828, +564, -4070, -4493, -377, -142, +2382, -2009, +159, -193, -269, +57, +124, +489, -1273, -596, -1558, -23, +257, +40, -292, +5, -420, -52, +361, +377, }, + { +26097, +18760, -8279, -6563, -9338, +2395, +4427, +354, -3683, -4052, -417, -411, +2686, -2127, +394, +208, -612, -267, -183, +547, -1260, -477, -1400, +110, +235, +53, -183, -98, -337, -206, +404, +296, }, + { +26298, +17883, -7539, -6429, -11135, +2349, +5208, +297, -3531, -3208, -932, -479, +3059, -2027, +586, +52, -1210, -243, -201, +410, -847, -361, -1398, +182, +205, +3, -91, -95, -338, -279, +476, +239, }, + { +25890, +16998, -6703, -6158, -12691, +2616, +5823, +131, -2985, -2746, -1498, -267, +3567, -2288, +598, -395, -1379, +90, -420, +614, -490, -504, -1389, +217, +123, -23, -40, -116, -280, -344, +578, +55, }, + { +24947, +16037, -5788, -5689, -13461, +2608, +6096, +412, -2813, -2278, -1596, -192, +3523, -2540, +252, -341, -1212, +134, -205, +703, -394, -619, -1225, +253, -81, +26, +22, -90, -211, -394, +628, +37, }, + { +23244, +14939, -4628, -4799, -13452, +2196, +6207, +481, -2338, -1733, -1999, -166, +3296, -2924, +380, -214, -1290, +655, -260, +536, -282, -573, -1003, -3, -66, +82, +42, -31, -237, -351, +712, -93, }, + { +21175, +13481, -2942, -3774, -12857, +1570, +5657, +1162, -1877, -1714, -2063, -297, +2743, -2620, +466, -249, -749, +711, -463, +451, -19, -579, -1025, +20, -106, +79, +79, -61, -143, -304, +645, -60, }, + { +18636, +12183, -1209, -2590, -11846, +582, +5371, +1609, -1826, -1340, -2309, -463, +2623, -2468, +485, +42, -543, +537, -540, +593, +20, -722, -705, -39, -175, +121, +49, +1, -116, -325, +749, +25, }, + { +14783, +9489, -2982, -1023, -3993, +3280, +1929, -1888, -1700, +251, +285, -164, +1356, -1971, +151, -32, -982, +252, -342, +144, -27, -417, -521, +840, -3, +115, +200, -545, +44, -370, -154, +120, }, + { +12635, +9338, -1709, -1092, -3762, +2615, +2298, -1249, -1537, +64, +39, -164, +1209, -1626, +74, +120, -732, +178, -175, +161, -27, -373, -563, +683, +104, +109, +334, -384, -11, -279, -152, +39, }, + { +10824, +8969, -613, -1061, -3373, +2063, +2390, -782, -1282, -126, -128, -73, +1005, -1268, +42, +226, -527, +136, -29, +165, -13, -304, -520, +542, +230, +129, +323, -272, -56, -204, -196, -8, }, + { +9429, +8572, +94, -1038, -2958, +1688, +2332, -399, -1026, -257, -166, -8, +812, -970, +100, +281, -355, +129, +93, +200, -9, -181, -418, +422, +262, +108, +261, -198, -85, -190, -163, -35, }, + { +8397, +8228, +487, -1075, -2536, +1429, +2193, -104, -809, -286, -144, +0, +671, -693, +170, +287, -214, +140, +163, +259, +27, -77, -357, +258, +245, +93, +144, -149, -82, -138, -162, -157, }, + { +7612, +7949, +682, -1088, -2127, +1227, +2044, +150, -627, -240, -104, +9, +638, -490, +230, +309, -107, +161, +239, +304, +6, -19, -321, +91, +206, +63, +62, -59, -63, -155, -170, -131, }, + { +6952, +7827, +766, -1099, -1787, +1048, +1937, +367, -509, -160, +31, -46, +675, -344, +225, +366, -39, +163, +218, +347, -43, -35, -270, -73, +151, +125, +10, -31, -65, -114, -144, -245, }, + { +6683, +7602, +752, -1154, -1469, +1022, +1800, +518, -463, -59, +237, -126, +665, -182, +167, +400, +1, +65, +208, +350, -66, -128, -244, -86, +66, +169, -26, -43, +21, -177, -95, +91, }, + { +6328, +7603, +798, -1168, -1193, +1003, +1709, +558, -362, -97, +468, -68, +448, +37, +143, +230, +82, +36, +95, +341, -19, -201, -272, +32, -38, +101, +81, -48, -56, +78, +109, -161, }, + { +6163, +7555, +882, -1153, -951, +1060, +1636, +512, -380, -40, +469, +81, +403, -73, +284, +126, -61, +146, +60, +209, +16, -67, -335, -42, +24, +86, +40, -88, +270, +181, -136, -94, }, + { +6061, +7646, +945, -1152, -751, +1127, +1592, +462, -481, -102, +513, +8, +479, -8, +120, +232, -121, -17, +188, +182, -85, +28, -226, -217, -8, +203, -144, +134, +478, -84, -37, -109, }, + { +6159, +7784, +897, -1252, -582, +1290, +1597, +367, -567, -244, +423, +7, +359, +71, +248, +105, -133, +44, +12, +207, +16, -67, -239, -155, +10, -42, -17, +448, +145, -81, +130, -268, }, + { +6295, +8059, +874, -1507, -476, +1456, +1680, +362, -767, -349, +394, -179, +254, +82, +233, +237, -141, -70, +113, +103, -91, +65, -321, -165, +80, -154, +82, +449, -28, -5, +49, -241, }, + { +6464, +8458, +884, -1785, -437, +1608, +1780, +391, -878, -494, +409, -232, +50, -6, +273, +208, -116, +68, +65, +99, -84, -65, -354, -36, +94, -209, +217, +427, -208, -3, +52, -229, }, + { +6683, +8970, +878, -2165, -376, +1753, +1869, +420, -1047, -513, +453, -325, -68, -78, +177, +166, -163, +34, +201, +156, -170, -62, -410, -135, +213, -121, +156, +486, -231, -110, -45, -202, }, + { +7075, +9466, +776, -2530, -318, +1953, +1897, +349, -1235, -528, +610, -389, -209, -190, +180, +87, -332, -2, +206, +167, -129, -48, -464, -135, +204, -153, +111, +606, -167, -304, -61, -230, }, + { +7586, +10041, +588, -2915, -206, +2202, +1934, +234, -1428, -528, +819, -402, -344, -230, +142, +32, -392, -123, +176, +151, -193, +34, -366, -138, +241, -59, -155, +594, +203, -426, -180, -174, }, + { +8284, +10637, +233, -3349, -82, +2488, +1888, +4, -1707, -479, +1011, -436, -518, -321, +180, -80, -546, -130, +97, -3, -257, -57, -259, -35, +170, +42, -299, +295, +418, -294, -381, -148, }, + { +9095, +11203, -132, -3724, +50, +2818, +1819, -242, -2019, -371, +1236, -443, -675, -423, +213, -107, -683, -188, +152, -197, -308, -122, -264, +206, +142, +61, -245, -25, +422, +25, -540, -195, }, + { +9889, +11877, -498, -4104, +128, +3146, +1756, -530, -2290, -350, +1519, -489, -802, -515, +198, -156, -758, -285, +204, -324, -438, -107, -349, +396, +198, +14, -193, -173, +148, +358, -427, -465, }, + { +10582, +12562, -764, -4376, +91, +3406, +1737, -827, -2467, -415, +1769, -548, -939, -576, +195, -234, -826, -350, +190, -335, -585, -69, -435, +512, +288, -37, -200, -225, -129, +406, -34, -688, }, + { +11306, +13237, -1039, -4536, -26, +3591, +1718, -1098, -2614, -469, +1956, -571, -1083, -632, +173, -288, -912, -416, +136, -318, -675, -9, -461, +518, +395, -47, -243, -278, -292, +231, +360, -602, }, + { +12126, +13732, -1324, -4530, -169, +3644, +1688, -1289, -2721, -525, +2075, -627, -1199, -662, +140, -328, -973, -491, +59, -292, -671, -19, -361, +462, +431, -38, -247, -401, -359, +38, +478, -239, }, + { +13174, +14048, -1834, -4351, -231, +3530, +1631, -1424, -2802, -592, +2109, -667, -1235, -700, +112, -376, -980, -597, +19, -296, -588, -73, -240, +447, +337, +12, -264, -517, -447, +2, +295, +152, }, + { +13912, +14689, -2549, -4087, -276, +3213, +1690, -1511, -2845, -778, +2173, -760, -1162, -780, +32, -364, -1053, -628, -102, -130, -603, -57, -165, +414, +214, +87, -300, -593, -470, -60, +82, +348, }, + { +15735, +14399, -3901, -3481, -192, +2964, +1469, -1601, -3017, -661, +2045, -910, -920, -980, +113, -561, -1039, -685, -48, -104, -573, +16, -218, +402, +105, +138, -407, -536, -465, -206, -44, +459, }, + { +17507, +14116, -5426, -2751, -192, +2764, +1237, -1736, -3152, -642, +2056, -1032, -638, -1128, +153, -726, -1046, -569, -51, -121, -478, +1, -396, +547, -27, +111, -333, -501, -516, -340, +19, +317, }, + { +19177, +13698, -6887, -1985, -243, +2671, +917, -1892, -3302, -557, +2196, -1213, -249, -1266, +144, -850, -933, -377, -271, +44, -521, -211, -328, +623, -195, +277, -239, -659, -459, -299, -118, +155, }, + { +20876, +12896, -8162, -1221, -405, +2653, +504, -2083, -3425, -193, +2142, -1312, +212, -1534, +162, -820, -758, -426, -316, +25, -728, -166, -380, +694, -108, +401, -373, -670, -277, -461, -291, +192, }, + { +22269, +11988, -9087, -514, -824, +2741, +141, -2324, -3367, +226, +1930, -1209, +579, -1900, +451, -737, -828, -320, -621, -37, -648, -370, -319, +1024, -166, +363, -328, -614, -348, -565, -333, +183, }, + { +22436, +12036, -9756, +12, -1484, +2839, +192, -2651, -3134, +304, +2024, -1272, +960, -2016, +603, -606, -966, -471, -868, +179, -932, -258, -187, +1149, -274, +472, -264, -806, -269, -673, -333, +115, }, + { +22976, +10737, -9315, +116, -2333, +3251, +276, -2916, -2945, +644, +1634, -1050, +1433, -2250, +799, -568, -1370, -360, -902, +24, -701, -266, -133, +1143, -162, +431, -320, -856, -257, -656, -421, +206, }, + { +22156, +10670, -8825, +31, -3255, +3766, +558, -3195, -2534, +719, +1180, -629, +1637, -2399, +832, -768, -1413, -237, -975, +241, -538, -329, -188, +1315, -228, +362, -275, -900, -124, -757, -357, +287, }, + { +21278, +9846, -7554, -398, -3803, +4159, +764, -3114, -2412, +808, +1003, -456, +1754, -2523, +558, -585, -1493, -124, -765, +323, -452, -399, -154, +1291, -276, +265, -134, -876, -130, -701, -238, +296, }, + { +19461, +9711, -6136, -768, -4103, +4127, +1289, -3000, -2181, +873, +525, -145, +1756, -2618, +563, -484, -1372, +57, -547, +215, -362, -384, -264, +1161, -298, +275, -17, -893, -58, -554, -229, +265, }, + { +17277, +9611, -4621, -998, -4208, +3875, +1658, -2589, -1841, +534, +392, -46, +1442, -2298, +316, -236, -1201, +192, -408, +85, -140, -396, -437, +993, -150, +202, +81, -782, +50, -474, -177, +231, }, + { +14172, +9673, -3573, -888, -1759, +2079, +423, -1986, -1744, +555, +790, -69, +1145, -1353, +372, +59, -818, -47, -210, +85, -314, -228, -308, +651, -194, +55, +187, -514, -187, -359, -318, -25, }, + { +12049, +9447, -2228, -1231, -1752, +1946, +840, -1465, -1503, +155, +682, -24, +949, -1008, +142, +192, -555, +85, -44, +97, -161, -260, -418, +506, -91, +11, +275, -348, -145, -288, -275, -84, }, + { +10303, +9142, -1241, -1401, -1603, +1679, +1080, -926, -1326, -100, +605, -40, +855, -692, +34, +292, -275, +96, +72, +212, -127, -202, -437, +334, -5, -31, +233, -223, -120, -232, -200, -174, }, + { +9017, +8838, -619, -1486, -1392, +1440, +1205, -522, -1133, -209, +558, -14, +754, -391, +50, +313, -71, +90, +164, +296, -77, -139, -435, +174, +4, -91, +133, -78, -72, -176, -176, -213, }, + { +8142, +8597, -267, -1538, -1157, +1261, +1254, -258, -963, -244, +541, +75, +679, -187, +160, +270, +1, +140, +187, +302, -55, -83, -444, +4, -35, -132, +106, +52, -76, -189, -49, -190, }, + { +7622, +8383, -48, -1578, -899, +1181, +1234, -71, -944, -214, +551, +137, +707, -144, +260, +320, -94, +109, +245, +239, -123, -2, -426, -196, -30, -52, +38, -6, +17, -57, -187, -8, }, + { +7332, +8319, +57, -1601, -631, +1238, +1135, +21, -967, -327, +659, +76, +672, +38, +199, +349, -41, -97, +208, +331, -233, -127, -295, -155, -164, -26, -20, -11, +90, -178, +192, +245, }, + { +7317, +8418, -7, -1642, -367, +1419, +1147, -111, -974, -384, +550, +84, +565, +94, +364, +313, -68, -10, +86, +197, -143, -149, -385, -44, -17, -229, -53, +121, -114, +140, +435, -168, }, + { +7367, +8733, -36, -1826, -124, +1616, +1200, -184, -1158, -452, +570, -113, +408, +131, +319, +450, -22, -49, +185, +112, -380, -100, -266, -117, -72, -71, +10, -213, +51, +444, +58, -226, }, + { +7780, +9068, -243, -2044, +77, +1858, +1206, -274, -1289, -551, +596, -178, +200, -29, +344, +432, -90, +103, +283, +41, -414, -210, -414, +8, +0, -140, +15, -152, +111, +292, -78, -206, }, + { +8345, +9580, -574, -2318, +346, +2040, +1168, -408, -1458, -527, +676, -258, +1, -124, +320, +310, -183, +87, +370, +84, -415, -236, -480, -21, -74, -20, +45, -230, +106, +351, -132, -434, }, + { +9094, +10154, -1025, -2554, +632, +2229, +994, -595, -1621, -437, +874, -416, -132, -257, +352, +267, -420, +47, +366, +3, -398, -143, -510, +98, -129, -195, +92, -138, -60, +248, +111, -492, }, + { +10041, +10768, -1614, -2719, +916, +2381, +756, -898, -1763, -304, +1151, -599, -325, -310, +358, +208, -536, -91, +282, -46, -529, -37, -456, +197, -31, -321, -13, -176, -156, +116, +291, -363, }, + { +11107, +11413, -2234, -2770, +1108, +2450, +521, -1208, -1885, -154, +1431, -759, -498, -367, +419, +97, -627, -128, +121, -90, -589, -28, -356, +273, +33, -279, -81, -291, -313, -16, +331, +7, }, + { +12280, +12021, -2906, -2717, +1134, +2377, +314, -1466, -2001, -37, +1641, -947, -595, -443, +445, +1, -745, -215, +22, -112, -652, -10, -293, +280, +22, -180, -159, -383, -467, -188, +208, +307, }, + { +13475, +12606, -3625, -2517, +1014, +2161, +183, -1625, -2084, +8, +1791, -1103, -612, -507, +435, -57, -847, -324, -41, -71, -678, +57, -282, +263, -37, -62, -209, -455, -522, -398, +113, +417, }, + { +14812, +13011, -4511, -2154, +806, +1834, +70, -1725, -2175, +16, +1867, -1208, -532, -590, +417, -130, -929, -396, -58, -66, -662, +121, -368, +291, -144, -1, -180, -513, -541, -616, +106, +375, }, + { +16380, +13067, -5650, -1592, +605, +1495, -131, -1780, -2290, +40, +2010, -1367, -305, -662, +396, -248, -905, -354, -222, +46, -645, +6, -354, +338, -292, +124, -80, -658, -520, -638, -12, +271, }, + { +17982, +12702, -6846, -878, +421, +1177, -463, -1808, -2473, +274, +2108, -1572, +121, -826, +355, -267, -731, -513, -308, +176, -908, +60, -376, +313, -223, +268, -136, -780, -340, -699, -278, +268, }, + { +19157, +12247, -7860, -132, +149, +912, -724, -1957, -2452, +536, +2076, -1557, +521, -1124, +525, -122, -855, -442, -535, +76, -863, -69, -379, +601, -217, +257, -79, -739, -389, -790, -342, +200, }, + { +19711, +11522, -8169, +401, -329, +905, -928, -2048, -2370, +752, +2017, -1467, +767, -1174, +691, -185, -760, -679, -713, +177, -1039, -25, -158, +692, -291, +423, -59, -878, -408, -762, -397, +110, }, + { +19465, +10779, -7703, +460, -893, +1187, -817, -2234, -2246, +1019, +1640, -1190, +1218, -1460, +857, -119, -1111, -511, -817, +81, -770, -57, -137, +805, -201, +370, -136, -863, -389, -713, -437, +67, }, + { +18333, +10203, -6584, +85, -1382, +1684, -563, -2346, -2049, +928, +1357, -671, +1226, -1413, +733, -229, -1009, -521, -666, +153, -618, -188, -102, +925, -315, +267, -79, -795, -334, -687, -385, +130, }, + { +16441, +9946, -5138, -473, -1669, +2005, -91, -2252, -1996, +837, +1093, -412, +1392, -1542, +535, -11, -1080, -203, -475, +134, -478, -260, -105, +753, -285, +165, +32, -670, -301, -515, -295, +22, }, + { +13089, +9529, -4017, -237, +483, +1183, -334, -1490, -1552, +647, +1264, -471, +629, -605, +562, -36, -643, -387, -501, +84, -371, -60, -86, +640, -100, +137, -1, -574, -350, -440, -324, -139, }, + { +11898, +9621, -3405, -1165, +243, +1679, +179, -1312, -1709, +64, +1140, -239, +692, -458, +479, +250, -561, -188, -174, +41, -320, -165, -304, +589, -101, -43, +124, -435, -309, -354, -282, -103, }, + { +10997, +9658, -2780, -1802, -26, +1783, +569, -947, -1662, -248, +967, -127, +729, -294, +466, +390, -354, -145, +46, +95, -329, -112, -479, +387, +17, -149, +90, -226, -277, -280, -149, -147, }, + { +10403, +9825, -2433, -2099, -53, +1814, +738, -808, -1574, -372, +873, -167, +701, -213, +490, +502, -294, -53, +103, +45, -399, -57, -537, +124, +113, -120, +21, -185, -188, -248, -114, -74, }, + { +10392, +9882, -2497, -2112, +324, +1944, +702, -937, -1618, -322, +898, -262, +449, -113, +460, +482, -188, -10, +186, -20, -556, -166, -451, +104, +9, -20, +13, -185, -148, -338, -76, +107, }, + { +10862, +9834, -2867, -1780, +884, +2063, +422, -1170, -1644, -165, +979, -389, +198, -268, +566, +314, -335, +218, +172, -127, -560, -306, -505, +230, +61, -85, -17, -114, -234, -393, -69, +141, }, + { +11578, +9958, -3241, -1267, +1305, +1835, +61, -1353, -1523, +172, +1158, -610, -59, -256, +501, +100, -434, +87, +156, -173, -554, -216, -452, +238, -8, +72, -57, -233, -279, -439, -54, +128, }, + { +12590, +10282, -3669, -831, +1213, +1299, -204, -1324, -1305, +642, +1310, -929, -150, -265, +378, -166, -511, -226, -25, -77, -591, -30, -316, +195, -77, +192, -79, -444, -281, -501, -173, +158, }, + { +13820, +10820, -4370, -641, +688, +719, -208, -1170, -1120, +1054, +1422, -1268, +60, -395, +126, -158, -785, -547, -55, -15, -622, +139, -270, +187, +11, +211, -203, -518, -250, -711, -272, +251, }, + { +14973, +10840, -5035, -319, +215, +327, -180, -1056, -1001, +1357, +1436, -1376, +373, -623, +75, -133, -1012, -696, -148, +89, -603, +166, -149, +257, +111, +201, -249, -478, -375, -806, -271, +241, }, + { +15155, +10582, -5238, +16, +115, +211, -361, -1141, -1066, +1374, +1522, -1325, +512, -561, +33, -137, -860, -968, -261, +174, -757, +281, -16, +297, +93, +272, -236, -631, -384, -770, -341, +145, }, + { +14475, +9926, -4814, +144, +323, +523, -528, -1336, -1319, +1198, +1433, -1025, +675, -646, +308, -70, -834, -739, -508, +86, -551, +161, +29, +400, +61, +248, -225, -631, -389, -587, -390, -22, }, + { +11096, +9607, -2841, -481, +1236, +612, -162, -1033, -885, +1058, +1197, -645, +47, -150, +202, +5, -433, -652, -254, -16, -605, +40, +33, +357, +315, +288, -273, -769, -633, -453, +2, +259, }, + }, + + /* HRIR Delays */ + { 12, 12, 13, 14, 14, 14, 13, 12, 11, 10, 10, 10, 11, 12, 13, 14, 15, 15, 16, 16, 16, 15, 15, 14, 13, 12, 11, 10, 9, 8, 8, 8, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 17, 18, 18, 18, 18, 18, 17, 16, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 7, 6, 6, 6, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 18, 19, 20, 20, 20, 20, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 8, 7, 6, 5, 5, 5, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 21, 22, 22, 22, 22, 22, 21, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23, 24, 24, 24, 24, 24, 23, 22, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 24, 25, 26, 26, 26, 26, 26, 25, 24, 24, 23, 22, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 28, 28, 27, 26, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 28, 28, 27, 26, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 24, 25, 26, 26, 26, 26, 26, 25, 24, 24, 23, 22, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23, 24, 24, 24, 24, 24, 23, 22, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 21, 22, 22, 22, 22, 22, 21, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 18, 19, 20, 20, 20, 20, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 8, 7, 6, 5, 5, 5, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 17, 18, 18, 18, 18, 18, 17, 16, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 7, 6, 6, 6, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 16, 16, 16, 15, 15, 14, 13, 12, 11, 10, 9, 8, 8, 8, 8, 8, 9, 10, 11, 12, 13, 14, 14, 14, 13, 12, 11, 10, 10, 10, 11, 12, } diff -r 000000000000 -r f9476ff7637e Alc/mixer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/mixer.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,848 @@ +/** + * 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 +#include +#include +#include +#include + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" +#include "alSource.h" +#include "alBuffer.h" +#include "alListener.h" +#include "alAuxEffectSlot.h" +#include "alu.h" +#include "bs2b.h" + + +static __inline ALdouble point32(const ALfloat *vals, ALint step, ALint frac) +{ return vals[0]; (void)step; (void)frac; } +static __inline ALdouble lerp32(const ALfloat *vals, ALint step, ALint frac) +{ return lerp(vals[0], vals[step], frac * (1.0/FRACTIONONE)); } +static __inline ALdouble cubic32(const ALfloat *vals, ALint step, ALint frac) +{ return cubic(vals[-step], vals[0], vals[step], vals[step+step], + frac * (1.0/FRACTIONONE)); } + +static __inline ALdouble point16(const ALshort *vals, ALint step, ALint frac) +{ return vals[0] * (1.0/32767.0); (void)step; (void)frac; } +static __inline ALdouble lerp16(const ALshort *vals, ALint step, ALint frac) +{ return lerp(vals[0], vals[step], frac * (1.0/FRACTIONONE)) * (1.0/32767.0); } +static __inline ALdouble cubic16(const ALshort *vals, ALint step, ALint frac) +{ return cubic(vals[-step], vals[0], vals[step], vals[step+step], + frac * (1.0/FRACTIONONE)) * (1.0/32767.0); } + +static __inline ALdouble point8(const ALbyte *vals, ALint step, ALint frac) +{ return vals[0] * (1.0/127.0); (void)step; (void)frac; } +static __inline ALdouble lerp8(const ALbyte *vals, ALint step, ALint frac) +{ return lerp(vals[0], vals[step], frac * (1.0/FRACTIONONE)) * (1.0/127.0); } +static __inline ALdouble cubic8(const ALbyte *vals, ALint step, ALint frac) +{ return cubic(vals[-step], vals[0], vals[step], vals[step+step], + frac * (1.0/FRACTIONONE)) * (1.0/127.0); } + +#ifdef __GNUC__ +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif + +#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_H) +#include + +static __inline void ApplyCoeffs(ALuint Offset, ALfloat (*RESTRICT Values)[2], + ALfloat (*RESTRICT Coeffs)[2], + ALfloat left, ALfloat right) +{ + ALuint c; + float32x4_t leftright4; + { + float32x2_t leftright2 = vdup_n_f32(0.0); + leftright2 = vset_lane_f32(left, leftright2, 0); + leftright2 = vset_lane_f32(right, leftright2, 1); + leftright4 = vcombine_f32(leftright2, leftright2); + } + for(c = 0;c < HRIR_LENGTH;c += 2) + { + const ALuint o0 = (Offset+c)&HRIR_MASK; + const ALuint o1 = (o0+1)&HRIR_MASK; + float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]), + vld1_f32((float32_t*)&Values[o1][0])); + float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]); + + vals = vmlaq_f32(vals, coefs, leftright4); + + vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals)); + vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals)); + } +} + +#else + +static __inline void ApplyCoeffs(ALuint Offset, ALfloat (*RESTRICT Values)[2], + ALfloat (*RESTRICT Coeffs)[2], + ALfloat left, ALfloat right) +{ + ALuint c; + for(c = 0;c < HRIR_LENGTH;c++) + { + const ALuint off = (Offset+c)&HRIR_MASK; + Values[off][0] += Coeffs[c][0] * left; + Values[off][1] += Coeffs[c][1] * right; + } +} + +#endif + +#define DECL_TEMPLATE(T, sampler) \ +static void Mix_Hrtf_##T##_##sampler(ALsource *Source, ALCdevice *Device, \ + const ALvoid *srcdata, ALuint *DataPosInt, ALuint *DataPosFrac, \ + ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) \ +{ \ + const ALuint NumChannels = Source->NumChannels; \ + const T *RESTRICT data = srcdata; \ + const ALint *RESTRICT DelayStep = Source->Params.HrtfDelayStep; \ + ALfloat (*RESTRICT DryBuffer)[MAXCHANNELS]; \ + ALfloat *RESTRICT ClickRemoval, *RESTRICT PendingClicks; \ + ALfloat (*RESTRICT CoeffStep)[2] = Source->Params.HrtfCoeffStep; \ + ALuint pos, frac; \ + FILTER *DryFilter; \ + ALuint BufferIdx; \ + ALuint increment; \ + ALuint i, out, c; \ + ALfloat value; \ + \ + increment = Source->Params.Step; \ + \ + DryBuffer = Device->DryBuffer; \ + ClickRemoval = Device->ClickRemoval; \ + PendingClicks = Device->PendingClicks; \ + DryFilter = &Source->Params.iirFilter; \ + \ + pos = 0; \ + frac = *DataPosFrac; \ + \ + for(i = 0;i < NumChannels;i++) \ + { \ + ALfloat (*RESTRICT TargetCoeffs)[2] = Source->Params.HrtfCoeffs[i]; \ + ALuint *RESTRICT TargetDelay = Source->Params.HrtfDelay[i]; \ + ALfloat *RESTRICT History = Source->HrtfHistory[i]; \ + ALfloat (*RESTRICT Values)[2] = Source->HrtfValues[i]; \ + ALint Counter = maxu(Source->HrtfCounter, OutPos) - OutPos; \ + ALuint Offset = Source->HrtfOffset + OutPos; \ + ALfloat Coeffs[HRIR_LENGTH][2]; \ + ALuint Delay[2]; \ + ALfloat left, right; \ + \ + pos = 0; \ + frac = *DataPosFrac; \ + \ + for(c = 0;c < HRIR_LENGTH;c++) \ + { \ + Coeffs[c][0] = TargetCoeffs[c][0] - (CoeffStep[c][0]*Counter); \ + Coeffs[c][1] = TargetCoeffs[c][1] - (CoeffStep[c][1]*Counter); \ + } \ + \ + Delay[0] = TargetDelay[0] - (DelayStep[0]*Counter) + 32768; \ + Delay[1] = TargetDelay[1] - (DelayStep[1]*Counter) + 32768; \ + \ + if(LIKELY(OutPos == 0)) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ + value = lpFilter2PC(DryFilter, i, value); \ + \ + History[Offset&SRC_HISTORY_MASK] = value; \ + left = History[(Offset-(Delay[0]>>16))&SRC_HISTORY_MASK]; \ + right = History[(Offset-(Delay[1]>>16))&SRC_HISTORY_MASK]; \ + \ + ClickRemoval[FRONT_LEFT] -= Values[(Offset+1)&HRIR_MASK][0] + \ + Coeffs[0][0] * left; \ + ClickRemoval[FRONT_RIGHT] -= Values[(Offset+1)&HRIR_MASK][1] + \ + Coeffs[0][1] * right; \ + } \ + for(BufferIdx = 0;BufferIdx < BufferSize && Counter > 0;BufferIdx++) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ + value = lpFilter2P(DryFilter, i, value); \ + \ + History[Offset&SRC_HISTORY_MASK] = value; \ + left = History[(Offset-(Delay[0]>>16))&SRC_HISTORY_MASK]; \ + right = History[(Offset-(Delay[1]>>16))&SRC_HISTORY_MASK]; \ + \ + Delay[0] += DelayStep[0]; \ + Delay[1] += DelayStep[1]; \ + \ + Values[Offset&HRIR_MASK][0] = 0.0f; \ + Values[Offset&HRIR_MASK][1] = 0.0f; \ + Offset++; \ + \ + for(c = 0;c < HRIR_LENGTH;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]; \ + } \ + \ + DryBuffer[OutPos][FRONT_LEFT] += Values[Offset&HRIR_MASK][0]; \ + DryBuffer[OutPos][FRONT_RIGHT] += Values[Offset&HRIR_MASK][1]; \ + \ + frac += increment; \ + pos += frac>>FRACTIONBITS; \ + frac &= FRACTIONMASK; \ + OutPos++; \ + Counter--; \ + } \ + \ + Delay[0] >>= 16; \ + Delay[1] >>= 16; \ + for(;BufferIdx < BufferSize;BufferIdx++) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ + value = lpFilter2P(DryFilter, i, value); \ + \ + History[Offset&SRC_HISTORY_MASK] = value; \ + left = History[(Offset-Delay[0])&SRC_HISTORY_MASK]; \ + right = History[(Offset-Delay[1])&SRC_HISTORY_MASK]; \ + \ + Values[Offset&HRIR_MASK][0] = 0.0f; \ + Values[Offset&HRIR_MASK][1] = 0.0f; \ + Offset++; \ + \ + ApplyCoeffs(Offset, Values, Coeffs, left, right); \ + DryBuffer[OutPos][FRONT_LEFT] += Values[Offset&HRIR_MASK][0]; \ + DryBuffer[OutPos][FRONT_RIGHT] += Values[Offset&HRIR_MASK][1]; \ + \ + frac += increment; \ + pos += frac>>FRACTIONBITS; \ + frac &= FRACTIONMASK; \ + OutPos++; \ + } \ + if(LIKELY(OutPos == SamplesToDo)) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ + value = lpFilter2PC(DryFilter, i, value); \ + \ + History[Offset&SRC_HISTORY_MASK] = value; \ + left = History[(Offset-Delay[0])&SRC_HISTORY_MASK]; \ + right = History[(Offset-Delay[1])&SRC_HISTORY_MASK]; \ + \ + PendingClicks[FRONT_LEFT] += Values[(Offset+1)&HRIR_MASK][0] + \ + Coeffs[0][0] * left; \ + PendingClicks[FRONT_RIGHT] += Values[(Offset+1)&HRIR_MASK][1] + \ + Coeffs[0][1] * right; \ + } \ + OutPos -= BufferSize; \ + } \ + \ + for(out = 0;out < Device->NumAuxSends;out++) \ + { \ + ALeffectslot *Slot = Source->Params.Send[out].Slot; \ + ALfloat WetSend; \ + ALfloat *RESTRICT WetBuffer; \ + ALfloat *RESTRICT WetClickRemoval; \ + ALfloat *RESTRICT WetPendingClicks; \ + FILTER *WetFilter; \ + \ + if(!Slot || Slot->effect.type == AL_EFFECT_NULL) \ + continue; \ + \ + WetBuffer = Slot->WetBuffer; \ + WetClickRemoval = Slot->ClickRemoval; \ + WetPendingClicks = Slot->PendingClicks; \ + WetFilter = &Source->Params.Send[out].iirFilter; \ + WetSend = Source->Params.Send[out].WetGain; \ + \ + for(i = 0;i < NumChannels;i++) \ + { \ + pos = 0; \ + frac = *DataPosFrac; \ + \ + if(LIKELY(OutPos == 0)) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels,frac);\ + value = lpFilter1PC(WetFilter, i, value); \ + \ + WetClickRemoval[0] -= value * WetSend; \ + } \ + for(BufferIdx = 0;BufferIdx < BufferSize;BufferIdx++) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels,frac);\ + value = lpFilter1P(WetFilter, i, value); \ + \ + WetBuffer[OutPos] += value * WetSend; \ + \ + frac += increment; \ + pos += frac>>FRACTIONBITS; \ + frac &= FRACTIONMASK; \ + OutPos++; \ + } \ + if(LIKELY(OutPos == SamplesToDo)) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels,frac);\ + value = lpFilter1PC(WetFilter, i, value); \ + \ + WetPendingClicks[0] += value * WetSend; \ + } \ + OutPos -= BufferSize; \ + } \ + } \ + *DataPosInt += pos; \ + *DataPosFrac = frac; \ +} + +DECL_TEMPLATE(ALfloat, point32) +DECL_TEMPLATE(ALfloat, lerp32) +DECL_TEMPLATE(ALfloat, cubic32) + +DECL_TEMPLATE(ALshort, point16) +DECL_TEMPLATE(ALshort, lerp16) +DECL_TEMPLATE(ALshort, cubic16) + +DECL_TEMPLATE(ALbyte, point8) +DECL_TEMPLATE(ALbyte, lerp8) +DECL_TEMPLATE(ALbyte, cubic8) + +#undef DECL_TEMPLATE + + +#define DECL_TEMPLATE(T, sampler) \ +static void Mix_##T##_##sampler(ALsource *Source, ALCdevice *Device, \ + const ALvoid *srcdata, ALuint *DataPosInt, ALuint *DataPosFrac, \ + ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) \ +{ \ + const ALuint NumChannels = Source->NumChannels; \ + const T *RESTRICT data = srcdata; \ + ALfloat (*DryBuffer)[MAXCHANNELS]; \ + ALfloat *ClickRemoval, *PendingClicks; \ + ALuint pos, frac; \ + ALfloat DrySend[MAXCHANNELS][MAXCHANNELS]; \ + FILTER *DryFilter; \ + ALuint BufferIdx; \ + ALuint increment; \ + ALuint i, out, c; \ + ALfloat value; \ + \ + increment = Source->Params.Step; \ + \ + DryBuffer = Device->DryBuffer; \ + ClickRemoval = Device->ClickRemoval; \ + PendingClicks = Device->PendingClicks; \ + DryFilter = &Source->Params.iirFilter; \ + for(i = 0;i < NumChannels;i++) \ + { \ + for(c = 0;c < MAXCHANNELS;c++) \ + DrySend[i][c] = Source->Params.DryGains[i][c]; \ + } \ + \ + pos = 0; \ + frac = *DataPosFrac; \ + \ + for(i = 0;i < NumChannels;i++) \ + { \ + pos = 0; \ + frac = *DataPosFrac; \ + \ + if(OutPos == 0) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ + \ + value = lpFilter2PC(DryFilter, i, value); \ + for(c = 0;c < MAXCHANNELS;c++) \ + ClickRemoval[c] -= value*DrySend[i][c]; \ + } \ + for(BufferIdx = 0;BufferIdx < BufferSize;BufferIdx++) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ + \ + value = lpFilter2P(DryFilter, i, value); \ + for(c = 0;c < MAXCHANNELS;c++) \ + DryBuffer[OutPos][c] += value*DrySend[i][c]; \ + \ + frac += increment; \ + pos += frac>>FRACTIONBITS; \ + frac &= FRACTIONMASK; \ + OutPos++; \ + } \ + if(OutPos == SamplesToDo) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ + \ + value = lpFilter2PC(DryFilter, i, value); \ + for(c = 0;c < MAXCHANNELS;c++) \ + PendingClicks[c] += value*DrySend[i][c]; \ + } \ + OutPos -= BufferSize; \ + } \ + \ + for(out = 0;out < Device->NumAuxSends;out++) \ + { \ + ALeffectslot *Slot = Source->Params.Send[out].Slot; \ + ALfloat WetSend; \ + ALfloat *WetBuffer; \ + ALfloat *WetClickRemoval; \ + ALfloat *WetPendingClicks; \ + FILTER *WetFilter; \ + \ + if(!Slot || Slot->effect.type == AL_EFFECT_NULL) \ + continue; \ + \ + WetBuffer = Slot->WetBuffer; \ + WetClickRemoval = Slot->ClickRemoval; \ + WetPendingClicks = Slot->PendingClicks; \ + WetFilter = &Source->Params.Send[out].iirFilter; \ + WetSend = Source->Params.Send[out].WetGain; \ + \ + for(i = 0;i < NumChannels;i++) \ + { \ + pos = 0; \ + frac = *DataPosFrac; \ + \ + if(OutPos == 0) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels,frac);\ + \ + value = lpFilter1PC(WetFilter, i, value); \ + WetClickRemoval[0] -= value * WetSend; \ + } \ + for(BufferIdx = 0;BufferIdx < BufferSize;BufferIdx++) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels,frac);\ + \ + value = lpFilter1P(WetFilter, i, value); \ + WetBuffer[OutPos] += value * WetSend; \ + \ + frac += increment; \ + pos += frac>>FRACTIONBITS; \ + frac &= FRACTIONMASK; \ + OutPos++; \ + } \ + if(OutPos == SamplesToDo) \ + { \ + value = sampler(data + pos*NumChannels + i, NumChannels,frac);\ + \ + value = lpFilter1PC(WetFilter, i, value); \ + WetPendingClicks[0] += value * WetSend; \ + } \ + OutPos -= BufferSize; \ + } \ + } \ + *DataPosInt += pos; \ + *DataPosFrac = frac; \ +} + +DECL_TEMPLATE(ALfloat, point32) +DECL_TEMPLATE(ALfloat, lerp32) +DECL_TEMPLATE(ALfloat, cubic32) + +DECL_TEMPLATE(ALshort, point16) +DECL_TEMPLATE(ALshort, lerp16) +DECL_TEMPLATE(ALshort, cubic16) + +DECL_TEMPLATE(ALbyte, point8) +DECL_TEMPLATE(ALbyte, lerp8) +DECL_TEMPLATE(ALbyte, cubic8) + +#undef DECL_TEMPLATE + + +#define DECL_TEMPLATE(sampler) \ +static MixerFunc Select_##sampler(enum FmtType FmtType) \ +{ \ + switch(FmtType) \ + { \ + case FmtByte: \ + return Mix_ALbyte_##sampler##8; \ + case FmtShort: \ + return Mix_ALshort_##sampler##16; \ + case FmtFloat: \ + return Mix_ALfloat_##sampler##32; \ + } \ + return NULL; \ +} + +DECL_TEMPLATE(point) +DECL_TEMPLATE(lerp) +DECL_TEMPLATE(cubic) + +#undef DECL_TEMPLATE + +MixerFunc SelectMixer(ALbuffer *Buffer, enum Resampler Resampler) +{ + switch(Resampler) + { + case POINT_RESAMPLER: + return Select_point(Buffer->FmtType); + case LINEAR_RESAMPLER: + return Select_lerp(Buffer->FmtType); + case CUBIC_RESAMPLER: + return Select_cubic(Buffer->FmtType); + case RESAMPLER_MIN: + case RESAMPLER_MAX: + break; + } + return NULL; +} + +#define DECL_TEMPLATE(sampler) \ +static MixerFunc Select_Hrtf_##sampler(enum FmtType FmtType) \ +{ \ + switch(FmtType) \ + { \ + case FmtByte: \ + return Mix_Hrtf_ALbyte_##sampler##8; \ + case FmtShort: \ + return Mix_Hrtf_ALshort_##sampler##16; \ + case FmtFloat: \ + return Mix_Hrtf_ALfloat_##sampler##32; \ + } \ + return NULL; \ +} + +DECL_TEMPLATE(point) +DECL_TEMPLATE(lerp) +DECL_TEMPLATE(cubic) + +#undef DECL_TEMPLATE + +MixerFunc SelectHrtfMixer(ALbuffer *Buffer, enum Resampler Resampler) +{ + switch(Resampler) + { + case POINT_RESAMPLER: + return Select_Hrtf_point(Buffer->FmtType); + case LINEAR_RESAMPLER: + return Select_Hrtf_lerp(Buffer->FmtType); + case CUBIC_RESAMPLER: + return Select_Hrtf_cubic(Buffer->FmtType); + case RESAMPLER_MIN: + case RESAMPLER_MAX: + break; + } + return NULL; +} + + +ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) +{ + ALbufferlistitem *BufferListItem; + ALuint DataPosInt, DataPosFrac; + ALuint BuffersPlayed; + ALboolean Looping; + ALuint increment; + enum Resampler Resampler; + ALenum State; + ALuint OutPos; + ALuint FrameSize; + ALint64 DataSize64; + ALuint i; + + /* Get source info */ + State = Source->state; + BuffersPlayed = Source->BuffersPlayed; + DataPosInt = Source->position; + DataPosFrac = Source->position_fraction; + Looping = Source->bLooping; + increment = Source->Params.Step; + Resampler = Source->Resampler; + FrameSize = Source->NumChannels * Source->SampleSize; + + /* Get current buffer queue item */ + BufferListItem = Source->queue; + for(i = 0;i < BuffersPlayed;i++) + BufferListItem = BufferListItem->next; + + OutPos = 0; + do { + const ALuint BufferPrePadding = ResamplerPrePadding[Resampler]; + const ALuint BufferPadding = ResamplerPadding[Resampler]; + ALubyte StackData[STACK_DATA_SIZE]; + ALubyte *SrcData = StackData; + ALuint SrcDataSize = 0; + ALuint BufferSize; + + /* Figure out how many buffer bytes will be needed */ + DataSize64 = SamplesToDo-OutPos+1; + DataSize64 *= increment; + DataSize64 += DataPosFrac+FRACTIONMASK; + DataSize64 >>= FRACTIONBITS; + DataSize64 += BufferPadding+BufferPrePadding; + DataSize64 *= FrameSize; + + BufferSize = ((DataSize64 > STACK_DATA_SIZE) ? STACK_DATA_SIZE : DataSize64); + BufferSize -= BufferSize%FrameSize; + + if(Source->lSourceType == AL_STATIC) + { + const ALbuffer *ALBuffer = Source->Buffer; + const ALubyte *Data = ALBuffer->data; + ALuint DataSize; + ALuint pos; + + /* If current pos is beyond the loop range, do not loop */ + if(Looping == AL_FALSE || DataPosInt >= (ALuint)ALBuffer->LoopEnd) + { + Looping = AL_FALSE; + + if(DataPosInt >= BufferPrePadding) + pos = (DataPosInt-BufferPrePadding)*FrameSize; + else + { + DataSize = (BufferPrePadding-DataPosInt)*FrameSize; + DataSize = minu(BufferSize, DataSize); + + memset(&SrcData[SrcDataSize], 0, DataSize); + SrcDataSize += DataSize; + BufferSize -= DataSize; + + pos = 0; + } + + /* Copy what's left to play in the source buffer, and clear the + * rest of the temp buffer */ + DataSize = ALBuffer->size - pos; + DataSize = minu(BufferSize, DataSize); + + memcpy(&SrcData[SrcDataSize], &Data[pos], DataSize); + SrcDataSize += DataSize; + BufferSize -= DataSize; + + memset(&SrcData[SrcDataSize], 0, BufferSize); + SrcDataSize += BufferSize; + BufferSize -= BufferSize; + } + else + { + ALuint LoopStart = ALBuffer->LoopStart; + ALuint LoopEnd = ALBuffer->LoopEnd; + + if(DataPosInt >= LoopStart) + { + pos = DataPosInt-LoopStart; + while(pos < BufferPrePadding) + pos += LoopEnd-LoopStart; + pos -= BufferPrePadding; + pos += LoopStart; + pos *= FrameSize; + } + else if(DataPosInt >= BufferPrePadding) + pos = (DataPosInt-BufferPrePadding)*FrameSize; + else + { + DataSize = (BufferPrePadding-DataPosInt)*FrameSize; + DataSize = minu(BufferSize, DataSize); + + memset(&SrcData[SrcDataSize], 0, DataSize); + SrcDataSize += DataSize; + BufferSize -= DataSize; + + pos = 0; + } + + /* Copy what's left of this loop iteration, then copy repeats + * of the loop section */ + DataSize = LoopEnd*FrameSize - pos; + DataSize = minu(BufferSize, DataSize); + + memcpy(&SrcData[SrcDataSize], &Data[pos], DataSize); + SrcDataSize += DataSize; + BufferSize -= DataSize; + + DataSize = (LoopEnd-LoopStart) * FrameSize; + while(BufferSize > 0) + { + DataSize = minu(BufferSize, DataSize); + + memcpy(&SrcData[SrcDataSize], &Data[LoopStart*FrameSize], DataSize); + SrcDataSize += DataSize; + BufferSize -= DataSize; + } + } + } + else + { + /* Crawl the buffer queue to fill in the temp buffer */ + ALbufferlistitem *BufferListIter = BufferListItem; + ALuint pos; + + if(DataPosInt >= BufferPrePadding) + pos = (DataPosInt-BufferPrePadding)*FrameSize; + else + { + pos = (BufferPrePadding-DataPosInt)*FrameSize; + while(pos > 0) + { + if(!BufferListIter->prev && !Looping) + { + ALuint DataSize = minu(BufferSize, pos); + + memset(&SrcData[SrcDataSize], 0, DataSize); + SrcDataSize += DataSize; + BufferSize -= DataSize; + + pos = 0; + break; + } + + if(BufferListIter->prev) + BufferListIter = BufferListIter->prev; + else + { + while(BufferListIter->next) + BufferListIter = BufferListIter->next; + } + + if(BufferListIter->buffer) + { + if((ALuint)BufferListIter->buffer->size > pos) + { + pos = BufferListIter->buffer->size - pos; + break; + } + pos -= BufferListIter->buffer->size; + } + } + } + + while(BufferListIter && BufferSize > 0) + { + const ALbuffer *ALBuffer; + if((ALBuffer=BufferListIter->buffer) != NULL) + { + const ALubyte *Data = ALBuffer->data; + ALuint DataSize = ALBuffer->size; + + /* Skip the data already played */ + if(DataSize <= pos) + pos -= DataSize; + else + { + Data += pos; + DataSize -= pos; + pos -= pos; + + DataSize = minu(BufferSize, DataSize); + memcpy(&SrcData[SrcDataSize], Data, DataSize); + SrcDataSize += DataSize; + BufferSize -= DataSize; + } + } + BufferListIter = BufferListIter->next; + if(!BufferListIter && Looping) + BufferListIter = Source->queue; + else if(!BufferListIter) + { + memset(&SrcData[SrcDataSize], 0, BufferSize); + SrcDataSize += BufferSize; + BufferSize -= BufferSize; + } + } + } + + /* Figure out how many samples we can mix. */ + DataSize64 = SrcDataSize / FrameSize; + DataSize64 -= BufferPadding+BufferPrePadding; + DataSize64 <<= FRACTIONBITS; + DataSize64 -= increment; + DataSize64 -= DataPosFrac; + + BufferSize = (ALuint)((DataSize64+(increment-1)) / increment); + BufferSize = minu(BufferSize, (SamplesToDo-OutPos)); + + SrcData += BufferPrePadding*FrameSize; + Source->Params.DoMix(Source, Device, SrcData, &DataPosInt, &DataPosFrac, + OutPos, SamplesToDo, BufferSize); + OutPos += BufferSize; + + /* Handle looping sources */ + while(1) + { + const ALbuffer *ALBuffer; + ALuint DataSize = 0; + ALuint LoopStart = 0; + ALuint LoopEnd = 0; + + if((ALBuffer=BufferListItem->buffer) != NULL) + { + DataSize = ALBuffer->size / FrameSize; + LoopStart = ALBuffer->LoopStart; + LoopEnd = ALBuffer->LoopEnd; + if(LoopEnd > DataPosInt) + break; + } + + if(Looping && Source->lSourceType == AL_STATIC) + { + BufferListItem = Source->queue; + DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart; + break; + } + + if(DataSize > DataPosInt) + break; + + if(BufferListItem->next) + { + BufferListItem = BufferListItem->next; + BuffersPlayed++; + } + else if(Looping) + { + BufferListItem = Source->queue; + BuffersPlayed = 0; + } + else + { + State = AL_STOPPED; + BufferListItem = Source->queue; + BuffersPlayed = Source->BuffersInQueue; + DataPosInt = 0; + DataPosFrac = 0; + break; + } + + DataPosInt -= DataSize; + } + } while(State == AL_PLAYING && OutPos < SamplesToDo); + + /* Update source info */ + Source->state = State; + Source->BuffersPlayed = BuffersPlayed; + Source->position = DataPosInt; + Source->position_fraction = DataPosFrac; + Source->Buffer = BufferListItem->buffer; + Source->HrtfOffset += OutPos; + if(State == AL_PLAYING) + { + Source->HrtfCounter = maxu(Source->HrtfCounter, OutPos) - OutPos; + Source->HrtfMoving = AL_TRUE; + } + else + { + Source->HrtfCounter = 0; + Source->HrtfMoving = AL_FALSE; + } +} diff -r 000000000000 -r f9476ff7637e Alc/panning.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/panning.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,313 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2010 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 +#include +#include +#include +#include + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" +#include "alu.h" + +static void SetSpeakerArrangement(const char *name, ALfloat SpeakerAngle[MAXCHANNELS], + enum Channel Speaker2Chan[MAXCHANNELS], ALint chans) +{ + char layout_str[256]; + char *confkey, *next; + char *sep, *end; + enum Channel val; + int i; + + if(!ConfigValueExists(NULL, name)) + name = "layout"; + + strncpy(layout_str, GetConfigValue(NULL, name, ""), sizeof(layout_str)); + layout_str[sizeof(layout_str)-1] = 0; + + if(!layout_str[0]) + return; + + next = confkey = layout_str; + while(next && *next) + { + confkey = next; + next = strchr(confkey, ','); + if(next) + { + *next = 0; + do { + next++; + } while(isspace(*next) || *next == ','); + } + + sep = strchr(confkey, '='); + if(!sep || confkey == sep) + continue; + + end = sep - 1; + while(isspace(*end) && end != confkey) + end--; + *(++end) = 0; + + if(strcmp(confkey, "fl") == 0 || strcmp(confkey, "front-left") == 0) + val = FRONT_LEFT; + else if(strcmp(confkey, "fr") == 0 || strcmp(confkey, "front-right") == 0) + val = FRONT_RIGHT; + else if(strcmp(confkey, "fc") == 0 || strcmp(confkey, "front-center") == 0) + val = FRONT_CENTER; + else if(strcmp(confkey, "bl") == 0 || strcmp(confkey, "back-left") == 0) + val = BACK_LEFT; + else if(strcmp(confkey, "br") == 0 || strcmp(confkey, "back-right") == 0) + val = BACK_RIGHT; + else if(strcmp(confkey, "bc") == 0 || strcmp(confkey, "back-center") == 0) + val = BACK_CENTER; + else if(strcmp(confkey, "sl") == 0 || strcmp(confkey, "side-left") == 0) + val = SIDE_LEFT; + else if(strcmp(confkey, "sr") == 0 || strcmp(confkey, "side-right") == 0) + val = SIDE_RIGHT; + else + { + ERR("Unknown speaker for %s: \"%s\"\n", name, confkey); + continue; + } + + *(sep++) = 0; + while(isspace(*sep)) + sep++; + + for(i = 0;i < chans;i++) + { + if(Speaker2Chan[i] == val) + { + long angle = strtol(sep, NULL, 10); + if(angle >= -180 && angle <= 180) + SpeakerAngle[i] = angle * M_PI/180.0f; + else + ERR("Invalid angle for speaker \"%s\": %ld\n", confkey, angle); + break; + } + } + } + + for(i = 0;i < chans;i++) + { + int min = i; + int i2; + + for(i2 = i+1;i2 < chans;i2++) + { + if(SpeakerAngle[i2] < SpeakerAngle[min]) + min = i2; + } + + if(min != i) + { + ALfloat tmpf; + enum Channel tmpc; + + tmpf = SpeakerAngle[i]; + SpeakerAngle[i] = SpeakerAngle[min]; + SpeakerAngle[min] = tmpf; + + tmpc = Speaker2Chan[i]; + Speaker2Chan[i] = Speaker2Chan[min]; + Speaker2Chan[min] = tmpc; + } + } +} + +static ALfloat aluLUTpos2Angle(ALint pos) +{ + if(pos < QUADRANT_NUM) + return aluAtan((ALfloat)pos / (ALfloat)(QUADRANT_NUM - pos)); + if(pos < 2 * QUADRANT_NUM) + return M_PI_2 + aluAtan((ALfloat)(pos - QUADRANT_NUM) / (ALfloat)(2 * QUADRANT_NUM - pos)); + if(pos < 3 * QUADRANT_NUM) + return aluAtan((ALfloat)(pos - 2 * QUADRANT_NUM) / (ALfloat)(3 * QUADRANT_NUM - pos)) - M_PI; + return aluAtan((ALfloat)(pos - 3 * QUADRANT_NUM) / (ALfloat)(4 * QUADRANT_NUM - pos)) - M_PI_2; +} + +ALint aluCart2LUTpos(ALfloat re, ALfloat im) +{ + ALint pos = 0; + ALfloat denom = aluFabs(re) + aluFabs(im); + if(denom > 0.0f) + pos = (ALint)(QUADRANT_NUM*aluFabs(im) / denom + 0.5); + + if(re < 0.0) + pos = 2 * QUADRANT_NUM - pos; + if(im < 0.0) + pos = LUT_NUM - pos; + return pos%LUT_NUM; +} + +ALvoid aluInitPanning(ALCdevice *Device) +{ + ALfloat SpeakerAngle[MAXCHANNELS]; + enum Channel *Speaker2Chan; + ALfloat Alpha, Theta; + ALint pos; + ALuint s; + + Speaker2Chan = Device->Speaker2Chan; + switch(Device->FmtChans) + { + case DevFmtMono: + Device->NumChan = 1; + Speaker2Chan[0] = FRONT_CENTER; + SpeakerAngle[0] = 0.0f * M_PI/180.0f; + break; + + case DevFmtStereo: + Device->NumChan = 2; + Speaker2Chan[0] = FRONT_LEFT; + Speaker2Chan[1] = FRONT_RIGHT; + SpeakerAngle[0] = -90.0f * M_PI/180.0f; + SpeakerAngle[1] = 90.0f * M_PI/180.0f; + SetSpeakerArrangement("layout_STEREO", SpeakerAngle, Speaker2Chan, Device->NumChan); + break; + + case DevFmtQuad: + Device->NumChan = 4; + Speaker2Chan[0] = BACK_LEFT; + Speaker2Chan[1] = FRONT_LEFT; + Speaker2Chan[2] = FRONT_RIGHT; + Speaker2Chan[3] = BACK_RIGHT; + SpeakerAngle[0] = -135.0f * M_PI/180.0f; + SpeakerAngle[1] = -45.0f * M_PI/180.0f; + SpeakerAngle[2] = 45.0f * M_PI/180.0f; + SpeakerAngle[3] = 135.0f * M_PI/180.0f; + SetSpeakerArrangement("layout_QUAD", SpeakerAngle, Speaker2Chan, Device->NumChan); + break; + + case DevFmtX51: + Device->NumChan = 5; + Speaker2Chan[0] = BACK_LEFT; + Speaker2Chan[1] = FRONT_LEFT; + Speaker2Chan[2] = FRONT_CENTER; + Speaker2Chan[3] = FRONT_RIGHT; + Speaker2Chan[4] = BACK_RIGHT; + SpeakerAngle[0] = -110.0f * M_PI/180.0f; + SpeakerAngle[1] = -30.0f * M_PI/180.0f; + SpeakerAngle[2] = 0.0f * M_PI/180.0f; + SpeakerAngle[3] = 30.0f * M_PI/180.0f; + SpeakerAngle[4] = 110.0f * M_PI/180.0f; + SetSpeakerArrangement("layout_51CHN", SpeakerAngle, Speaker2Chan, Device->NumChan); + break; + + case DevFmtX51Side: + Device->NumChan = 5; + Speaker2Chan[0] = SIDE_LEFT; + Speaker2Chan[1] = FRONT_LEFT; + Speaker2Chan[2] = FRONT_CENTER; + Speaker2Chan[3] = FRONT_RIGHT; + Speaker2Chan[4] = SIDE_RIGHT; + SpeakerAngle[0] = -90.0f * M_PI/180.0f; + SpeakerAngle[1] = -30.0f * M_PI/180.0f; + SpeakerAngle[2] = 0.0f * M_PI/180.0f; + SpeakerAngle[3] = 30.0f * M_PI/180.0f; + SpeakerAngle[4] = 90.0f * M_PI/180.0f; + SetSpeakerArrangement("layout_51SIDECHN", SpeakerAngle, Speaker2Chan, Device->NumChan); + break; + + case DevFmtX61: + Device->NumChan = 6; + Speaker2Chan[0] = SIDE_LEFT; + Speaker2Chan[1] = FRONT_LEFT; + Speaker2Chan[2] = FRONT_CENTER; + Speaker2Chan[3] = FRONT_RIGHT; + Speaker2Chan[4] = SIDE_RIGHT; + Speaker2Chan[5] = BACK_CENTER; + SpeakerAngle[0] = -90.0f * M_PI/180.0f; + SpeakerAngle[1] = -30.0f * M_PI/180.0f; + SpeakerAngle[2] = 0.0f * M_PI/180.0f; + SpeakerAngle[3] = 30.0f * M_PI/180.0f; + SpeakerAngle[4] = 90.0f * M_PI/180.0f; + SpeakerAngle[5] = 180.0f * M_PI/180.0f; + SetSpeakerArrangement("layout_61CHN", SpeakerAngle, Speaker2Chan, Device->NumChan); + break; + + case DevFmtX71: + Device->NumChan = 7; + Speaker2Chan[0] = BACK_LEFT; + Speaker2Chan[1] = SIDE_LEFT; + Speaker2Chan[2] = FRONT_LEFT; + Speaker2Chan[3] = FRONT_CENTER; + Speaker2Chan[4] = FRONT_RIGHT; + Speaker2Chan[5] = SIDE_RIGHT; + Speaker2Chan[6] = BACK_RIGHT; + SpeakerAngle[0] = -150.0f * M_PI/180.0f; + SpeakerAngle[1] = -90.0f * M_PI/180.0f; + SpeakerAngle[2] = -30.0f * M_PI/180.0f; + SpeakerAngle[3] = 0.0f * M_PI/180.0f; + SpeakerAngle[4] = 30.0f * M_PI/180.0f; + SpeakerAngle[5] = 90.0f * M_PI/180.0f; + SpeakerAngle[6] = 150.0f * M_PI/180.0f; + SetSpeakerArrangement("layout_71CHN", SpeakerAngle, Speaker2Chan, Device->NumChan); + break; + } + + for(pos = 0; pos < LUT_NUM; pos++) + { + ALfloat *PanningLUT = Device->PanningLUT[pos]; + + /* clear all values */ + for(s = 0; s < MAXCHANNELS; s++) + PanningLUT[s] = 0.0f; + + if(Device->NumChan == 1) + { + PanningLUT[Speaker2Chan[0]] = 1.0f; + continue; + } + + /* source angle */ + Theta = aluLUTpos2Angle(pos); + + /* set panning values */ + for(s = 0; s < Device->NumChan - 1; s++) + { + if(Theta >= SpeakerAngle[s] && Theta < SpeakerAngle[s+1]) + { + /* source between speaker s and speaker s+1 */ + Alpha = M_PI_2 * (Theta-SpeakerAngle[s]) / + (SpeakerAngle[s+1]-SpeakerAngle[s]); + PanningLUT[Speaker2Chan[s]] = cos(Alpha); + PanningLUT[Speaker2Chan[s+1]] = sin(Alpha); + break; + } + } + if(s == Device->NumChan - 1) + { + /* source between last and first speaker */ + if(Theta < SpeakerAngle[0]) + Theta += 2.0f * M_PI; + Alpha = M_PI_2 * (Theta-SpeakerAngle[s]) / + (2.0f * M_PI + SpeakerAngle[0]-SpeakerAngle[s]); + PanningLUT[Speaker2Chan[s]] = cos(Alpha); + PanningLUT[Speaker2Chan[0]] = sin(Alpha); + } + } +} diff -r 000000000000 -r f9476ff7637e Alc/utils.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/utils.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,123 @@ +void pauseAllSources(ALCcontext *ctx){ + ALCcontext *current = alcGetCurrentContext(); + alcMakeContextCurrent(ctx); + ALsource **src, **src_end; + src = ctx->ActiveSources; + src_end = src + ctx->ActiveSourceCount; + while(src != src_end){ + if (AL_PLAYING == (*src)->state){ + //if (AL_TRUE){ + ALuint source_id = (*src)->source; + //printf("pausing ONE source\n"); + alSourcePause(source_id); + } + src++; + } + alcMakeContextCurrent(current); +} + + +#define RUNONLY(n) \ + {static int __runonce = n; \ + if (__runonce-- <= 0){__runonce = 0;return;}} + +#define RUNAT(n) \ + {static int __runat = n; \ + if (0 != __runat--){return;}} + + +#define DsyncSourcei(sourceID1, sourceID2, ctx1, ctx2, param) \ + {/*printf("synci : " #param "\n");*/ \ + syncSourcei(sourceID1, sourceID2, ctx1, ctx2, param);} + +#define DsyncSourcef(sourceID1, sourceID2, ctx1, ctx2, param) \ + {/*printf("syncf : " #param "\n");*/ \ + syncSourcef(sourceID1, sourceID2, ctx1, ctx2, param);} + +#define DsyncSource3i(sourceID1, sourceID2, ctx1, ctx2, param) \ + {/*printf("sync3i : " #param "\n");*/ \ + syncSource3i(sourceID1, sourceID2, ctx1, ctx2, param);} + +#define DsyncSource3f(sourceID1, sourceID2, ctx1, ctx2, param) \ + {/*printf("sync3f : " #param "\n");*/ \ + syncSource3f(sourceID1, sourceID2, ctx1, ctx2, param);} + +void printError(void){ + ALenum error = alGetError(); + printf("%s\n", GetALCErrorString(error)); + printf("%s\n", GetALErrorString(error)); +} + + + +char* GetALCErrorString(ALenum err) +{ + switch(err) + { + case ALC_NO_ERROR: + return "AL_NO_ERROR"; + break; + + case ALC_INVALID_DEVICE: + return "ALC_INVALID_DEVICE"; + break; + + case ALC_INVALID_CONTEXT: + return "ALC_INVALID_CONTEXT"; + break; + + case ALC_INVALID_ENUM: + return "ALC_INVALID_ENUM"; + break; + + case ALC_INVALID_VALUE: + return "ALC_INVALID_VALUE"; + break; + + case ALC_OUT_OF_MEMORY: + return "ALC_OUT_OF_MEMORY"; + break; + }; + return "UNknown error."; +} + + + + + + + +char* GetALErrorString(ALenum err) +{ + switch(err) + { + case AL_NO_ERROR: + return "AL_NO_ERROR"; + break; + + case AL_INVALID_NAME: + return "AL_INVALID_NAME"; + break; + + case AL_INVALID_ENUM: + return "AL_INVALID_ENUM"; + break; + + case AL_INVALID_VALUE: + return "AL_INVALID_VALUE"; + break; + + case AL_INVALID_OPERATION: + return "AL_INVALID_OPERATION"; + break; + + case AL_OUT_OF_MEMORY: + return "AL_OUT_OF_MEMORY"; + break; + }; + return "UNknown error."; +} + + + + diff -r 000000000000 -r f9476ff7637e CMakeLists-works.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CMakeLists-works.txt Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,431 @@ +# CMake build file list for OpenAL + +CMAKE_MINIMUM_REQUIRED(VERSION 2.4) + +IF(COMMAND CMAKE_POLICY) + CMAKE_POLICY(SET CMP0003 NEW) +ENDIF(COMMAND CMAKE_POLICY) + +SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + +INCLUDE(CheckFunctionExists) +INCLUDE(CheckLibraryExists) +INCLUDE(CheckSharedFunctionExists) +INCLUDE(CheckIncludeFile) +INCLUDE(CheckIncludeFiles) +INCLUDE(CheckSymbolExists) +INCLUDE(CheckCCompilerFlag) +INCLUDE(CheckCSourceCompiles) +INCLUDE(CheckTypeSize) + + +PROJECT(OpenAL C) + + +SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE) + + +OPTION(ALSA "Check for ALSA backend" ON) +OPTION(OSS "Check for OSS backend" ON) +OPTION(SOLARIS "Check for Solaris backend" ON) +OPTION(SNDIO "Check for SndIO backend" ON) +OPTION(MMDEVAPI "Check for MMDevApi" ON) +OPTION(DSOUND "Check for DirectSound backend" ON) +OPTION(WINMM "Check for Windows Multimedia backend" ON) +OPTION(PORTAUDIO "Check for PortAudio backend" ON) +OPTION(PULSEAUDIO "Check for PulseAudio backend" ON) +OPTION(COREAUDIO "Check for CoreAudio backend" ON) +OPTION(OPENSL "Check for OpenSL backend" ON) +OPTION(WAVE "Enable Wave Writer backend" ON) +OPTION(RECORD "Enable Recorder Backend" ON) + +OPTION(REQUIRE_ALSA "Require ALSA backend" OFF) +OPTION(REQUIRE_OSS "Require OSS backend" OFF) +OPTION(REQUIRE_SOLARIS "Require Solaris backend" OFF) +OPTION(REQUIRE_SNDIO "Require SndIO backend" OFF) +OPTION(REQUIRE_MMDEVAPI "Require MMDevApi" OFF) +OPTION(REQUIRE_DSOUND "Require DirectSound backend" OFF) +OPTION(REQUIRE_WINMM "Require Windows Multimedia backend" OFF) +OPTION(REQUIRE_PORTAUDIO "Require PortAudio backend" OFF) +OPTION(REQUIRE_PULSEAUDIO "Require PulseAudio backend" OFF) +OPTION(REQUIRE_COREAUDIO "Require CoreAudio backend" OFF) +OPTION(REQUIRE_OPENSL "Require OpenSL backend" OFF) + +OPTION(DLOPEN "Check for the dlopen API for loading optional libs" ON) + +OPTION(WERROR "Treat compile warnings as errors" OFF) + +OPTION(UTILS "Build and install utility programs" ON) + +OPTION(ALSOFT_CONFIG "Install alsoft.conf configuration file" OFF) + + +SET(LIBNAME openal) + +SET(LIBTYPE SHARED) + +SET(LIB_MAJOR_VERSION "1") +SET(LIB_MINOR_VERSION "13") +SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}") + +SET(EXPORT_DECL "") + + +# Add definitions, compiler switches, etc. +INCLUDE_DIRECTORIES(OpenAL32/Include include "${OpenAL_BINARY_DIR}") + +ADD_DEFINITIONS(-Winline -Wall) +CHECK_C_COMPILER_FLAG(-Wextra HAVE_W_EXTRA) +IF(HAVE_W_EXTRA) + ADD_DEFINITIONS(-Wextra) +ENDIF() + +IF(WERROR) + ADD_DEFINITIONS(-Werror) +ENDIF() + +SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -O2 -D_DEBUG" CACHE STRING + "Flags used by the compiler during Release with Debug Info builds." + FORCE) +SET(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG" CACHE STRING + "Flags used by the compiler during release minsize builds." + FORCE) +SET(CMAKE_C_FLAGS_RELEASE "-O2 -fomit-frame-pointer -DNDEBUG" CACHE STRING + "Flags used by the compiler during release builds" + FORCE) +SET(CMAKE_C_FLAGS_DEBUG "-g3 -D_DEBUG" CACHE STRING + "Flags used by the compiler during debug builds." + FORCE) + +CHECK_INCLUDE_FILE(fenv.h HAVE_FENV_H) +CHECK_INCLUDE_FILE(float.h HAVE_FLOAT_H) +CHECK_INCLUDE_FILE(ieeefp.h HAVE_IEEEFP_H) +CHECK_INCLUDE_FILE(guiddef.h HAVE_GUIDDEF_H) +IF(NOT HAVE_GUIDDEF_H) + CHECK_INCLUDE_FILE(initguid.h HAVE_INITGUID_H) +ENDIF() +CHECK_INCLUDE_FILE(arm_neon.h HAVE_ARM_NEON_H) + +CHECK_LIBRARY_EXISTS(m powf "" HAVE_POWF) +CHECK_LIBRARY_EXISTS(m sqrtf "" HAVE_SQRTF) +CHECK_LIBRARY_EXISTS(m acosf "" HAVE_ACOSF) +CHECK_LIBRARY_EXISTS(m atanf "" HAVE_ATANF) +CHECK_LIBRARY_EXISTS(m fabsf "" HAVE_FABSF) +IF(HAVE_FENV_H) + CHECK_LIBRARY_EXISTS(m fesetround "" HAVE_FESETROUND) +ENDIF() +IF(HAVE_SQRTF OR HAVE_ACOSF OR HAVE_ATANF OR HAVE_FABSF OR HAVE_FESETROUND) + SET(EXTRA_LIBS m ${EXTRA_LIBS}) +ENDIF() +CHECK_FUNCTION_EXISTS(strtof HAVE_STRTOF) +CHECK_FUNCTION_EXISTS(_controlfp HAVE__CONTROLFP) + +CHECK_FUNCTION_EXISTS(stat HAVE_STAT) +CHECK_FUNCTION_EXISTS(strcasecmp HAVE_STRCASECMP) +IF(NOT HAVE_STRCASECMP) + CHECK_FUNCTION_EXISTS(_stricmp HAVE__STRICMP) + IF(NOT HAVE__STRICMP) + MESSAGE(FATAL_ERROR "No case-insensitive compare function found, please report!") + ENDIF() + + ADD_DEFINITIONS(-Dstrcasecmp=_stricmp) +ENDIF() + +CHECK_FUNCTION_EXISTS(strncasecmp HAVE_STRNCASECMP) +IF(NOT HAVE_STRNCASECMP) + CHECK_FUNCTION_EXISTS(_strnicmp HAVE__STRNICMP) + IF(NOT HAVE__STRNICMP) + MESSAGE(FATAL_ERROR "No case-insensitive size-limitted compare function found, please report!") + ENDIF() + + ADD_DEFINITIONS(-Dstrncasecmp=_strnicmp) +ENDIF() + +CHECK_FUNCTION_EXISTS(snprintf HAVE_SNPRINTF) +IF(NOT HAVE_SNPRINTF) + CHECK_FUNCTION_EXISTS(_snprintf HAVE__SNPRINTF) + IF(NOT HAVE__SNPRINTF) + MESSAGE(FATAL_ERROR "No snprintf function found, please report!") + ENDIF() + + ADD_DEFINITIONS(-Dsnprintf=_snprintf) +ENDIF() + +CHECK_FUNCTION_EXISTS(vsnprintf HAVE_VSNPRINTF) +IF(NOT HAVE_VSNPRINTF) + CHECK_FUNCTION_EXISTS(_vsnprintf HAVE__VSNPRINTF) + IF(NOT HAVE__VSNPRINTF) + MESSAGE(FATAL_ERROR "No vsnprintf function found, please report!") + ENDIF() + + ADD_DEFINITIONS(-Dvsnprintf=_vsnprintf) +ENDIF() + +CHECK_SYMBOL_EXISTS(isfinite math.h HAVE_ISFINITE) +IF(NOT HAVE_ISFINITE) + CHECK_FUNCTION_EXISTS(finite HAVE_FINITE) + IF(NOT HAVE_FINITE) + CHECK_FUNCTION_EXISTS(_finite HAVE__FINITE) + IF(NOT HAVE__FINITE) + MESSAGE(FATAL_ERROR "No isfinite function found, please report!") + ENDIF() + ADD_DEFINITIONS(-Disfinite=_finite) + ELSE() + ADD_DEFINITIONS(-Disfinite=finite) + ENDIF() +ENDIF() + +CHECK_SYMBOL_EXISTS(isnan math.h HAVE_ISNAN) +IF(NOT HAVE_ISNAN) + CHECK_FUNCTION_EXISTS(_isnan HAVE__ISNAN) + IF(NOT HAVE__ISNAN) + MESSAGE(FATAL_ERROR "No isnan function found, please report!") + ENDIF() + + ADD_DEFINITIONS(-Disnan=_isnan) +ENDIF() + + +# Check for the dlopen API (for dynamicly loading backend libs) +IF(DLOPEN) + CHECK_INCLUDE_FILE(dlfcn.h HAVE_DLFCN_H) + IF(HAVE_DLFCN_H) + CHECK_LIBRARY_EXISTS(dl dlopen "" HAVE_LIBDL) + IF(HAVE_LIBDL) + SET(EXTRA_LIBS dl ${EXTRA_LIBS}) + ENDIF() + ENDIF() +ENDIF() + +# Check if we have Windows headers +CHECK_INCLUDE_FILE(windows.h HAVE_WINDOWS_H -D_WIN32_WINNT=0x0500) +IF(NOT HAVE_WINDOWS_H) + CHECK_FUNCTION_EXISTS(gettimeofday HAVE_GETTIMEOFDAY) + IF(NOT HAVE_GETTIMEOFDAY) + MESSAGE(FATAL_ERROR "No timing function found!") + ENDIF() + + CHECK_FUNCTION_EXISTS(nanosleep HAVE_NANOSLEEP) + IF(NOT HAVE_NANOSLEEP) + MESSAGE(FATAL_ERROR "No sleep function found!") + ENDIF() + + CHECK_C_COMPILER_FLAG(-pthread HAVE_PTHREAD) + IF(HAVE_PTHREAD) + ADD_DEFINITIONS(-pthread) + SET(EXTRA_LIBS ${EXTRA_LIBS} -pthread) + ENDIF() + + # We need pthreads outside of Windows + CHECK_INCLUDE_FILE(pthread.h HAVE_PTHREAD_H) + IF(NOT HAVE_PTHREAD_H) + MESSAGE(FATAL_ERROR "PThreads is required for non-Windows builds!") + ENDIF() + # Some systems need pthread_np.h to get recursive mutexes + CHECK_INCLUDE_FILES("pthread.h;pthread_np.h" HAVE_PTHREAD_NP_H) + + # _GNU_SOURCE is needed on some systems for extra attributes, and + # _REENTRANT is needed for libc thread-safety + ADD_DEFINITIONS(-D_GNU_SOURCE=1) + CHECK_LIBRARY_EXISTS(pthread pthread_create "" HAVE_LIBPTHREAD) + IF(HAVE_LIBPTHREAD) + SET(EXTRA_LIBS pthread ${EXTRA_LIBS}) + ENDIF() + + CHECK_LIBRARY_EXISTS(pthread pthread_setschedparam "" HAVE_PTHREAD_SETSCHEDPARAM) + + CHECK_LIBRARY_EXISTS(rt clock_gettime "" HAVE_LIBRT) + IF(HAVE_LIBRT) + SET(EXTRA_LIBS rt ${EXTRA_LIBS}) + ENDIF() +ENDIF() + +# Check for a 64-bit type +CHECK_INCLUDE_FILE(stdint.h HAVE_STDINT_H) +IF(NOT HAVE_STDINT_H) + IF(HAVE_WINDOWS_H) + CHECK_C_SOURCE_COMPILES("\#define _WIN32_WINNT 0x0500 + \#include + __int64 foo; + int main() {return 0;}" HAVE___INT64) + ENDIF() + IF(NOT HAVE___INT64) + IF(NOT SIZEOF_LONG MATCHES "8") + IF(NOT SIZEOF_LONG_LONG MATCHES "8") + MESSAGE(FATAL_ERROR "No 64-bit types found, please report!") + ENDIF() + ENDIF() + ENDIF() +ENDIF() + +# Windows needs winmm for timeGetTime, even if the backend is disabled +CHECK_SHARED_FUNCTION_EXISTS(timeGetTime "windows.h;mmsystem.h" winmm "" HAVE_LIBWINMM) +IF(HAVE_LIBWINMM) + SET(EXTRA_LIBS winmm ${EXTRA_LIBS}) + SET(PKG_CONFIG_LIBS ${PKG_CONFIG_LIBS} -lwinmm) +ENDIF() + + +SET(OPENAL_OBJS OpenAL32/alAuxEffectSlot.c + OpenAL32/alBuffer.c + OpenAL32/alEffect.c + OpenAL32/alError.c + OpenAL32/alExtension.c + OpenAL32/alFilter.c + OpenAL32/alListener.c + OpenAL32/alSource.c + OpenAL32/alState.c + OpenAL32/alThunk.c +) +SET(ALC_OBJS Alc/ALc.c + Alc/ALu.c + Alc/alcConfig.c + Alc/alcDedicated.c + Alc/alcEcho.c + Alc/alcModulator.c + Alc/alcReverb.c + Alc/alcRing.c + Alc/alcThread.c + Alc/bs2b.c + Alc/helpers.c + Alc/hrtf.c + Alc/mixer.c + Alc/panning.c + # Default backends, always available + Alc/backends/loopback.c + Alc/backends/null.c + # RLM: add record device + Alc/backends/record.c +) + +SET(BACKENDS "") +SET(HAVE_ALSA 0) +SET(HAVE_OSS 0) +SET(HAVE_SOLARIS 0) +SET(HAVE_SNDIO 0) +SET(HAVE_DSOUND 0) +SET(HAVE_WINMM 0) +SET(HAVE_PORTAUDIO 0) +SET(HAVE_PULSEAUDIO 0) +SET(HAVE_COREAUDIO 0) +SET(HAVE_OPENSL 0) +SET(HAVE_WAVE 0) + + +# Check for MMDevApi backend +IF(HAVE_WINDOWS_H) + IF(MMDEVAPI) + CHECK_INCLUDE_FILE(mmdeviceapi.h HAVE_MMDEVICEAPI_H) + IF(HAVE_MMDEVICEAPI_H) + SET(HAVE_MMDEVAPI 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/mmdevapi.c) + + SET(BACKENDS "${BACKENDS} MMDevApi,") + ENDIF() + ENDIF() +ENDIF() +IF(REQUIRE_MMDEVAPI AND NOT HAVE_MMDEVAPI) + MESSAGE(FATAL_ERROR "Failed to enabled required MMDevApi backend") +ENDIF() + + +IF(HAVE_WINDOWS_H) + IF(WINMM) + CHECK_INCLUDE_FILES("windows.h;mmsystem.h" HAVE_MMSYSTEM_H -D_WIN32_WINNT=0x0500) + IF(HAVE_MMSYSTEM_H AND HAVE_LIBWINMM) + SET(HAVE_WINMM 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/winmm.c) + SET(BACKENDS "${BACKENDS} WinMM,") + ENDIF() + ENDIF() +ENDIF() +IF(REQUIRE_WINMM AND NOT HAVE_WINMM) + MESSAGE(FATAL_ERROR "Failed to enable required WinMM backend") +ENDIF() + +# Optionally enable the Wave Writer backend +IF(WAVE) + SET(HAVE_WAVE 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/wave.c) + SET(BACKENDS "${BACKENDS} WaveFile,") +ENDIF() + + +# RLM: enable the record backend +SET(ALC_OBJS ${ALC_OBJS} Alc/backends/record.c) +SET(BACKENDS "${BACKENDS} Record,") + + +# This is always available +SET(BACKENDS "${BACKENDS} Null") + +# Needed for openal.pc.in +SET(prefix ${CMAKE_INSTALL_PREFIX}) +SET(exec_prefix "\${prefix}") +SET(libdir "\${exec_prefix}/lib${LIB_SUFFIX}") +SET(bindir "\${exec_prefix}/bin") +SET(includedir "\${prefix}/include") +SET(PACKAGE_VERSION "${LIB_VERSION}") + +# End configuration +CONFIGURE_FILE( + "${OpenAL_SOURCE_DIR}/config.h.in" + "${OpenAL_BINARY_DIR}/config.h") +CONFIGURE_FILE( + "${OpenAL_SOURCE_DIR}/openal.pc.in" + "${OpenAL_BINARY_DIR}/openal.pc" + @ONLY) + + +# RLM link jni the dumb way for now: +INCLUDE_DIRECTORIES("/usr/lib/jvm/java-6-sun-1.6.0.20/include/") +INCLUDE_DIRECTORIES("/usr/lib/jvm/java-6-sun-1.6.0.20/include/linux") + + +# Build a library +ADD_LIBRARY(${LIBNAME} SHARED ${OPENAL_OBJS} ${ALC_OBJS}) +SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES DEFINE_SYMBOL AL_BUILD_LIBRARY + COMPILE_FLAGS -DAL_ALEXT_PROTOTYPES + VERSION ${LIB_VERSION}.0 + SOVERSION ${LIB_MAJOR_VERSION}) + + +TARGET_LINK_LIBRARIES(${LIBNAME} ${EXTRA_LIBS}) + +# Add an install target here +INSTALL(TARGETS ${LIBNAME} + RUNTIME DESTINATION bin + LIBRARY DESTINATION "lib${LIB_SUFFIX}" + ARCHIVE DESTINATION "lib${LIB_SUFFIX}" +) +INSTALL(FILES include/AL/al.h + include/AL/alc.h + include/AL/alext.h + include/AL/efx.h + include/AL/efx-creative.h + DESTINATION include/AL +) +INSTALL(FILES "${OpenAL_BINARY_DIR}/openal.pc" + DESTINATION "lib${LIB_SUFFIX}/pkgconfig") + + +MESSAGE(STATUS "") +MESSAGE(STATUS "Building OpenAL with support for the following backends:") +MESSAGE(STATUS " ${BACKENDS}") +MESSAGE(STATUS "") + +# Install alsoft.conf configuration file + +IF(UTILS) + ADD_EXECUTABLE(openal-info utils/openal-info.c) + TARGET_LINK_LIBRARIES(openal-info ${LIBNAME}) + INSTALL(TARGETS openal-info + RUNTIME DESTINATION bin + LIBRARY DESTINATION "lib${LIB_SUFFIX}" + ARCHIVE DESTINATION "lib${LIB_SUFFIX}" + ) + MESSAGE(STATUS "Building utility programs") + MESSAGE(STATUS "") +ENDIF() + + diff -r 000000000000 -r f9476ff7637e CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CMakeLists.txt Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,715 @@ +# CMake build file list for OpenAL + +CMAKE_MINIMUM_REQUIRED(VERSION 2.4) + +IF(COMMAND CMAKE_POLICY) + CMAKE_POLICY(SET CMP0003 NEW) +ENDIF(COMMAND CMAKE_POLICY) + +SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + +INCLUDE(CheckFunctionExists) +INCLUDE(CheckLibraryExists) +INCLUDE(CheckSharedFunctionExists) +INCLUDE(CheckIncludeFile) +INCLUDE(CheckIncludeFiles) +INCLUDE(CheckSymbolExists) +INCLUDE(CheckCCompilerFlag) +INCLUDE(CheckCSourceCompiles) +INCLUDE(CheckTypeSize) + + +PROJECT(OpenAL C) + + +SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE) + + +OPTION(ALSA "Check for ALSA backend" ON) +OPTION(OSS "Check for OSS backend" ON) +OPTION(SOLARIS "Check for Solaris backend" ON) +OPTION(SNDIO "Check for SndIO backend" ON) +OPTION(MMDEVAPI "Check for MMDevApi" ON) +OPTION(DSOUND "Check for DirectSound backend" ON) +OPTION(WINMM "Check for Windows Multimedia backend" ON) +OPTION(PORTAUDIO "Check for PortAudio backend" ON) +OPTION(PULSEAUDIO "Check for PulseAudio backend" ON) +OPTION(COREAUDIO "Check for CoreAudio backend" ON) +OPTION(OPENSL "Check for OpenSL backend" ON) +OPTION(WAVE "Enable Wave Writer backend" ON) +OPTION(SEND "Enable Send Backend" ON) + +OPTION(REQUIRE_ALSA "Require ALSA backend" OFF) +OPTION(REQUIRE_OSS "Require OSS backend" OFF) +OPTION(REQUIRE_SOLARIS "Require Solaris backend" OFF) +OPTION(REQUIRE_SNDIO "Require SndIO backend" OFF) +OPTION(REQUIRE_MMDEVAPI "Require MMDevApi" OFF) +OPTION(REQUIRE_DSOUND "Require DirectSound backend" OFF) +OPTION(REQUIRE_WINMM "Require Windows Multimedia backend" OFF) +OPTION(REQUIRE_PORTAUDIO "Require PortAudio backend" OFF) +OPTION(REQUIRE_PULSEAUDIO "Require PulseAudio backend" OFF) +OPTION(REQUIRE_COREAUDIO "Require CoreAudio backend" OFF) +OPTION(REQUIRE_OPENSL "Require OpenSL backend" OFF) + +OPTION(DLOPEN "Check for the dlopen API for loading optional libs" ON) + +OPTION(WERROR "Treat compile warnings as errors" OFF) + +OPTION(UTILS "Build and install utility programs" ON) + +OPTION(ALSOFT_CONFIG "Install alsoft.conf configuration file" OFF) + + +IF(WIN32) + SET(LIBNAME OpenAL32) + ADD_DEFINITIONS("-D_WIN32") +ELSE() + SET(LIBNAME openal) +ENDIF() + +IF(NOT LIBTYPE) + SET(LIBTYPE SHARED) +ENDIF() + +SET(LIB_MAJOR_VERSION "1") +SET(LIB_MINOR_VERSION "13") +SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}") + +SET(EXPORT_DECL "") + + +CHECK_TYPE_SIZE("long" SIZEOF_LONG) +CHECK_TYPE_SIZE("long long" SIZEOF_LONG_LONG) +CHECK_TYPE_SIZE("unsigned int" SIZEOF_UINT) +CHECK_TYPE_SIZE("void*" SIZEOF_VOIDP) + + +CHECK_C_SOURCE_COMPILES("int *restrict foo; + int main() {return 0;}" HAVE_RESTRICT) +CHECK_C_SOURCE_COMPILES("int *__restrict foo; + int main() {return 0;}" HAVE___RESTRICT) + + +# Add definitions, compiler switches, etc. +INCLUDE_DIRECTORIES(OpenAL32/Include include "${OpenAL_BINARY_DIR}") + +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING + "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." + FORCE) +ENDIF() +IF(NOT CMAKE_DEBUG_POSTFIX) + SET(CMAKE_DEBUG_POSTFIX "" CACHE STRING + "Library postfix for debug builds. Normally left blank." + FORCE) +ENDIF() + + + +IF(MSVC) + # ??? + SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -D_DEBUG") + SET(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} -DNDEBUG") + SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG") + ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) + ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE) + + IF(NOT DXSDK_DIR) + STRING(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}") + ELSE() + STRING(REGEX REPLACE "\\\\" "/" DXSDK_DIR "${DXSDK_DIR}") + ENDIF() + IF(DXSDK_DIR) + MESSAGE(STATUS "Using DirectX SDK directory: ${DXSDK_DIR}") + SET(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} "${DXSDK_DIR}/Include") + INCLUDE_DIRECTORIES("${DXSDK_DIR}/Include") + LINK_DIRECTORIES("${DXSDK_DIR}/Lib") + ENDIF() + + OPTION(FORCE_STATIC_VCRT "Force /MT for static VC runtimes" OFF) + IF(FORCE_STATIC_VCRT) + FOREACH(flag_var + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO) + IF(${flag_var} MATCHES "/MD") + STRING(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + ENDIF() + ENDFOREACH(flag_var) + ENDIF() +ELSE() + ADD_DEFINITIONS(-Winline -Wall) + CHECK_C_COMPILER_FLAG(-Wextra HAVE_W_EXTRA) + IF(HAVE_W_EXTRA) + ADD_DEFINITIONS(-Wextra) + ENDIF() + + IF(WERROR) + ADD_DEFINITIONS(-Werror) + ENDIF() + + SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -O2 -D_DEBUG" CACHE STRING + "Flags used by the compiler during Release with Debug Info builds." + FORCE) + SET(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG" CACHE STRING + "Flags used by the compiler during release minsize builds." + FORCE) + SET(CMAKE_C_FLAGS_RELEASE "-O2 -fomit-frame-pointer -DNDEBUG" CACHE STRING + "Flags used by the compiler during release builds" + FORCE) + SET(CMAKE_C_FLAGS_DEBUG "-g3 -D_DEBUG" CACHE STRING + "Flags used by the compiler during debug builds." + FORCE) + + CHECK_C_SOURCE_COMPILES("int foo() __attribute__((destructor)); + int main() {return 0;}" HAVE_GCC_DESTRUCTOR) +ENDIF() + +# Set visibility/export options if available +IF(WIN32) + SET(EXPORT_DECL "__declspec(dllexport)") + + OPTION(WINE "Enable use of Wine headers when compiling" OFF) + IF(WINE) + FIND_PATH(WINE_INCLUDE_DIR library.h + PATHS + /usr/include/wine + /usr/local/include/wine + CMAKE_FIND_ROOT_PATH_BOTH) + IF(WINE_INCLUDE_DIR) + MESSAGE(STATUS "Found Wine header files - ${WINE_INCLUDE_DIR}" ) + INCLUDE_DIRECTORIES("${WINE_INCLUDE_DIR}/windows") + SET(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} "${WINE_INCLUDE_DIR}/windows") + ELSE() + MESSAGE(STATUS "Could not find Wine header files" ) + ENDIF() + ENDIF() +ELSE() + CHECK_C_COMPILER_FLAG(-fvisibility=internal HAVE_VISIBILITY_SWITCH) + CHECK_C_SOURCE_COMPILES("int foo() __attribute__((visibility(\"protected\"))); + int main() {return 0;}" HAVE_GCC_VISIBILITY) + IF(HAVE_VISIBILITY_SWITCH AND HAVE_GCC_VISIBILITY) + #ADD_DEFINITIONS(-fvisibility=internal) + #SET(EXPORT_DECL "__attribute__((visibility(\"protected\")))") + ENDIF() +ENDIF() + +CHECK_C_SOURCE_COMPILES("int foo(const char *str, ...) __attribute__((format(printf, 1, 2))); + int main() {return 0;}" HAVE_GCC_FORMAT) + +CHECK_INCLUDE_FILE(fenv.h HAVE_FENV_H) +CHECK_INCLUDE_FILE(float.h HAVE_FLOAT_H) +CHECK_INCLUDE_FILE(ieeefp.h HAVE_IEEEFP_H) +CHECK_INCLUDE_FILE(guiddef.h HAVE_GUIDDEF_H) +IF(NOT HAVE_GUIDDEF_H) + CHECK_INCLUDE_FILE(initguid.h HAVE_INITGUID_H) +ENDIF() +CHECK_INCLUDE_FILE(arm_neon.h HAVE_ARM_NEON_H) + +CHECK_LIBRARY_EXISTS(m powf "" HAVE_POWF) +CHECK_LIBRARY_EXISTS(m sqrtf "" HAVE_SQRTF) +CHECK_LIBRARY_EXISTS(m acosf "" HAVE_ACOSF) +CHECK_LIBRARY_EXISTS(m atanf "" HAVE_ATANF) +CHECK_LIBRARY_EXISTS(m fabsf "" HAVE_FABSF) +IF(HAVE_FENV_H) + CHECK_LIBRARY_EXISTS(m fesetround "" HAVE_FESETROUND) +ENDIF() +IF(HAVE_SQRTF OR HAVE_ACOSF OR HAVE_ATANF OR HAVE_FABSF OR HAVE_FESETROUND) + SET(EXTRA_LIBS m ${EXTRA_LIBS}) +ENDIF() +CHECK_FUNCTION_EXISTS(strtof HAVE_STRTOF) +CHECK_FUNCTION_EXISTS(_controlfp HAVE__CONTROLFP) + +CHECK_FUNCTION_EXISTS(stat HAVE_STAT) +CHECK_FUNCTION_EXISTS(strcasecmp HAVE_STRCASECMP) +IF(NOT HAVE_STRCASECMP) + CHECK_FUNCTION_EXISTS(_stricmp HAVE__STRICMP) + IF(NOT HAVE__STRICMP) + MESSAGE(FATAL_ERROR "No case-insensitive compare function found, please report!") + ENDIF() + + ADD_DEFINITIONS(-Dstrcasecmp=_stricmp) +ENDIF() + +CHECK_FUNCTION_EXISTS(strncasecmp HAVE_STRNCASECMP) +IF(NOT HAVE_STRNCASECMP) + CHECK_FUNCTION_EXISTS(_strnicmp HAVE__STRNICMP) + IF(NOT HAVE__STRNICMP) + MESSAGE(FATAL_ERROR "No case-insensitive size-limitted compare function found, please report!") + ENDIF() + + ADD_DEFINITIONS(-Dstrncasecmp=_strnicmp) +ENDIF() + +CHECK_FUNCTION_EXISTS(snprintf HAVE_SNPRINTF) +IF(NOT HAVE_SNPRINTF) + CHECK_FUNCTION_EXISTS(_snprintf HAVE__SNPRINTF) + IF(NOT HAVE__SNPRINTF) + MESSAGE(FATAL_ERROR "No snprintf function found, please report!") + ENDIF() + + ADD_DEFINITIONS(-Dsnprintf=_snprintf) +ENDIF() + +CHECK_FUNCTION_EXISTS(vsnprintf HAVE_VSNPRINTF) +IF(NOT HAVE_VSNPRINTF) + CHECK_FUNCTION_EXISTS(_vsnprintf HAVE__VSNPRINTF) + IF(NOT HAVE__VSNPRINTF) + MESSAGE(FATAL_ERROR "No vsnprintf function found, please report!") + ENDIF() + + ADD_DEFINITIONS(-Dvsnprintf=_vsnprintf) +ENDIF() + +CHECK_SYMBOL_EXISTS(isfinite math.h HAVE_ISFINITE) +IF(NOT HAVE_ISFINITE) + CHECK_FUNCTION_EXISTS(finite HAVE_FINITE) + IF(NOT HAVE_FINITE) + CHECK_FUNCTION_EXISTS(_finite HAVE__FINITE) + IF(NOT HAVE__FINITE) + MESSAGE(FATAL_ERROR "No isfinite function found, please report!") + ENDIF() + ADD_DEFINITIONS(-Disfinite=_finite) + ELSE() + ADD_DEFINITIONS(-Disfinite=finite) + ENDIF() +ENDIF() + +CHECK_SYMBOL_EXISTS(isnan math.h HAVE_ISNAN) +IF(NOT HAVE_ISNAN) + CHECK_FUNCTION_EXISTS(_isnan HAVE__ISNAN) + IF(NOT HAVE__ISNAN) + MESSAGE(FATAL_ERROR "No isnan function found, please report!") + ENDIF() + + ADD_DEFINITIONS(-Disnan=_isnan) +ENDIF() + + +# Check for the dlopen API (for dynamicly loading backend libs) +IF(DLOPEN) + CHECK_INCLUDE_FILE(dlfcn.h HAVE_DLFCN_H) + IF(HAVE_DLFCN_H) + CHECK_LIBRARY_EXISTS(dl dlopen "" HAVE_LIBDL) + IF(HAVE_LIBDL) + SET(EXTRA_LIBS dl ${EXTRA_LIBS}) + ENDIF() + ENDIF() +ENDIF() + +# Check if we have Windows headers +CHECK_INCLUDE_FILE(windows.h HAVE_WINDOWS_H -D_WIN32_WINNT=0x0500) +IF(NOT HAVE_WINDOWS_H) + CHECK_FUNCTION_EXISTS(gettimeofday HAVE_GETTIMEOFDAY) + IF(NOT HAVE_GETTIMEOFDAY) + MESSAGE(FATAL_ERROR "No timing function found!") + ENDIF() + + CHECK_FUNCTION_EXISTS(nanosleep HAVE_NANOSLEEP) + IF(NOT HAVE_NANOSLEEP) + MESSAGE(FATAL_ERROR "No sleep function found!") + ENDIF() + + CHECK_C_COMPILER_FLAG(-pthread HAVE_PTHREAD) + IF(HAVE_PTHREAD) + ADD_DEFINITIONS(-pthread) + SET(EXTRA_LIBS ${EXTRA_LIBS} -pthread) + ENDIF() + + # We need pthreads outside of Windows + CHECK_INCLUDE_FILE(pthread.h HAVE_PTHREAD_H) + IF(NOT HAVE_PTHREAD_H) + MESSAGE(FATAL_ERROR "PThreads is required for non-Windows builds!") + ENDIF() + # Some systems need pthread_np.h to get recursive mutexes + CHECK_INCLUDE_FILES("pthread.h;pthread_np.h" HAVE_PTHREAD_NP_H) + + # _GNU_SOURCE is needed on some systems for extra attributes, and + # _REENTRANT is needed for libc thread-safety + ADD_DEFINITIONS(-D_GNU_SOURCE=1) + CHECK_LIBRARY_EXISTS(pthread pthread_create "" HAVE_LIBPTHREAD) + IF(HAVE_LIBPTHREAD) + SET(EXTRA_LIBS pthread ${EXTRA_LIBS}) + ENDIF() + + CHECK_LIBRARY_EXISTS(pthread pthread_setschedparam "" HAVE_PTHREAD_SETSCHEDPARAM) + + CHECK_LIBRARY_EXISTS(rt clock_gettime "" HAVE_LIBRT) + IF(HAVE_LIBRT) + SET(EXTRA_LIBS rt ${EXTRA_LIBS}) + ENDIF() +ENDIF() + +# Check for a 64-bit type +CHECK_INCLUDE_FILE(stdint.h HAVE_STDINT_H) +IF(NOT HAVE_STDINT_H) + IF(HAVE_WINDOWS_H) + CHECK_C_SOURCE_COMPILES("\#define _WIN32_WINNT 0x0500 + \#include + __int64 foo; + int main() {return 0;}" HAVE___INT64) + ENDIF() + IF(NOT HAVE___INT64) + IF(NOT SIZEOF_LONG MATCHES "8") + IF(NOT SIZEOF_LONG_LONG MATCHES "8") + MESSAGE(FATAL_ERROR "No 64-bit types found, please report!") + ENDIF() + ENDIF() + ENDIF() +ENDIF() + +# Windows needs winmm for timeGetTime, even if the backend is disabled +CHECK_SHARED_FUNCTION_EXISTS(timeGetTime "windows.h;mmsystem.h" winmm "" HAVE_LIBWINMM) +IF(HAVE_LIBWINMM) + SET(EXTRA_LIBS winmm ${EXTRA_LIBS}) + SET(PKG_CONFIG_LIBS ${PKG_CONFIG_LIBS} -lwinmm) +ENDIF() + + +SET(OPENAL_OBJS OpenAL32/alAuxEffectSlot.c + OpenAL32/alBuffer.c + OpenAL32/alEffect.c + OpenAL32/alError.c + OpenAL32/alExtension.c + OpenAL32/alFilter.c + OpenAL32/alListener.c + OpenAL32/alSource.c + OpenAL32/alState.c + OpenAL32/alThunk.c +) +SET(ALC_OBJS Alc/ALc.c + Alc/ALu.c + Alc/alcConfig.c + Alc/alcDedicated.c + Alc/alcEcho.c + Alc/alcModulator.c + Alc/alcReverb.c + Alc/alcRing.c + Alc/alcThread.c + Alc/bs2b.c + Alc/helpers.c + Alc/hrtf.c + Alc/mixer.c + Alc/panning.c + # Default backends, always available + Alc/backends/loopback.c + Alc/backends/null.c + # RLM: add send device + Alc/backends/send.c +) + +SET(BACKENDS "") +SET(HAVE_ALSA 0) +SET(HAVE_OSS 0) +SET(HAVE_SOLARIS 0) +SET(HAVE_SNDIO 0) +SET(HAVE_DSOUND 0) +SET(HAVE_WINMM 0) +SET(HAVE_PORTAUDIO 0) +SET(HAVE_PULSEAUDIO 0) +SET(HAVE_COREAUDIO 0) +SET(HAVE_OPENSL 0) +SET(HAVE_WAVE 0) + + +# Check ALSA backend +IF(ALSA) + CHECK_INCLUDE_FILE(alsa/asoundlib.h HAVE_ALSA_ASOUNDLIB_H) + IF(HAVE_ALSA_ASOUNDLIB_H) + CHECK_SHARED_FUNCTION_EXISTS(snd_pcm_open "alsa/asoundlib.h" asound "" HAVE_LIBASOUND) + IF(HAVE_LIBASOUND OR HAVE_DLFCN_H OR WIN32) + SET(HAVE_ALSA 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/alsa.c) + IF(HAVE_DLFCN_H OR WIN32) + SET(BACKENDS "${BACKENDS} ALSA,") + ELSE() + SET(BACKENDS "${BACKENDS} ALSA \(linked\),") + SET(EXTRA_LIBS asound ${EXTRA_LIBS}) + ENDIF() + ENDIF() + ENDIF() +ENDIF() +IF(REQUIRE_ALSA AND NOT HAVE_ALSA) + MESSAGE(FATAL_ERROR "Failed to enabled required ALSA backend") +ENDIF() + +# Check OSS backend +IF(OSS) + CHECK_INCLUDE_FILE(sys/soundcard.h HAVE_SYS_SOUNDCARD_H) + IF(HAVE_SYS_SOUNDCARD_H) + SET(HAVE_OSS 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/oss.c) + SET(BACKENDS "${BACKENDS} OSS,") + ENDIF() +ENDIF() +IF(REQUIRE_OSS AND NOT HAVE_OSS) + MESSAGE(FATAL_ERROR "Failed to enabled required OSS backend") +ENDIF() + +# Check Solaris backend +IF(SOLARIS) + CHECK_INCLUDE_FILE(sys/audioio.h HAVE_SYS_AUDIOIO_H) + IF(HAVE_SYS_AUDIOIO_H) + SET(HAVE_SOLARIS 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/solaris.c) + SET(BACKENDS "${BACKENDS} Solaris,") + ENDIF() +ENDIF() +IF(REQUIRE_SOLARIS AND NOT HAVE_SOLARIS) + MESSAGE(FATAL_ERROR "Failed to enabled required Solaris backend") +ENDIF() + +# Check SndIO backend +IF(SNDIO) + CHECK_INCLUDE_FILE(sndio.h HAVE_SNDIO_H) + IF(HAVE_SNDIO_H) + CHECK_SHARED_FUNCTION_EXISTS(sio_open "sndio.h" sndio "" HAVE_LIBSNDIO) + IF(HAVE_LIBSNDIO OR HAVE_DLFCN_H OR WIN32) + SET(HAVE_SNDIO 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/sndio.c) + IF(HAVE_DLFCN_H OR WIN32) + SET(BACKENDS "${BACKENDS} SndIO,") + ELSE() + SET(BACKENDS "${BACKENDS} SndIO \(linked\),") + SET(EXTRA_LIBS sndio ${EXTRA_LIBS}) + ENDIF() + ENDIF() + ENDIF() +ENDIF() +IF(REQUIRE_SNDIO AND NOT HAVE_SNDIO) + MESSAGE(FATAL_ERROR "Failed to enabled required SndIO backend") +ENDIF() + +# Check for MMDevApi backend +IF(HAVE_WINDOWS_H) + IF(MMDEVAPI) + CHECK_INCLUDE_FILE(mmdeviceapi.h HAVE_MMDEVICEAPI_H) + IF(HAVE_MMDEVICEAPI_H) + SET(HAVE_MMDEVAPI 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/mmdevapi.c) + + SET(BACKENDS "${BACKENDS} MMDevApi,") + ENDIF() + ENDIF() +ENDIF() +IF(REQUIRE_MMDEVAPI AND NOT HAVE_MMDEVAPI) + MESSAGE(FATAL_ERROR "Failed to enabled required MMDevApi backend") +ENDIF() + +# Check DSound/MMSystem backend +IF(DSOUND) + CHECK_INCLUDE_FILE(dsound.h HAVE_DSOUND_H) + IF(HAVE_DSOUND_H) + CHECK_SHARED_FUNCTION_EXISTS(DirectSoundCreate "dsound.h" dsound "" HAVE_LIBDSOUND) + IF(HAVE_LIBDSOUND OR HAVE_DLFCN_H OR WIN32) + SET(HAVE_DSOUND 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/dsound.c) + + IF(HAVE_DLFCN_H OR WIN32) + SET(BACKENDS "${BACKENDS} DirectSound,") + ELSE() + SET(BACKENDS "${BACKENDS} DirectSound \(linked\),") + SET(EXTRA_LIBS dsound ${EXTRA_LIBS}) + ENDIF() + ENDIF() + ENDIF() +ENDIF() +IF(REQUIRE_DSOUND AND NOT HAVE_DSOUND) + MESSAGE(FATAL_ERROR "Failed to enabled required DSound backend") +ENDIF() + +IF(HAVE_WINDOWS_H) + IF(WINMM) + CHECK_INCLUDE_FILES("windows.h;mmsystem.h" HAVE_MMSYSTEM_H -D_WIN32_WINNT=0x0500) + IF(HAVE_MMSYSTEM_H AND HAVE_LIBWINMM) + SET(HAVE_WINMM 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/winmm.c) + SET(BACKENDS "${BACKENDS} WinMM,") + ENDIF() + ENDIF() +ENDIF() +IF(REQUIRE_WINMM AND NOT HAVE_WINMM) + MESSAGE(FATAL_ERROR "Failed to enable required WinMM backend") +ENDIF() + +# Check PortAudio backend +IF(PORTAUDIO) + CHECK_INCLUDE_FILE(portaudio.h HAVE_PORTAUDIO_H) + IF(HAVE_PORTAUDIO_H) + CHECK_SHARED_FUNCTION_EXISTS(Pa_Initialize "portaudio.h" portaudio "" HAVE_LIBPORTAUDIO) + IF(HAVE_LIBPORTAUDIO OR HAVE_DLFCN_H OR WIN32) + SET(HAVE_PORTAUDIO 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/portaudio.c) + IF(HAVE_DLFCN_H OR WIN32) + SET(BACKENDS "${BACKENDS} PortAudio,") + ELSE() + SET(BACKENDS "${BACKENDS} PortAudio \(linked\),") + SET(EXTRA_LIBS portaudio ${EXTRA_LIBS}) + ENDIF() + ENDIF() + ENDIF() +ENDIF() +IF(REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO) + MESSAGE(FATAL_ERROR "Failed to enabled required PortAudio backend") +ENDIF() + +# Check PulseAudio backend +IF(PULSEAUDIO) + CHECK_INCLUDE_FILE(pulse/pulseaudio.h HAVE_PULSE_PULSEAUDIO_H) + IF(HAVE_PULSE_PULSEAUDIO_H) + CHECK_SHARED_FUNCTION_EXISTS(pa_context_new "pulse/pulseaudio.h" pulse "" HAVE_LIBPULSE) + IF(HAVE_LIBPULSE OR HAVE_DLFCN_H OR WIN32) + SET(HAVE_PULSEAUDIO 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/pulseaudio.c) + IF(HAVE_DLFCN_H OR WIN32) + SET(BACKENDS "${BACKENDS} PulseAudio,") + ELSE() + SET(BACKENDS "${BACKENDS} PulseAudio \(linked\),") + SET(EXTRA_LIBS pulse ${EXTRA_LIBS}) + ENDIF() + ENDIF() + ENDIF() +ENDIF() +IF(REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO) + MESSAGE(FATAL_ERROR "Failed to enabled required PulseAudio backend") +ENDIF() + +# Check CoreAudio backend +IF(COREAUDIO) + CHECK_INCLUDE_FILE(/System/Library/Frameworks/CoreAudio.framework/Headers/CoreAudio.h HAVE_COREAUDIO_FRAMEWORK) + IF(HAVE_COREAUDIO_FRAMEWORK) + SET(HAVE_COREAUDIO 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/coreaudio.c) + SET(BACKENDS "${BACKENDS} CoreAudio,") + SET(EXTRA_LIBS /System/Library/Frameworks/CoreAudio.framework ${EXTRA_LIBS}) + SET(EXTRA_LIBS /System/Library/Frameworks/AudioUnit.framework ${EXTRA_LIBS}) + SET(EXTRA_LIBS /System/Library/Frameworks/ApplicationServices.framework ${EXTRA_LIBS}) + ENDIF() +ENDIF() +IF(REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO) + MESSAGE(FATAL_ERROR "Failed to enabled required CoreAudio backend") +ENDIF() + +# Check for OpenSL (Android) backend +IF(OPENSL) + CHECK_INCLUDE_FILE(SLES/OpenSLES_Android.h HAVE_SLES_OPENSLES_ANDROID_H) + IF(HAVE_SLES_OPENSLES_ANDROID_H) + CHECK_SHARED_FUNCTION_EXISTS(slCreateEngine "SLES/OpenSLES.h" OpenSLES "" HAVE_LIBOPENSLES) + IF(HAVE_LIBOPENSLES) + SET(HAVE_OPENSL 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/opensl.c) + SET(BACKENDS "${BACKENDS} OpenSL,") + SET(EXTRA_LIBS OpenSLES ${EXTRA_LIBS}) + ENDIF() + ENDIF() +ENDIF() +IF(REQUIRE_OPENSL AND NOT HAVE_OPENSL) + MESSAGE(FATAL_ERROR "Failed to enable required OpenSL backend") +ENDIF() + +# Optionally enable the Wave Writer backend +IF(WAVE) + SET(HAVE_WAVE 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/wave.c) + SET(BACKENDS "${BACKENDS} WaveFile,") +ENDIF() + + +SET(ALC_OBJS ${ALC_OBJS} Alc/backends/send.c) +SET(BACKENDS "${BACKENDS} Send,") + + +# This is always available +SET(BACKENDS "${BACKENDS} Null") + +IF(LIBTYPE STREQUAL "STATIC") + ADD_DEFINITIONS(-DAL_LIBTYPE_STATIC) + SET(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC ${PKG_CONFIG_CFLAGS}) +ENDIF() + +# Needed for openal.pc.in +SET(prefix ${CMAKE_INSTALL_PREFIX}) +SET(exec_prefix "\${prefix}") +SET(libdir "\${exec_prefix}/lib${LIB_SUFFIX}") +SET(bindir "\${exec_prefix}/bin") +SET(includedir "\${prefix}/include") +SET(PACKAGE_VERSION "${LIB_VERSION}") + +# End configuration +CONFIGURE_FILE( + "${OpenAL_SOURCE_DIR}/config.h.in" + "${OpenAL_BINARY_DIR}/config.h") +CONFIGURE_FILE( + "${OpenAL_SOURCE_DIR}/openal.pc.in" + "${OpenAL_BINARY_DIR}/openal.pc" + @ONLY) + +# Build a library +ADD_LIBRARY(${LIBNAME} ${LIBTYPE} ${OPENAL_OBJS} ${ALC_OBJS}) +SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES DEFINE_SYMBOL AL_BUILD_LIBRARY + COMPILE_FLAGS -DAL_ALEXT_PROTOTYPES + VERSION ${LIB_VERSION}.0 + SOVERSION ${LIB_MAJOR_VERSION}) +IF(WIN32 AND NOT LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES PREFIX "") +ENDIF() + +# RLM link jni the dumb way for now: + +INCLUDE_DIRECTORIES("/opt/jdk/include/") +INCLUDE_DIRECTORIES("/opt/jdk/include/linux") +TARGET_LINK_LIBRARIES(${LIBNAME} ${EXTRA_LIBS}) + +# Add an install target here +INSTALL(TARGETS ${LIBNAME} + RUNTIME DESTINATION bin + LIBRARY DESTINATION "lib${LIB_SUFFIX}" + ARCHIVE DESTINATION "lib${LIB_SUFFIX}" +) +INSTALL(FILES include/AL/al.h + include/AL/alc.h + include/AL/alext.h + include/AL/efx.h + include/AL/efx-creative.h + DESTINATION include/AL +) +INSTALL(FILES "${OpenAL_BINARY_DIR}/openal.pc" + DESTINATION "lib${LIB_SUFFIX}/pkgconfig") + + +MESSAGE(STATUS "") +MESSAGE(STATUS "Building OpenAL with support for the following backends:") +MESSAGE(STATUS " ${BACKENDS}") +MESSAGE(STATUS "") + +IF(WIN32) + IF(NOT HAVE_DSOUND) + MESSAGE(STATUS "WARNING: Building the Windows version without DirectSound output") + MESSAGE(STATUS " This is probably NOT what you want!") + MESSAGE(STATUS "") + ENDIF() +ENDIF() + +# Install alsoft.conf configuration file +IF(ALSOFT_CONFIG) + INSTALL(FILES alsoftrc.sample + DESTINATION /etc/openal + RENAME alsoft.conf + ) + MESSAGE(STATUS "Installing sample alsoft.conf") + MESSAGE(STATUS "") +ENDIF() + +IF(UTILS) + ADD_EXECUTABLE(openal-info utils/openal-info.c) + TARGET_LINK_LIBRARIES(openal-info ${LIBNAME}) + INSTALL(TARGETS openal-info + RUNTIME DESTINATION bin + LIBRARY DESTINATION "lib${LIB_SUFFIX}" + ARCHIVE DESTINATION "lib${LIB_SUFFIX}" + ) + MESSAGE(STATUS "Building utility programs") + MESSAGE(STATUS "") +ENDIF() + + diff -r 000000000000 -r f9476ff7637e COPYING --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/COPYING Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,484 @@ + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/alAuxEffectSlot.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/alAuxEffectSlot.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,66 @@ +#ifndef _AL_AUXEFFECTSLOT_H_ +#define _AL_AUXEFFECTSLOT_H_ + +#include "AL/al.h" +#include "alEffect.h" +#include "alFilter.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ALeffectState ALeffectState; + +typedef struct ALeffectslot +{ + ALeffect effect; + + ALfloat Gain; + ALboolean AuxSendAuto; + + ALboolean NeedsUpdate; + ALeffectState *EffectState; + + ALfloat WetBuffer[BUFFERSIZE]; + + ALfloat ClickRemoval[1]; + ALfloat PendingClicks[1]; + + ALuint refcount; + + // Index to itself + ALuint effectslot; + + struct ALeffectslot *next; +} ALeffectslot; + + +ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context); + + +struct ALeffectState { + ALvoid (*Destroy)(ALeffectState *State); + ALboolean (*DeviceUpdate)(ALeffectState *State, ALCdevice *Device); + ALvoid (*Update)(ALeffectState *State, ALCcontext *Context, const ALeffectslot *Slot); + ALvoid (*Process)(ALeffectState *State, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]); +}; + +ALeffectState *NoneCreate(void); +ALeffectState *EAXVerbCreate(void); +ALeffectState *VerbCreate(void); +ALeffectState *EchoCreate(void); +ALeffectState *ModulatorCreate(void); +ALeffectState *DedicatedDLGCreate(void); +ALeffectState *DedicatedLFECreate(void); + +#define ALEffect_Destroy(a) ((a)->Destroy((a))) +#define ALEffect_DeviceUpdate(a,b) ((a)->DeviceUpdate((a),(b))) +#define ALEffect_Update(a,b,c) ((a)->Update((a),(b),(c))) +#define ALEffect_Process(a,b,c,d,e) ((a)->Process((a),(b),(c),(d),(e))) + + +#ifdef __cplusplus +} +#endif + +#endif diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/alBuffer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/alBuffer.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,100 @@ +#ifndef _AL_BUFFER_H_ +#define _AL_BUFFER_H_ + +#include "AL/al.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* User formats */ +enum UserFmtType { + UserFmtByte = AL_BYTE, + UserFmtUByte = AL_UNSIGNED_BYTE, + UserFmtShort = AL_SHORT, + UserFmtUShort = AL_UNSIGNED_SHORT, + UserFmtInt = AL_INT, + UserFmtUInt = AL_UNSIGNED_INT, + UserFmtFloat = AL_FLOAT, + UserFmtDouble = AL_DOUBLE, + UserFmtMulaw = AL_MULAW, + UserFmtIMA4 = AL_IMA4, + UserFmtByte3 = AL_BYTE3, + UserFmtUByte3 = AL_UNSIGNED_BYTE3, +}; +enum UserFmtChannels { + UserFmtMono = AL_MONO, + UserFmtStereo = AL_STEREO, + UserFmtRear = AL_REAR, + UserFmtQuad = AL_QUAD, + UserFmtX51 = AL_5POINT1, /* (WFX order) */ + UserFmtX61 = AL_6POINT1, /* (WFX order) */ + UserFmtX71 = AL_7POINT1 /* (WFX order) */ +}; + +ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, + enum UserFmtType *type); +ALuint BytesFromUserFmt(enum UserFmtType type); +ALuint ChannelsFromUserFmt(enum UserFmtChannels chans); +static __inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, + enum UserFmtType type) +{ + return ChannelsFromUserFmt(chans) * BytesFromUserFmt(type); +} + + +/* Storable formats */ +enum FmtType { + FmtByte = UserFmtByte, + FmtShort = UserFmtShort, + FmtFloat = UserFmtFloat, +}; +enum FmtChannels { + FmtMono = UserFmtMono, + FmtStereo = UserFmtStereo, + FmtRear = UserFmtRear, + FmtQuad = UserFmtQuad, + FmtX51 = UserFmtX51, + FmtX61 = UserFmtX61, + FmtX71 = UserFmtX71, +}; + +ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type); +ALuint BytesFromFmt(enum FmtType type); +ALuint ChannelsFromFmt(enum FmtChannels chans); +static __inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type) +{ + return ChannelsFromFmt(chans) * BytesFromFmt(type); +} + + +typedef struct ALbuffer +{ + ALvoid *data; + ALsizei size; + + ALsizei Frequency; + enum FmtChannels FmtChannels; + enum FmtType FmtType; + + enum UserFmtChannels OriginalChannels; + enum UserFmtType OriginalType; + ALsizei OriginalSize; + ALsizei OriginalAlign; + + ALsizei LoopStart; + ALsizei LoopEnd; + + ALuint refcount; // Number of sources using this buffer (deletion can only occur when this is 0) + + // Index to itself + ALuint buffer; +} ALbuffer; + +ALvoid ReleaseALBuffers(ALCdevice *device); + +#ifdef __cplusplus +} +#endif + +#endif diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/alEffect.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/alEffect.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,93 @@ +#ifndef _AL_EFFECT_H_ +#define _AL_EFFECT_H_ + +#include "AL/al.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + EAXREVERB = 0, + REVERB, + ECHO, + MODULATOR, + DEDICATED, + + MAX_EFFECTS +}; +extern ALboolean DisabledEffects[MAX_EFFECTS]; + +extern ALfloat ReverbBoost; +extern ALboolean EmulateEAXReverb; + +typedef struct ALeffect +{ + // Effect type (AL_EFFECT_NULL, ...) + ALenum type; + + union { + struct { + // Shared Reverb Properties + ALfloat Density; + ALfloat Diffusion; + ALfloat Gain; + ALfloat GainHF; + ALfloat DecayTime; + ALfloat DecayHFRatio; + ALfloat ReflectionsGain; + ALfloat ReflectionsDelay; + ALfloat LateReverbGain; + ALfloat LateReverbDelay; + ALfloat AirAbsorptionGainHF; + ALfloat RoomRolloffFactor; + ALboolean DecayHFLimit; + + // Additional EAX Reverb Properties + ALfloat GainLF; + ALfloat DecayLFRatio; + ALfloat ReflectionsPan[3]; + ALfloat LateReverbPan[3]; + ALfloat EchoTime; + ALfloat EchoDepth; + ALfloat ModulationTime; + ALfloat ModulationDepth; + ALfloat HFReference; + ALfloat LFReference; + } Reverb; + + struct { + ALfloat Delay; + ALfloat LRDelay; + + ALfloat Damping; + ALfloat Feedback; + + ALfloat Spread; + } Echo; + + struct { + ALfloat Frequency; + ALfloat HighPassCutoff; + ALint Waveform; + } Modulator; + + struct { + ALfloat Gain; + } Dedicated; + } Params; + + // Index to itself + ALuint effect; +} ALeffect; + +static __inline ALboolean IsReverbEffect(ALenum type) +{ return type == AL_EFFECT_REVERB || type == AL_EFFECT_EAXREVERB; } + +ALvoid ReleaseALEffects(ALCdevice *device); + +#ifdef __cplusplus +} +#endif + +#endif diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/alError.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/alError.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,17 @@ +#ifndef _AL_ERROR_H_ +#define _AL_ERROR_H_ + +#include "AL/al.h" +#include "AL/alc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ALvoid alSetError(ALCcontext *Context, ALenum errorCode); + +#ifdef __cplusplus +} +#endif + +#endif diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/alFilter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/alFilter.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,92 @@ +#ifndef _AL_FILTER_H_ +#define _AL_FILTER_H_ + +#include "AL/al.h" +#include "alu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + ALfloat coeff; +#ifndef _MSC_VER + ALfloat history[0]; +#else + ALfloat history[1]; +#endif +} FILTER; + +static __inline ALfloat lpFilter2P(FILTER *iir, ALuint offset, ALfloat input) +{ + ALfloat *history = &iir->history[offset*2]; + ALfloat a = iir->coeff; + ALfloat output = input; + + output = output + (history[0]-output)*a; + history[0] = output; + output = output + (history[1]-output)*a; + history[1] = output; + + return output; +} +static __inline ALfloat lpFilter1P(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 output; +} + +static __inline ALfloat lpFilter2PC(const FILTER *iir, ALuint offset, ALfloat input) +{ + const ALfloat *history = &iir->history[offset*2]; + ALfloat a = iir->coeff; + ALfloat output = input; + + output = output + (history[0]-output)*a; + output = output + (history[1]-output)*a; + + return output; +} +static __inline ALfloat lpFilter1PC(FILTER *iir, ALuint offset, ALfloat input) +{ + const ALfloat *history = &iir->history[offset]; + ALfloat a = iir->coeff; + ALfloat output = input; + + output = output + (history[0]-output)*a; + + return output; +} + +/* Calculates the low-pass filter coefficient given the pre-scaled gain and + * cos(w) value. Note that g should be pre-scaled (sqr(gain) for one-pole, + * sqrt(gain) for four-pole, etc) */ +ALfloat lpCoeffCalc(ALfloat g, ALfloat cw); + + +typedef struct ALfilter +{ + // Filter type (AL_FILTER_NULL, ...) + ALenum type; + + ALfloat Gain; + ALfloat GainHF; + + // Index to itself + ALuint filter; +} ALfilter; + + +ALvoid ReleaseALFilters(ALCdevice *device); + +#ifdef __cplusplus +} +#endif + +#endif diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/alListener.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/alListener.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,24 @@ +#ifndef _AL_LISTENER_H_ +#define _AL_LISTENER_H_ + +#include "AL/al.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ALlistener_struct +{ + ALfloat Position[3]; + ALfloat Velocity[3]; + ALfloat Forward[3]; + ALfloat Up[3]; + ALfloat Gain; + ALfloat MetersPerUnit; +} ALlistener; + +#ifdef __cplusplus +} +#endif + +#endif diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/alMain.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/alMain.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,598 @@ +#ifndef AL_MAIN_H +#define AL_MAIN_H + +#include +#include +#include + +#ifdef HAVE_FENV_H +#include +#endif + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#ifndef ALC_SOFT_device_loopback +#define ALC_SOFT_device_loopback 1 +#define ALC_FORMAT_CHANNELS_SOFT 0x1990 +#define ALC_FORMAT_TYPE_SOFT 0x1991 + +/* Sample types */ +#define ALC_BYTE 0x1400 +#define ALC_UNSIGNED_BYTE 0x1401 +#define ALC_SHORT 0x1402 +#define ALC_UNSIGNED_SHORT 0x1403 +#define ALC_INT 0x1404 +#define ALC_UNSIGNED_INT 0x1405 +#define ALC_FLOAT 0x1406 + +/* Channel configurations */ +#define ALC_MONO 0x1500 +#define ALC_STEREO 0x1501 +#define ALC_QUAD 0x1503 +#define ALC_5POINT1 0x1504 /* (WFX order) */ +#define ALC_6POINT1 0x1505 /* (WFX order) */ +#define ALC_7POINT1 0x1506 /* (WFX order) */ + +typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(void); +typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type); +typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); +#ifdef AL_ALEXT_PROTOTYPES +ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(void); +ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type); +ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); +#endif +#endif + +#ifndef AL_SOFT_buffer_samples +#define AL_SOFT_buffer_samples 1 +/* Sample types */ +#define AL_BYTE 0x1400 +#define AL_UNSIGNED_BYTE 0x1401 +#define AL_SHORT 0x1402 +#define AL_UNSIGNED_SHORT 0x1403 +#define AL_INT 0x1404 +#define AL_UNSIGNED_INT 0x1405 +#define AL_FLOAT 0x1406 +#define AL_DOUBLE 0x1407 +#define AL_BYTE3 0x1408 +#define AL_UNSIGNED_BYTE3 0x1409 +#define AL_MULAW 0x1410 +#define AL_IMA4 0x1411 + +/* Channel configurations */ +#define AL_MONO 0x1500 +#define AL_STEREO 0x1501 +#define AL_REAR 0x1502 +#define AL_QUAD 0x1503 +#define AL_5POINT1 0x1504 /* (WFX order) */ +#define AL_6POINT1 0x1505 /* (WFX order) */ +#define AL_7POINT1 0x1506 /* (WFX order) */ + +/* Storage formats */ +#define AL_MONO8 0x1100 +#define AL_MONO16 0x1101 +#define AL_MONO32F 0x10010 +#define AL_STEREO8 0x1102 +#define AL_STEREO16 0x1103 +#define AL_STEREO32F 0x10011 +#define AL_QUAD8 0x1204 +#define AL_QUAD16 0x1205 +#define AL_QUAD32F 0x1206 +#define AL_REAR8 0x1207 +#define AL_REAR16 0x1208 +#define AL_REAR32F 0x1209 +#define AL_5POINT1_8 0x120A +#define AL_5POINT1_16 0x120B +#define AL_5POINT1_32F 0x120C +#define AL_6POINT1_8 0x120D +#define AL_6POINT1_16 0x120E +#define AL_6POINT1_32F 0x120F +#define AL_7POINT1_8 0x1210 +#define AL_7POINT1_16 0x1211 +#define AL_7POINT1_32F 0x1212 + +typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*); +typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*); +typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*); +typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum); +#ifdef AL_ALEXT_PROTOTYPES +AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, + ALuint samplerate, ALenum internalformat, ALsizei frames, + ALenum channels, ALenum type, const ALvoid *data); +AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, + ALsizei offset, ALsizei frames, + ALenum channels, ALenum type, const ALvoid *data); +AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, + ALsizei offset, ALsizei frames, + ALenum channels, ALenum type, ALvoid *data); +AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format); +#endif +#endif + +#ifndef AL_SOFT_non_virtual_channels +#define AL_SOFT_non_virtual_channels 1 +#define AL_VIRTUAL_CHANNELS_SOFT 0x1033 +#endif + +#ifndef AL_SOFT_deferred_updates +#define AL_SOFT_deferred_updates 1 +#define AL_DEFERRED_UPDATES_SOFT 0xC002 +typedef ALvoid (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void); +typedef ALvoid (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void); +#ifdef AL_ALEXT_PROTOTYPES +AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void); +AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void); +#endif +#endif + + +#if defined(HAVE_STDINT_H) +#include +typedef int64_t ALint64; +typedef uint64_t ALuint64; +#elif defined(HAVE___INT64) +typedef __int64 ALint64; +typedef unsigned __int64 ALuint64; +#elif (SIZEOF_LONG == 8) +typedef long ALint64; +typedef unsigned long ALuint64; +#elif (SIZEOF_LONG_LONG == 8) +typedef long long ALint64; +typedef unsigned long long ALuint64; +#endif + +typedef ptrdiff_t ALintptrEXT; +typedef ptrdiff_t ALsizeiptrEXT; + +#ifdef HAVE_GCC_FORMAT +#define PRINTF_STYLE(x, y) __attribute__((format(printf, (x), (y)))) +#else +#define PRINTF_STYLE(x, y) +#endif + +#if defined(HAVE_RESTRICT) +#define RESTRICT restrict +#elif defined(HAVE___RESTRICT) +#define RESTRICT __restrict +#else +#define RESTRICT +#endif + +#ifdef _WIN32 + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif +#include + +typedef DWORD tls_type; +#define tls_create(x) (*(x) = TlsAlloc()) +#define tls_delete(x) TlsFree((x)) +#define tls_get(x) TlsGetValue((x)) +#define tls_set(x, a) TlsSetValue((x), (a)) + +#define HAVE_DYNLOAD 1 +void *LoadLib(const char *name); +void CloseLib(void *handle); +void *GetSymbol(void *handle, const char *name); + +typedef LONG pthread_once_t; +#define PTHREAD_ONCE_INIT 0 +void pthread_once(pthread_once_t *once, void (*callback)(void)); + +#else + +#include +#include +#include +#ifdef HAVE_PTHREAD_NP_H +#include +#endif +#include +#include +#include + +#define IsBadWritePtr(a,b) ((a) == NULL && (b) != 0) + +typedef pthread_key_t tls_type; +#define tls_create(x) pthread_key_create((x), NULL) +#define tls_delete(x) pthread_key_delete((x)) +#define tls_get(x) pthread_getspecific((x)) +#define tls_set(x, a) pthread_setspecific((x), (a)) + +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); + +static __inline void Sleep(ALuint t) +{ + struct timespec tv, rem; + tv.tv_nsec = (t*1000000)%1000000000; + tv.tv_sec = t/1000; + + while(nanosleep(&tv, &rem) == -1 && errno == EINTR) + tv = rem; +} + +#if defined(HAVE_DLFCN_H) +#define HAVE_DYNLOAD 1 +void *LoadLib(const char *name); +void CloseLib(void *handle); +void *GetSymbol(void *handle, const char *name); +#endif + +#endif + +#include "alListener.h" +#include "alu.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#define DEFAULT_OUTPUT_RATE (44100) + +#define SPEEDOFSOUNDMETRESPERSEC (343.3f) +#define AIRABSORBGAINHF (0.99426) /* -0.05dB */ + +#define LOWPASSFREQCUTOFF (5000) + + +// Find the next power-of-2 for non-power-of-2 numbers. +static __inline ALuint NextPowerOf2(ALuint value) +{ + ALuint powerOf2 = 1; + + if(value) + { + value--; + while(value) + { + value >>= 1; + powerOf2 <<= 1; + } + } + return powerOf2; +} + + +enum DevProbe { + DEVICE_PROBE, + ALL_DEVICE_PROBE, + CAPTURE_DEVICE_PROBE +}; + +typedef struct { + ALCboolean (*OpenPlayback)(ALCdevice*, const ALCchar*); + void (*ClosePlayback)(ALCdevice*); + ALCboolean (*ResetPlayback)(ALCdevice*); + void (*StopPlayback)(ALCdevice*); + + ALCboolean (*OpenCapture)(ALCdevice*, const ALCchar*); + void (*CloseCapture)(ALCdevice*); + void (*StartCapture)(ALCdevice*); + void (*StopCapture)(ALCdevice*); + void (*CaptureSamples)(ALCdevice*, void*, ALCuint); + ALCuint (*AvailableSamples)(ALCdevice*); +} BackendFuncs; + +struct BackendInfo { + const char *name; + ALCboolean (*Init)(BackendFuncs*); + void (*Deinit)(void); + void (*Probe)(enum DevProbe); + BackendFuncs Funcs; +}; + +ALCboolean alc_alsa_init(BackendFuncs *func_list); +void alc_alsa_deinit(void); +void alc_alsa_probe(enum DevProbe type); +ALCboolean alc_oss_init(BackendFuncs *func_list); +void alc_oss_deinit(void); +void alc_oss_probe(enum DevProbe type); +ALCboolean alc_solaris_init(BackendFuncs *func_list); +void alc_solaris_deinit(void); +void alc_solaris_probe(enum DevProbe type); +ALCboolean alc_sndio_init(BackendFuncs *func_list); +void alc_sndio_deinit(void); +void alc_sndio_probe(enum DevProbe type); +ALCboolean alcMMDevApiInit(BackendFuncs *func_list); +void alcMMDevApiDeinit(void); +void alcMMDevApiProbe(enum DevProbe type); +ALCboolean alcDSoundInit(BackendFuncs *func_list); +void alcDSoundDeinit(void); +void alcDSoundProbe(enum DevProbe type); +ALCboolean alcWinMMInit(BackendFuncs *FuncList); +void alcWinMMDeinit(void); +void alcWinMMProbe(enum DevProbe type); +ALCboolean alc_pa_init(BackendFuncs *func_list); +void alc_pa_deinit(void); +void alc_pa_probe(enum DevProbe type); +ALCboolean alc_wave_init(BackendFuncs *func_list); +void alc_wave_deinit(void); +void alc_wave_probe(enum DevProbe type); +ALCboolean alc_pulse_init(BackendFuncs *func_list); +void alc_pulse_deinit(void); +void alc_pulse_probe(enum DevProbe type); +ALCboolean alc_ca_init(BackendFuncs *func_list); +void alc_ca_deinit(void); +void alc_ca_probe(enum DevProbe type); +ALCboolean alc_opensl_init(BackendFuncs *func_list); +void alc_opensl_deinit(void); +void alc_opensl_probe(enum DevProbe type); +ALCboolean alc_null_init(BackendFuncs *func_list); +void alc_null_deinit(void); +void alc_null_probe(enum DevProbe type); +ALCboolean alc_loopback_init(BackendFuncs *func_list); +void alc_loopback_deinit(void); +void alc_loopback_probe(enum DevProbe type); + +//RLM: aurellem send device insertion +ALCboolean alc_send_init(BackendFuncs *func_list); +void alc_send_deinit(void); +void alc_send_probe(enum DevProbe type); + + +typedef struct UIntMap { + struct { + ALuint key; + ALvoid *value; + } *array; + ALsizei size; + ALsizei maxsize; +} UIntMap; + +void InitUIntMap(UIntMap *map); +void ResetUIntMap(UIntMap *map); +ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value); +void RemoveUIntMapKey(UIntMap *map, ALuint key); +ALvoid *LookupUIntMapKey(UIntMap *map, ALuint key); + +/* Device formats */ +enum DevFmtType { + DevFmtByte = AL_BYTE, + DevFmtUByte = AL_UNSIGNED_BYTE, + DevFmtShort = AL_SHORT, + DevFmtUShort = AL_UNSIGNED_SHORT, + DevFmtFloat = AL_FLOAT +}; +enum DevFmtChannels { + DevFmtMono = AL_MONO, + DevFmtStereo = AL_STEREO, + DevFmtQuad = AL_QUAD, + DevFmtX51 = AL_5POINT1, + DevFmtX61 = AL_6POINT1, + DevFmtX71 = AL_7POINT1, + + /* Similar to 5.1, except using the side channels instead of back */ + DevFmtX51Side = 0x80000000 | AL_5POINT1 +}; + +ALuint BytesFromDevFmt(enum DevFmtType type); +ALuint ChannelsFromDevFmt(enum DevFmtChannels chans); +static __inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, + enum DevFmtType type) +{ + return ChannelsFromDevFmt(chans) * BytesFromDevFmt(type); +} + + +extern const struct EffectList { + const char *name; + int type; + const char *ename; + ALenum val; +} EffectList[]; + + +struct ALCdevice_struct +{ + ALCboolean Connected; + ALboolean IsCaptureDevice; + ALboolean IsLoopbackDevice; + + CRITICAL_SECTION Mutex; + + ALuint Frequency; + ALuint UpdateSize; + ALuint NumUpdates; + enum DevFmtChannels FmtChans; + enum DevFmtType FmtType; + + ALCchar *szDeviceName; + + ALCenum LastError; + + // Maximum number of sources that can be created + ALuint MaxNoOfSources; + // Maximum number of slots that can be created + ALuint AuxiliaryEffectSlotMax; + + ALCuint NumMonoSources; + ALCuint NumStereoSources; + ALuint NumAuxSends; + + // Map of Buffers for this device + UIntMap BufferMap; + + // Map of Effects for this device + UIntMap EffectMap; + + // Map of Filters for this device + UIntMap FilterMap; + + // Stereo-to-binaural filter + struct bs2b *Bs2b; + ALCint Bs2bLevel; + + // Device flags + ALuint Flags; + + // Dry path buffer mix + ALfloat DryBuffer[BUFFERSIZE][MAXCHANNELS]; + + enum Channel DevChannels[MAXCHANNELS]; + + enum Channel Speaker2Chan[MAXCHANNELS]; + ALfloat PanningLUT[LUT_NUM][MAXCHANNELS]; + ALuint NumChan; + + ALfloat ClickRemoval[MAXCHANNELS]; + ALfloat PendingClicks[MAXCHANNELS]; + + // Contexts created on this device + ALCcontext **Contexts; + ALuint NumContexts; + + BackendFuncs *Funcs; + void *ExtraData; // For the backend's use + + ALCdevice *next; +}; + +#define ALCdevice_OpenPlayback(a,b) ((a)->Funcs->OpenPlayback((a), (b))) +#define ALCdevice_ClosePlayback(a) ((a)->Funcs->ClosePlayback((a))) +#define ALCdevice_ResetPlayback(a) ((a)->Funcs->ResetPlayback((a))) +#define ALCdevice_StopPlayback(a) ((a)->Funcs->StopPlayback((a))) +#define ALCdevice_OpenCapture(a,b) ((a)->Funcs->OpenCapture((a), (b))) +#define ALCdevice_CloseCapture(a) ((a)->Funcs->CloseCapture((a))) +#define ALCdevice_StartCapture(a) ((a)->Funcs->StartCapture((a))) +#define ALCdevice_StopCapture(a) ((a)->Funcs->StopCapture((a))) +#define ALCdevice_CaptureSamples(a,b,c) ((a)->Funcs->CaptureSamples((a), (b), (c))) +#define ALCdevice_AvailableSamples(a) ((a)->Funcs->AvailableSamples((a))) + +// Duplicate stereo sources on the side/rear channels +#define DEVICE_DUPLICATE_STEREO (1<<0) +// Use HRTF filters for mixing sounds +#define DEVICE_USE_HRTF (1<<1) +// Frequency was requested by the app or config file +#define DEVICE_FREQUENCY_REQUEST (1<<2) +// Channel configuration was requested by the config file +#define DEVICE_CHANNELS_REQUEST (1<<3) + +// Specifies if the device is currently running +#define DEVICE_RUNNING (1<<31) + +struct ALCcontext_struct +{ + ALlistener Listener; + + UIntMap SourceMap; + UIntMap EffectSlotMap; + + ALenum LastError; + + ALboolean UpdateSources; + + enum DistanceModel DistanceModel; + ALboolean SourceDistanceModel; + + ALfloat DopplerFactor; + ALfloat DopplerVelocity; + ALfloat flSpeedOfSound; + ALboolean DeferUpdates; + + struct ALsource **ActiveSources; + ALsizei ActiveSourceCount; + ALsizei MaxActiveSources; + + ALCdevice *Device; + const ALCchar *ExtensionList; + + ALCcontext *next; +}; + +void AppendDeviceList(const ALCchar *name); +void AppendAllDeviceList(const ALCchar *name); +void AppendCaptureDeviceList(const ALCchar *name); + +ALCvoid alcSetError(ALCdevice *device, ALenum errorCode); + +ALCvoid LockDevice(ALCdevice *device); +ALCvoid UnlockDevice(ALCdevice *device); +ALCvoid LockContext(ALCcontext *context); +ALCvoid UnlockContext(ALCcontext *context); + +ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr); +ALuint StopThread(ALvoid *thread); + +ALCcontext *GetLockedContext(void); + +typedef struct RingBuffer RingBuffer; +RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length); +void DestroyRingBuffer(RingBuffer *ring); +ALsizei RingBufferSize(RingBuffer *ring); +void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len); +void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len); + +void ReadALConfig(void); +void FreeALConfig(void); +int ConfigValueExists(const char *blockName, const char *keyName); +const char *GetConfigValue(const char *blockName, const char *keyName, const char *def); +int GetConfigValueInt(const char *blockName, const char *keyName, int def); +float GetConfigValueFloat(const char *blockName, const char *keyName, float def); +int GetConfigValueBool(const char *blockName, const char *keyName, int def); + +void SetRTPriority(void); + +void SetDefaultChannelOrder(ALCdevice *device); +void SetDefaultWFXChannelOrder(ALCdevice *device); + +const ALCchar *DevFmtTypeString(enum DevFmtType type); +const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans); + +#define HRIR_BITS (5) +#define HRIR_LENGTH (1<= LogTrace) \ + AL_PRINT(__VA_ARGS__); \ +} while(0) + +#define WARN(...) do { \ + if(LogLevel >= LogWarning) \ + AL_PRINT(__VA_ARGS__); \ +} while(0) + +#define ERR(...) do { \ + if(LogLevel >= LogError) \ + AL_PRINT(__VA_ARGS__); \ +} while(0) + + +extern ALdouble ConeScale; +extern ALdouble ZScale; + +extern ALint RTPrioLevel; + +#ifdef __cplusplus +} +#endif + +#endif diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/alSource.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/alSource.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,142 @@ +#ifndef _AL_SOURCE_H_ +#define _AL_SOURCE_H_ + +#define MAX_SENDS 4 + +#include "alFilter.h" +#include "alu.h" +#include "AL/al.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SRC_HISTORY_BITS (6) +#define SRC_HISTORY_LENGTH (1<Update(s,a)) + +ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state); +ALboolean ApplyOffset(ALsource *Source); + +ALvoid ReleaseALSources(ALCcontext *Context); + +#ifdef __cplusplus +} +#endif + +#endif diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/alState.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/alState.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,14 @@ +#ifndef _AL_STATE_H_ +#define _AL_STATE_H_ + +#include "AL/al.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/alThunk.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/alThunk.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,20 @@ +#ifndef ALTHUNK_H +#define ALTHUNK_H + +#include "alMain.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void ThunkInit(void); +void ThunkExit(void); +ALenum NewThunkEntry(ALuint *index); +void FreeThunkEntry(ALuint index); + +#ifdef __cplusplus +} +#endif + +#endif //ALTHUNK_H + diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/alu.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/alu.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,178 @@ +#ifndef _ALU_H_ +#define _ALU_H_ + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include +#include +#ifdef HAVE_FLOAT_H +#include +#endif +#ifdef HAVE_IEEEFP_H +#include +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 /* pi */ +#define M_PI_2 1.57079632679489661923 /* pi/2 */ +#endif + +#ifdef HAVE_POWF +#define aluPow(x,y) (powf((x),(y))) +#else +#define aluPow(x,y) ((ALfloat)pow((double)(x),(double)(y))) +#endif + +#ifdef HAVE_SQRTF +#define aluSqrt(x) (sqrtf((x))) +#else +#define aluSqrt(x) ((ALfloat)sqrt((double)(x))) +#endif + +#ifdef HAVE_ACOSF +#define aluAcos(x) (acosf((x))) +#else +#define aluAcos(x) ((ALfloat)acos((double)(x))) +#endif + +#ifdef HAVE_ATANF +#define aluAtan(x) (atanf((x))) +#else +#define aluAtan(x) ((ALfloat)atan((double)(x))) +#endif + +#ifdef HAVE_FABSF +#define aluFabs(x) (fabsf((x))) +#else +#define aluFabs(x) ((ALfloat)fabs((double)(x))) +#endif + +#define QUADRANT_NUM 128 +#define LUT_NUM (4 * QUADRANT_NUM) + +#ifdef __cplusplus +extern "C" { +#endif + +struct ALsource; +struct ALbuffer; + +typedef ALvoid (*MixerFunc)(struct ALsource *self, ALCdevice *Device, + const ALvoid *RESTRICT data, + ALuint *DataPosInt, ALuint *DataPosFrac, + ALuint OutPos, ALuint SamplesToDo, + ALuint BufferSize); + +enum Resampler { + POINT_RESAMPLER = 0, + LINEAR_RESAMPLER, + CUBIC_RESAMPLER, + + RESAMPLER_MAX, + RESAMPLER_MIN = -1, + RESAMPLER_DEFAULT = LINEAR_RESAMPLER +}; + +enum Channel { + FRONT_LEFT = 0, + FRONT_RIGHT, + FRONT_CENTER, + LFE, + BACK_LEFT, + BACK_RIGHT, + BACK_CENTER, + SIDE_LEFT, + SIDE_RIGHT, + + MAXCHANNELS +}; + +enum DistanceModel { + InverseDistanceClamped = AL_INVERSE_DISTANCE_CLAMPED, + LinearDistanceClamped = AL_LINEAR_DISTANCE_CLAMPED, + ExponentDistanceClamped = AL_EXPONENT_DISTANCE_CLAMPED, + InverseDistance = AL_INVERSE_DISTANCE, + LinearDistance = AL_LINEAR_DISTANCE, + ExponentDistance = AL_EXPONENT_DISTANCE, + DisableDistance = AL_NONE +}; + +#define BUFFERSIZE 4096 + +#define FRACTIONBITS (14) +#define FRACTIONONE (1< b) ? b : a); } +static __inline ALfloat maxf(ALfloat a, ALfloat b) +{ return ((a > b) ? a : b); } +static __inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max) +{ return minf(max, maxf(min, val)); } + +static __inline ALuint minu(ALuint a, ALuint b) +{ return ((a > b) ? b : a); } +static __inline ALuint maxu(ALuint a, ALuint b) +{ return ((a > b) ? a : b); } +static __inline ALuint clampu(ALuint val, ALuint min, ALuint max) +{ return minu(max, maxu(min, val)); } + +static __inline ALint mini(ALint a, ALint b) +{ return ((a > b) ? b : a); } +static __inline ALint maxi(ALint a, ALint b) +{ return ((a > b) ? a : b); } +static __inline ALint clampi(ALint val, ALint min, ALint max) +{ return mini(max, maxi(min, val)); } + + +static __inline ALdouble lerp(ALdouble val1, ALdouble val2, ALdouble mu) +{ + return val1 + (val2-val1)*mu; +} +static __inline ALdouble cubic(ALdouble val0, ALdouble val1, ALdouble val2, ALdouble val3, ALdouble mu) +{ + ALdouble mu2 = mu*mu; + ALdouble a0 = -0.5*val0 + 1.5*val1 + -1.5*val2 + 0.5*val3; + ALdouble a1 = val0 + -2.5*val1 + 2.0*val2 + -0.5*val3; + ALdouble a2 = -0.5*val0 + 0.5*val2; + ALdouble a3 = val1; + + return a0*mu*mu2 + a1*mu2 + a2*mu + a3; +} + +ALvoid aluInitPanning(ALCdevice *Device); +ALint aluCart2LUTpos(ALfloat re, ALfloat im); + +ALvoid CalcSourceParams(struct ALsource *ALSource, const ALCcontext *ALContext); +ALvoid CalcNonAttnSourceParams(struct ALsource *ALSource, const ALCcontext *ALContext); + +MixerFunc SelectMixer(struct ALbuffer *Buffer, enum Resampler Resampler); +MixerFunc SelectHrtfMixer(struct ALbuffer *Buffer, enum Resampler Resampler); + +ALvoid MixSource(struct ALsource *Source, ALCdevice *Device, ALuint SamplesToDo); + +ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size); +ALvoid aluHandleDisconnect(ALCdevice *device); + +#ifdef __cplusplus +} +#endif + +#endif + diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/bs2b.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/bs2b.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 2005 Boris Mikhaylov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef BS2B_H +#define BS2B_H + +/* Number of crossfeed levels */ +#define BS2B_CLEVELS 3 + +/* Normal crossfeed levels */ +#define BS2B_HIGH_CLEVEL 3 +#define BS2B_MIDDLE_CLEVEL 2 +#define BS2B_LOW_CLEVEL 1 + +/* Easy crossfeed levels */ +#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS +#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS +#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS + +/* Default crossfeed levels */ +#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL +/* Default sample rate (Hz) */ +#define BS2B_DEFAULT_SRATE 44100 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct bs2b { + int level; /* Crossfeed level */ + int srate; /* Sample rate (Hz) */ + + /* Lowpass IIR filter coefficients */ + double a0_lo; + double b1_lo; + + /* Highboost IIR filter coefficients */ + double a0_hi; + double a1_hi; + double b1_hi; + + /* Global gain against overloading */ + double gain; + + /* Buffer of last filtered sample. + * [0] - first channel, [1] - second channel + */ + struct t_last_sample { + double asis[2]; + double lo[2]; + double hi[2]; + } last_sample; +}; + +/* Clear buffers and set new coefficients with new crossfeed level value. + * level - crossfeed level of *LEVEL values. + */ +void bs2b_set_level(struct bs2b *bs2b, int level); + +/* Return current crossfeed level value */ +int bs2b_get_level(struct bs2b *bs2b); + +/* Clear buffers and set new coefficients with new sample rate value. + * srate - sample rate by Hz. + */ +void bs2b_set_srate(struct bs2b *bs2b, int srate); + +/* Return current sample rate value */ +int bs2b_get_srate(struct bs2b *bs2b); + +/* Clear buffer */ +void bs2b_clear(struct bs2b *bs2b); + +/* Return 1 if buffer is clear */ +int bs2b_is_clear(struct bs2b *bs2b); + +/* Crossfeeds one stereo sample that are pointed by sample. + * [0] - first channel, [1] - second channel. + * Returns crossfided samle by sample pointer. + */ + +/* sample poits to floats */ +void bs2b_cross_feed(struct bs2b *bs2b, float *sample); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* BS2B_H */ diff -r 000000000000 -r f9476ff7637e OpenAL32/Include/com_aurellem_capture_AudioSend.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/Include/com_aurellem_capture_AudioSend.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,63 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class com_aurellem_capture_AudioSend */ + +#ifndef _Included_com_aurellem_capture_AudioSend +#define _Included_com_aurellem_capture_AudioSend +#ifdef __cplusplus +extern "C" { +#endif +#undef com_aurellem_capture_AudioSend_BYTES_PER_SAMPLE +#define com_aurellem_capture_AudioSend_BYTES_PER_SAMPLE 4L +/* + * Class: com_aurellem_capture_AudioSend + * Method: ninitDevice + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_ninitDevice + (JNIEnv *, jclass, jlong); + +/* + * Class: com_aurellem_capture_AudioSend + * Method: nstep + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_nstep + (JNIEnv *, jclass, jlong, jint); + +/* + * Class: com_aurellem_capture_AudioSend + * Method: ngetSamples + * Signature: (JLjava/nio/ByteBuffer;III)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_ngetSamples + (JNIEnv *, jclass, jlong, jobject, jint, jint, jint); + +/* + * Class: com_aurellem_capture_AudioSend + * Method: naddListener + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_naddListener + (JNIEnv *, jclass, jlong); + +/* + * Class: com_aurellem_capture_AudioSend + * Method: nsetNthListener3f + * Signature: (IFFFJI)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_nsetNthListener3f + (JNIEnv *, jclass, jint, jfloat, jfloat, jfloat, jlong, jint); + +/* + * Class: com_aurellem_capture_AudioSend + * Method: nsetNthListenerf + * Signature: (IFJI)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_nsetNthListenerf + (JNIEnv *, jclass, jint, jfloat, jlong, jint); + +#ifdef __cplusplus +} +#endif +#endif diff -r 000000000000 -r f9476ff7637e OpenAL32/alAuxEffectSlot.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/alAuxEffectSlot.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,540 @@ +/** + * 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 +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "alMain.h" +#include "alAuxEffectSlot.h" +#include "alThunk.h" +#include "alError.h" +#include "alSource.h" + + +static ALvoid InitializeEffect(ALCcontext *Context, ALeffectslot *EffectSlot, ALeffect *effect); + +#define LookupEffectSlot(m, k) ((ALeffectslot*)LookupUIntMapKey(&(m), (k))) +#define LookupEffect(m, k) ((ALeffect*)LookupUIntMapKey(&(m), (k))) + +AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots) +{ + ALCcontext *Context; + ALCdevice *Device; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if(n < 0 || IsBadWritePtr((void*)effectslots, n * sizeof(ALuint))) + alSetError(Context, AL_INVALID_VALUE); + else if((ALuint)n > Device->AuxiliaryEffectSlotMax - Context->EffectSlotMap.size) + alSetError(Context, AL_INVALID_VALUE); + else + { + ALenum err; + ALsizei i, j; + + i = 0; + while(i < n) + { + ALeffectslot *slot = calloc(1, sizeof(ALeffectslot)); + if(!slot || !(slot->EffectState=NoneCreate())) + { + free(slot); + // We must have run out or memory + alSetError(Context, AL_OUT_OF_MEMORY); + alDeleteAuxiliaryEffectSlots(i, effectslots); + break; + } + + err = NewThunkEntry(&slot->effectslot); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&Context->EffectSlotMap, slot->effectslot, slot); + if(err != AL_NO_ERROR) + { + FreeThunkEntry(slot->effectslot); + ALEffect_Destroy(slot->EffectState); + free(slot); + + alSetError(Context, err); + alDeleteAuxiliaryEffectSlots(i, effectslots); + break; + } + + effectslots[i++] = slot->effectslot; + + slot->Gain = 1.0; + slot->AuxSendAuto = AL_TRUE; + slot->NeedsUpdate = AL_FALSE; + for(j = 0;j < BUFFERSIZE;j++) + slot->WetBuffer[j] = 0.0f; + for(j = 0;j < 1;j++) + { + slot->ClickRemoval[j] = 0.0f; + slot->PendingClicks[j] = 0.0f; + } + slot->refcount = 0; + } + } + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots) +{ + ALCcontext *Context; + ALeffectslot *EffectSlot; + ALboolean SlotsValid = AL_FALSE; + ALsizei i; + + Context = GetLockedContext(); + if(!Context) return; + + if(n < 0) + alSetError(Context, AL_INVALID_VALUE); + else + { + SlotsValid = AL_TRUE; + // Check that all effectslots are valid + for(i = 0;i < n;i++) + { + if((EffectSlot=LookupEffectSlot(Context->EffectSlotMap, effectslots[i])) == NULL) + { + alSetError(Context, AL_INVALID_NAME); + SlotsValid = AL_FALSE; + break; + } + else if(EffectSlot->refcount > 0) + { + alSetError(Context, AL_INVALID_NAME); + SlotsValid = AL_FALSE; + break; + } + } + } + + if(SlotsValid) + { + // All effectslots are valid + for(i = 0;i < n;i++) + { + // Recheck that the effectslot is valid, because there could be duplicated names + if((EffectSlot=LookupEffectSlot(Context->EffectSlotMap, effectslots[i])) == NULL) + continue; + + ALEffect_Destroy(EffectSlot->EffectState); + + RemoveUIntMapKey(&Context->EffectSlotMap, EffectSlot->effectslot); + FreeThunkEntry(EffectSlot->effectslot); + + memset(EffectSlot, 0, sizeof(ALeffectslot)); + free(EffectSlot); + } + } + + UnlockContext(Context); +} + +AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot) +{ + ALCcontext *Context; + ALboolean result; + + Context = GetLockedContext(); + if(!Context) return AL_FALSE; + + result = (LookupEffectSlot(Context->EffectSlotMap, effectslot) ? + AL_TRUE : AL_FALSE); + + UnlockContext(Context); + + return result; +} + +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue) +{ + ALCdevice *Device; + ALCcontext *Context; + ALeffectslot *EffectSlot; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if((EffectSlot=LookupEffectSlot(Context->EffectSlotMap, effectslot)) != NULL) + { + switch(param) + { + case AL_EFFECTSLOT_EFFECT: { + ALeffect *effect = NULL; + + if(iValue == 0 || + (effect=LookupEffect(Device->EffectMap, iValue)) != NULL) + { + InitializeEffect(Context, EffectSlot, effect); + Context->UpdateSources = AL_TRUE; + } + else + alSetError(Context, AL_INVALID_VALUE); + } break; + + case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: + if(iValue == AL_TRUE || iValue == AL_FALSE) + { + EffectSlot->AuxSendAuto = iValue; + Context->UpdateSources = AL_TRUE; + } + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues) +{ + ALCcontext *Context; + + switch(param) + { + case AL_EFFECTSLOT_EFFECT: + case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: + alAuxiliaryEffectSloti(effectslot, param, piValues[0]); + return; + } + + Context = GetLockedContext(); + if(!Context) return; + + if(LookupEffectSlot(Context->EffectSlotMap, effectslot) != NULL) + { + switch(param) + { + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue) +{ + ALCcontext *Context; + ALeffectslot *EffectSlot; + + Context = GetLockedContext(); + if(!Context) return; + + if((EffectSlot=LookupEffectSlot(Context->EffectSlotMap, effectslot)) != NULL) + { + switch(param) + { + case AL_EFFECTSLOT_GAIN: + if(flValue >= 0.0f && flValue <= 1.0f) + { + EffectSlot->Gain = flValue; + EffectSlot->NeedsUpdate = AL_TRUE; + } + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues) +{ + ALCcontext *Context; + + switch(param) + { + case AL_EFFECTSLOT_GAIN: + alAuxiliaryEffectSlotf(effectslot, param, pflValues[0]); + return; + } + + Context = GetLockedContext(); + if(!Context) return; + + if(LookupEffectSlot(Context->EffectSlotMap, effectslot) != NULL) + { + switch(param) + { + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue) +{ + ALCcontext *Context; + ALeffectslot *EffectSlot; + + Context = GetLockedContext(); + if(!Context) return; + + if((EffectSlot=LookupEffectSlot(Context->EffectSlotMap, effectslot)) != NULL) + { + switch(param) + { + case AL_EFFECTSLOT_EFFECT: + *piValue = EffectSlot->effect.effect; + break; + + case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: + *piValue = EffectSlot->AuxSendAuto; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues) +{ + ALCcontext *Context; + + switch(param) + { + case AL_EFFECTSLOT_EFFECT: + case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: + alGetAuxiliaryEffectSloti(effectslot, param, piValues); + return; + } + + Context = GetLockedContext(); + if(!Context) return; + + if(LookupEffectSlot(Context->EffectSlotMap, effectslot) != NULL) + { + switch(param) + { + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue) +{ + ALCcontext *Context; + ALeffectslot *EffectSlot; + + Context = GetLockedContext(); + if(!Context) return; + + if((EffectSlot=LookupEffectSlot(Context->EffectSlotMap, effectslot)) != NULL) + { + switch(param) + { + case AL_EFFECTSLOT_GAIN: + *pflValue = EffectSlot->Gain; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues) +{ + ALCcontext *Context; + + switch(param) + { + case AL_EFFECTSLOT_GAIN: + alGetAuxiliaryEffectSlotf(effectslot, param, pflValues); + return; + } + + Context = GetLockedContext(); + if(!Context) return; + + if(LookupEffectSlot(Context->EffectSlotMap, effectslot) != NULL) + { + switch(param) + { + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + + +static ALvoid NoneDestroy(ALeffectState *State) +{ free(State); } +static ALboolean NoneDeviceUpdate(ALeffectState *State, ALCdevice *Device) +{ + return AL_TRUE; + (void)State; + (void)Device; +} +static ALvoid NoneUpdate(ALeffectState *State, ALCcontext *Context, const ALeffectslot *Slot) +{ + (void)State; + (void)Context; + (void)Slot; +} +static ALvoid NoneProcess(ALeffectState *State, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) +{ + (void)State; + (void)Slot; + (void)SamplesToDo; + (void)SamplesIn; + (void)SamplesOut; +} +ALeffectState *NoneCreate(void) +{ + ALeffectState *state; + + state = calloc(1, sizeof(*state)); + if(!state) + return NULL; + + state->Destroy = NoneDestroy; + state->DeviceUpdate = NoneDeviceUpdate; + state->Update = NoneUpdate; + state->Process = NoneProcess; + + return state; +} + +static ALvoid InitializeEffect(ALCcontext *Context, ALeffectslot *EffectSlot, ALeffect *effect) +{ + if(EffectSlot->effect.type != (effect?effect->type:AL_EFFECT_NULL)) + { + ALeffectState *NewState = NULL; + if(!effect || effect->type == AL_EFFECT_NULL) + NewState = NoneCreate(); + else if(effect->type == AL_EFFECT_EAXREVERB) + NewState = EAXVerbCreate(); + else if(effect->type == AL_EFFECT_REVERB) + NewState = VerbCreate(); + else if(effect->type == AL_EFFECT_ECHO) + NewState = EchoCreate(); + else if(effect->type == AL_EFFECT_RING_MODULATOR) + NewState = ModulatorCreate(); + else if(effect->type == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) + NewState = DedicatedLFECreate(); + else if(effect->type == AL_EFFECT_DEDICATED_DIALOGUE) + NewState = DedicatedDLGCreate(); + /* No new state? An error occured.. */ + if(NewState == NULL || + ALEffect_DeviceUpdate(NewState, Context->Device) == AL_FALSE) + { + if(NewState) + ALEffect_Destroy(NewState); + alSetError(Context, AL_OUT_OF_MEMORY); + return; + } + if(EffectSlot->EffectState) + ALEffect_Destroy(EffectSlot->EffectState); + EffectSlot->EffectState = NewState; + + if(!effect) + memset(&EffectSlot->effect, 0, sizeof(EffectSlot->effect)); + else + memcpy(&EffectSlot->effect, effect, sizeof(*effect)); + /* FIXME: This should be done asychronously, but since the EfefctState + * object was changed, it needs an update before its Process method can + * be called (coming changes may not guarantee an update when the + * NeedsUpdate flag is set). */ + EffectSlot->NeedsUpdate = AL_FALSE; + ALEffect_Update(EffectSlot->EffectState, Context, EffectSlot); + } + else + { + if(!effect) + memset(&EffectSlot->effect, 0, sizeof(EffectSlot->effect)); + else + memcpy(&EffectSlot->effect, effect, sizeof(*effect)); + EffectSlot->NeedsUpdate = AL_TRUE; + } +} + + +ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context) +{ + ALsizei pos; + for(pos = 0;pos < Context->EffectSlotMap.size;pos++) + { + ALeffectslot *temp = Context->EffectSlotMap.array[pos].value; + Context->EffectSlotMap.array[pos].value = NULL; + + // Release effectslot structure + ALEffect_Destroy(temp->EffectState); + + FreeThunkEntry(temp->effectslot); + memset(temp, 0, sizeof(ALeffectslot)); + free(temp); + } +} diff -r 000000000000 -r f9476ff7637e OpenAL32/alBuffer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/alBuffer.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,2211 @@ +/** + * 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 +#include +#include +#include + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" +#include "alError.h" +#include "alBuffer.h" +#include "alThunk.h" + + +static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels chans, enum UserFmtType type, const ALvoid *data, ALboolean storesrc); +static void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len); +static ALboolean IsValidType(ALenum type); +static ALboolean IsValidChannels(ALenum channels); + +#define LookupBuffer(m, k) ((ALbuffer*)LookupUIntMapKey(&(m), (k))) + + +/* + * Global Variables + */ + +/* IMA ADPCM Stepsize table */ +static const long IMAStep_size[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, + 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, + 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, + 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, + 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, + 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, + 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,10442, + 11487,12635,13899,15289,16818,18500,20350,22358,24633,27086,29794, + 32767 +}; + +/* IMA4 ADPCM Codeword decode table */ +static const long IMA4Codeword[16] = { + 1, 3, 5, 7, 9, 11, 13, 15, + -1,-3,-5,-7,-9,-11,-13,-15, +}; + +/* IMA4 ADPCM Step index adjust decode table */ +static const long IMA4Index_adjust[16] = { + -1,-1,-1,-1, 2, 4, 6, 8, + -1,-1,-1,-1, 2, 4, 6, 8 +}; + +/* A quick'n'dirty lookup table to decode a muLaw-encoded byte sample into a + * signed 16-bit sample */ +static const ALshort muLawDecompressionTable[256] = { + -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, + -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, + -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, + -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0 +}; + +/* Values used when encoding a muLaw sample */ +static const int muLawBias = 0x84; +static const int muLawClip = 32635; +static const char muLawCompressTable[256] = +{ + 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +/* + * alGenBuffers(ALsizei n, ALuint *buffers) + * + * Generates n AL Buffers, and stores the Buffers Names in the array pointed + * to by buffers + */ +AL_API ALvoid AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) +{ + ALCcontext *Context; + ALsizei i=0; + + Context = GetLockedContext(); + if(!Context) return; + + /* Check that we are actually generating some Buffers */ + if(n < 0 || IsBadWritePtr((void*)buffers, n * sizeof(ALuint))) + alSetError(Context, AL_INVALID_VALUE); + else + { + ALCdevice *device = Context->Device; + ALenum err; + + // Create all the new Buffers + while(i < n) + { + ALbuffer *buffer = calloc(1, sizeof(ALbuffer)); + if(!buffer) + { + alSetError(Context, AL_OUT_OF_MEMORY); + alDeleteBuffers(i, buffers); + break; + } + + err = NewThunkEntry(&buffer->buffer); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&device->BufferMap, buffer->buffer, buffer); + if(err != AL_NO_ERROR) + { + FreeThunkEntry(buffer->buffer); + memset(buffer, 0, sizeof(ALbuffer)); + free(buffer); + + alSetError(Context, err); + alDeleteBuffers(i, buffers); + break; + } + buffers[i++] = buffer->buffer; + } + } + + UnlockContext(Context); +} + +/* + * alDeleteBuffers(ALsizei n, ALuint *buffers) + * + * Deletes the n AL Buffers pointed to by buffers + */ +AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers) +{ + ALCcontext *Context; + ALCdevice *device; + ALboolean Failed; + ALbuffer *ALBuf; + ALsizei i; + + Context = GetLockedContext(); + if(!Context) return; + + Failed = AL_TRUE; + device = Context->Device; + /* Check we are actually Deleting some Buffers */ + if(n < 0) + alSetError(Context, AL_INVALID_VALUE); + else + { + Failed = AL_FALSE; + + /* Check that all the buffers are valid and can actually be deleted */ + for(i = 0;i < n;i++) + { + if(!buffers[i]) + continue; + + /* Check for valid Buffer ID */ + if((ALBuf=LookupBuffer(device->BufferMap, buffers[i])) == NULL) + { + alSetError(Context, AL_INVALID_NAME); + Failed = AL_TRUE; + break; + } + else if(ALBuf->refcount != 0) + { + /* Buffer still in use, cannot be deleted */ + alSetError(Context, AL_INVALID_OPERATION); + Failed = AL_TRUE; + break; + } + } + } + + /* If all the Buffers were valid (and have Reference Counts of 0), then we + * can delete them */ + if(!Failed) + { + for(i = 0;i < n;i++) + { + if((ALBuf=LookupBuffer(device->BufferMap, buffers[i])) == NULL) + continue; + + /* Release the memory used to store audio data */ + free(ALBuf->data); + + /* Release buffer structure */ + RemoveUIntMapKey(&device->BufferMap, ALBuf->buffer); + FreeThunkEntry(ALBuf->buffer); + + memset(ALBuf, 0, sizeof(ALbuffer)); + free(ALBuf); + } + } + + UnlockContext(Context); +} + +/* + * alIsBuffer(ALuint buffer) + * + * Checks if buffer is a valid Buffer Name + */ +AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer) +{ + ALCcontext *Context; + ALboolean result; + + Context = GetLockedContext(); + if(!Context) return AL_FALSE; + + result = ((!buffer || LookupBuffer(Context->Device->BufferMap, buffer)) ? + AL_TRUE : AL_FALSE); + + UnlockContext(Context); + + return result; +} + +/* + * alBufferData(ALuint buffer, ALenum format, const ALvoid *data, + * ALsizei size, ALsizei freq) + * + * Fill buffer with audio data + */ +AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer,ALenum format,const ALvoid *data,ALsizei size,ALsizei freq) +{ + enum UserFmtChannels SrcChannels; + enum UserFmtType SrcType; + ALCcontext *Context; + ALCdevice *device; + ALbuffer *ALBuf; + ALenum err; + + Context = GetLockedContext(); + if(!Context) return; + + device = Context->Device; + if((ALBuf=LookupBuffer(device->BufferMap, buffer)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(ALBuf->refcount != 0) + alSetError(Context, AL_INVALID_VALUE); + else if(size < 0 || freq < 0) + alSetError(Context, AL_INVALID_VALUE); + else if(DecomposeUserFormat(format, &SrcChannels, &SrcType) == AL_FALSE) + alSetError(Context, AL_INVALID_ENUM); + else switch(SrcType) + { + case UserFmtByte: + case UserFmtUByte: + case UserFmtShort: + case UserFmtUShort: + case UserFmtInt: + case UserFmtUInt: + case UserFmtFloat: { + ALuint FrameSize = FrameSizeFromUserFmt(SrcChannels, SrcType); + if((size%FrameSize) != 0) + err = AL_INVALID_VALUE; + else + err = LoadData(ALBuf, freq, format, size/FrameSize, + SrcChannels, SrcType, data, AL_TRUE); + if(err != AL_NO_ERROR) + alSetError(Context, err); + } break; + + case UserFmtByte3: + case UserFmtUByte3: + case UserFmtDouble: { + ALuint FrameSize = FrameSizeFromUserFmt(SrcChannels, SrcType); + ALenum NewFormat = AL_FORMAT_MONO_FLOAT32; + switch(SrcChannels) + { + case UserFmtMono: NewFormat = AL_FORMAT_MONO_FLOAT32; break; + case UserFmtStereo: NewFormat = AL_FORMAT_STEREO_FLOAT32; break; + case UserFmtRear: NewFormat = AL_FORMAT_REAR32; break; + case UserFmtQuad: NewFormat = AL_FORMAT_QUAD32; break; + case UserFmtX51: NewFormat = AL_FORMAT_51CHN32; break; + case UserFmtX61: NewFormat = AL_FORMAT_61CHN32; break; + case UserFmtX71: NewFormat = AL_FORMAT_71CHN32; break; + } + if((size%FrameSize) != 0) + err = AL_INVALID_VALUE; + else + err = LoadData(ALBuf, freq, NewFormat, size/FrameSize, + SrcChannels, SrcType, data, AL_TRUE); + if(err != AL_NO_ERROR) + alSetError(Context, err); + } break; + + case UserFmtMulaw: + case UserFmtIMA4: { + /* Here is where things vary: + * nVidia and Apple use 64+1 sample frames per block -> block_size=36 bytes per channel + * Most PC sound software uses 2040+1 sample frames per block -> block_size=1024 bytes per channel + */ + ALuint FrameSize = (SrcType == UserFmtIMA4) ? + (ChannelsFromUserFmt(SrcChannels) * 36) : + FrameSizeFromUserFmt(SrcChannels, SrcType); + ALenum NewFormat = AL_FORMAT_MONO16; + switch(SrcChannels) + { + case UserFmtMono: NewFormat = AL_FORMAT_MONO16; break; + case UserFmtStereo: NewFormat = AL_FORMAT_STEREO16; break; + case UserFmtRear: NewFormat = AL_FORMAT_REAR16; break; + case UserFmtQuad: NewFormat = AL_FORMAT_QUAD16; break; + case UserFmtX51: NewFormat = AL_FORMAT_51CHN16; break; + case UserFmtX61: NewFormat = AL_FORMAT_61CHN16; break; + case UserFmtX71: NewFormat = AL_FORMAT_71CHN16; break; + } + if((size%FrameSize) != 0) + err = AL_INVALID_VALUE; + else + err = LoadData(ALBuf, freq, NewFormat, size/FrameSize, + SrcChannels, SrcType, data, AL_TRUE); + if(err != AL_NO_ERROR) + alSetError(Context, err); + } break; + } + + UnlockContext(Context); +} + +/* + * alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, + * ALsizei offset, ALsizei length) + * + * Update buffer's audio data + */ +AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length) +{ + enum UserFmtChannels SrcChannels; + enum UserFmtType SrcType; + ALCcontext *Context; + ALCdevice *device; + ALbuffer *ALBuf; + + Context = GetLockedContext(); + if(!Context) return; + + device = Context->Device; + if((ALBuf=LookupBuffer(device->BufferMap, buffer)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(length < 0 || offset < 0 || (length > 0 && data == NULL)) + alSetError(Context, AL_INVALID_VALUE); + else if(DecomposeUserFormat(format, &SrcChannels, &SrcType) == AL_FALSE || + SrcChannels != ALBuf->OriginalChannels || + SrcType != ALBuf->OriginalType) + alSetError(Context, AL_INVALID_ENUM); + else if(offset > ALBuf->OriginalSize || + length > ALBuf->OriginalSize-offset || + (offset%ALBuf->OriginalAlign) != 0 || + (length%ALBuf->OriginalAlign) != 0) + alSetError(Context, AL_INVALID_VALUE); + else + { + ALuint Channels = ChannelsFromFmt(ALBuf->FmtChannels); + ALuint Bytes = BytesFromFmt(ALBuf->FmtType); + if(SrcType == UserFmtIMA4) + { + /* offset -> byte offset, length -> block count */ + offset /= 36; + offset *= 65; + offset *= Bytes; + length /= ALBuf->OriginalAlign; + } + else + { + ALuint OldBytes = BytesFromUserFmt(SrcType); + + offset /= OldBytes; + offset *= Bytes; + length /= OldBytes * Channels; + } + ConvertData(&((ALubyte*)ALBuf->data)[offset], ALBuf->FmtType, + data, SrcType, Channels, length); + } + + UnlockContext(Context); +} + + +AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, + ALuint samplerate, ALenum internalformat, ALsizei frames, + ALenum channels, ALenum type, const ALvoid *data) +{ + ALCcontext *Context; + ALCdevice *device; + ALbuffer *ALBuf; + ALenum err; + + Context = GetLockedContext(); + if(!Context) return; + + device = Context->Device; + if((ALBuf=LookupBuffer(device->BufferMap, buffer)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(ALBuf->refcount != 0) + alSetError(Context, AL_INVALID_VALUE); + else if(frames < 0 || samplerate == 0) + alSetError(Context, AL_INVALID_VALUE); + else if(IsValidType(type) == AL_FALSE || IsValidChannels(channels) == AL_FALSE) + alSetError(Context, AL_INVALID_ENUM); + else + { + err = AL_NO_ERROR; + if(type == UserFmtIMA4) + { + if((frames%65) == 0) frames /= 65; + else err = AL_INVALID_VALUE; + } + if(err == AL_NO_ERROR) + err = LoadData(ALBuf, samplerate, internalformat, frames, + channels, type, data, AL_FALSE); + if(err != AL_NO_ERROR) + alSetError(Context, err); + } + + UnlockContext(Context); +} + +AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, + ALsizei offset, ALsizei frames, + ALenum channels, ALenum type, const ALvoid *data) +{ + ALCcontext *Context; + ALCdevice *device; + ALbuffer *ALBuf; + + Context = GetLockedContext(); + if(!Context) return; + + device = Context->Device; + if((ALBuf=LookupBuffer(device->BufferMap, buffer)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(frames < 0 || offset < 0 || (frames > 0 && data == NULL)) + alSetError(Context, AL_INVALID_VALUE); + else if(channels != (ALenum)ALBuf->FmtChannels || + IsValidType(type) == AL_FALSE) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALuint FrameSize = FrameSizeFromFmt(ALBuf->FmtChannels, ALBuf->FmtType); + ALuint FrameCount = ALBuf->size / FrameSize; + if((ALuint)offset > FrameCount || (ALuint)frames > FrameCount-offset) + alSetError(Context, AL_INVALID_VALUE); + else if(type == UserFmtIMA4 && (frames%65) != 0) + alSetError(Context, AL_INVALID_VALUE); + else + { + /* offset -> byte offset */ + offset *= FrameSize; + /* frames -> IMA4 block count */ + if(type == UserFmtIMA4) frames /= 65; + ConvertData(&((ALubyte*)ALBuf->data)[offset], ALBuf->FmtType, + data, type, + ChannelsFromFmt(ALBuf->FmtChannels), frames); + } + } + + UnlockContext(Context); +} + +AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, + ALsizei offset, ALsizei frames, + ALenum channels, ALenum type, ALvoid *data) +{ + ALCcontext *Context; + ALCdevice *device; + ALbuffer *ALBuf; + + Context = GetLockedContext(); + if(!Context) return; + + device = Context->Device; + if((ALBuf=LookupBuffer(device->BufferMap, buffer)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(frames < 0 || offset < 0 || (frames > 0 && data == NULL)) + alSetError(Context, AL_INVALID_VALUE); + else if(channels != (ALenum)ALBuf->FmtChannels || + IsValidType(type) == AL_FALSE) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALuint FrameSize = FrameSizeFromFmt(ALBuf->FmtChannels, ALBuf->FmtType); + ALuint FrameCount = ALBuf->size / FrameSize; + if((ALuint)offset > FrameCount || (ALuint)frames > FrameCount-offset) + alSetError(Context, AL_INVALID_VALUE); + else if(type == UserFmtIMA4 && (frames%65) != 0) + alSetError(Context, AL_INVALID_VALUE); + else + { + /* offset -> byte offset */ + offset *= FrameSize; + /* frames -> IMA4 block count */ + if(type == UserFmtIMA4) frames /= 65; + ConvertData(data, type, + &((ALubyte*)ALBuf->data)[offset], ALBuf->FmtType, + ChannelsFromFmt(ALBuf->FmtChannels), frames); + } + } + + UnlockContext(Context); +} + +AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format) +{ + enum FmtChannels DstChannels; + enum FmtType DstType; + ALCcontext *Context; + ALboolean ret; + + Context = GetLockedContext(); + if(!Context) return AL_FALSE; + + ret = DecomposeFormat(format, &DstChannels, &DstType); + + UnlockContext(Context); + + return ret; +} + + +AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum eParam, ALfloat flValue) +{ + ALCcontext *pContext; + ALCdevice *device; + + (void)flValue; + + pContext = GetLockedContext(); + if(!pContext) return; + + device = pContext->Device; + if(LookupBuffer(device->BufferMap, buffer) == NULL) + alSetError(pContext, AL_INVALID_NAME); + else + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum eParam, ALfloat flValue1, ALfloat flValue2, ALfloat flValue3) +{ + ALCcontext *pContext; + ALCdevice *device; + + (void)flValue1; + (void)flValue2; + (void)flValue3; + + pContext = GetLockedContext(); + if(!pContext) return; + + device = pContext->Device; + if(LookupBuffer(device->BufferMap, buffer) == NULL) + alSetError(pContext, AL_INVALID_NAME); + else + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum eParam, const ALfloat* flValues) +{ + ALCcontext *pContext; + ALCdevice *device; + + pContext = GetLockedContext(); + if(!pContext) return; + + device = pContext->Device; + if(!flValues) + alSetError(pContext, AL_INVALID_VALUE); + else if(LookupBuffer(device->BufferMap, buffer) == NULL) + alSetError(pContext, AL_INVALID_NAME); + else + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum eParam, ALint lValue) +{ + ALCcontext *pContext; + ALCdevice *device; + + (void)lValue; + + pContext = GetLockedContext(); + if(!pContext) return; + + device = pContext->Device; + if(LookupBuffer(device->BufferMap, buffer) == NULL) + alSetError(pContext, AL_INVALID_NAME); + else + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alBuffer3i( ALuint buffer, ALenum eParam, ALint lValue1, ALint lValue2, ALint lValue3) +{ + ALCcontext *pContext; + ALCdevice *device; + + (void)lValue1; + (void)lValue2; + (void)lValue3; + + pContext = GetLockedContext(); + if(!pContext) return; + + device = pContext->Device; + if(LookupBuffer(device->BufferMap, buffer) == NULL) + alSetError(pContext, AL_INVALID_NAME); + else + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum eParam, const ALint* plValues) +{ + ALCcontext *pContext; + ALCdevice *device; + ALbuffer *ALBuf; + + pContext = GetLockedContext(); + if(!pContext) return; + + device = pContext->Device; + if(!plValues) + alSetError(pContext, AL_INVALID_VALUE); + else if((ALBuf=LookupBuffer(device->BufferMap, buffer)) == NULL) + alSetError(pContext, AL_INVALID_NAME); + else + { + switch(eParam) + { + case AL_LOOP_POINTS_SOFT: + if(ALBuf->refcount > 0) + alSetError(pContext, AL_INVALID_OPERATION); + else if(plValues[0] < 0 || plValues[1] < 0 || + plValues[0] >= plValues[1] || ALBuf->size == 0) + alSetError(pContext, AL_INVALID_VALUE); + else + { + ALint maxlen = ALBuf->size / + FrameSizeFromFmt(ALBuf->FmtChannels, ALBuf->FmtType); + if(plValues[0] > maxlen || plValues[1] > maxlen) + alSetError(pContext, AL_INVALID_VALUE); + else + { + ALBuf->LoopStart = plValues[0]; + ALBuf->LoopEnd = plValues[1]; + } + } + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum eParam, ALfloat *pflValue) +{ + ALCcontext *pContext; + ALCdevice *device; + + pContext = GetLockedContext(); + if(!pContext) return; + + device = pContext->Device; + if(!pflValue) + alSetError(pContext, AL_INVALID_VALUE); + else if(LookupBuffer(device->BufferMap, buffer) == NULL) + alSetError(pContext, AL_INVALID_NAME); + else + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum eParam, ALfloat* pflValue1, ALfloat* pflValue2, ALfloat* pflValue3) +{ + ALCcontext *pContext; + ALCdevice *device; + + pContext = GetLockedContext(); + if(!pContext) return; + + device = pContext->Device; + if(!pflValue1 || !pflValue2 || !pflValue3) + alSetError(pContext, AL_INVALID_VALUE); + else if(LookupBuffer(device->BufferMap, buffer) == NULL) + alSetError(pContext, AL_INVALID_NAME); + else + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum eParam, ALfloat* pflValues) +{ + ALCcontext *pContext; + ALCdevice *device; + + pContext = GetLockedContext(); + if(!pContext) return; + + device = pContext->Device; + if(!pflValues) + alSetError(pContext, AL_INVALID_VALUE); + else if(LookupBuffer(device->BufferMap, buffer) == NULL) + alSetError(pContext, AL_INVALID_NAME); + else + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum eParam, ALint *plValue) +{ + ALCcontext *pContext; + ALbuffer *pBuffer; + ALCdevice *device; + + pContext = GetLockedContext(); + if(!pContext) return; + + device = pContext->Device; + if(!plValue) + alSetError(pContext, AL_INVALID_VALUE); + else if((pBuffer=LookupBuffer(device->BufferMap, buffer)) == NULL) + alSetError(pContext, AL_INVALID_NAME); + else + { + switch(eParam) + { + case AL_FREQUENCY: + *plValue = pBuffer->Frequency; + break; + + case AL_BITS: + *plValue = BytesFromFmt(pBuffer->FmtType) * 8; + break; + + case AL_CHANNELS: + *plValue = ChannelsFromFmt(pBuffer->FmtChannels); + break; + + case AL_SIZE: + *plValue = pBuffer->size; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum eParam, ALint* plValue1, ALint* plValue2, ALint* plValue3) +{ + ALCcontext *pContext; + ALCdevice *device; + + pContext = GetLockedContext(); + if(!pContext) return; + + device = pContext->Device; + if(!plValue1 || !plValue2 || !plValue3) + alSetError(pContext, AL_INVALID_VALUE); + else if(LookupBuffer(device->BufferMap, buffer) == NULL) + alSetError(pContext, AL_INVALID_NAME); + else + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum eParam, ALint* plValues) +{ + ALCcontext *pContext; + ALCdevice *device; + ALbuffer *ALBuf; + + switch(eParam) + { + case AL_FREQUENCY: + case AL_BITS: + case AL_CHANNELS: + case AL_SIZE: + alGetBufferi(buffer, eParam, plValues); + return; + } + + pContext = GetLockedContext(); + if(!pContext) return; + + device = pContext->Device; + if(!plValues) + alSetError(pContext, AL_INVALID_VALUE); + else if((ALBuf=LookupBuffer(device->BufferMap, buffer)) == NULL) + alSetError(pContext, AL_INVALID_NAME); + else + { + switch(eParam) + { + case AL_LOOP_POINTS_SOFT: + plValues[0] = ALBuf->LoopStart; + plValues[1] = ALBuf->LoopEnd; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + + UnlockContext(pContext); +} + + +typedef ALubyte ALmulaw; +typedef ALubyte ALima4; +typedef struct { + ALbyte b[3]; +} ALbyte3; +typedef struct { + ALubyte b[3]; +} ALubyte3; + +static __inline ALshort DecodeMuLaw(ALmulaw val) +{ return muLawDecompressionTable[val]; } + +static ALmulaw EncodeMuLaw(ALshort val) +{ + ALint mant, exp, sign; + + sign = (val>>8) & 0x80; + if(sign) + { + /* -32768 doesn't properly negate on a short; it results in itself. + * So clamp to -32767 */ + val = maxi(val, -32767); + val = -val; + } + + val = mini(val, muLawClip); + val += muLawBias; + + exp = muLawCompressTable[(val>>7) & 0xff]; + mant = (val >> (exp+3)) & 0x0f; + + return ~(sign | (exp<<4) | mant); +} + +static void DecodeIMA4Block(ALshort *dst, const ALima4 *src, ALint numchans) +{ + ALint sample[MAXCHANNELS], index[MAXCHANNELS]; + ALuint code[MAXCHANNELS]; + ALsizei j,k,c; + + for(c = 0;c < numchans;c++) + { + sample[c] = *(src++); + sample[c] |= *(src++) << 8; + sample[c] = (sample[c]^0x8000) - 32768; + index[c] = *(src++); + index[c] |= *(src++) << 8; + index[c] = (index[c]^0x8000) - 32768; + + index[c] = clampi(index[c], 0, 88); + + dst[c] = sample[c]; + } + + j = 1; + while(j < 65) + { + for(c = 0;c < numchans;c++) + { + code[c] = *(src++); + code[c] |= *(src++) << 8; + code[c] |= *(src++) << 16; + code[c] |= *(src++) << 24; + } + + for(k = 0;k < 8;k++,j++) + { + for(c = 0;c < numchans;c++) + { + int nibble = code[c]&0xf; + code[c] >>= 4; + + sample[c] += IMA4Codeword[nibble] * IMAStep_size[index[c]] / 8; + sample[c] = clampi(sample[c], -32768, 32767); + + index[c] += IMA4Index_adjust[nibble]; + index[c] = clampi(index[c], 0, 88); + + dst[j*numchans + c] = sample[c]; + } + } + } +} + +static void EncodeIMA4Block(ALima4 *dst, const ALshort *src, ALint *sample, ALint *index, ALint numchans) +{ + ALsizei j,k,c; + + for(c = 0;c < numchans;c++) + { + int diff = src[c] - sample[c]; + int step = IMAStep_size[index[c]]; + int nibble; + + nibble = 0; + if(diff < 0) + { + nibble = 0x8; + diff = -diff; + } + + diff = mini(step*2, diff); + nibble |= (diff*8/step - 1) / 2; + + sample[c] += IMA4Codeword[nibble] * step / 8; + sample[c] = clampi(sample[c], -32768, 32767); + + index[c] += IMA4Index_adjust[nibble]; + index[c] = clampi(index[c], 0, 88); + + *(dst++) = sample[c] & 0xff; + *(dst++) = (sample[c]>>8) & 0xff; + *(dst++) = index[c] & 0xff; + *(dst++) = (index[c]>>8) & 0xff; + } + + j = 1; + while(j < 65) + { + for(c = 0;c < numchans;c++) + { + for(k = 0;k < 8;k++) + { + int diff = src[(j+k)*numchans + c] - sample[c]; + int step = IMAStep_size[index[c]]; + int nibble; + + nibble = 0; + if(diff < 0) + { + nibble = 0x8; + diff = -diff; + } + + diff = mini(step*2, diff); + nibble |= (diff*8/step - 1) / 2; + + sample[c] += IMA4Codeword[nibble] * step / 8; + sample[c] = clampi(sample[c], -32768, 32767); + + index[c] += IMA4Index_adjust[nibble]; + index[c] = clampi(index[c], 0, 88); + + if(!(k&1)) *dst = nibble; + else *(dst++) |= nibble<<4; + } + } + j += 8; + } +} + +static const union { + ALuint u; + ALubyte b[sizeof(ALuint)]; +} EndianTest = { 1 }; +#define IS_LITTLE_ENDIAN (EndianTest.b[0] == 1) + +static __inline ALint DecodeByte3(ALbyte3 val) +{ + if(IS_LITTLE_ENDIAN) + return (val.b[2]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[0]); + return (val.b[0]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[2]); +} + +static __inline ALbyte3 EncodeByte3(ALint val) +{ + if(IS_LITTLE_ENDIAN) + { + ALbyte3 ret = {{ val, val>>8, val>>16 }}; + return ret; + } + else + { + ALbyte3 ret = {{ val>>16, val>>8, val }}; + return ret; + } +} + +static __inline ALint DecodeUByte3(ALubyte3 val) +{ + if(IS_LITTLE_ENDIAN) + return (val.b[2]<<16) | (val.b[1]<<8) | (val.b[0]); + return (val.b[0]<<16) | (val.b[1]<<8) | val.b[2]; +} + +static __inline ALubyte3 EncodeUByte3(ALint val) +{ + if(IS_LITTLE_ENDIAN) + { + ALubyte3 ret = {{ val, val>>8, val>>16 }}; + return ret; + } + else + { + ALubyte3 ret = {{ val>>16, val>>8, val }}; + return ret; + } +} + + +static __inline ALbyte Conv_ALbyte_ALbyte(ALbyte val) +{ return val; } +static __inline ALbyte Conv_ALbyte_ALubyte(ALubyte val) +{ return val-128; } +static __inline ALbyte Conv_ALbyte_ALshort(ALshort val) +{ return val>>8; } +static __inline ALbyte Conv_ALbyte_ALushort(ALushort val) +{ return (val>>8)-128; } +static __inline ALbyte Conv_ALbyte_ALint(ALint val) +{ return val>>24; } +static __inline ALbyte Conv_ALbyte_ALuint(ALuint val) +{ return (val>>24)-128; } +static __inline ALbyte Conv_ALbyte_ALfloat(ALfloat val) +{ + if(val > 1.0f) return 127; + if(val < -1.0f) return -128; + return (ALint)(val * 127.0f); +} +static __inline ALbyte Conv_ALbyte_ALdouble(ALdouble val) +{ + if(val > 1.0) return 127; + if(val < -1.0) return -128; + return (ALint)(val * 127.0); +} +static __inline ALbyte Conv_ALbyte_ALmulaw(ALmulaw val) +{ return Conv_ALbyte_ALshort(DecodeMuLaw(val)); } +static __inline ALbyte Conv_ALbyte_ALbyte3(ALbyte3 val) +{ return DecodeByte3(val)>>16; } +static __inline ALbyte Conv_ALbyte_ALubyte3(ALubyte3 val) +{ return (DecodeUByte3(val)>>16)-128; } + +static __inline ALubyte Conv_ALubyte_ALbyte(ALbyte val) +{ return val+128; } +static __inline ALubyte Conv_ALubyte_ALubyte(ALubyte val) +{ return val; } +static __inline ALubyte Conv_ALubyte_ALshort(ALshort val) +{ return (val>>8)+128; } +static __inline ALubyte Conv_ALubyte_ALushort(ALushort val) +{ return val>>8; } +static __inline ALubyte Conv_ALubyte_ALint(ALint val) +{ return (val>>24)+128; } +static __inline ALubyte Conv_ALubyte_ALuint(ALuint val) +{ return val>>24; } +static __inline ALubyte Conv_ALubyte_ALfloat(ALfloat val) +{ + if(val > 1.0f) return 255; + if(val < -1.0f) return 0; + return (ALint)(val * 127.0f) + 128; +} +static __inline ALubyte Conv_ALubyte_ALdouble(ALdouble val) +{ + if(val > 1.0) return 255; + if(val < -1.0) return 0; + return (ALint)(val * 127.0) + 128; +} +static __inline ALubyte Conv_ALubyte_ALmulaw(ALmulaw val) +{ return Conv_ALubyte_ALshort(DecodeMuLaw(val)); } +static __inline ALubyte Conv_ALubyte_ALbyte3(ALbyte3 val) +{ return (DecodeByte3(val)>>16)+128; } +static __inline ALubyte Conv_ALubyte_ALubyte3(ALubyte3 val) +{ return DecodeUByte3(val)>>16; } + +static __inline ALshort Conv_ALshort_ALbyte(ALbyte val) +{ return val<<8; } +static __inline ALshort Conv_ALshort_ALubyte(ALubyte val) +{ return (val-128)<<8; } +static __inline ALshort Conv_ALshort_ALshort(ALshort val) +{ return val; } +static __inline ALshort Conv_ALshort_ALushort(ALushort val) +{ return val-32768; } +static __inline ALshort Conv_ALshort_ALint(ALint val) +{ return val>>16; } +static __inline ALshort Conv_ALshort_ALuint(ALuint val) +{ return (val>>16)-32768; } +static __inline ALshort Conv_ALshort_ALfloat(ALfloat val) +{ + if(val > 1.0f) return 32767; + if(val < -1.0f) return -32768; + return (ALint)(val * 32767.0f); +} +static __inline ALshort Conv_ALshort_ALdouble(ALdouble val) +{ + if(val > 1.0) return 32767; + if(val < -1.0) return -32768; + return (ALint)(val * 32767.0); +} +static __inline ALshort Conv_ALshort_ALmulaw(ALmulaw val) +{ return Conv_ALshort_ALshort(DecodeMuLaw(val)); } +static __inline ALshort Conv_ALshort_ALbyte3(ALbyte3 val) +{ return DecodeByte3(val)>>8; } +static __inline ALshort Conv_ALshort_ALubyte3(ALubyte3 val) +{ return (DecodeUByte3(val)>>8)-32768; } + +static __inline ALushort Conv_ALushort_ALbyte(ALbyte val) +{ return (val+128)<<8; } +static __inline ALushort Conv_ALushort_ALubyte(ALubyte val) +{ return val<<8; } +static __inline ALushort Conv_ALushort_ALshort(ALshort val) +{ return val+32768; } +static __inline ALushort Conv_ALushort_ALushort(ALushort val) +{ return val; } +static __inline ALushort Conv_ALushort_ALint(ALint val) +{ return (val>>16)+32768; } +static __inline ALushort Conv_ALushort_ALuint(ALuint val) +{ return val>>16; } +static __inline ALushort Conv_ALushort_ALfloat(ALfloat val) +{ + if(val > 1.0f) return 65535; + if(val < -1.0f) return 0; + return (ALint)(val * 32767.0f) + 32768; +} +static __inline ALushort Conv_ALushort_ALdouble(ALdouble val) +{ + if(val > 1.0) return 65535; + if(val < -1.0) return 0; + return (ALint)(val * 32767.0) + 32768; +} +static __inline ALushort Conv_ALushort_ALmulaw(ALmulaw val) +{ return Conv_ALushort_ALshort(DecodeMuLaw(val)); } +static __inline ALushort Conv_ALushort_ALbyte3(ALbyte3 val) +{ return (DecodeByte3(val)>>8)+32768; } +static __inline ALushort Conv_ALushort_ALubyte3(ALubyte3 val) +{ return DecodeUByte3(val)>>8; } + +static __inline ALint Conv_ALint_ALbyte(ALbyte val) +{ return val<<24; } +static __inline ALint Conv_ALint_ALubyte(ALubyte val) +{ return (val-128)<<24; } +static __inline ALint Conv_ALint_ALshort(ALshort val) +{ return val<<16; } +static __inline ALint Conv_ALint_ALushort(ALushort val) +{ return (val-32768)<<16; } +static __inline ALint Conv_ALint_ALint(ALint val) +{ return val; } +static __inline ALint Conv_ALint_ALuint(ALuint val) +{ return val-2147483648u; } +static __inline ALint Conv_ALint_ALfloat(ALfloat val) +{ + if(val > 1.0f) return 2147483647; + if(val < -1.0f) return -2147483647-1; + return (ALint)(val * 2147483647.0); +} +static __inline ALint Conv_ALint_ALdouble(ALdouble val) +{ + if(val > 1.0) return 2147483647; + if(val < -1.0) return -2147483647-1; + return (ALint)(val * 2147483647.0); +} +static __inline ALint Conv_ALint_ALmulaw(ALmulaw val) +{ return Conv_ALint_ALshort(DecodeMuLaw(val)); } +static __inline ALint Conv_ALint_ALbyte3(ALbyte3 val) +{ return DecodeByte3(val)<<8; } +static __inline ALint Conv_ALint_ALubyte3(ALubyte3 val) +{ return (DecodeUByte3(val)-8388608)<<8; } + +static __inline ALuint Conv_ALuint_ALbyte(ALbyte val) +{ return (val+128)<<24; } +static __inline ALuint Conv_ALuint_ALubyte(ALubyte val) +{ return val<<24; } +static __inline ALuint Conv_ALuint_ALshort(ALshort val) +{ return (val+32768)<<16; } +static __inline ALuint Conv_ALuint_ALushort(ALushort val) +{ return val<<16; } +static __inline ALuint Conv_ALuint_ALint(ALint val) +{ return val+2147483648u; } +static __inline ALuint Conv_ALuint_ALuint(ALuint val) +{ return val; } +static __inline ALuint Conv_ALuint_ALfloat(ALfloat val) +{ + if(val > 1.0f) return 4294967295u; + if(val < -1.0f) return 0; + return (ALint)(val * 2147483647.0) + 2147483648u; +} +static __inline ALuint Conv_ALuint_ALdouble(ALdouble val) +{ + if(val > 1.0) return 4294967295u; + if(val < -1.0) return 0; + return (ALint)(val * 2147483647.0) + 2147483648u; +} +static __inline ALuint Conv_ALuint_ALmulaw(ALmulaw val) +{ return Conv_ALuint_ALshort(DecodeMuLaw(val)); } +static __inline ALuint Conv_ALuint_ALbyte3(ALbyte3 val) +{ return (DecodeByte3(val)+8388608)<<8; } +static __inline ALuint Conv_ALuint_ALubyte3(ALubyte3 val) +{ return DecodeUByte3(val)<<8; } + +static __inline ALfloat Conv_ALfloat_ALbyte(ALbyte val) +{ return val * (1.0f/127.0f); } +static __inline ALfloat Conv_ALfloat_ALubyte(ALubyte val) +{ return (val-128) * (1.0f/127.0f); } +static __inline ALfloat Conv_ALfloat_ALshort(ALshort val) +{ return val * (1.0f/32767.0f); } +static __inline ALfloat Conv_ALfloat_ALushort(ALushort val) +{ return (val-32768) * (1.0f/32767.0f); } +static __inline ALfloat Conv_ALfloat_ALint(ALint val) +{ return val * (1.0/2147483647.0); } +static __inline ALfloat Conv_ALfloat_ALuint(ALuint val) +{ return (ALint)(val-2147483648u) * (1.0/2147483647.0); } +static __inline ALfloat Conv_ALfloat_ALfloat(ALfloat val) +{ return (val==val) ? val : 0.0f; } +static __inline ALfloat Conv_ALfloat_ALdouble(ALdouble val) +{ return (val==val) ? val : 0.0; } +static __inline ALfloat Conv_ALfloat_ALmulaw(ALmulaw val) +{ return Conv_ALfloat_ALshort(DecodeMuLaw(val)); } +static __inline ALfloat Conv_ALfloat_ALbyte3(ALbyte3 val) +{ return DecodeByte3(val) * (1.0/8388607.0); } +static __inline ALfloat Conv_ALfloat_ALubyte3(ALubyte3 val) +{ return (DecodeUByte3(val)-8388608) * (1.0/8388607.0); } + +static __inline ALdouble Conv_ALdouble_ALbyte(ALbyte val) +{ return val * (1.0/127.0); } +static __inline ALdouble Conv_ALdouble_ALubyte(ALubyte val) +{ return (val-128) * (1.0/127.0); } +static __inline ALdouble Conv_ALdouble_ALshort(ALshort val) +{ return val * (1.0/32767.0); } +static __inline ALdouble Conv_ALdouble_ALushort(ALushort val) +{ return (val-32768) * (1.0/32767.0); } +static __inline ALdouble Conv_ALdouble_ALint(ALint val) +{ return val * (1.0/2147483647.0); } +static __inline ALdouble Conv_ALdouble_ALuint(ALuint val) +{ return (ALint)(val-2147483648u) * (1.0/2147483647.0); } +static __inline ALdouble Conv_ALdouble_ALfloat(ALfloat val) +{ return (val==val) ? val : 0.0f; } +static __inline ALdouble Conv_ALdouble_ALdouble(ALdouble val) +{ return (val==val) ? val : 0.0; } +static __inline ALdouble Conv_ALdouble_ALmulaw(ALmulaw val) +{ return Conv_ALdouble_ALshort(DecodeMuLaw(val)); } +static __inline ALdouble Conv_ALdouble_ALbyte3(ALbyte3 val) +{ return DecodeByte3(val) * (1.0/8388607.0); } +static __inline ALdouble Conv_ALdouble_ALubyte3(ALubyte3 val) +{ return (DecodeUByte3(val)-8388608) * (1.0/8388607.0); } + +#define DECL_TEMPLATE(T) \ +static __inline ALmulaw Conv_ALmulaw_##T(T val) \ +{ return EncodeMuLaw(Conv_ALshort_##T(val)); } + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +static __inline ALmulaw Conv_ALmulaw_ALmulaw(ALmulaw val) +{ return val; } +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static __inline ALbyte3 Conv_ALbyte3_##T(T val) \ +{ return EncodeByte3(Conv_ALint_##T(val)>>8); } + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +static __inline ALbyte3 Conv_ALbyte3_ALbyte3(ALbyte3 val) +{ return val; } +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static __inline ALubyte3 Conv_ALubyte3_##T(T val) \ +{ return EncodeUByte3(Conv_ALuint_##T(val)>>8); } + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALbyte3) +static __inline ALubyte3 Conv_ALubyte3_ALubyte3(ALubyte3 val) +{ return val; } + +#undef DECL_TEMPLATE + + +#define DECL_TEMPLATE(T1, T2) \ +static void Convert_##T1##_##T2(T1 *dst, const T2 *src, ALuint numchans, \ + ALuint len) \ +{ \ + ALuint i, j; \ + for(i = 0;i < len;i++) \ + { \ + for(j = 0;j < numchans;j++) \ + *(dst++) = Conv_##T1##_##T2(*(src++)); \ + } \ +} + +DECL_TEMPLATE(ALbyte, ALbyte) +DECL_TEMPLATE(ALbyte, ALubyte) +DECL_TEMPLATE(ALbyte, ALshort) +DECL_TEMPLATE(ALbyte, ALushort) +DECL_TEMPLATE(ALbyte, ALint) +DECL_TEMPLATE(ALbyte, ALuint) +DECL_TEMPLATE(ALbyte, ALfloat) +DECL_TEMPLATE(ALbyte, ALdouble) +DECL_TEMPLATE(ALbyte, ALmulaw) +DECL_TEMPLATE(ALbyte, ALbyte3) +DECL_TEMPLATE(ALbyte, ALubyte3) + +DECL_TEMPLATE(ALubyte, ALbyte) +DECL_TEMPLATE(ALubyte, ALubyte) +DECL_TEMPLATE(ALubyte, ALshort) +DECL_TEMPLATE(ALubyte, ALushort) +DECL_TEMPLATE(ALubyte, ALint) +DECL_TEMPLATE(ALubyte, ALuint) +DECL_TEMPLATE(ALubyte, ALfloat) +DECL_TEMPLATE(ALubyte, ALdouble) +DECL_TEMPLATE(ALubyte, ALmulaw) +DECL_TEMPLATE(ALubyte, ALbyte3) +DECL_TEMPLATE(ALubyte, ALubyte3) + +DECL_TEMPLATE(ALshort, ALbyte) +DECL_TEMPLATE(ALshort, ALubyte) +DECL_TEMPLATE(ALshort, ALshort) +DECL_TEMPLATE(ALshort, ALushort) +DECL_TEMPLATE(ALshort, ALint) +DECL_TEMPLATE(ALshort, ALuint) +DECL_TEMPLATE(ALshort, ALfloat) +DECL_TEMPLATE(ALshort, ALdouble) +DECL_TEMPLATE(ALshort, ALmulaw) +DECL_TEMPLATE(ALshort, ALbyte3) +DECL_TEMPLATE(ALshort, ALubyte3) + +DECL_TEMPLATE(ALushort, ALbyte) +DECL_TEMPLATE(ALushort, ALubyte) +DECL_TEMPLATE(ALushort, ALshort) +DECL_TEMPLATE(ALushort, ALushort) +DECL_TEMPLATE(ALushort, ALint) +DECL_TEMPLATE(ALushort, ALuint) +DECL_TEMPLATE(ALushort, ALfloat) +DECL_TEMPLATE(ALushort, ALdouble) +DECL_TEMPLATE(ALushort, ALmulaw) +DECL_TEMPLATE(ALushort, ALbyte3) +DECL_TEMPLATE(ALushort, ALubyte3) + +DECL_TEMPLATE(ALint, ALbyte) +DECL_TEMPLATE(ALint, ALubyte) +DECL_TEMPLATE(ALint, ALshort) +DECL_TEMPLATE(ALint, ALushort) +DECL_TEMPLATE(ALint, ALint) +DECL_TEMPLATE(ALint, ALuint) +DECL_TEMPLATE(ALint, ALfloat) +DECL_TEMPLATE(ALint, ALdouble) +DECL_TEMPLATE(ALint, ALmulaw) +DECL_TEMPLATE(ALint, ALbyte3) +DECL_TEMPLATE(ALint, ALubyte3) + +DECL_TEMPLATE(ALuint, ALbyte) +DECL_TEMPLATE(ALuint, ALubyte) +DECL_TEMPLATE(ALuint, ALshort) +DECL_TEMPLATE(ALuint, ALushort) +DECL_TEMPLATE(ALuint, ALint) +DECL_TEMPLATE(ALuint, ALuint) +DECL_TEMPLATE(ALuint, ALfloat) +DECL_TEMPLATE(ALuint, ALdouble) +DECL_TEMPLATE(ALuint, ALmulaw) +DECL_TEMPLATE(ALuint, ALbyte3) +DECL_TEMPLATE(ALuint, ALubyte3) + +DECL_TEMPLATE(ALfloat, ALbyte) +DECL_TEMPLATE(ALfloat, ALubyte) +DECL_TEMPLATE(ALfloat, ALshort) +DECL_TEMPLATE(ALfloat, ALushort) +DECL_TEMPLATE(ALfloat, ALint) +DECL_TEMPLATE(ALfloat, ALuint) +DECL_TEMPLATE(ALfloat, ALfloat) +DECL_TEMPLATE(ALfloat, ALdouble) +DECL_TEMPLATE(ALfloat, ALmulaw) +DECL_TEMPLATE(ALfloat, ALbyte3) +DECL_TEMPLATE(ALfloat, ALubyte3) + +DECL_TEMPLATE(ALdouble, ALbyte) +DECL_TEMPLATE(ALdouble, ALubyte) +DECL_TEMPLATE(ALdouble, ALshort) +DECL_TEMPLATE(ALdouble, ALushort) +DECL_TEMPLATE(ALdouble, ALint) +DECL_TEMPLATE(ALdouble, ALuint) +DECL_TEMPLATE(ALdouble, ALfloat) +DECL_TEMPLATE(ALdouble, ALdouble) +DECL_TEMPLATE(ALdouble, ALmulaw) +DECL_TEMPLATE(ALdouble, ALbyte3) +DECL_TEMPLATE(ALdouble, ALubyte3) + +DECL_TEMPLATE(ALmulaw, ALbyte) +DECL_TEMPLATE(ALmulaw, ALubyte) +DECL_TEMPLATE(ALmulaw, ALshort) +DECL_TEMPLATE(ALmulaw, ALushort) +DECL_TEMPLATE(ALmulaw, ALint) +DECL_TEMPLATE(ALmulaw, ALuint) +DECL_TEMPLATE(ALmulaw, ALfloat) +DECL_TEMPLATE(ALmulaw, ALdouble) +DECL_TEMPLATE(ALmulaw, ALmulaw) +DECL_TEMPLATE(ALmulaw, ALbyte3) +DECL_TEMPLATE(ALmulaw, ALubyte3) + +DECL_TEMPLATE(ALbyte3, ALbyte) +DECL_TEMPLATE(ALbyte3, ALubyte) +DECL_TEMPLATE(ALbyte3, ALshort) +DECL_TEMPLATE(ALbyte3, ALushort) +DECL_TEMPLATE(ALbyte3, ALint) +DECL_TEMPLATE(ALbyte3, ALuint) +DECL_TEMPLATE(ALbyte3, ALfloat) +DECL_TEMPLATE(ALbyte3, ALdouble) +DECL_TEMPLATE(ALbyte3, ALmulaw) +DECL_TEMPLATE(ALbyte3, ALbyte3) +DECL_TEMPLATE(ALbyte3, ALubyte3) + +DECL_TEMPLATE(ALubyte3, ALbyte) +DECL_TEMPLATE(ALubyte3, ALubyte) +DECL_TEMPLATE(ALubyte3, ALshort) +DECL_TEMPLATE(ALubyte3, ALushort) +DECL_TEMPLATE(ALubyte3, ALint) +DECL_TEMPLATE(ALubyte3, ALuint) +DECL_TEMPLATE(ALubyte3, ALfloat) +DECL_TEMPLATE(ALubyte3, ALdouble) +DECL_TEMPLATE(ALubyte3, ALmulaw) +DECL_TEMPLATE(ALubyte3, ALbyte3) +DECL_TEMPLATE(ALubyte3, ALubyte3) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static void Convert_##T##_ALima4(T *dst, const ALima4 *src, ALuint numchans, \ + ALuint numblocks) \ +{ \ + ALuint i, j; \ + ALshort tmp[65*MAXCHANNELS]; /* Max samples an IMA4 frame can be */ \ + for(i = 0;i < numblocks;i++) \ + { \ + DecodeIMA4Block(tmp, src, numchans); \ + src += 36*numchans; \ + for(j = 0;j < 65*numchans;j++) \ + *(dst++) = Conv_##T##_ALshort(tmp[j]); \ + } \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static void Convert_ALima4_##T(ALima4 *dst, const T *src, ALuint numchans, \ + ALuint numblocks) \ +{ \ + ALuint i, j; \ + ALshort tmp[65*MAXCHANNELS]; /* Max samples an IMA4 frame can be */ \ + ALint sample[MAXCHANNELS] = {0,0,0,0,0,0,0,0}; \ + ALint index[MAXCHANNELS] = {0,0,0,0,0,0,0,0}; \ + for(i = 0;i < numblocks;i++) \ + { \ + for(j = 0;j < 65*numchans;j++) \ + tmp[j] = Conv_ALshort_##T(*(src++)); \ + EncodeIMA4Block(dst, tmp, sample, index, numchans); \ + dst += 36*numchans; \ + } \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +static void Convert_ALima4_ALima4(ALima4 *dst, const ALima4 *src, + ALuint numchans, ALuint numblocks) +{ memcpy(dst, src, numblocks*36*numchans); } +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static void Convert_##T(T *dst, const ALvoid *src, enum UserFmtType srcType, \ + ALsizei numchans, ALsizei len) \ +{ \ + switch(srcType) \ + { \ + case UserFmtByte: \ + Convert_##T##_ALbyte(dst, src, numchans, len); \ + break; \ + case UserFmtUByte: \ + Convert_##T##_ALubyte(dst, src, numchans, len); \ + break; \ + case UserFmtShort: \ + Convert_##T##_ALshort(dst, src, numchans, len); \ + break; \ + case UserFmtUShort: \ + Convert_##T##_ALushort(dst, src, numchans, len); \ + break; \ + case UserFmtInt: \ + Convert_##T##_ALint(dst, src, numchans, len); \ + break; \ + case UserFmtUInt: \ + Convert_##T##_ALuint(dst, src, numchans, len); \ + break; \ + case UserFmtFloat: \ + Convert_##T##_ALfloat(dst, src, numchans, len); \ + break; \ + case UserFmtDouble: \ + Convert_##T##_ALdouble(dst, src, numchans, len); \ + break; \ + case UserFmtMulaw: \ + Convert_##T##_ALmulaw(dst, src, numchans, len); \ + break; \ + case UserFmtIMA4: \ + Convert_##T##_ALima4(dst, src, numchans, len); \ + break; \ + case UserFmtByte3: \ + Convert_##T##_ALbyte3(dst, src, numchans, len); \ + break; \ + case UserFmtUByte3: \ + Convert_##T##_ALubyte3(dst, src, numchans, len); \ + break; \ + } \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALima4) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + + +static void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len) +{ + switch(dstType) + { + case UserFmtByte: + Convert_ALbyte(dst, src, srcType, numchans, len); + break; + case UserFmtUByte: + Convert_ALubyte(dst, src, srcType, numchans, len); + break; + case UserFmtShort: + Convert_ALshort(dst, src, srcType, numchans, len); + break; + case UserFmtUShort: + Convert_ALushort(dst, src, srcType, numchans, len); + break; + case UserFmtInt: + Convert_ALint(dst, src, srcType, numchans, len); + break; + case UserFmtUInt: + Convert_ALuint(dst, src, srcType, numchans, len); + break; + case UserFmtFloat: + Convert_ALfloat(dst, src, srcType, numchans, len); + break; + case UserFmtDouble: + Convert_ALdouble(dst, src, srcType, numchans, len); + break; + case UserFmtMulaw: + Convert_ALmulaw(dst, src, srcType, numchans, len); + break; + case UserFmtIMA4: + Convert_ALima4(dst, src, srcType, numchans, len); + break; + case UserFmtByte3: + Convert_ALbyte3(dst, src, srcType, numchans, len); + break; + case UserFmtUByte3: + Convert_ALubyte3(dst, src, srcType, numchans, len); + break; + } +} + + +/* + * LoadData + * + * Loads the specified data into the buffer, using the specified formats. + * Currently, the new format must have the same channel configuration as the + * original format. + */ +static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALboolean storesrc) +{ + ALuint NewChannels, NewBytes; + enum FmtChannels DstChannels; + enum FmtType DstType; + ALuint64 newsize; + ALvoid *temp; + + if(DecomposeFormat(NewFormat, &DstChannels, &DstType) == AL_FALSE || + (long)SrcChannels != (long)DstChannels) + return AL_INVALID_ENUM; + + NewChannels = ChannelsFromFmt(DstChannels); + NewBytes = BytesFromFmt(DstType); + + if(SrcType == UserFmtIMA4) + { + ALuint OrigChannels = ChannelsFromUserFmt(SrcChannels); + + newsize = frames; + newsize *= 65; + newsize *= NewBytes; + newsize *= NewChannels; + if(newsize > INT_MAX) + return AL_OUT_OF_MEMORY; + + temp = realloc(ALBuf->data, newsize); + if(!temp && newsize) return AL_OUT_OF_MEMORY; + ALBuf->data = temp; + ALBuf->size = newsize; + + if(data != NULL) + ConvertData(ALBuf->data, DstType, data, SrcType, NewChannels, frames); + + if(storesrc) + { + ALBuf->OriginalChannels = SrcChannels; + ALBuf->OriginalType = SrcType; + ALBuf->OriginalSize = frames * 36 * OrigChannels; + ALBuf->OriginalAlign = 36 * OrigChannels; + } + } + else + { + ALuint OrigBytes = BytesFromUserFmt(SrcType); + ALuint OrigChannels = ChannelsFromUserFmt(SrcChannels); + + newsize = frames; + newsize *= NewBytes; + newsize *= NewChannels; + if(newsize > INT_MAX) + return AL_OUT_OF_MEMORY; + + temp = realloc(ALBuf->data, newsize); + if(!temp && newsize) return AL_OUT_OF_MEMORY; + ALBuf->data = temp; + ALBuf->size = newsize; + + if(data != NULL) + ConvertData(ALBuf->data, DstType, data, SrcType, NewChannels, frames); + + if(storesrc) + { + ALBuf->OriginalChannels = SrcChannels; + ALBuf->OriginalType = SrcType; + ALBuf->OriginalSize = frames * OrigBytes * OrigChannels; + ALBuf->OriginalAlign = OrigBytes * OrigChannels; + } + } + + if(!storesrc) + { + ALBuf->OriginalChannels = DstChannels; + ALBuf->OriginalType = DstType; + ALBuf->OriginalSize = frames * NewBytes * NewChannels; + ALBuf->OriginalAlign = NewBytes * NewChannels; + } + ALBuf->Frequency = freq; + ALBuf->FmtChannels = DstChannels; + ALBuf->FmtType = DstType; + + ALBuf->LoopStart = 0; + ALBuf->LoopEnd = newsize / NewChannels / NewBytes; + + return AL_NO_ERROR; +} + + +ALuint BytesFromUserFmt(enum UserFmtType type) +{ + switch(type) + { + case UserFmtByte: return sizeof(ALbyte); + case UserFmtUByte: return sizeof(ALubyte); + case UserFmtShort: return sizeof(ALshort); + case UserFmtUShort: return sizeof(ALushort); + case UserFmtInt: return sizeof(ALint); + case UserFmtUInt: return sizeof(ALuint); + case UserFmtFloat: return sizeof(ALfloat); + case UserFmtDouble: return sizeof(ALdouble); + case UserFmtByte3: return sizeof(ALbyte3); + case UserFmtUByte3: return sizeof(ALubyte3); + case UserFmtMulaw: return sizeof(ALubyte); + case UserFmtIMA4: break; /* not handled here */ + } + return 0; +} +ALuint ChannelsFromUserFmt(enum UserFmtChannels chans) +{ + switch(chans) + { + case UserFmtMono: return 1; + case UserFmtStereo: return 2; + case UserFmtRear: return 2; + case UserFmtQuad: return 4; + case UserFmtX51: return 6; + case UserFmtX61: return 7; + case UserFmtX71: return 8; + } + return 0; +} +ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, + enum UserFmtType *type) +{ + switch(format) + { + case AL_FORMAT_MONO8: + *chans = UserFmtMono; + *type = UserFmtUByte; + return AL_TRUE; + case AL_FORMAT_MONO16: + *chans = UserFmtMono; + *type = UserFmtShort; + return AL_TRUE; + case AL_FORMAT_MONO_FLOAT32: + *chans = UserFmtMono; + *type = UserFmtFloat; + return AL_TRUE; + case AL_FORMAT_MONO_DOUBLE_EXT: + *chans = UserFmtMono; + *type = UserFmtDouble; + return AL_TRUE; + case AL_FORMAT_MONO_IMA4: + *chans = UserFmtMono; + *type = UserFmtIMA4; + return AL_TRUE; + case AL_FORMAT_STEREO8: + *chans = UserFmtStereo; + *type = UserFmtUByte; + return AL_TRUE; + case AL_FORMAT_STEREO16: + *chans = UserFmtStereo; + *type = UserFmtShort; + return AL_TRUE; + case AL_FORMAT_STEREO_FLOAT32: + *chans = UserFmtStereo; + *type = UserFmtFloat; + return AL_TRUE; + case AL_FORMAT_STEREO_DOUBLE_EXT: + *chans = UserFmtStereo; + *type = UserFmtDouble; + return AL_TRUE; + case AL_FORMAT_STEREO_IMA4: + *chans = UserFmtStereo; + *type = UserFmtIMA4; + return AL_TRUE; + case AL_FORMAT_QUAD8_LOKI: + case AL_FORMAT_QUAD8: + *chans = UserFmtQuad; + *type = UserFmtUByte; + return AL_TRUE; + case AL_FORMAT_QUAD16_LOKI: + case AL_FORMAT_QUAD16: + *chans = UserFmtQuad; + *type = UserFmtShort; + return AL_TRUE; + case AL_FORMAT_QUAD32: + *chans = UserFmtQuad; + *type = UserFmtFloat; + return AL_TRUE; + case AL_FORMAT_REAR8: + *chans = UserFmtRear; + *type = UserFmtUByte; + return AL_TRUE; + case AL_FORMAT_REAR16: + *chans = UserFmtRear; + *type = UserFmtShort; + return AL_TRUE; + case AL_FORMAT_REAR32: + *chans = UserFmtRear; + *type = UserFmtFloat; + return AL_TRUE; + case AL_FORMAT_51CHN8: + *chans = UserFmtX51; + *type = UserFmtUByte; + return AL_TRUE; + case AL_FORMAT_51CHN16: + *chans = UserFmtX51; + *type = UserFmtShort; + return AL_TRUE; + case AL_FORMAT_51CHN32: + *chans = UserFmtX51; + *type = UserFmtFloat; + return AL_TRUE; + case AL_FORMAT_61CHN8: + *chans = UserFmtX61; + *type = UserFmtUByte; + return AL_TRUE; + case AL_FORMAT_61CHN16: + *chans = UserFmtX61; + *type = UserFmtShort; + return AL_TRUE; + case AL_FORMAT_61CHN32: + *chans = UserFmtX61; + *type = UserFmtFloat; + return AL_TRUE; + case AL_FORMAT_71CHN8: + *chans = UserFmtX71; + *type = UserFmtUByte; + return AL_TRUE; + case AL_FORMAT_71CHN16: + *chans = UserFmtX71; + *type = UserFmtShort; + return AL_TRUE; + case AL_FORMAT_71CHN32: + *chans = UserFmtX71; + *type = UserFmtFloat; + return AL_TRUE; + case AL_FORMAT_MONO_MULAW: + *chans = UserFmtMono; + *type = UserFmtMulaw; + return AL_TRUE; + case AL_FORMAT_STEREO_MULAW: + *chans = UserFmtStereo; + *type = UserFmtMulaw; + return AL_TRUE; + case AL_FORMAT_QUAD_MULAW: + *chans = UserFmtQuad; + *type = UserFmtMulaw; + return AL_TRUE; + case AL_FORMAT_REAR_MULAW: + *chans = UserFmtRear; + *type = UserFmtMulaw; + return AL_TRUE; + case AL_FORMAT_51CHN_MULAW: + *chans = UserFmtX51; + *type = UserFmtMulaw; + return AL_TRUE; + case AL_FORMAT_61CHN_MULAW: + *chans = UserFmtX61; + *type = UserFmtMulaw; + return AL_TRUE; + case AL_FORMAT_71CHN_MULAW: + *chans = UserFmtX71; + *type = UserFmtMulaw; + return AL_TRUE; + } + return AL_FALSE; +} + +ALuint BytesFromFmt(enum FmtType type) +{ + switch(type) + { + case FmtByte: return sizeof(ALbyte); + case FmtShort: return sizeof(ALshort); + case FmtFloat: return sizeof(ALfloat); + } + return 0; +} +ALuint ChannelsFromFmt(enum FmtChannels chans) +{ + switch(chans) + { + case FmtMono: return 1; + case FmtStereo: return 2; + case FmtRear: return 2; + case FmtQuad: return 4; + case FmtX51: return 6; + case FmtX61: return 7; + case FmtX71: return 8; + } + return 0; +} +ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type) +{ + switch(format) + { + case AL_MONO8: + *chans = FmtMono; + *type = FmtByte; + return AL_TRUE; + case AL_MONO16: + *chans = FmtMono; + *type = FmtShort; + return AL_TRUE; + case AL_MONO32F: + *chans = FmtMono; + *type = FmtFloat; + return AL_TRUE; + case AL_STEREO8: + *chans = FmtStereo; + *type = FmtByte; + return AL_TRUE; + case AL_STEREO16: + *chans = FmtStereo; + *type = FmtShort; + return AL_TRUE; + case AL_STEREO32F: + *chans = FmtStereo; + *type = FmtFloat; + return AL_TRUE; + case AL_FORMAT_QUAD8_LOKI: + case AL_QUAD8: + *chans = FmtQuad; + *type = FmtByte; + return AL_TRUE; + case AL_FORMAT_QUAD16_LOKI: + case AL_QUAD16: + *chans = FmtQuad; + *type = FmtShort; + return AL_TRUE; + case AL_QUAD32F: + *chans = FmtQuad; + *type = FmtFloat; + return AL_TRUE; + case AL_REAR8: + *chans = FmtRear; + *type = FmtByte; + return AL_TRUE; + case AL_REAR16: + *chans = FmtRear; + *type = FmtShort; + return AL_TRUE; + case AL_REAR32F: + *chans = FmtRear; + *type = FmtFloat; + return AL_TRUE; + case AL_5POINT1_8: + *chans = FmtX51; + *type = FmtByte; + return AL_TRUE; + case AL_5POINT1_16: + *chans = FmtX51; + *type = FmtShort; + return AL_TRUE; + case AL_5POINT1_32F: + *chans = FmtX51; + *type = FmtFloat; + return AL_TRUE; + case AL_6POINT1_8: + *chans = FmtX61; + *type = FmtByte; + return AL_TRUE; + case AL_6POINT1_16: + *chans = FmtX61; + *type = FmtShort; + return AL_TRUE; + case AL_6POINT1_32F: + *chans = FmtX61; + *type = FmtFloat; + return AL_TRUE; + case AL_7POINT1_8: + *chans = FmtX71; + *type = FmtByte; + return AL_TRUE; + case AL_7POINT1_16: + *chans = FmtX71; + *type = FmtShort; + return AL_TRUE; + case AL_7POINT1_32F: + *chans = FmtX71; + *type = FmtFloat; + return AL_TRUE; + } + return AL_FALSE; +} + + +static ALboolean IsValidType(ALenum type) +{ + switch(type) + { + case AL_BYTE: + case AL_UNSIGNED_BYTE: + case AL_SHORT: + case AL_UNSIGNED_SHORT: + case AL_INT: + case AL_UNSIGNED_INT: + case AL_FLOAT: + case AL_DOUBLE: + case AL_MULAW: + case AL_IMA4: + case AL_BYTE3: + case AL_UNSIGNED_BYTE3: + return AL_TRUE; + } + return AL_FALSE; +} + +static ALboolean IsValidChannels(ALenum channels) +{ + switch(channels) + { + case AL_MONO: + case AL_STEREO: + case AL_REAR: + case AL_QUAD: + case AL_5POINT1: + case AL_6POINT1: + case AL_7POINT1: + return AL_TRUE; + } + return AL_FALSE; +} + + +/* + * ReleaseALBuffers() + * + * INTERNAL: Called to destroy any buffers that still exist on the device + */ +ALvoid ReleaseALBuffers(ALCdevice *device) +{ + ALsizei i; + for(i = 0;i < device->BufferMap.size;i++) + { + ALbuffer *temp = device->BufferMap.array[i].value; + device->BufferMap.array[i].value = NULL; + + free(temp->data); + + FreeThunkEntry(temp->buffer); + memset(temp, 0, sizeof(ALbuffer)); + free(temp); + } +} diff -r 000000000000 -r f9476ff7637e OpenAL32/alEffect.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/alEffect.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,1142 @@ +/** + * 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 +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "alMain.h" +#include "alEffect.h" +#include "alThunk.h" +#include "alError.h" + + +ALboolean DisabledEffects[MAX_EFFECTS]; + + +static void InitEffectParams(ALeffect *effect, ALenum type); + +#define LookupEffect(m, k) ((ALeffect*)LookupUIntMapKey(&(m), (k))) + +AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects) +{ + ALCcontext *Context; + ALsizei i=0; + + Context = GetLockedContext(); + if(!Context) return; + + if(n < 0 || IsBadWritePtr((void*)effects, n * sizeof(ALuint))) + alSetError(Context, AL_INVALID_VALUE); + else + { + ALCdevice *device = Context->Device; + ALenum err; + + while(i < n) + { + ALeffect *effect = calloc(1, sizeof(ALeffect)); + if(!effect) + { + alSetError(Context, AL_OUT_OF_MEMORY); + alDeleteEffects(i, effects); + break; + } + + err = NewThunkEntry(&effect->effect); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&device->EffectMap, effect->effect, effect); + if(err != AL_NO_ERROR) + { + FreeThunkEntry(effect->effect); + memset(effect, 0, sizeof(ALeffect)); + free(effect); + + alSetError(Context, err); + alDeleteEffects(i, effects); + break; + } + + effects[i++] = effect->effect; + InitEffectParams(effect, AL_EFFECT_NULL); + } + } + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, ALuint *effects) +{ + ALCcontext *Context; + ALCdevice *device; + ALeffect *ALEffect; + ALboolean Failed; + ALsizei i; + + Context = GetLockedContext(); + if(!Context) return; + + Failed = AL_TRUE; + device = Context->Device; + if(n < 0) + alSetError(Context, AL_INVALID_VALUE); + else + { + Failed = AL_FALSE; + // Check that all effects are valid + for(i = 0;i < n;i++) + { + if(!effects[i]) + continue; + + if(LookupEffect(device->EffectMap, effects[i]) == NULL) + { + alSetError(Context, AL_INVALID_NAME); + Failed = AL_TRUE; + break; + } + } + } + + if(!Failed) + { + // All effects are valid + for(i = 0;i < n;i++) + { + // Recheck that the effect is valid, because there could be duplicated names + if((ALEffect=LookupEffect(device->EffectMap, effects[i])) == NULL) + continue; + + RemoveUIntMapKey(&device->EffectMap, ALEffect->effect); + FreeThunkEntry(ALEffect->effect); + + memset(ALEffect, 0, sizeof(ALeffect)); + free(ALEffect); + } + } + + UnlockContext(Context); +} + +AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect) +{ + ALCcontext *Context; + ALboolean result; + + Context = GetLockedContext(); + if(!Context) return AL_FALSE; + + result = ((!effect || LookupEffect(Context->Device->EffectMap, effect)) ? + AL_TRUE : AL_FALSE); + + UnlockContext(Context); + + return result; +} + +AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device->EffectMap, effect)) != NULL) + { + if(param == AL_EFFECT_TYPE) + { + ALboolean isOk = (iValue == AL_EFFECT_NULL); + ALint i; + for(i = 0;!isOk && EffectList[i].val;i++) + { + if(iValue == EffectList[i].val && + !DisabledEffects[EffectList[i].type]) + isOk = AL_TRUE; + } + + if(isOk) + InitEffectParams(ALEffect, iValue); + else + alSetError(Context, AL_INVALID_VALUE); + } + else if(ALEffect->type == AL_EFFECT_EAXREVERB) + { + switch(param) + { + case AL_EAXREVERB_DECAY_HFLIMIT: + if(iValue >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && + iValue <= AL_EAXREVERB_MAX_DECAY_HFLIMIT) + ALEffect->Params.Reverb.DecayHFLimit = iValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_REVERB) + { + switch(param) + { + case AL_REVERB_DECAY_HFLIMIT: + if(iValue >= AL_REVERB_MIN_DECAY_HFLIMIT && + iValue <= AL_REVERB_MAX_DECAY_HFLIMIT) + ALEffect->Params.Reverb.DecayHFLimit = iValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_ECHO) + { + switch(param) + { + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_RING_MODULATOR) + { + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: + if(iValue >= AL_RING_MODULATOR_MIN_FREQUENCY && + iValue <= AL_RING_MODULATOR_MAX_FREQUENCY) + ALEffect->Params.Modulator.Frequency = iValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: + if(iValue >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && + iValue <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF) + ALEffect->Params.Modulator.HighPassCutoff = iValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_RING_MODULATOR_WAVEFORM: + if(iValue >= AL_RING_MODULATOR_MIN_WAVEFORM && + iValue <= AL_RING_MODULATOR_MAX_WAVEFORM) + ALEffect->Params.Modulator.Waveform = iValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_ENUM); + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, ALint *piValues) +{ + /* There are no multi-value int effect parameters */ + alEffecti(effect, param, piValues[0]); +} + +AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device->EffectMap, effect)) != NULL) + { + if(ALEffect->type == AL_EFFECT_EAXREVERB) + { + switch(param) + { + case AL_EAXREVERB_DENSITY: + if(flValue >= AL_EAXREVERB_MIN_DENSITY && + flValue <= AL_EAXREVERB_MAX_DENSITY) + ALEffect->Params.Reverb.Density = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_DIFFUSION: + if(flValue >= AL_EAXREVERB_MIN_DIFFUSION && + flValue <= AL_EAXREVERB_MAX_DIFFUSION) + ALEffect->Params.Reverb.Diffusion = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_GAIN: + if(flValue >= AL_EAXREVERB_MIN_GAIN && + flValue <= AL_EAXREVERB_MAX_GAIN) + ALEffect->Params.Reverb.Gain = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_GAINHF: + if(flValue >= AL_EAXREVERB_MIN_GAINHF && + flValue <= AL_EAXREVERB_MAX_GAINHF) + ALEffect->Params.Reverb.GainHF = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_GAINLF: + if(flValue >= AL_EAXREVERB_MIN_GAINLF && + flValue <= AL_EAXREVERB_MAX_GAINLF) + ALEffect->Params.Reverb.GainLF = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_DECAY_TIME: + if(flValue >= AL_EAXREVERB_MIN_DECAY_TIME && + flValue <= AL_EAXREVERB_MAX_DECAY_TIME) + ALEffect->Params.Reverb.DecayTime = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_DECAY_HFRATIO: + if(flValue >= AL_EAXREVERB_MIN_DECAY_HFRATIO && + flValue <= AL_EAXREVERB_MAX_DECAY_HFRATIO) + ALEffect->Params.Reverb.DecayHFRatio = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_DECAY_LFRATIO: + if(flValue >= AL_EAXREVERB_MIN_DECAY_LFRATIO && + flValue <= AL_EAXREVERB_MAX_DECAY_LFRATIO) + ALEffect->Params.Reverb.DecayLFRatio = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_REFLECTIONS_GAIN: + if(flValue >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && + flValue <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN) + ALEffect->Params.Reverb.ReflectionsGain = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_REFLECTIONS_DELAY: + if(flValue >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && + flValue <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY) + ALEffect->Params.Reverb.ReflectionsDelay = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_LATE_REVERB_GAIN: + if(flValue >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && + flValue <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN) + ALEffect->Params.Reverb.LateReverbGain = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_LATE_REVERB_DELAY: + if(flValue >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && + flValue <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY) + ALEffect->Params.Reverb.LateReverbDelay = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: + if(flValue >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && + flValue <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF) + ALEffect->Params.Reverb.AirAbsorptionGainHF = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_ECHO_TIME: + if(flValue >= AL_EAXREVERB_MIN_ECHO_TIME && + flValue <= AL_EAXREVERB_MAX_ECHO_TIME) + ALEffect->Params.Reverb.EchoTime = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_ECHO_DEPTH: + if(flValue >= AL_EAXREVERB_MIN_ECHO_DEPTH && + flValue <= AL_EAXREVERB_MAX_ECHO_DEPTH) + ALEffect->Params.Reverb.EchoDepth = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_MODULATION_TIME: + if(flValue >= AL_EAXREVERB_MIN_MODULATION_TIME && + flValue <= AL_EAXREVERB_MAX_MODULATION_TIME) + ALEffect->Params.Reverb.ModulationTime = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_MODULATION_DEPTH: + if(flValue >= AL_EAXREVERB_MIN_MODULATION_DEPTH && + flValue <= AL_EAXREVERB_MAX_MODULATION_DEPTH) + ALEffect->Params.Reverb.ModulationDepth = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_HFREFERENCE: + if(flValue >= AL_EAXREVERB_MIN_HFREFERENCE && + flValue <= AL_EAXREVERB_MAX_HFREFERENCE) + ALEffect->Params.Reverb.HFReference = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_LFREFERENCE: + if(flValue >= AL_EAXREVERB_MIN_LFREFERENCE && + flValue <= AL_EAXREVERB_MAX_LFREFERENCE) + ALEffect->Params.Reverb.LFReference = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: + if(flValue >= 0.0f && flValue <= 10.0f) + ALEffect->Params.Reverb.RoomRolloffFactor = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_REVERB) + { + switch(param) + { + case AL_REVERB_DENSITY: + if(flValue >= AL_REVERB_MIN_DENSITY && + flValue <= AL_REVERB_MAX_DENSITY) + ALEffect->Params.Reverb.Density = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_REVERB_DIFFUSION: + if(flValue >= AL_REVERB_MIN_DIFFUSION && + flValue <= AL_REVERB_MAX_DIFFUSION) + ALEffect->Params.Reverb.Diffusion = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_REVERB_GAIN: + if(flValue >= AL_REVERB_MIN_GAIN && + flValue <= AL_REVERB_MAX_GAIN) + ALEffect->Params.Reverb.Gain = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_REVERB_GAINHF: + if(flValue >= AL_REVERB_MIN_GAINHF && + flValue <= AL_REVERB_MAX_GAINHF) + ALEffect->Params.Reverb.GainHF = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_REVERB_DECAY_TIME: + if(flValue >= AL_REVERB_MIN_DECAY_TIME && + flValue <= AL_REVERB_MAX_DECAY_TIME) + ALEffect->Params.Reverb.DecayTime = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_REVERB_DECAY_HFRATIO: + if(flValue >= AL_REVERB_MIN_DECAY_HFRATIO && + flValue <= AL_REVERB_MAX_DECAY_HFRATIO) + ALEffect->Params.Reverb.DecayHFRatio = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_REVERB_REFLECTIONS_GAIN: + if(flValue >= AL_REVERB_MIN_REFLECTIONS_GAIN && + flValue <= AL_REVERB_MAX_REFLECTIONS_GAIN) + ALEffect->Params.Reverb.ReflectionsGain = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_REVERB_REFLECTIONS_DELAY: + if(flValue >= AL_REVERB_MIN_REFLECTIONS_DELAY && + flValue <= AL_REVERB_MAX_REFLECTIONS_DELAY) + ALEffect->Params.Reverb.ReflectionsDelay = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_REVERB_LATE_REVERB_GAIN: + if(flValue >= AL_REVERB_MIN_LATE_REVERB_GAIN && + flValue <= AL_REVERB_MAX_LATE_REVERB_GAIN) + ALEffect->Params.Reverb.LateReverbGain = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_REVERB_LATE_REVERB_DELAY: + if(flValue >= AL_REVERB_MIN_LATE_REVERB_DELAY && + flValue <= AL_REVERB_MAX_LATE_REVERB_DELAY) + ALEffect->Params.Reverb.LateReverbDelay = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_REVERB_AIR_ABSORPTION_GAINHF: + if(flValue >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && + flValue <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF) + ALEffect->Params.Reverb.AirAbsorptionGainHF = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_REVERB_ROOM_ROLLOFF_FACTOR: + if(flValue >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && + flValue <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR) + ALEffect->Params.Reverb.RoomRolloffFactor = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_ECHO) + { + switch(param) + { + case AL_ECHO_DELAY: + if(flValue >= AL_ECHO_MIN_DELAY && flValue <= AL_ECHO_MAX_DELAY) + ALEffect->Params.Echo.Delay = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_ECHO_LRDELAY: + if(flValue >= AL_ECHO_MIN_LRDELAY && flValue <= AL_ECHO_MAX_LRDELAY) + ALEffect->Params.Echo.LRDelay = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_ECHO_DAMPING: + if(flValue >= AL_ECHO_MIN_DAMPING && flValue <= AL_ECHO_MAX_DAMPING) + ALEffect->Params.Echo.Damping = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_ECHO_FEEDBACK: + if(flValue >= AL_ECHO_MIN_FEEDBACK && flValue <= AL_ECHO_MAX_FEEDBACK) + ALEffect->Params.Echo.Feedback = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_ECHO_SPREAD: + if(flValue >= AL_ECHO_MIN_SPREAD && flValue <= AL_ECHO_MAX_SPREAD) + ALEffect->Params.Echo.Spread = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_RING_MODULATOR) + { + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: + if(flValue >= AL_RING_MODULATOR_MIN_FREQUENCY && + flValue <= AL_RING_MODULATOR_MAX_FREQUENCY) + ALEffect->Params.Modulator.Frequency = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: + if(flValue >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && + flValue <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF) + ALEffect->Params.Modulator.HighPassCutoff = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT || + ALEffect->type == AL_EFFECT_DEDICATED_DIALOGUE) + { + switch(param) + { + case AL_DEDICATED_GAIN: + if(flValue >= 0.0f && isfinite(flValue)) + ALEffect->Params.Dedicated.Gain = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_ENUM); + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, ALfloat *pflValues) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device->EffectMap, effect)) != NULL) + { + if(ALEffect->type == AL_EFFECT_EAXREVERB) + { + switch(param) + { + case AL_EAXREVERB_REFLECTIONS_PAN: + if(isfinite(pflValues[0]) && isfinite(pflValues[1]) && isfinite(pflValues[2])) + { + ALEffect->Params.Reverb.ReflectionsPan[0] = pflValues[0]; + ALEffect->Params.Reverb.ReflectionsPan[1] = pflValues[1]; + ALEffect->Params.Reverb.ReflectionsPan[2] = pflValues[2]; + } + else + alSetError(Context, AL_INVALID_VALUE); + break; + case AL_EAXREVERB_LATE_REVERB_PAN: + if(isfinite(pflValues[0]) && isfinite(pflValues[1]) && isfinite(pflValues[2])) + { + ALEffect->Params.Reverb.LateReverbPan[0] = pflValues[0]; + ALEffect->Params.Reverb.LateReverbPan[1] = pflValues[1]; + ALEffect->Params.Reverb.LateReverbPan[2] = pflValues[2]; + } + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + UnlockContext(Context); + alEffectf(effect, param, pflValues[0]); + return; + } + } + else + { + UnlockContext(Context); + alEffectf(effect, param, pflValues[0]); + return; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device->EffectMap, effect)) != NULL) + { + if(param == AL_EFFECT_TYPE) + { + *piValue = ALEffect->type; + } + else if(ALEffect->type == AL_EFFECT_EAXREVERB) + { + switch(param) + { + case AL_EAXREVERB_DECAY_HFLIMIT: + *piValue = ALEffect->Params.Reverb.DecayHFLimit; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_REVERB) + { + switch(param) + { + case AL_REVERB_DECAY_HFLIMIT: + *piValue = ALEffect->Params.Reverb.DecayHFLimit; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_ECHO) + { + switch(param) + { + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_RING_MODULATOR) + { + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: + *piValue = (ALint)ALEffect->Params.Modulator.Frequency; + break; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: + *piValue = (ALint)ALEffect->Params.Modulator.HighPassCutoff; + break; + case AL_RING_MODULATOR_WAVEFORM: + *piValue = ALEffect->Params.Modulator.Waveform; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_ENUM); + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues) +{ + /* There are no multi-value int effect parameters */ + alGetEffecti(effect, param, piValues); +} + +AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device->EffectMap, effect)) != NULL) + { + if(ALEffect->type == AL_EFFECT_EAXREVERB) + { + switch(param) + { + case AL_EAXREVERB_DENSITY: + *pflValue = ALEffect->Params.Reverb.Density; + break; + + case AL_EAXREVERB_DIFFUSION: + *pflValue = ALEffect->Params.Reverb.Diffusion; + break; + + case AL_EAXREVERB_GAIN: + *pflValue = ALEffect->Params.Reverb.Gain; + break; + + case AL_EAXREVERB_GAINHF: + *pflValue = ALEffect->Params.Reverb.GainHF; + break; + + case AL_EAXREVERB_GAINLF: + *pflValue = ALEffect->Params.Reverb.GainLF; + break; + + case AL_EAXREVERB_DECAY_TIME: + *pflValue = ALEffect->Params.Reverb.DecayTime; + break; + + case AL_EAXREVERB_DECAY_HFRATIO: + *pflValue = ALEffect->Params.Reverb.DecayHFRatio; + break; + + case AL_EAXREVERB_DECAY_LFRATIO: + *pflValue = ALEffect->Params.Reverb.DecayLFRatio; + break; + + case AL_EAXREVERB_REFLECTIONS_GAIN: + *pflValue = ALEffect->Params.Reverb.ReflectionsGain; + break; + + case AL_EAXREVERB_REFLECTIONS_DELAY: + *pflValue = ALEffect->Params.Reverb.ReflectionsDelay; + break; + + case AL_EAXREVERB_LATE_REVERB_GAIN: + *pflValue = ALEffect->Params.Reverb.LateReverbGain; + break; + + case AL_EAXREVERB_LATE_REVERB_DELAY: + *pflValue = ALEffect->Params.Reverb.LateReverbDelay; + break; + + case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: + *pflValue = ALEffect->Params.Reverb.AirAbsorptionGainHF; + break; + + case AL_EAXREVERB_ECHO_TIME: + *pflValue = ALEffect->Params.Reverb.EchoTime; + break; + + case AL_EAXREVERB_ECHO_DEPTH: + *pflValue = ALEffect->Params.Reverb.EchoDepth; + break; + + case AL_EAXREVERB_MODULATION_TIME: + *pflValue = ALEffect->Params.Reverb.ModulationTime; + break; + + case AL_EAXREVERB_MODULATION_DEPTH: + *pflValue = ALEffect->Params.Reverb.ModulationDepth; + break; + + case AL_EAXREVERB_HFREFERENCE: + *pflValue = ALEffect->Params.Reverb.HFReference; + break; + + case AL_EAXREVERB_LFREFERENCE: + *pflValue = ALEffect->Params.Reverb.LFReference; + break; + + case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: + *pflValue = ALEffect->Params.Reverb.RoomRolloffFactor; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_REVERB) + { + switch(param) + { + case AL_REVERB_DENSITY: + *pflValue = ALEffect->Params.Reverb.Density; + break; + + case AL_REVERB_DIFFUSION: + *pflValue = ALEffect->Params.Reverb.Diffusion; + break; + + case AL_REVERB_GAIN: + *pflValue = ALEffect->Params.Reverb.Gain; + break; + + case AL_REVERB_GAINHF: + *pflValue = ALEffect->Params.Reverb.GainHF; + break; + + case AL_REVERB_DECAY_TIME: + *pflValue = ALEffect->Params.Reverb.DecayTime; + break; + + case AL_REVERB_DECAY_HFRATIO: + *pflValue = ALEffect->Params.Reverb.DecayHFRatio; + break; + + case AL_REVERB_REFLECTIONS_GAIN: + *pflValue = ALEffect->Params.Reverb.ReflectionsGain; + break; + + case AL_REVERB_REFLECTIONS_DELAY: + *pflValue = ALEffect->Params.Reverb.ReflectionsDelay; + break; + + case AL_REVERB_LATE_REVERB_GAIN: + *pflValue = ALEffect->Params.Reverb.LateReverbGain; + break; + + case AL_REVERB_LATE_REVERB_DELAY: + *pflValue = ALEffect->Params.Reverb.LateReverbDelay; + break; + + case AL_REVERB_AIR_ABSORPTION_GAINHF: + *pflValue = ALEffect->Params.Reverb.AirAbsorptionGainHF; + break; + + case AL_REVERB_ROOM_ROLLOFF_FACTOR: + *pflValue = ALEffect->Params.Reverb.RoomRolloffFactor; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_ECHO) + { + switch(param) + { + case AL_ECHO_DELAY: + *pflValue = ALEffect->Params.Echo.Delay; + break; + + case AL_ECHO_LRDELAY: + *pflValue = ALEffect->Params.Echo.LRDelay; + break; + + case AL_ECHO_DAMPING: + *pflValue = ALEffect->Params.Echo.Damping; + break; + + case AL_ECHO_FEEDBACK: + *pflValue = ALEffect->Params.Echo.Feedback; + break; + + case AL_ECHO_SPREAD: + *pflValue = ALEffect->Params.Echo.Spread; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_RING_MODULATOR) + { + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: + *pflValue = ALEffect->Params.Modulator.Frequency; + break; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: + *pflValue = ALEffect->Params.Modulator.HighPassCutoff; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else if(ALEffect->type == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT || + ALEffect->type == AL_EFFECT_DEDICATED_DIALOGUE) + { + switch(param) + { + case AL_DEDICATED_GAIN: + *pflValue = ALEffect->Params.Dedicated.Gain; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_ENUM); + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device->EffectMap, effect)) != NULL) + { + if(ALEffect->type == AL_EFFECT_EAXREVERB) + { + switch(param) + { + case AL_EAXREVERB_REFLECTIONS_PAN: + pflValues[0] = ALEffect->Params.Reverb.ReflectionsPan[0]; + pflValues[1] = ALEffect->Params.Reverb.ReflectionsPan[1]; + pflValues[2] = ALEffect->Params.Reverb.ReflectionsPan[2]; + break; + case AL_EAXREVERB_LATE_REVERB_PAN: + pflValues[0] = ALEffect->Params.Reverb.LateReverbPan[0]; + pflValues[1] = ALEffect->Params.Reverb.LateReverbPan[1]; + pflValues[2] = ALEffect->Params.Reverb.LateReverbPan[2]; + break; + + default: + UnlockContext(Context); + alGetEffectf(effect, param, pflValues); + return; + } + } + else + { + UnlockContext(Context); + alGetEffectf(effect, param, pflValues); + return; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + + +ALvoid ReleaseALEffects(ALCdevice *device) +{ + ALsizei i; + for(i = 0;i < device->EffectMap.size;i++) + { + ALeffect *temp = device->EffectMap.array[i].value; + device->EffectMap.array[i].value = NULL; + + // Release effect structure + FreeThunkEntry(temp->effect); + memset(temp, 0, sizeof(ALeffect)); + free(temp); + } +} + + +static void InitEffectParams(ALeffect *effect, ALenum type) +{ + effect->type = type; + switch(type) + { + /* NOTE: Standard reverb and EAX reverb use the same defaults for the + * shared parameters, and EAX's additional parameters default to + * values assumed by standard reverb. + */ + case AL_EFFECT_EAXREVERB: + case AL_EFFECT_REVERB: + effect->Params.Reverb.Density = AL_EAXREVERB_DEFAULT_DENSITY; + effect->Params.Reverb.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION; + effect->Params.Reverb.Gain = AL_EAXREVERB_DEFAULT_GAIN; + effect->Params.Reverb.GainHF = AL_EAXREVERB_DEFAULT_GAINHF; + effect->Params.Reverb.GainLF = AL_EAXREVERB_DEFAULT_GAINLF; + effect->Params.Reverb.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME; + effect->Params.Reverb.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO; + effect->Params.Reverb.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO; + effect->Params.Reverb.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN; + effect->Params.Reverb.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY; + effect->Params.Reverb.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + effect->Params.Reverb.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + effect->Params.Reverb.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + effect->Params.Reverb.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN; + effect->Params.Reverb.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY; + effect->Params.Reverb.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + effect->Params.Reverb.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + effect->Params.Reverb.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + effect->Params.Reverb.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME; + effect->Params.Reverb.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH; + effect->Params.Reverb.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME; + effect->Params.Reverb.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH; + effect->Params.Reverb.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF; + effect->Params.Reverb.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE; + effect->Params.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE; + effect->Params.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; + effect->Params.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT; + break; + case AL_EFFECT_ECHO: + effect->Params.Echo.Delay = AL_ECHO_DEFAULT_DELAY; + effect->Params.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY; + effect->Params.Echo.Damping = AL_ECHO_DEFAULT_DAMPING; + effect->Params.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK; + effect->Params.Echo.Spread = AL_ECHO_DEFAULT_SPREAD; + break; + case AL_EFFECT_RING_MODULATOR: + effect->Params.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY; + effect->Params.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF; + effect->Params.Modulator.Waveform = AL_RING_MODULATOR_DEFAULT_WAVEFORM; + break; + case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: + case AL_EFFECT_DEDICATED_DIALOGUE: + effect->Params.Dedicated.Gain = 1.0f; + break; + } +} diff -r 000000000000 -r f9476ff7637e OpenAL32/alError.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/alError.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,47 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 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 "alMain.h" +#include "AL/alc.h" +#include "alError.h" + +AL_API ALenum AL_APIENTRY alGetError(ALvoid) +{ + ALCcontext *Context; + ALenum errorCode; + + Context = GetLockedContext(); + if(!Context) return AL_INVALID_OPERATION; + + errorCode = Context->LastError; + Context->LastError = AL_NO_ERROR; + + UnlockContext(Context); + + return errorCode; +} + +ALvoid alSetError(ALCcontext *Context, ALenum errorCode) +{ + if(Context->LastError == AL_NO_ERROR) + Context->LastError = errorCode; +} diff -r 000000000000 -r f9476ff7637e OpenAL32/alExtension.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/alExtension.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,399 @@ +/** + * 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 +#include +#include + +#include "alError.h" +#include "alMain.h" +#include "alFilter.h" +#include "alEffect.h" +#include "alAuxEffectSlot.h" +#include "alSource.h" +#include "alBuffer.h" +#include "AL/al.h" +#include "AL/alc.h" + +typedef struct ALenums { + const ALchar *enumName; + ALenum value; +} ALenums; + + +static const ALenums enumeration[] = { + // Types + { "AL_INVALID", AL_INVALID }, + { "AL_NONE", AL_NONE }, + { "AL_FALSE", AL_FALSE }, + { "AL_TRUE", AL_TRUE }, + + // Source and Listener Properties + { "AL_SOURCE_RELATIVE", AL_SOURCE_RELATIVE }, + { "AL_CONE_INNER_ANGLE", AL_CONE_INNER_ANGLE }, + { "AL_CONE_OUTER_ANGLE", AL_CONE_OUTER_ANGLE }, + { "AL_PITCH", AL_PITCH }, + { "AL_POSITION", AL_POSITION }, + { "AL_DIRECTION", AL_DIRECTION }, + { "AL_VELOCITY", AL_VELOCITY }, + { "AL_LOOPING", AL_LOOPING }, + { "AL_BUFFER", AL_BUFFER }, + { "AL_GAIN", AL_GAIN }, + { "AL_MIN_GAIN", AL_MIN_GAIN }, + { "AL_MAX_GAIN", AL_MAX_GAIN }, + { "AL_ORIENTATION", AL_ORIENTATION }, + { "AL_REFERENCE_DISTANCE", AL_REFERENCE_DISTANCE }, + { "AL_ROLLOFF_FACTOR", AL_ROLLOFF_FACTOR }, + { "AL_CONE_OUTER_GAIN", AL_CONE_OUTER_GAIN }, + { "AL_MAX_DISTANCE", AL_MAX_DISTANCE }, + { "AL_SEC_OFFSET", AL_SEC_OFFSET }, + { "AL_SAMPLE_OFFSET", AL_SAMPLE_OFFSET }, + { "AL_SAMPLE_RW_OFFSETS_SOFT", AL_SAMPLE_RW_OFFSETS_SOFT }, + { "AL_BYTE_OFFSET", AL_BYTE_OFFSET }, + { "AL_BYTE_RW_OFFSETS_SOFT", AL_BYTE_RW_OFFSETS_SOFT }, + { "AL_SOURCE_TYPE", AL_SOURCE_TYPE }, + { "AL_STATIC", AL_STATIC }, + { "AL_STREAMING", AL_STREAMING }, + { "AL_UNDETERMINED", AL_UNDETERMINED }, + { "AL_METERS_PER_UNIT", AL_METERS_PER_UNIT }, + { "AL_VIRTUAL_CHANNELS_SOFT", AL_VIRTUAL_CHANNELS_SOFT }, + + // Source EFX Properties + { "AL_DIRECT_FILTER", AL_DIRECT_FILTER }, + { "AL_AUXILIARY_SEND_FILTER", AL_AUXILIARY_SEND_FILTER }, + { "AL_AIR_ABSORPTION_FACTOR", AL_AIR_ABSORPTION_FACTOR }, + { "AL_ROOM_ROLLOFF_FACTOR", AL_ROOM_ROLLOFF_FACTOR }, + { "AL_CONE_OUTER_GAINHF", AL_CONE_OUTER_GAINHF }, + { "AL_DIRECT_FILTER_GAINHF_AUTO", AL_DIRECT_FILTER_GAINHF_AUTO }, + { "AL_AUXILIARY_SEND_FILTER_GAIN_AUTO", AL_AUXILIARY_SEND_FILTER_GAIN_AUTO }, + { "AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO", AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO}, + + // Source State information + { "AL_SOURCE_STATE", AL_SOURCE_STATE }, + { "AL_INITIAL", AL_INITIAL }, + { "AL_PLAYING", AL_PLAYING }, + { "AL_PAUSED", AL_PAUSED }, + { "AL_STOPPED", AL_STOPPED }, + + // Queue information + { "AL_BUFFERS_QUEUED", AL_BUFFERS_QUEUED }, + { "AL_BUFFERS_PROCESSED", AL_BUFFERS_PROCESSED }, + + // Buffer Formats + { "AL_FORMAT_MONO8", AL_FORMAT_MONO8 }, + { "AL_FORMAT_MONO16", AL_FORMAT_MONO16 }, + { "AL_FORMAT_MONO_FLOAT32", AL_FORMAT_MONO_FLOAT32 }, + { "AL_FORMAT_MONO_DOUBLE_EXT", AL_FORMAT_MONO_DOUBLE_EXT }, + { "AL_FORMAT_STEREO8", AL_FORMAT_STEREO8 }, + { "AL_FORMAT_STEREO16", AL_FORMAT_STEREO16 }, + { "AL_FORMAT_STEREO_FLOAT32", AL_FORMAT_STEREO_FLOAT32 }, + { "AL_FORMAT_STEREO_DOUBLE_EXT", AL_FORMAT_STEREO_DOUBLE_EXT }, + { "AL_FORMAT_MONO_IMA4", AL_FORMAT_MONO_IMA4 }, + { "AL_FORMAT_STEREO_IMA4", AL_FORMAT_STEREO_IMA4 }, + { "AL_FORMAT_QUAD8_LOKI", AL_FORMAT_QUAD8_LOKI }, + { "AL_FORMAT_QUAD16_LOKI", AL_FORMAT_QUAD16_LOKI }, + { "AL_FORMAT_QUAD8", AL_FORMAT_QUAD8 }, + { "AL_FORMAT_QUAD16", AL_FORMAT_QUAD16 }, + { "AL_FORMAT_QUAD32", AL_FORMAT_QUAD32 }, + { "AL_FORMAT_51CHN8", AL_FORMAT_51CHN8 }, + { "AL_FORMAT_51CHN16", AL_FORMAT_51CHN16 }, + { "AL_FORMAT_51CHN32", AL_FORMAT_51CHN32 }, + { "AL_FORMAT_61CHN8", AL_FORMAT_61CHN8 }, + { "AL_FORMAT_61CHN16", AL_FORMAT_61CHN16 }, + { "AL_FORMAT_61CHN32", AL_FORMAT_61CHN32 }, + { "AL_FORMAT_71CHN8", AL_FORMAT_71CHN8 }, + { "AL_FORMAT_71CHN16", AL_FORMAT_71CHN16 }, + { "AL_FORMAT_71CHN32", AL_FORMAT_71CHN32 }, + { "AL_FORMAT_REAR8", AL_FORMAT_REAR8 }, + { "AL_FORMAT_REAR16", AL_FORMAT_REAR16 }, + { "AL_FORMAT_REAR32", AL_FORMAT_REAR32 }, + { "AL_FORMAT_MONO_MULAW", AL_FORMAT_MONO_MULAW }, + { "AL_FORMAT_MONO_MULAW_EXT", AL_FORMAT_MONO_MULAW }, + { "AL_FORMAT_STEREO_MULAW", AL_FORMAT_STEREO_MULAW }, + { "AL_FORMAT_STEREO_MULAW_EXT", AL_FORMAT_STEREO_MULAW }, + { "AL_FORMAT_QUAD_MULAW", AL_FORMAT_QUAD_MULAW }, + { "AL_FORMAT_51CHN_MULAW", AL_FORMAT_51CHN_MULAW }, + { "AL_FORMAT_61CHN_MULAW", AL_FORMAT_61CHN_MULAW }, + { "AL_FORMAT_71CHN_MULAW", AL_FORMAT_71CHN_MULAW }, + { "AL_FORMAT_REAR_MULAW", AL_FORMAT_REAR_MULAW }, + + // Internal Buffer Formats + { "AL_MONO8", AL_MONO8 }, + { "AL_MONO16", AL_MONO16 }, + { "AL_MONO32F", AL_MONO32F }, + { "AL_STEREO8", AL_STEREO8 }, + { "AL_STEREO16", AL_STEREO16 }, + { "AL_STEREO32F", AL_STEREO32F }, + { "AL_QUAD8", AL_QUAD8 }, + { "AL_QUAD16", AL_QUAD16 }, + { "AL_QUAD32F", AL_QUAD32F }, + { "AL_REAR8", AL_REAR8 }, + { "AL_REAR16", AL_REAR16 }, + { "AL_REAR32F", AL_REAR32F }, + { "AL_5POINT1_8", AL_5POINT1_8 }, + { "AL_5POINT1_16", AL_5POINT1_16 }, + { "AL_5POINT1_32F", AL_5POINT1_32F }, + { "AL_6POINT1_8", AL_6POINT1_8 }, + { "AL_6POINT1_16", AL_6POINT1_16 }, + { "AL_6POINT1_32F", AL_6POINT1_32F }, + { "AL_7POINT1_8", AL_7POINT1_8 }, + { "AL_7POINT1_16", AL_7POINT1_16 }, + { "AL_7POINT1_32F", AL_7POINT1_32F }, + + // Buffer Channel Configurations + { "AL_MONO", AL_MONO }, + { "AL_STEREO", AL_STEREO }, + { "AL_QUAD", AL_QUAD }, + { "AL_REAR", AL_REAR }, + { "AL_5POINT1", AL_5POINT1 }, + { "AL_6POINT1", AL_6POINT1 }, + { "AL_7POINT1", AL_7POINT1 }, + + // Buffer Sample Types + { "AL_BYTE", AL_BYTE }, + { "AL_UNSIGNED_BYTE", AL_UNSIGNED_BYTE }, + { "AL_SHORT", AL_SHORT }, + { "AL_UNSIGNED_SHORT", AL_UNSIGNED_SHORT }, + { "AL_INT", AL_INT }, + { "AL_UNSIGNED_INT", AL_UNSIGNED_INT }, + { "AL_FLOAT", AL_FLOAT }, + { "AL_DOUBLE", AL_DOUBLE }, + { "AL_MULAW", AL_MULAW }, + { "AL_IMA4", AL_IMA4 }, + { "AL_BYTE3", AL_BYTE3 }, + { "AL_UNSIGNED_BYTE3", AL_UNSIGNED_BYTE3 }, + + // Buffer attributes + { "AL_FREQUENCY", AL_FREQUENCY }, + { "AL_BITS", AL_BITS }, + { "AL_CHANNELS", AL_CHANNELS }, + { "AL_SIZE", AL_SIZE }, + + // Buffer States (not supported yet) + { "AL_UNUSED", AL_UNUSED }, + { "AL_PENDING", AL_PENDING }, + { "AL_PROCESSED", AL_PROCESSED }, + + // AL Error Messages + { "AL_NO_ERROR", AL_NO_ERROR }, + { "AL_INVALID_NAME", AL_INVALID_NAME }, + { "AL_INVALID_ENUM", AL_INVALID_ENUM }, + { "AL_INVALID_VALUE", AL_INVALID_VALUE }, + { "AL_INVALID_OPERATION", AL_INVALID_OPERATION }, + { "AL_OUT_OF_MEMORY", AL_OUT_OF_MEMORY }, + + // Context strings + { "AL_VENDOR", AL_VENDOR }, + { "AL_VERSION", AL_VERSION }, + { "AL_RENDERER", AL_RENDERER }, + { "AL_EXTENSIONS", AL_EXTENSIONS }, + + // Global states + { "AL_DOPPLER_FACTOR", AL_DOPPLER_FACTOR }, + { "AL_DOPPLER_VELOCITY", AL_DOPPLER_VELOCITY }, + { "AL_DISTANCE_MODEL", AL_DISTANCE_MODEL }, + { "AL_SPEED_OF_SOUND", AL_SPEED_OF_SOUND }, + { "AL_SOURCE_DISTANCE_MODEL", AL_SOURCE_DISTANCE_MODEL }, + { "AL_DEFERRED_UPDATES_SOFT", AL_DEFERRED_UPDATES_SOFT }, + + // Distance Models + { "AL_INVERSE_DISTANCE", AL_INVERSE_DISTANCE }, + { "AL_INVERSE_DISTANCE_CLAMPED", AL_INVERSE_DISTANCE_CLAMPED }, + { "AL_LINEAR_DISTANCE", AL_LINEAR_DISTANCE }, + { "AL_LINEAR_DISTANCE_CLAMPED", AL_LINEAR_DISTANCE_CLAMPED }, + { "AL_EXPONENT_DISTANCE", AL_EXPONENT_DISTANCE }, + { "AL_EXPONENT_DISTANCE_CLAMPED", AL_EXPONENT_DISTANCE_CLAMPED }, + + // Filter types + { "AL_FILTER_TYPE", AL_FILTER_TYPE }, + { "AL_FILTER_NULL", AL_FILTER_NULL }, + { "AL_FILTER_LOWPASS", AL_FILTER_LOWPASS }, +#if 0 + { "AL_FILTER_HIGHPASS", AL_FILTER_HIGHPASS }, + { "AL_FILTER_BANDPASS", AL_FILTER_BANDPASS }, +#endif + + // Filter params + { "AL_LOWPASS_GAIN", AL_LOWPASS_GAIN }, + { "AL_LOWPASS_GAINHF", AL_LOWPASS_GAINHF }, + + // Effect types + { "AL_EFFECT_TYPE", AL_EFFECT_TYPE }, + { "AL_EFFECT_NULL", AL_EFFECT_NULL }, + { "AL_EFFECT_REVERB", AL_EFFECT_REVERB }, + { "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB }, +#if 0 + { "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS }, + { "AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION }, +#endif + { "AL_EFFECT_ECHO", AL_EFFECT_ECHO }, +#if 0 + { "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER }, + { "AL_EFFECT_FREQUENCY_SHIFTER", AL_EFFECT_FREQUENCY_SHIFTER }, + { "AL_EFFECT_VOCAL_MORPHER", AL_EFFECT_VOCAL_MORPHER }, + { "AL_EFFECT_PITCH_SHIFTER", AL_EFFECT_PITCH_SHIFTER }, +#endif + { "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR }, +#if 0 + { "AL_EFFECT_AUTOWAH", AL_EFFECT_AUTOWAH }, + { "AL_EFFECT_COMPRESSOR", AL_EFFECT_COMPRESSOR }, + { "AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER }, +#endif + { "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT",AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT}, + { "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE }, + + // Reverb params + { "AL_REVERB_DENSITY", AL_REVERB_DENSITY }, + { "AL_REVERB_DIFFUSION", AL_REVERB_DIFFUSION }, + { "AL_REVERB_GAIN", AL_REVERB_GAIN }, + { "AL_REVERB_GAINHF", AL_REVERB_GAINHF }, + { "AL_REVERB_DECAY_TIME", AL_REVERB_DECAY_TIME }, + { "AL_REVERB_DECAY_HFRATIO", AL_REVERB_DECAY_HFRATIO }, + { "AL_REVERB_REFLECTIONS_GAIN", AL_REVERB_REFLECTIONS_GAIN }, + { "AL_REVERB_REFLECTIONS_DELAY", AL_REVERB_REFLECTIONS_DELAY }, + { "AL_REVERB_LATE_REVERB_GAIN", AL_REVERB_LATE_REVERB_GAIN }, + { "AL_REVERB_LATE_REVERB_DELAY", AL_REVERB_LATE_REVERB_DELAY }, + { "AL_REVERB_AIR_ABSORPTION_GAINHF", AL_REVERB_AIR_ABSORPTION_GAINHF }, + { "AL_REVERB_ROOM_ROLLOFF_FACTOR", AL_REVERB_ROOM_ROLLOFF_FACTOR }, + { "AL_REVERB_DECAY_HFLIMIT", AL_REVERB_DECAY_HFLIMIT }, + + // EAX Reverb params + { "AL_EAXREVERB_DENSITY", AL_EAXREVERB_DENSITY }, + { "AL_EAXREVERB_DIFFUSION", AL_EAXREVERB_DIFFUSION }, + { "AL_EAXREVERB_GAIN", AL_EAXREVERB_GAIN }, + { "AL_EAXREVERB_GAINHF", AL_EAXREVERB_GAINHF }, + { "AL_EAXREVERB_GAINLF", AL_EAXREVERB_GAINLF }, + { "AL_EAXREVERB_DECAY_TIME", AL_EAXREVERB_DECAY_TIME }, + { "AL_EAXREVERB_DECAY_HFRATIO", AL_EAXREVERB_DECAY_HFRATIO }, + { "AL_EAXREVERB_DECAY_LFRATIO", AL_EAXREVERB_DECAY_LFRATIO }, + { "AL_EAXREVERB_REFLECTIONS_GAIN", AL_EAXREVERB_REFLECTIONS_GAIN }, + { "AL_EAXREVERB_REFLECTIONS_DELAY", AL_EAXREVERB_REFLECTIONS_DELAY }, + { "AL_EAXREVERB_REFLECTIONS_PAN", AL_EAXREVERB_REFLECTIONS_PAN }, + { "AL_EAXREVERB_LATE_REVERB_GAIN", AL_EAXREVERB_LATE_REVERB_GAIN }, + { "AL_EAXREVERB_LATE_REVERB_DELAY", AL_EAXREVERB_LATE_REVERB_DELAY }, + { "AL_EAXREVERB_LATE_REVERB_PAN", AL_EAXREVERB_LATE_REVERB_PAN }, + { "AL_EAXREVERB_ECHO_TIME", AL_EAXREVERB_ECHO_TIME }, + { "AL_EAXREVERB_ECHO_DEPTH", AL_EAXREVERB_ECHO_DEPTH }, + { "AL_EAXREVERB_MODULATION_TIME", AL_EAXREVERB_MODULATION_TIME }, + { "AL_EAXREVERB_MODULATION_DEPTH", AL_EAXREVERB_MODULATION_DEPTH }, + { "AL_EAXREVERB_AIR_ABSORPTION_GAINHF", AL_EAXREVERB_AIR_ABSORPTION_GAINHF }, + { "AL_EAXREVERB_HFREFERENCE", AL_EAXREVERB_HFREFERENCE }, + { "AL_EAXREVERB_LFREFERENCE", AL_EAXREVERB_LFREFERENCE }, + { "AL_EAXREVERB_ROOM_ROLLOFF_FACTOR", AL_EAXREVERB_ROOM_ROLLOFF_FACTOR }, + { "AL_EAXREVERB_DECAY_HFLIMIT", AL_EAXREVERB_DECAY_HFLIMIT }, + + // Echo params + { "AL_ECHO_DELAY", AL_ECHO_DELAY }, + { "AL_ECHO_LRDELAY", AL_ECHO_LRDELAY }, + { "AL_ECHO_DAMPING", AL_ECHO_DAMPING }, + { "AL_ECHO_FEEDBACK", AL_ECHO_FEEDBACK }, + { "AL_ECHO_SPREAD", AL_ECHO_SPREAD }, + + // Ring Modulator params + { "AL_RING_MODULATOR_FREQUENCY", AL_RING_MODULATOR_FREQUENCY }, + { "AL_RING_MODULATOR_HIGHPASS_CUTOFF", AL_RING_MODULATOR_HIGHPASS_CUTOFF }, + { "AL_RING_MODULATOR_WAVEFORM", AL_RING_MODULATOR_WAVEFORM }, + + // Dedicated Dialogue/LFE params + { "AL_DEDICATED_GAIN", AL_DEDICATED_GAIN }, + + + // Default + { NULL, (ALenum)0 } +}; + + +const struct EffectList EffectList[] = { + { "eaxreverb", EAXREVERB, "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB }, + { "reverb", REVERB, "AL_EFFECT_REVERB", AL_EFFECT_REVERB }, + { "echo", ECHO, "AL_EFFECT_ECHO", AL_EFFECT_ECHO }, + { "modulator", MODULATOR, "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR }, + { "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT }, + { "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE }, + { NULL, 0, NULL, (ALenum)0 } +}; + + +AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName) +{ + ALboolean bIsSupported = AL_FALSE; + ALCcontext *Context; + const char *ptr; + size_t len; + + Context = GetLockedContext(); + if(!Context) return AL_FALSE; + + if(!extName) + alSetError(Context, AL_INVALID_VALUE); + else + { + len = strlen(extName); + ptr = Context->ExtensionList; + while(ptr && *ptr) + { + if(strncasecmp(ptr, extName, len) == 0 && + (ptr[len] == '\0' || isspace(ptr[len]))) + { + bIsSupported = AL_TRUE; + break; + } + if((ptr=strchr(ptr, ' ')) != NULL) + { + do { + ++ptr; + } while(isspace(*ptr)); + } + } + } + + UnlockContext(Context); + + return bIsSupported; +} + + +AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName) +{ + if(!funcName) + return NULL; + return alcGetProcAddress(NULL, funcName); +} + +AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) +{ + ALsizei i; + + for(i = 0;EffectList[i].ename;i++) + { + if(DisabledEffects[EffectList[i].type] && + strcmp(EffectList[i].ename, enumName) == 0) + return (ALenum)0; + } + + i = 0; + while(enumeration[i].enumName && + strcmp(enumeration[i].enumName, enumName) != 0) + i++; + + return enumeration[i].value; +} diff -r 000000000000 -r f9476ff7637e OpenAL32/alFilter.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/alFilter.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,420 @@ +/** + * 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 + +#include "AL/al.h" +#include "AL/alc.h" +#include "alMain.h" +#include "alFilter.h" +#include "alThunk.h" +#include "alError.h" + + +static void InitFilterParams(ALfilter *filter, ALenum type); + +#define LookupFilter(m, k) ((ALfilter*)LookupUIntMapKey(&(m), (k))) + +AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters) +{ + ALCcontext *Context; + ALsizei i=0; + + Context = GetLockedContext(); + if(!Context) return; + + if(n < 0 || IsBadWritePtr((void*)filters, n * sizeof(ALuint))) + alSetError(Context, AL_INVALID_VALUE); + else + { + ALCdevice *device = Context->Device; + ALenum err; + + while(i < n) + { + ALfilter *filter = calloc(1, sizeof(ALfilter)); + if(!filter) + { + alSetError(Context, AL_OUT_OF_MEMORY); + alDeleteFilters(i, filters); + break; + } + + err = NewThunkEntry(&filter->filter); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&device->FilterMap, filter->filter, filter); + if(err != AL_NO_ERROR) + { + FreeThunkEntry(filter->filter); + memset(filter, 0, sizeof(ALfilter)); + free(filter); + + alSetError(Context, err); + alDeleteFilters(i, filters); + break; + } + + filters[i++] = filter->filter; + InitFilterParams(filter, AL_FILTER_NULL); + } + } + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, ALuint *filters) +{ + ALCcontext *Context; + ALCdevice *device; + ALfilter *ALFilter; + ALboolean Failed; + ALsizei i; + + Context = GetLockedContext(); + if(!Context) return; + + Failed = AL_TRUE; + device = Context->Device; + if(n < 0) + alSetError(Context, AL_INVALID_VALUE); + else + { + Failed = AL_FALSE; + // Check that all filters are valid + for(i = 0;i < n;i++) + { + if(!filters[i]) + continue; + + if(LookupFilter(device->FilterMap, filters[i]) == NULL) + { + alSetError(Context, AL_INVALID_NAME); + Failed = AL_TRUE; + break; + } + } + } + + if(!Failed) + { + // All filters are valid + for(i = 0;i < n;i++) + { + // Recheck that the filter is valid, because there could be duplicated names + if((ALFilter=LookupFilter(device->FilterMap, filters[i])) == NULL) + continue; + + RemoveUIntMapKey(&device->FilterMap, ALFilter->filter); + FreeThunkEntry(ALFilter->filter); + + memset(ALFilter, 0, sizeof(ALfilter)); + free(ALFilter); + } + } + + UnlockContext(Context); +} + +AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter) +{ + ALCcontext *Context; + ALboolean result; + + Context = GetLockedContext(); + if(!Context) return AL_FALSE; + + result = ((!filter || LookupFilter(Context->Device->FilterMap, filter)) ? + AL_TRUE : AL_FALSE); + + UnlockContext(Context); + + return result; +} + +AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue) +{ + ALCcontext *Context; + ALCdevice *Device; + ALfilter *ALFilter; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if((ALFilter=LookupFilter(Device->FilterMap, filter)) != NULL) + { + switch(param) + { + case AL_FILTER_TYPE: + if(iValue == AL_FILTER_NULL || + iValue == AL_FILTER_LOWPASS) + InitFilterParams(ALFilter, iValue); + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, ALint *piValues) +{ + ALCcontext *Context; + ALCdevice *Device; + + switch(param) + { + case AL_FILTER_TYPE: + alFilteri(filter, param, piValues[0]); + return; + } + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if(LookupFilter(Device->FilterMap, filter) != NULL) + { + switch(param) + { + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue) +{ + ALCcontext *Context; + ALCdevice *Device; + ALfilter *ALFilter; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if((ALFilter=LookupFilter(Device->FilterMap, filter)) != NULL) + { + switch(ALFilter->type) + { + case AL_FILTER_LOWPASS: + switch(param) + { + case AL_LOWPASS_GAIN: + if(flValue >= AL_LOWPASS_MIN_GAIN && + flValue <= AL_LOWPASS_MAX_GAIN) + ALFilter->Gain = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + case AL_LOWPASS_GAINHF: + if(flValue >= AL_LOWPASS_MIN_GAINHF && + flValue <= AL_LOWPASS_MAX_GAINHF) + ALFilter->GainHF = flValue; + else + alSetError(Context, AL_INVALID_VALUE); + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, ALfloat *pflValues) +{ + /* There are currently no multi-value filter parameters */ + alFilterf(filter, param, pflValues[0]); +} + +AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue) +{ + ALCcontext *Context; + ALCdevice *Device; + ALfilter *ALFilter; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if((ALFilter=LookupFilter(Device->FilterMap, filter)) != NULL) + { + switch(param) + { + case AL_FILTER_TYPE: + *piValue = ALFilter->type; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues) +{ + ALCcontext *Context; + ALCdevice *Device; + + switch(param) + { + case AL_FILTER_TYPE: + alGetFilteri(filter, param, piValues); + return; + } + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if(LookupFilter(Device->FilterMap, filter) != NULL) + { + switch(param) + { + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue) +{ + ALCcontext *Context; + ALCdevice *Device; + ALfilter *ALFilter; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if((ALFilter=LookupFilter(Device->FilterMap, filter)) != NULL) + { + switch(ALFilter->type) + { + case AL_FILTER_LOWPASS: + switch(param) + { + case AL_LOWPASS_GAIN: + *pflValue = ALFilter->Gain; + break; + + case AL_LOWPASS_GAINHF: + *pflValue = ALFilter->GainHF; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + alSetError(Context, AL_INVALID_NAME); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues) +{ + /* There are currently no multi-value filter parameters */ + alGetFilterf(filter, param, pflValues); +} + + +ALfloat lpCoeffCalc(ALfloat g, ALfloat cw) +{ + ALfloat a = 0.0f; + + /* Be careful with gains < 0.01, as that causes the coefficient + * head towards 1, which will flatten the signal */ + if(g < 0.9999f) /* 1-epsilon */ + { + g = maxf(g, 0.01f); + a = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / + (1 - g); + } + + return a; +} + +ALvoid ReleaseALFilters(ALCdevice *device) +{ + ALsizei i; + for(i = 0;i < device->FilterMap.size;i++) + { + ALfilter *temp = device->FilterMap.array[i].value; + device->FilterMap.array[i].value = NULL; + + // Release filter structure + FreeThunkEntry(temp->filter); + memset(temp, 0, sizeof(ALfilter)); + free(temp); + } +} + + +static void InitFilterParams(ALfilter *filter, ALenum type) +{ + filter->type = type; + + filter->Gain = AL_LOWPASS_DEFAULT_GAIN; + filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF; +} diff -r 000000000000 -r f9476ff7637e OpenAL32/alListener.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/alListener.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,465 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 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 "alMain.h" +#include "AL/alc.h" +#include "alError.h" +#include "alListener.h" +#include "alSource.h" + +AL_API ALvoid AL_APIENTRY alListenerf(ALenum eParam, ALfloat flValue) +{ + ALCcontext *pContext; + + pContext = GetLockedContext(); + if(!pContext) return; + + switch(eParam) + { + case AL_GAIN: + if(flValue >= 0.0f && isfinite(flValue)) + { + pContext->Listener.Gain = flValue; + pContext->UpdateSources = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_METERS_PER_UNIT: + if(flValue > 0.0f && isfinite(flValue)) + { + pContext->Listener.MetersPerUnit = flValue; + pContext->UpdateSources = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alListener3f(ALenum eParam, ALfloat flValue1, ALfloat flValue2, ALfloat flValue3) +{ + ALCcontext *pContext; + + pContext = GetLockedContext(); + if(!pContext) return; + + switch(eParam) + { + case AL_POSITION: + if(isfinite(flValue1) && isfinite(flValue2) && isfinite(flValue3)) + { + pContext->Listener.Position[0] = flValue1; + pContext->Listener.Position[1] = flValue2; + pContext->Listener.Position[2] = flValue3; + pContext->UpdateSources = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_VELOCITY: + if(isfinite(flValue1) && isfinite(flValue2) && isfinite(flValue3)) + { + pContext->Listener.Velocity[0] = flValue1; + pContext->Listener.Velocity[1] = flValue2; + pContext->Listener.Velocity[2] = flValue3; + pContext->UpdateSources = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alListenerfv(ALenum eParam, const ALfloat *pflValues) +{ + ALCcontext *pContext; + + if(pflValues) + { + switch(eParam) + { + case AL_GAIN: + case AL_METERS_PER_UNIT: + alListenerf(eParam, pflValues[0]); + return; + + case AL_POSITION: + case AL_VELOCITY: + alListener3f(eParam, pflValues[0], pflValues[1], pflValues[2]); + return; + } + } + + pContext = GetLockedContext(); + if(!pContext) return; + + if(pflValues) + { + switch(eParam) + { + case AL_ORIENTATION: + if(isfinite(pflValues[0]) && isfinite(pflValues[1]) && + isfinite(pflValues[2]) && isfinite(pflValues[3]) && + isfinite(pflValues[4]) && isfinite(pflValues[5])) + { + // AT then UP + pContext->Listener.Forward[0] = pflValues[0]; + pContext->Listener.Forward[1] = pflValues[1]; + pContext->Listener.Forward[2] = pflValues[2]; + pContext->Listener.Up[0] = pflValues[3]; + pContext->Listener.Up[1] = pflValues[4]; + pContext->Listener.Up[2] = pflValues[5]; + pContext->UpdateSources = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alListeneri(ALenum eParam, ALint lValue) +{ + ALCcontext *pContext; + + (void)lValue; + + pContext = GetLockedContext(); + if(!pContext) return; + + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alListener3i(ALenum eParam, ALint lValue1, ALint lValue2, ALint lValue3) +{ + ALCcontext *pContext; + + switch(eParam) + { + case AL_POSITION: + case AL_VELOCITY: + alListener3f(eParam, (ALfloat)lValue1, (ALfloat)lValue2, (ALfloat)lValue3); + return; + } + + pContext = GetLockedContext(); + if(!pContext) return; + + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alListeneriv( ALenum eParam, const ALint* plValues ) +{ + ALCcontext *pContext; + ALfloat flValues[6]; + + if(plValues) + { + switch(eParam) + { + case AL_POSITION: + case AL_VELOCITY: + alListener3f(eParam, plValues[0], plValues[1], plValues[2]); + return; + + case AL_ORIENTATION: + flValues[0] = (ALfloat)plValues[0]; + flValues[1] = (ALfloat)plValues[1]; + flValues[2] = (ALfloat)plValues[2]; + flValues[3] = (ALfloat)plValues[3]; + flValues[4] = (ALfloat)plValues[4]; + flValues[5] = (ALfloat)plValues[5]; + alListenerfv(eParam, flValues); + return; + } + } + + pContext = GetLockedContext(); + if(!pContext) return; + + if(plValues) + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum eParam, ALfloat *pflValue) +{ + ALCcontext *pContext; + + pContext = GetLockedContext(); + if(!pContext) return; + + if(pflValue) + { + switch(eParam) + { + case AL_GAIN: + *pflValue = pContext->Listener.Gain; + break; + + case AL_METERS_PER_UNIT: + *pflValue = pContext->Listener.MetersPerUnit; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum eParam, ALfloat *pflValue1, ALfloat *pflValue2, ALfloat *pflValue3) +{ + ALCcontext *pContext; + + pContext = GetLockedContext(); + if(!pContext) return; + + if(pflValue1 && pflValue2 && pflValue3) + { + switch(eParam) + { + case AL_POSITION: + *pflValue1 = pContext->Listener.Position[0]; + *pflValue2 = pContext->Listener.Position[1]; + *pflValue3 = pContext->Listener.Position[2]; + break; + + case AL_VELOCITY: + *pflValue1 = pContext->Listener.Velocity[0]; + *pflValue2 = pContext->Listener.Velocity[1]; + *pflValue3 = pContext->Listener.Velocity[2]; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum eParam, ALfloat *pflValues) +{ + ALCcontext *pContext; + + switch(eParam) + { + case AL_GAIN: + case AL_METERS_PER_UNIT: + alGetListenerf(eParam, pflValues); + return; + + case AL_POSITION: + case AL_VELOCITY: + alGetListener3f(eParam, pflValues+0, pflValues+1, pflValues+2); + return; + } + + pContext = GetLockedContext(); + if(!pContext) return; + + if(pflValues) + { + switch(eParam) + { + case AL_ORIENTATION: + // AT then UP + pflValues[0] = pContext->Listener.Forward[0]; + pflValues[1] = pContext->Listener.Forward[1]; + pflValues[2] = pContext->Listener.Forward[2]; + pflValues[3] = pContext->Listener.Up[0]; + pflValues[4] = pContext->Listener.Up[1]; + pflValues[5] = pContext->Listener.Up[2]; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum eParam, ALint *plValue) +{ + ALCcontext *pContext; + + pContext = GetLockedContext(); + if(!pContext) return; + + if(plValue) + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alGetListener3i(ALenum eParam, ALint *plValue1, ALint *plValue2, ALint *plValue3) +{ + ALCcontext *pContext; + + pContext = GetLockedContext(); + if(!pContext) return; + + if(plValue1 && plValue2 && plValue3) + { + switch (eParam) + { + case AL_POSITION: + *plValue1 = (ALint)pContext->Listener.Position[0]; + *plValue2 = (ALint)pContext->Listener.Position[1]; + *plValue3 = (ALint)pContext->Listener.Position[2]; + break; + + case AL_VELOCITY: + *plValue1 = (ALint)pContext->Listener.Velocity[0]; + *plValue2 = (ALint)pContext->Listener.Velocity[1]; + *plValue3 = (ALint)pContext->Listener.Velocity[2]; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alGetListeneriv(ALenum eParam, ALint* plValues) +{ + ALCcontext *pContext; + + switch(eParam) + { + case AL_POSITION: + case AL_VELOCITY: + alGetListener3i(eParam, plValues+0, plValues+1, plValues+2); + return; + } + + pContext = GetLockedContext(); + if(!pContext) return; + + if(plValues) + { + switch(eParam) + { + case AL_ORIENTATION: + // AT then UP + plValues[0] = (ALint)pContext->Listener.Forward[0]; + plValues[1] = (ALint)pContext->Listener.Forward[1]; + plValues[2] = (ALint)pContext->Listener.Forward[2]; + plValues[3] = (ALint)pContext->Listener.Up[0]; + plValues[4] = (ALint)pContext->Listener.Up[1]; + plValues[5] = (ALint)pContext->Listener.Up[2]; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} diff -r 000000000000 -r f9476ff7637e OpenAL32/alSource.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/alSource.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,2172 @@ +/** + * 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 +#include +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" +#include "alError.h" +#include "alSource.h" +#include "alBuffer.h" +#include "alThunk.h" +#include "alAuxEffectSlot.h" + + +enum Resampler DefaultResampler; +const ALsizei ResamplerPadding[RESAMPLER_MAX] = { + 0, /* Point */ + 1, /* Linear */ + 2, /* Cubic */ +}; +const ALsizei ResamplerPrePadding[RESAMPLER_MAX] = { + 0, /* Point */ + 0, /* Linear */ + 1, /* Cubic */ +}; + + +static ALvoid InitSourceParams(ALsource *Source); +static ALvoid GetSourceOffset(ALsource *Source, ALenum eName, ALdouble *Offsets, ALdouble updateLen); +static ALint GetByteOffset(ALsource *Source); + +#define LookupSource(m, k) ((ALsource*)LookupUIntMapKey(&(m), (k))) +#define LookupBuffer(m, k) ((ALbuffer*)LookupUIntMapKey(&(m), (k))) +#define LookupFilter(m, k) ((ALfilter*)LookupUIntMapKey(&(m), (k))) +#define LookupEffectSlot(m, k) ((ALeffectslot*)LookupUIntMapKey(&(m), (k))) + +AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n,ALuint *sources) +{ + ALCcontext *Context; + ALCdevice *Device; + + Context = GetLockedContext(); + if(!Context) return; + + Device = Context->Device; + if(n < 0 || IsBadWritePtr((void*)sources, n * sizeof(ALuint))) + alSetError(Context, AL_INVALID_VALUE); + else if((ALuint)n > Device->MaxNoOfSources - Context->SourceMap.size) + alSetError(Context, AL_INVALID_VALUE); + else + { + ALenum err; + ALsizei i; + + // Add additional sources to the list + i = 0; + while(i < n) + { + ALsource *source = calloc(1, sizeof(ALsource)); + if(!source) + { + alSetError(Context, AL_OUT_OF_MEMORY); + alDeleteSources(i, sources); + break; + } + + err = NewThunkEntry(&source->source); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&Context->SourceMap, source->source, source); + if(err != AL_NO_ERROR) + { + FreeThunkEntry(source->source); + memset(source, 0, sizeof(ALsource)); + free(source); + + alSetError(Context, err); + alDeleteSources(i, sources); + break; + } + + sources[i++] = source->source; + InitSourceParams(source); + } + } + + UnlockContext(Context); +} + + +AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) +{ + ALCcontext *Context; + ALsource *Source; + ALsizei i, j; + ALbufferlistitem *BufferList; + ALboolean SourcesValid = AL_FALSE; + + Context = GetLockedContext(); + if(!Context) return; + + if(n < 0) + alSetError(Context, AL_INVALID_VALUE); + else + { + SourcesValid = AL_TRUE; + // Check that all Sources are valid (and can therefore be deleted) + for(i = 0;i < n;i++) + { + if(LookupSource(Context->SourceMap, sources[i]) == NULL) + { + alSetError(Context, AL_INVALID_NAME); + SourcesValid = AL_FALSE; + break; + } + } + } + + if(SourcesValid) + { + // All Sources are valid, and can be deleted + for(i = 0;i < n;i++) + { + // Recheck that the Source is valid, because there could be duplicated Source names + if((Source=LookupSource(Context->SourceMap, sources[i])) == NULL) + continue; + + for(j = 0;j < Context->ActiveSourceCount;j++) + { + if(Context->ActiveSources[j] == Source) + { + ALsizei end = --(Context->ActiveSourceCount); + Context->ActiveSources[j] = Context->ActiveSources[end]; + break; + } + } + + // For each buffer in the source's queue... + while(Source->queue != NULL) + { + BufferList = Source->queue; + Source->queue = BufferList->next; + + if(BufferList->buffer != NULL) + BufferList->buffer->refcount--; + free(BufferList); + } + + for(j = 0;j < MAX_SENDS;++j) + { + if(Source->Send[j].Slot) + Source->Send[j].Slot->refcount--; + Source->Send[j].Slot = NULL; + } + + // Remove Source from list of Sources + RemoveUIntMapKey(&Context->SourceMap, Source->source); + FreeThunkEntry(Source->source); + + memset(Source,0,sizeof(ALsource)); + free(Source); + } + } + + UnlockContext(Context); +} + + +AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) +{ + ALCcontext *Context; + ALboolean result; + + Context = GetLockedContext(); + if(!Context) return AL_FALSE; + + result = (LookupSource(Context->SourceMap, source) ? AL_TRUE : AL_FALSE); + + UnlockContext(Context); + + return result; +} + + +AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum eParam, ALfloat flValue) +{ + ALCcontext *pContext; + ALsource *Source; + + pContext = GetLockedContext(); + if(!pContext) return; + + if((Source=LookupSource(pContext->SourceMap, source)) != NULL) + { + switch(eParam) + { + case AL_PITCH: + if(flValue >= 0.0f) + { + Source->flPitch = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_CONE_INNER_ANGLE: + if(flValue >= 0.0f && flValue <= 360.0f) + { + Source->flInnerAngle = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_CONE_OUTER_ANGLE: + if(flValue >= 0.0f && flValue <= 360.0f) + { + Source->flOuterAngle = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_GAIN: + if(flValue >= 0.0f) + { + Source->flGain = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_MAX_DISTANCE: + if(flValue >= 0.0f) + { + Source->flMaxDistance = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_ROLLOFF_FACTOR: + if(flValue >= 0.0f) + { + Source->flRollOffFactor = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_REFERENCE_DISTANCE: + if(flValue >= 0.0f) + { + Source->flRefDistance = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_MIN_GAIN: + if(flValue >= 0.0f && flValue <= 1.0f) + { + Source->flMinGain = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_MAX_GAIN: + if(flValue >= 0.0f && flValue <= 1.0f) + { + Source->flMaxGain = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_CONE_OUTER_GAIN: + if(flValue >= 0.0f && flValue <= 1.0f) + { + Source->flOuterGain = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_CONE_OUTER_GAINHF: + if(flValue >= 0.0f && flValue <= 1.0f) + { + Source->OuterGainHF = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_AIR_ABSORPTION_FACTOR: + if(flValue >= 0.0f && flValue <= 10.0f) + { + Source->AirAbsorptionFactor = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_ROOM_ROLLOFF_FACTOR: + if(flValue >= 0.0f && flValue <= 10.0f) + { + Source->RoomRolloffFactor = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_DOPPLER_FACTOR: + if(flValue >= 0.0f && flValue <= 1.0f) + { + Source->DopplerFactor = flValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + if(flValue >= 0.0f) + { + Source->lOffsetType = eParam; + + // Store Offset (convert Seconds into Milliseconds) + if(eParam == AL_SEC_OFFSET) + Source->lOffset = (ALint)(flValue * 1000.0f); + else + Source->lOffset = (ALint)flValue; + + if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && + !pContext->DeferUpdates) + { + if(ApplyOffset(Source) == AL_FALSE) + alSetError(pContext, AL_INVALID_VALUE); + } + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + { + // Invalid Source Name + alSetError(pContext, AL_INVALID_NAME); + } + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum eParam, ALfloat flValue1,ALfloat flValue2,ALfloat flValue3) +{ + ALCcontext *pContext; + ALsource *Source; + + pContext = GetLockedContext(); + if(!pContext) return; + + if((Source=LookupSource(pContext->SourceMap, source)) != NULL) + { + switch(eParam) + { + case AL_POSITION: + if(isfinite(flValue1) && isfinite(flValue2) && isfinite(flValue3)) + { + Source->vPosition[0] = flValue1; + Source->vPosition[1] = flValue2; + Source->vPosition[2] = flValue3; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_VELOCITY: + if(isfinite(flValue1) && isfinite(flValue2) && isfinite(flValue3)) + { + Source->vVelocity[0] = flValue1; + Source->vVelocity[1] = flValue2; + Source->vVelocity[2] = flValue3; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_DIRECTION: + if(isfinite(flValue1) && isfinite(flValue2) && isfinite(flValue3)) + { + Source->vOrientation[0] = flValue1; + Source->vOrientation[1] = flValue2; + Source->vOrientation[2] = flValue3; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_NAME); + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum eParam, const ALfloat *pflValues) +{ + ALCcontext *pContext; + + if(pflValues) + { + switch(eParam) + { + case AL_PITCH: + case AL_CONE_INNER_ANGLE: + case AL_CONE_OUTER_ANGLE: + case AL_GAIN: + case AL_MAX_DISTANCE: + case AL_ROLLOFF_FACTOR: + case AL_REFERENCE_DISTANCE: + case AL_MIN_GAIN: + case AL_MAX_GAIN: + case AL_CONE_OUTER_GAIN: + case AL_CONE_OUTER_GAINHF: + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + case AL_AIR_ABSORPTION_FACTOR: + case AL_ROOM_ROLLOFF_FACTOR: + alSourcef(source, eParam, pflValues[0]); + return; + + case AL_POSITION: + case AL_VELOCITY: + case AL_DIRECTION: + alSource3f(source, eParam, pflValues[0], pflValues[1], pflValues[2]); + return; + } + } + + pContext = GetLockedContext(); + if(!pContext) return; + + if(pflValues) + { + if(LookupSource(pContext->SourceMap, source) != NULL) + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_NAME); + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alSourcei(ALuint source,ALenum eParam,ALint lValue) +{ + ALCcontext *pContext; + ALsource *Source; + ALbufferlistitem *BufferListItem; + + switch(eParam) + { + case AL_MAX_DISTANCE: + case AL_ROLLOFF_FACTOR: + case AL_CONE_INNER_ANGLE: + case AL_CONE_OUTER_ANGLE: + case AL_REFERENCE_DISTANCE: + alSourcef(source, eParam, (ALfloat)lValue); + return; + } + + pContext = GetLockedContext(); + if(!pContext) return; + + if((Source=LookupSource(pContext->SourceMap, source)) != NULL) + { + ALCdevice *device = pContext->Device; + + switch(eParam) + { + case AL_SOURCE_RELATIVE: + if(lValue == AL_FALSE || lValue == AL_TRUE) + { + Source->bHeadRelative = (ALboolean)lValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_LOOPING: + if(lValue == AL_FALSE || lValue == AL_TRUE) + Source->bLooping = (ALboolean)lValue; + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_BUFFER: + if(Source->state == AL_STOPPED || Source->state == AL_INITIAL) + { + ALbuffer *buffer = NULL; + + if(lValue == 0 || + (buffer=LookupBuffer(device->BufferMap, lValue)) != NULL) + { + // Remove all elements in the queue + while(Source->queue != NULL) + { + BufferListItem = Source->queue; + Source->queue = BufferListItem->next; + + if(BufferListItem->buffer) + BufferListItem->buffer->refcount--; + free(BufferListItem); + } + Source->BuffersInQueue = 0; + + // Add the buffer to the queue (as long as it is NOT the NULL buffer) + if(buffer != NULL) + { + // Source is now in STATIC mode + Source->lSourceType = AL_STATIC; + + // Add the selected buffer to the queue + BufferListItem = malloc(sizeof(ALbufferlistitem)); + BufferListItem->buffer = buffer; + BufferListItem->next = NULL; + BufferListItem->prev = NULL; + + Source->queue = BufferListItem; + Source->BuffersInQueue = 1; + + Source->NumChannels = ChannelsFromFmt(buffer->FmtChannels); + Source->SampleSize = BytesFromFmt(buffer->FmtType); + if(buffer->FmtChannels == FmtMono) + Source->Update = CalcSourceParams; + else + Source->Update = CalcNonAttnSourceParams; + + // Increment reference counter for buffer + buffer->refcount++; + } + else + { + // Source is now in UNDETERMINED mode + Source->lSourceType = AL_UNDETERMINED; + } + Source->BuffersPlayed = 0; + + // Update AL_BUFFER parameter + Source->Buffer = buffer; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + } + else + alSetError(pContext, AL_INVALID_OPERATION); + break; + + case AL_SOURCE_STATE: + // Query only + alSetError(pContext, AL_INVALID_OPERATION); + break; + + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + if(lValue >= 0) + { + Source->lOffsetType = eParam; + + // Store Offset (convert Seconds into Milliseconds) + if(eParam == AL_SEC_OFFSET) + Source->lOffset = lValue * 1000; + else + Source->lOffset = lValue; + + if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && + !pContext->DeferUpdates) + { + if(ApplyOffset(Source) == AL_FALSE) + alSetError(pContext, AL_INVALID_VALUE); + } + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_DIRECT_FILTER: { + ALfilter *filter = NULL; + + if(lValue == 0 || + (filter=LookupFilter(pContext->Device->FilterMap, lValue)) != NULL) + { + if(!filter) + { + Source->DirectFilter.type = AL_FILTER_NULL; + Source->DirectFilter.filter = 0; + } + else + memcpy(&Source->DirectFilter, filter, sizeof(*filter)); + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + } break; + + case AL_DIRECT_FILTER_GAINHF_AUTO: + if(lValue == AL_TRUE || lValue == AL_FALSE) + { + Source->DryGainHFAuto = lValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + if(lValue == AL_TRUE || lValue == AL_FALSE) + { + Source->WetGainAuto = lValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: + if(lValue == AL_TRUE || lValue == AL_FALSE) + { + Source->WetGainHFAuto = lValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_VIRTUAL_CHANNELS_SOFT: + if(lValue == AL_TRUE || lValue == AL_FALSE) + { + Source->VirtualChannels = lValue; + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + case AL_DISTANCE_MODEL: + if(lValue == AL_NONE || + lValue == AL_INVERSE_DISTANCE || + lValue == AL_INVERSE_DISTANCE_CLAMPED || + lValue == AL_LINEAR_DISTANCE || + lValue == AL_LINEAR_DISTANCE_CLAMPED || + lValue == AL_EXPONENT_DISTANCE || + lValue == AL_EXPONENT_DISTANCE_CLAMPED) + { + Source->DistanceModel = lValue; + if(pContext->SourceDistanceModel) + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_NAME); + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum eParam, ALint lValue1, ALint lValue2, ALint lValue3) +{ + ALCcontext *pContext; + ALsource *Source; + + switch(eParam) + { + case AL_POSITION: + case AL_VELOCITY: + case AL_DIRECTION: + alSource3f(source, eParam, (ALfloat)lValue1, (ALfloat)lValue2, (ALfloat)lValue3); + return; + } + + pContext = GetLockedContext(); + if(!pContext) return; + + if((Source=LookupSource(pContext->SourceMap, source)) != NULL) + { + ALCdevice *device = pContext->Device; + + switch(eParam) + { + case AL_AUXILIARY_SEND_FILTER: { + ALeffectslot *ALEffectSlot = NULL; + ALfilter *ALFilter = NULL; + + if((ALuint)lValue2 < device->NumAuxSends && + (lValue1 == 0 || + (ALEffectSlot=LookupEffectSlot(pContext->EffectSlotMap, lValue1)) != NULL) && + (lValue3 == 0 || + (ALFilter=LookupFilter(device->FilterMap, lValue3)) != NULL)) + { + /* Release refcount on the previous slot, and add one for + * the new slot */ + if(Source->Send[lValue2].Slot) + Source->Send[lValue2].Slot->refcount--; + Source->Send[lValue2].Slot = ALEffectSlot; + if(Source->Send[lValue2].Slot) + Source->Send[lValue2].Slot->refcount++; + + if(!ALFilter) + { + /* Disable filter */ + Source->Send[lValue2].WetFilter.type = 0; + Source->Send[lValue2].WetFilter.filter = 0; + } + else + memcpy(&Source->Send[lValue2].WetFilter, ALFilter, sizeof(*ALFilter)); + Source->NeedsUpdate = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + } break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_NAME); + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum eParam, const ALint* plValues) +{ + ALCcontext *pContext; + + if(plValues) + { + switch(eParam) + { + case AL_SOURCE_RELATIVE: + case AL_CONE_INNER_ANGLE: + case AL_CONE_OUTER_ANGLE: + case AL_LOOPING: + case AL_BUFFER: + case AL_SOURCE_STATE: + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + case AL_MAX_DISTANCE: + case AL_ROLLOFF_FACTOR: + case AL_REFERENCE_DISTANCE: + case AL_DIRECT_FILTER: + case AL_DIRECT_FILTER_GAINHF_AUTO: + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: + case AL_DISTANCE_MODEL: + case AL_VIRTUAL_CHANNELS_SOFT: + alSourcei(source, eParam, plValues[0]); + return; + + case AL_POSITION: + case AL_VELOCITY: + case AL_DIRECTION: + case AL_AUXILIARY_SEND_FILTER: + alSource3i(source, eParam, plValues[0], plValues[1], plValues[2]); + return; + } + } + + pContext = GetLockedContext(); + if(!pContext) return; + + if(plValues) + { + if(LookupSource(pContext->SourceMap, source) != NULL) + { + switch(eParam) + { + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_NAME); + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum eParam, ALfloat *pflValue) +{ + ALCcontext *pContext; + ALsource *Source; + ALdouble Offsets[2]; + ALdouble updateLen; + + pContext = GetLockedContext(); + if(!pContext) return; + + if(pflValue) + { + if((Source=LookupSource(pContext->SourceMap, source)) != NULL) + { + switch(eParam) + { + case AL_PITCH: + *pflValue = Source->flPitch; + break; + + case AL_GAIN: + *pflValue = Source->flGain; + break; + + case AL_MIN_GAIN: + *pflValue = Source->flMinGain; + break; + + case AL_MAX_GAIN: + *pflValue = Source->flMaxGain; + break; + + case AL_MAX_DISTANCE: + *pflValue = Source->flMaxDistance; + break; + + case AL_ROLLOFF_FACTOR: + *pflValue = Source->flRollOffFactor; + break; + + case AL_CONE_OUTER_GAIN: + *pflValue = Source->flOuterGain; + break; + + case AL_CONE_OUTER_GAINHF: + *pflValue = Source->OuterGainHF; + break; + + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + updateLen = (ALdouble)pContext->Device->UpdateSize / + pContext->Device->Frequency; + GetSourceOffset(Source, eParam, Offsets, updateLen); + *pflValue = Offsets[0]; + break; + + case AL_CONE_INNER_ANGLE: + *pflValue = Source->flInnerAngle; + break; + + case AL_CONE_OUTER_ANGLE: + *pflValue = Source->flOuterAngle; + break; + + case AL_REFERENCE_DISTANCE: + *pflValue = Source->flRefDistance; + break; + + case AL_AIR_ABSORPTION_FACTOR: + *pflValue = Source->AirAbsorptionFactor; + break; + + case AL_ROOM_ROLLOFF_FACTOR: + *pflValue = Source->RoomRolloffFactor; + break; + + case AL_DOPPLER_FACTOR: + *pflValue = Source->DopplerFactor; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_NAME); + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum eParam, ALfloat* pflValue1, ALfloat* pflValue2, ALfloat* pflValue3) +{ + ALCcontext *pContext; + ALsource *Source; + + pContext = GetLockedContext(); + if(!pContext) return; + + if(pflValue1 && pflValue2 && pflValue3) + { + if((Source=LookupSource(pContext->SourceMap, source)) != NULL) + { + switch(eParam) + { + case AL_POSITION: + *pflValue1 = Source->vPosition[0]; + *pflValue2 = Source->vPosition[1]; + *pflValue3 = Source->vPosition[2]; + break; + + case AL_VELOCITY: + *pflValue1 = Source->vVelocity[0]; + *pflValue2 = Source->vVelocity[1]; + *pflValue3 = Source->vVelocity[2]; + break; + + case AL_DIRECTION: + *pflValue1 = Source->vOrientation[0]; + *pflValue2 = Source->vOrientation[1]; + *pflValue3 = Source->vOrientation[2]; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_NAME); + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum eParam, ALfloat *pflValues) +{ + ALCcontext *pContext; + ALsource *Source; + ALdouble Offsets[2]; + ALdouble updateLen; + + switch(eParam) + { + case AL_PITCH: + case AL_GAIN: + case AL_MIN_GAIN: + case AL_MAX_GAIN: + case AL_MAX_DISTANCE: + case AL_ROLLOFF_FACTOR: + case AL_DOPPLER_FACTOR: + case AL_CONE_OUTER_GAIN: + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + case AL_CONE_INNER_ANGLE: + case AL_CONE_OUTER_ANGLE: + case AL_REFERENCE_DISTANCE: + case AL_CONE_OUTER_GAINHF: + case AL_AIR_ABSORPTION_FACTOR: + case AL_ROOM_ROLLOFF_FACTOR: + alGetSourcef(source, eParam, pflValues); + return; + + case AL_POSITION: + case AL_VELOCITY: + case AL_DIRECTION: + alGetSource3f(source, eParam, pflValues+0, pflValues+1, pflValues+2); + return; + } + + pContext = GetLockedContext(); + if(!pContext) return; + + if(pflValues) + { + if((Source=LookupSource(pContext->SourceMap, source)) != NULL) + { + switch(eParam) + { + case AL_SAMPLE_RW_OFFSETS_SOFT: + case AL_BYTE_RW_OFFSETS_SOFT: + updateLen = (ALdouble)pContext->Device->UpdateSize / + pContext->Device->Frequency; + GetSourceOffset(Source, eParam, Offsets, updateLen); + pflValues[0] = Offsets[0]; + pflValues[1] = Offsets[1]; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_NAME); + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum eParam, ALint *plValue) +{ + ALCcontext *pContext; + ALsource *Source; + ALdouble Offsets[2]; + ALdouble updateLen; + + pContext = GetLockedContext(); + if(!pContext) return; + + if(plValue) + { + if((Source=LookupSource(pContext->SourceMap, source)) != NULL) + { + switch(eParam) + { + case AL_MAX_DISTANCE: + *plValue = (ALint)Source->flMaxDistance; + break; + + case AL_ROLLOFF_FACTOR: + *plValue = (ALint)Source->flRollOffFactor; + break; + + case AL_REFERENCE_DISTANCE: + *plValue = (ALint)Source->flRefDistance; + break; + + case AL_SOURCE_RELATIVE: + *plValue = Source->bHeadRelative; + break; + + case AL_CONE_INNER_ANGLE: + *plValue = (ALint)Source->flInnerAngle; + break; + + case AL_CONE_OUTER_ANGLE: + *plValue = (ALint)Source->flOuterAngle; + break; + + case AL_LOOPING: + *plValue = Source->bLooping; + break; + + case AL_BUFFER: + *plValue = (Source->Buffer ? Source->Buffer->buffer : 0); + break; + + case AL_SOURCE_STATE: + *plValue = Source->state; + break; + + case AL_BUFFERS_QUEUED: + *plValue = Source->BuffersInQueue; + break; + + case AL_BUFFERS_PROCESSED: + if(Source->bLooping || Source->lSourceType != AL_STREAMING) + { + /* Buffers on a looping source are in a perpetual state + * of PENDING, so don't report any as PROCESSED */ + *plValue = 0; + } + else + *plValue = Source->BuffersPlayed; + break; + + case AL_SOURCE_TYPE: + *plValue = Source->lSourceType; + break; + + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + updateLen = (ALdouble)pContext->Device->UpdateSize / + pContext->Device->Frequency; + GetSourceOffset(Source, eParam, Offsets, updateLen); + *plValue = (ALint)Offsets[0]; + break; + + case AL_DIRECT_FILTER: + *plValue = Source->DirectFilter.filter; + break; + + case AL_DIRECT_FILTER_GAINHF_AUTO: + *plValue = Source->DryGainHFAuto; + break; + + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + *plValue = Source->WetGainAuto; + break; + + case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: + *plValue = Source->WetGainHFAuto; + break; + + case AL_DOPPLER_FACTOR: + *plValue = (ALint)Source->DopplerFactor; + break; + + case AL_VIRTUAL_CHANNELS_SOFT: + *plValue = Source->VirtualChannels; + break; + + case AL_DISTANCE_MODEL: + *plValue = Source->DistanceModel; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_NAME); + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum eParam, ALint* plValue1, ALint* plValue2, ALint* plValue3) +{ + ALCcontext *pContext; + ALsource *Source; + + pContext = GetLockedContext(); + if(!pContext) return; + + if(plValue1 && plValue2 && plValue3) + { + if((Source=LookupSource(pContext->SourceMap, source)) != NULL) + { + switch(eParam) + { + case AL_POSITION: + *plValue1 = (ALint)Source->vPosition[0]; + *plValue2 = (ALint)Source->vPosition[1]; + *plValue3 = (ALint)Source->vPosition[2]; + break; + + case AL_VELOCITY: + *plValue1 = (ALint)Source->vVelocity[0]; + *plValue2 = (ALint)Source->vVelocity[1]; + *plValue3 = (ALint)Source->vVelocity[2]; + break; + + case AL_DIRECTION: + *plValue1 = (ALint)Source->vOrientation[0]; + *plValue2 = (ALint)Source->vOrientation[1]; + *plValue3 = (ALint)Source->vOrientation[2]; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_NAME); + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum eParam, ALint* plValues) +{ + ALCcontext *pContext; + ALsource *Source; + ALdouble Offsets[2]; + ALdouble updateLen; + + switch(eParam) + { + case AL_SOURCE_RELATIVE: + case AL_CONE_INNER_ANGLE: + case AL_CONE_OUTER_ANGLE: + case AL_LOOPING: + case AL_BUFFER: + case AL_SOURCE_STATE: + case AL_BUFFERS_QUEUED: + case AL_BUFFERS_PROCESSED: + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + case AL_MAX_DISTANCE: + case AL_ROLLOFF_FACTOR: + case AL_DOPPLER_FACTOR: + case AL_REFERENCE_DISTANCE: + case AL_SOURCE_TYPE: + case AL_DIRECT_FILTER: + case AL_DIRECT_FILTER_GAINHF_AUTO: + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: + case AL_DISTANCE_MODEL: + case AL_VIRTUAL_CHANNELS_SOFT: + alGetSourcei(source, eParam, plValues); + return; + + case AL_POSITION: + case AL_VELOCITY: + case AL_DIRECTION: + alGetSource3i(source, eParam, plValues+0, plValues+1, plValues+2); + return; + } + + pContext = GetLockedContext(); + if(!pContext) return; + + if(plValues) + { + if((Source=LookupSource(pContext->SourceMap, source)) != NULL) + { + switch(eParam) + { + case AL_SAMPLE_RW_OFFSETS_SOFT: + case AL_BYTE_RW_OFFSETS_SOFT: + updateLen = (ALdouble)pContext->Device->UpdateSize / + pContext->Device->Frequency; + GetSourceOffset(Source, eParam, Offsets, updateLen); + plValues[0] = (ALint)Offsets[0]; + plValues[1] = (ALint)Offsets[1]; + break; + + default: + alSetError(pContext, AL_INVALID_ENUM); + break; + } + } + else + alSetError(pContext, AL_INVALID_NAME); + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + + +AL_API ALvoid AL_APIENTRY alSourcePlay(ALuint source) +{ + alSourcePlayv(1, &source); +} + +AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) +{ + ALCcontext *Context; + ALsource *Source; + ALsizei i; + + Context = GetLockedContext(); + if(!Context) return; + + if(n < 0) + { + alSetError(Context, AL_INVALID_VALUE); + goto done; + } + if(n > 0 && !sources) + { + alSetError(Context, AL_INVALID_VALUE); + goto done; + } + + // Check that all the Sources are valid + for(i = 0;i < n;i++) + { + if(!LookupSource(Context->SourceMap, sources[i])) + { + alSetError(Context, AL_INVALID_NAME); + goto done; + } + } + + while(Context->MaxActiveSources-Context->ActiveSourceCount < n) + { + void *temp = NULL; + ALsizei newcount; + + newcount = Context->MaxActiveSources << 1; + if(newcount > 0) + temp = realloc(Context->ActiveSources, + sizeof(*Context->ActiveSources) * newcount); + if(!temp) + { + alSetError(Context, AL_OUT_OF_MEMORY); + goto done; + } + + Context->ActiveSources = temp; + Context->MaxActiveSources = newcount; + } + + for(i = 0;i < n;i++) + { + Source = LookupSource(Context->SourceMap, sources[i]); + if(Context->DeferUpdates) Source->new_state = AL_PLAYING; + else SetSourceState(Source, Context, AL_PLAYING); + } + +done: + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alSourcePause(ALuint source) +{ + alSourcePausev(1, &source); +} + +AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) +{ + ALCcontext *Context; + ALsource *Source; + ALsizei i; + + Context = GetLockedContext(); + if(!Context) return; + + if(n < 0) + { + alSetError(Context, AL_INVALID_VALUE); + goto done; + } + if(n > 0 && !sources) + { + alSetError(Context, AL_INVALID_VALUE); + goto done; + } + + // Check all the Sources are valid + for(i = 0;i < n;i++) + { + if(!LookupSource(Context->SourceMap, sources[i])) + { + alSetError(Context, AL_INVALID_NAME); + goto done; + } + } + + for(i = 0;i < n;i++) + { + Source = LookupSource(Context->SourceMap, sources[i]); + if(Context->DeferUpdates) Source->new_state = AL_PAUSED; + else SetSourceState(Source, Context, AL_PAUSED); + } + +done: + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alSourceStop(ALuint source) +{ + alSourceStopv(1, &source); +} + +AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) +{ + ALCcontext *Context; + ALsource *Source; + ALsizei i; + + Context = GetLockedContext(); + if(!Context) return; + + if(n < 0) + { + alSetError(Context, AL_INVALID_VALUE); + goto done; + } + if(n > 0 && !sources) + { + alSetError(Context, AL_INVALID_VALUE); + goto done; + } + + // Check all the Sources are valid + for(i = 0;i < n;i++) + { + if(!LookupSource(Context->SourceMap, sources[i])) + { + alSetError(Context, AL_INVALID_NAME); + goto done; + } + } + + for(i = 0;i < n;i++) + { + Source = LookupSource(Context->SourceMap, sources[i]); + if(Context->DeferUpdates) Source->new_state = AL_STOPPED; + else SetSourceState(Source, Context, AL_STOPPED); + } + +done: + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alSourceRewind(ALuint source) +{ + alSourceRewindv(1, &source); +} + +AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) +{ + ALCcontext *Context; + ALsource *Source; + ALsizei i; + + Context = GetLockedContext(); + if(!Context) return; + + if(n < 0) + { + alSetError(Context, AL_INVALID_VALUE); + goto done; + } + if(n > 0 && !sources) + { + alSetError(Context, AL_INVALID_VALUE); + goto done; + } + + // Check all the Sources are valid + for(i = 0;i < n;i++) + { + if(!LookupSource(Context->SourceMap, sources[i])) + { + alSetError(Context, AL_INVALID_NAME); + goto done; + } + } + + for(i = 0;i < n;i++) + { + Source = LookupSource(Context->SourceMap, sources[i]); + if(Context->DeferUpdates) Source->new_state = AL_INITIAL; + else SetSourceState(Source, Context, AL_INITIAL); + } + +done: + UnlockContext(Context); +} + + +AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei n, const ALuint *buffers) +{ + ALCcontext *Context; + ALCdevice *device; + ALsource *Source; + ALbuffer *buffer; + ALsizei i; + ALbufferlistitem *BufferListStart; + ALbufferlistitem *BufferList; + ALbuffer *BufferFmt; + + if(n == 0) + return; + + Context = GetLockedContext(); + if(!Context) return; + + if(n < 0) + { + alSetError(Context, AL_INVALID_VALUE); + goto done; + } + + // Check that all buffers are valid or zero and that the source is valid + + // Check that this is a valid source + if((Source=LookupSource(Context->SourceMap, source)) == NULL) + { + alSetError(Context, AL_INVALID_NAME); + goto done; + } + + // Check that this is not a STATIC Source + if(Source->lSourceType == AL_STATIC) + { + // Invalid Source Type (can't queue on a Static Source) + alSetError(Context, AL_INVALID_OPERATION); + goto done; + } + + device = Context->Device; + + BufferFmt = NULL; + + // Check existing Queue (if any) for a valid Buffers and get its frequency and format + BufferList = Source->queue; + while(BufferList) + { + if(BufferList->buffer) + { + BufferFmt = BufferList->buffer; + break; + } + BufferList = BufferList->next; + } + + for(i = 0;i < n;i++) + { + if(!buffers[i]) + continue; + + if((buffer=LookupBuffer(device->BufferMap, buffers[i])) == NULL) + { + alSetError(Context, AL_INVALID_NAME); + goto done; + } + + if(BufferFmt == NULL) + { + BufferFmt = buffer; + + Source->NumChannels = ChannelsFromFmt(buffer->FmtChannels); + Source->SampleSize = BytesFromFmt(buffer->FmtType); + if(buffer->FmtChannels == FmtMono) + Source->Update = CalcSourceParams; + else + Source->Update = CalcNonAttnSourceParams; + + Source->NeedsUpdate = AL_TRUE; + } + else if(BufferFmt->Frequency != buffer->Frequency || + BufferFmt->OriginalChannels != buffer->OriginalChannels || + BufferFmt->OriginalType != buffer->OriginalType) + { + alSetError(Context, AL_INVALID_OPERATION); + goto done; + } + } + + // Change Source Type + Source->lSourceType = AL_STREAMING; + + buffer = LookupBuffer(device->BufferMap, buffers[0]); + + // All buffers are valid - so add them to the list + BufferListStart = malloc(sizeof(ALbufferlistitem)); + BufferListStart->buffer = buffer; + BufferListStart->next = NULL; + BufferListStart->prev = NULL; + + // Increment reference counter for buffer + if(buffer) buffer->refcount++; + + BufferList = BufferListStart; + + for(i = 1;i < n;i++) + { + buffer = LookupBuffer(device->BufferMap, buffers[i]); + + BufferList->next = malloc(sizeof(ALbufferlistitem)); + BufferList->next->buffer = buffer; + BufferList->next->next = NULL; + BufferList->next->prev = BufferList; + + // Increment reference counter for buffer + if(buffer) buffer->refcount++; + + BufferList = BufferList->next; + } + + if(Source->queue == NULL) + { + Source->queue = BufferListStart; + // Update Current Buffer + Source->Buffer = BufferListStart->buffer; + } + else + { + // Find end of queue + BufferList = Source->queue; + while(BufferList->next != NULL) + BufferList = BufferList->next; + + BufferList->next = BufferListStart; + BufferList->next->prev = BufferList; + } + + // Update number of buffers in queue + Source->BuffersInQueue += n; + +done: + UnlockContext(Context); +} + + +// Implementation assumes that n is the number of buffers to be removed from the queue and buffers is +// an array of buffer IDs that are to be filled with the names of the buffers removed +AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers( ALuint source, ALsizei n, ALuint* buffers ) +{ + ALCcontext *Context; + ALsource *Source; + ALsizei i; + ALbufferlistitem *BufferList; + + if(n == 0) + return; + + Context = GetLockedContext(); + if(!Context) return; + + if(n < 0) + { + alSetError(Context, AL_INVALID_VALUE); + goto done; + } + + if((Source=LookupSource(Context->SourceMap, source)) == NULL) + { + alSetError(Context, AL_INVALID_NAME); + goto done; + } + + if(Source->bLooping || Source->lSourceType != AL_STREAMING || + (ALuint)n > Source->BuffersPlayed) + { + // Some buffers can't be unqueue because they have not been processed + alSetError(Context, AL_INVALID_VALUE); + goto done; + } + + for(i = 0;i < n;i++) + { + BufferList = Source->queue; + Source->queue = BufferList->next; + + if(BufferList->buffer) + { + // Record name of buffer + buffers[i] = BufferList->buffer->buffer; + // Decrement buffer reference counter + BufferList->buffer->refcount--; + } + else + buffers[i] = 0; + + // Release memory for buffer list item + free(BufferList); + Source->BuffersInQueue--; + } + if(Source->queue) + Source->queue->prev = NULL; + + if(Source->state != AL_PLAYING) + { + if(Source->queue) + Source->Buffer = Source->queue->buffer; + else + Source->Buffer = NULL; + } + Source->BuffersPlayed -= n; + +done: + UnlockContext(Context); +} + + +static ALvoid InitSourceParams(ALsource *Source) +{ + Source->flInnerAngle = 360.0f; + Source->flOuterAngle = 360.0f; + Source->flPitch = 1.0f; + Source->vPosition[0] = 0.0f; + Source->vPosition[1] = 0.0f; + Source->vPosition[2] = 0.0f; + Source->vOrientation[0] = 0.0f; + Source->vOrientation[1] = 0.0f; + Source->vOrientation[2] = 0.0f; + Source->vVelocity[0] = 0.0f; + Source->vVelocity[1] = 0.0f; + Source->vVelocity[2] = 0.0f; + Source->flRefDistance = 1.0f; + Source->flMaxDistance = FLT_MAX; + Source->flRollOffFactor = 1.0f; + Source->bLooping = AL_FALSE; + Source->flGain = 1.0f; + Source->flMinGain = 0.0f; + Source->flMaxGain = 1.0f; + Source->flOuterGain = 0.0f; + Source->OuterGainHF = 1.0f; + + Source->DryGainHFAuto = AL_TRUE; + Source->WetGainAuto = AL_TRUE; + Source->WetGainHFAuto = AL_TRUE; + Source->AirAbsorptionFactor = 0.0f; + Source->RoomRolloffFactor = 0.0f; + Source->DopplerFactor = 1.0f; + Source->VirtualChannels = AL_TRUE; + + Source->DistanceModel = AL_INVERSE_DISTANCE_CLAMPED; + + Source->Resampler = DefaultResampler; + + Source->state = AL_INITIAL; + Source->new_state = AL_NONE; + Source->lSourceType = AL_UNDETERMINED; + Source->lOffset = -1; + + Source->NeedsUpdate = AL_TRUE; + + Source->Buffer = NULL; + + Source->HrtfMoving = AL_FALSE; + Source->HrtfCounter = 0; +} + + +/* + * SetSourceState + * + * Sets the source's new play state given its current state + */ +ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state) +{ + if(state == AL_PLAYING) + { + ALbufferlistitem *BufferList; + ALsizei j, k; + + /* Check that there is a queue containing at least one non-null, non zero length AL Buffer */ + BufferList = Source->queue; + while(BufferList) + { + if(BufferList->buffer != NULL && BufferList->buffer->size) + break; + BufferList = BufferList->next; + } + + /* If there's nothing to play, or device is disconnected, go right to + * stopped */ + if(!BufferList || !Context->Device->Connected) + { + SetSourceState(Source, Context, AL_STOPPED); + return; + } + + if(Source->state != AL_PLAYING) + { + for(j = 0;j < MAXCHANNELS;j++) + { + for(k = 0;k < SRC_HISTORY_LENGTH;k++) + Source->HrtfHistory[j][k] = 0.0f; + for(k = 0;k < HRIR_LENGTH;k++) + { + Source->HrtfValues[j][k][0] = 0.0f; + Source->HrtfValues[j][k][1] = 0.0f; + } + } + } + + if(Source->state != AL_PAUSED) + { + Source->state = AL_PLAYING; + Source->position = 0; + Source->position_fraction = 0; + Source->BuffersPlayed = 0; + + Source->Buffer = Source->queue->buffer; + } + else + Source->state = AL_PLAYING; + + // Check if an Offset has been set + if(Source->lOffset != -1) + ApplyOffset(Source); + + for(j = 0;j < Context->ActiveSourceCount;j++) + { + if(Context->ActiveSources[j] == Source) + break; + } + if(j == Context->ActiveSourceCount) + Context->ActiveSources[Context->ActiveSourceCount++] = Source; + } + else if(state == AL_PAUSED) + { + if(Source->state == AL_PLAYING) + { + Source->state = AL_PAUSED; + Source->HrtfMoving = AL_FALSE; + Source->HrtfCounter = 0; + } + } + else if(state == AL_STOPPED) + { + if(Source->state != AL_INITIAL) + { + Source->state = AL_STOPPED; + Source->BuffersPlayed = Source->BuffersInQueue; + Source->HrtfMoving = AL_FALSE; + Source->HrtfCounter = 0; + } + Source->lOffset = -1; + } + else if(state == AL_INITIAL) + { + if(Source->state != AL_INITIAL) + { + Source->state = AL_INITIAL; + Source->position = 0; + Source->position_fraction = 0; + Source->BuffersPlayed = 0; + if(Source->queue) + Source->Buffer = Source->queue->buffer; + Source->HrtfMoving = AL_FALSE; + Source->HrtfCounter = 0; + } + Source->lOffset = -1; + } +} + +/* + GetSourceOffset + + Gets the current playback position in the given Source, in the appropriate format (Bytes, Samples or MilliSeconds) + The offset is relative to the start of the queue (not the start of the current buffer) +*/ +static ALvoid GetSourceOffset(ALsource *Source, ALenum name, ALdouble *offset, ALdouble updateLen) +{ + const ALbufferlistitem *BufferList; + const ALbuffer *Buffer = NULL; + enum UserFmtType OriginalType; + ALsizei BufferFreq; + ALint Channels, Bytes; + ALuint readPos, writePos; + ALuint TotalBufferDataSize; + ALuint i; + + // Find the first non-NULL Buffer in the Queue + BufferList = Source->queue; + while(BufferList) + { + if(BufferList->buffer) + { + Buffer = BufferList->buffer; + break; + } + BufferList = BufferList->next; + } + + if((Source->state != AL_PLAYING && Source->state != AL_PAUSED) || !Buffer) + { + offset[0] = 0.0; + offset[1] = 0.0; + return; + } + + // Get Current Buffer Size and frequency (in milliseconds) + BufferFreq = Buffer->Frequency; + OriginalType = Buffer->OriginalType; + Channels = ChannelsFromFmt(Buffer->FmtChannels); + Bytes = BytesFromFmt(Buffer->FmtType); + + // Get Current BytesPlayed (NOTE : This is the byte offset into the *current* buffer) + readPos = Source->position * Channels * Bytes; + // Add byte length of any processed buffers in the queue + TotalBufferDataSize = 0; + BufferList = Source->queue; + for(i = 0;BufferList;i++) + { + if(BufferList->buffer) + { + if(i < Source->BuffersPlayed) + readPos += BufferList->buffer->size; + TotalBufferDataSize += BufferList->buffer->size; + } + BufferList = BufferList->next; + } + if(Source->state == AL_PLAYING) + writePos = readPos + ((ALuint)(updateLen*BufferFreq) * Channels * Bytes); + else + writePos = readPos; + + if(Source->bLooping) + { + readPos %= TotalBufferDataSize; + writePos %= TotalBufferDataSize; + } + else + { + // Wrap positions back to 0 + if(readPos >= TotalBufferDataSize) + readPos = 0; + if(writePos >= TotalBufferDataSize) + writePos = 0; + } + + switch(name) + { + case AL_SEC_OFFSET: + offset[0] = (ALdouble)readPos / (Channels * Bytes * BufferFreq); + offset[1] = (ALdouble)writePos / (Channels * Bytes * BufferFreq); + break; + case AL_SAMPLE_OFFSET: + case AL_SAMPLE_RW_OFFSETS_SOFT: + offset[0] = (ALdouble)(readPos / (Channels * Bytes)); + offset[1] = (ALdouble)(writePos / (Channels * Bytes)); + break; + case AL_BYTE_OFFSET: + case AL_BYTE_RW_OFFSETS_SOFT: + // Take into account the original format of the Buffer + if(OriginalType == UserFmtIMA4) + { + ALuint FrameBlockSize = 65 * Bytes * Channels; + ALuint BlockSize = 36 * Channels; + + // Round down to nearest ADPCM block + offset[0] = (ALdouble)(readPos / FrameBlockSize * BlockSize); + if(Source->state != AL_PLAYING) + offset[1] = offset[0]; + else + { + // Round up to nearest ADPCM block + offset[1] = (ALdouble)((writePos+FrameBlockSize-1) / + FrameBlockSize * BlockSize); + } + } + else + { + ALuint OrigBytes = BytesFromUserFmt(OriginalType); + offset[0] = (ALdouble)(readPos / Bytes * OrigBytes); + offset[1] = (ALdouble)(writePos / Bytes * OrigBytes); + } + break; + } +} + + +/* + ApplyOffset + + Apply a playback offset to the Source. This function will update the queue (to correctly + mark buffers as 'pending' or 'processed' depending upon the new offset. +*/ +ALboolean ApplyOffset(ALsource *Source) +{ + const ALbufferlistitem *BufferList; + const ALbuffer *Buffer; + ALint lBufferSize, lTotalBufferSize; + ALint BuffersPlayed; + ALint lByteOffset; + + // Get true byte offset + lByteOffset = GetByteOffset(Source); + + // If the offset is invalid, don't apply it + if(lByteOffset == -1) + return AL_FALSE; + + // Sort out the queue (pending and processed states) + BufferList = Source->queue; + lTotalBufferSize = 0; + BuffersPlayed = 0; + + while(BufferList) + { + Buffer = BufferList->buffer; + lBufferSize = Buffer ? Buffer->size : 0; + + if(lBufferSize <= lByteOffset-lTotalBufferSize) + { + // Offset is past this buffer so increment BuffersPlayed + BuffersPlayed++; + } + else if(lTotalBufferSize <= lByteOffset) + { + // Offset is within this buffer + // Set Current Buffer + Source->Buffer = BufferList->buffer; + Source->BuffersPlayed = BuffersPlayed; + + // SW Mixer Positions are in Samples + Source->position = (lByteOffset - lTotalBufferSize) / + FrameSizeFromFmt(Buffer->FmtChannels, Buffer->FmtType); + return AL_TRUE; + } + + // Increment the TotalBufferSize + lTotalBufferSize += lBufferSize; + + // Move on to next buffer in the Queue + BufferList = BufferList->next; + } + // Offset is out of range of the buffer queue + return AL_FALSE; +} + + +/* + GetByteOffset + + Returns the 'true' byte offset into the Source's queue (from the Sample, Byte or Millisecond + offset supplied by the application). This takes into account the fact that the buffer format + may have been modifed by AL (e.g 8bit samples are converted to float) +*/ +static ALint GetByteOffset(ALsource *Source) +{ + const ALbuffer *Buffer = NULL; + const ALbufferlistitem *BufferList; + ALint ByteOffset = -1; + + // Find the first non-NULL Buffer in the Queue + BufferList = Source->queue; + while(BufferList) + { + if(BufferList->buffer) + { + Buffer = BufferList->buffer; + break; + } + BufferList = BufferList->next; + } + + if(!Buffer) + { + Source->lOffset = -1; + return -1; + } + + // Determine the ByteOffset (and ensure it is block aligned) + switch(Source->lOffsetType) + { + case AL_BYTE_OFFSET: + // Take into consideration the original format + ByteOffset = Source->lOffset; + if(Buffer->OriginalType == UserFmtIMA4) + { + // Round down to nearest ADPCM block + ByteOffset /= 36 * ChannelsFromUserFmt(Buffer->OriginalChannels); + // Multiply by compression rate (65 sample frames per block) + ByteOffset *= 65; + } + else + ByteOffset /= FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType); + ByteOffset *= FrameSizeFromFmt(Buffer->FmtChannels, Buffer->FmtType); + break; + + case AL_SAMPLE_OFFSET: + ByteOffset = Source->lOffset * FrameSizeFromFmt(Buffer->FmtChannels, Buffer->FmtType); + break; + + case AL_SEC_OFFSET: + // Note - lOffset is internally stored as Milliseconds + ByteOffset = (ALint)(Source->lOffset / 1000.0 * Buffer->Frequency); + ByteOffset *= FrameSizeFromFmt(Buffer->FmtChannels, Buffer->FmtType); + break; + } + // Clear Offset + Source->lOffset = -1; + + return ByteOffset; +} + + +ALvoid ReleaseALSources(ALCcontext *Context) +{ + ALsizei pos; + ALuint j; + for(pos = 0;pos < Context->SourceMap.size;pos++) + { + ALsource *temp = Context->SourceMap.array[pos].value; + Context->SourceMap.array[pos].value = NULL; + + // For each buffer in the source's queue, decrement its reference counter and remove it + while(temp->queue != NULL) + { + ALbufferlistitem *BufferList = temp->queue; + temp->queue = BufferList->next; + + if(BufferList->buffer != NULL) + BufferList->buffer->refcount--; + free(BufferList); + } + + for(j = 0;j < MAX_SENDS;++j) + { + if(temp->Send[j].Slot) + temp->Send[j].Slot->refcount--; + temp->Send[j].Slot = NULL; + } + + // Release source structure + FreeThunkEntry(temp->source); + memset(temp, 0, sizeof(ALsource)); + free(temp); + } +} diff -r 000000000000 -r f9476ff7637e OpenAL32/alState.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/alState.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,658 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 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 +#include "alMain.h" +#include "AL/alc.h" +#include "AL/alext.h" +#include "alError.h" +#include "alSource.h" +#include "alAuxEffectSlot.h" +#include "alState.h" + +static const ALchar alVendor[] = "OpenAL Community"; +static const ALchar alVersion[] = "1.1 ALSOFT "ALSOFT_VERSION; +static const ALchar alRenderer[] = "OpenAL Soft"; + +// Error Messages +static const ALchar alNoError[] = "No Error"; +static const ALchar alErrInvalidName[] = "Invalid Name"; +static const ALchar alErrInvalidEnum[] = "Invalid Enum"; +static const ALchar alErrInvalidValue[] = "Invalid Value"; +static const ALchar alErrInvalidOp[] = "Invalid Operation"; +static const ALchar alErrOutOfMemory[] = "Out of Memory"; + +AL_API ALvoid AL_APIENTRY alEnable(ALenum capability) +{ + ALCcontext *Context; + + Context = GetLockedContext(); + if(!Context) return; + + switch(capability) + { + case AL_SOURCE_DISTANCE_MODEL: + Context->SourceDistanceModel = AL_TRUE; + Context->UpdateSources = AL_TRUE; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alDisable(ALenum capability) +{ + ALCcontext *Context; + + Context = GetLockedContext(); + if(!Context) return; + + switch(capability) + { + case AL_SOURCE_DISTANCE_MODEL: + Context->SourceDistanceModel = AL_FALSE; + Context->UpdateSources = AL_TRUE; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + + UnlockContext(Context); +} + +AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability) +{ + ALCcontext *Context; + ALboolean value=AL_FALSE; + + Context = GetLockedContext(); + if(!Context) return AL_FALSE; + + switch(capability) + { + case AL_SOURCE_DISTANCE_MODEL: + value = Context->SourceDistanceModel; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + + UnlockContext(Context); + + return value; +} + +AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname) +{ + ALCcontext *Context; + ALboolean value=AL_FALSE; + + Context = GetLockedContext(); + if(!Context) return AL_FALSE; + + switch(pname) + { + case AL_DOPPLER_FACTOR: + if(Context->DopplerFactor != 0.0f) + value = AL_TRUE; + break; + + case AL_DOPPLER_VELOCITY: + if(Context->DopplerVelocity != 0.0f) + value = AL_TRUE; + break; + + case AL_DISTANCE_MODEL: + if(Context->DistanceModel == AL_INVERSE_DISTANCE_CLAMPED) + value = AL_TRUE; + break; + + case AL_SPEED_OF_SOUND: + if(Context->flSpeedOfSound != 0.0f) + value = AL_TRUE; + break; + + case AL_DEFERRED_UPDATES_SOFT: + value = Context->DeferUpdates; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + + UnlockContext(Context); + + return value; +} + +AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname) +{ + ALCcontext *Context; + ALdouble value = 0.0; + + Context = GetLockedContext(); + if(!Context) return 0.0; + + switch(pname) + { + case AL_DOPPLER_FACTOR: + value = (double)Context->DopplerFactor; + break; + + case AL_DOPPLER_VELOCITY: + value = (double)Context->DopplerVelocity; + break; + + case AL_DISTANCE_MODEL: + value = (double)Context->DistanceModel; + break; + + case AL_SPEED_OF_SOUND: + value = (double)Context->flSpeedOfSound; + break; + + case AL_DEFERRED_UPDATES_SOFT: + value = (ALdouble)Context->DeferUpdates; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + + UnlockContext(Context); + + return value; +} + +AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname) +{ + ALCcontext *Context; + ALfloat value = 0.0f; + + Context = GetLockedContext(); + if(!Context) return 0.0f; + + switch(pname) + { + case AL_DOPPLER_FACTOR: + value = Context->DopplerFactor; + break; + + case AL_DOPPLER_VELOCITY: + value = Context->DopplerVelocity; + break; + + case AL_DISTANCE_MODEL: + value = (float)Context->DistanceModel; + break; + + case AL_SPEED_OF_SOUND: + value = Context->flSpeedOfSound; + break; + + case AL_DEFERRED_UPDATES_SOFT: + value = (ALfloat)Context->DeferUpdates; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + + UnlockContext(Context); + + return value; +} + +AL_API ALint AL_APIENTRY alGetInteger(ALenum pname) +{ + ALCcontext *Context; + ALint value = 0; + + Context = GetLockedContext(); + if(!Context) return 0; + + switch(pname) + { + case AL_DOPPLER_FACTOR: + value = (ALint)Context->DopplerFactor; + break; + + case AL_DOPPLER_VELOCITY: + value = (ALint)Context->DopplerVelocity; + break; + + case AL_DISTANCE_MODEL: + value = (ALint)Context->DistanceModel; + break; + + case AL_SPEED_OF_SOUND: + value = (ALint)Context->flSpeedOfSound; + break; + + case AL_DEFERRED_UPDATES_SOFT: + value = (ALint)Context->DeferUpdates; + break; + + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + + UnlockContext(Context); + + return value; +} + +AL_API ALvoid AL_APIENTRY alGetBooleanv(ALenum pname,ALboolean *data) +{ + ALCcontext *Context; + + if(data) + { + switch(pname) + { + case AL_DOPPLER_FACTOR: + case AL_DOPPLER_VELOCITY: + case AL_DISTANCE_MODEL: + case AL_SPEED_OF_SOUND: + case AL_DEFERRED_UPDATES_SOFT: + *data = alGetBoolean(pname); + return; + } + } + + Context = GetLockedContext(); + if(!Context) return; + + if(data) + { + switch(pname) + { + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + { + // data is a NULL pointer + alSetError(Context, AL_INVALID_VALUE); + } + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetDoublev(ALenum pname,ALdouble *data) +{ + ALCcontext *Context; + + if(data) + { + switch(pname) + { + case AL_DOPPLER_FACTOR: + case AL_DOPPLER_VELOCITY: + case AL_DISTANCE_MODEL: + case AL_SPEED_OF_SOUND: + case AL_DEFERRED_UPDATES_SOFT: + *data = alGetDouble(pname); + return; + } + } + + Context = GetLockedContext(); + if(!Context) return; + + if(data) + { + switch(pname) + { + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + { + // data is a NULL pointer + alSetError(Context, AL_INVALID_VALUE); + } + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetFloatv(ALenum pname,ALfloat *data) +{ + ALCcontext *Context; + + if(data) + { + switch(pname) + { + case AL_DOPPLER_FACTOR: + case AL_DOPPLER_VELOCITY: + case AL_DISTANCE_MODEL: + case AL_SPEED_OF_SOUND: + case AL_DEFERRED_UPDATES_SOFT: + *data = alGetFloat(pname); + return; + } + } + + Context = GetLockedContext(); + if(!Context) return; + + if(data) + { + switch(pname) + { + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + { + // data is a NULL pointer + alSetError(Context, AL_INVALID_VALUE); + } + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alGetIntegerv(ALenum pname,ALint *data) +{ + ALCcontext *Context; + + if(data) + { + switch(pname) + { + case AL_DOPPLER_FACTOR: + case AL_DOPPLER_VELOCITY: + case AL_DISTANCE_MODEL: + case AL_SPEED_OF_SOUND: + case AL_DEFERRED_UPDATES_SOFT: + *data = alGetInteger(pname); + return; + } + } + + Context = GetLockedContext(); + if(!Context) return; + + if(data) + { + switch(pname) + { + default: + alSetError(Context, AL_INVALID_ENUM); + break; + } + } + else + { + // data is a NULL pointer + alSetError(Context, AL_INVALID_VALUE); + } + + UnlockContext(Context); +} + +AL_API const ALchar* AL_APIENTRY alGetString(ALenum pname) +{ + const ALchar *value; + ALCcontext *pContext; + + pContext = GetLockedContext(); + if(!pContext) return NULL; + + switch(pname) + { + case AL_VENDOR: + value=alVendor; + break; + + case AL_VERSION: + value=alVersion; + break; + + case AL_RENDERER: + value=alRenderer; + break; + + case AL_EXTENSIONS: + value=pContext->ExtensionList;//alExtensions; + break; + + case AL_NO_ERROR: + value=alNoError; + break; + + case AL_INVALID_NAME: + value=alErrInvalidName; + break; + + case AL_INVALID_ENUM: + value=alErrInvalidEnum; + break; + + case AL_INVALID_VALUE: + value=alErrInvalidValue; + break; + + case AL_INVALID_OPERATION: + value=alErrInvalidOp; + break; + + case AL_OUT_OF_MEMORY: + value=alErrOutOfMemory; + break; + + default: + value=NULL; + alSetError(pContext, AL_INVALID_ENUM); + break; + } + + UnlockContext(pContext); + + return value; +} + +AL_API ALvoid AL_APIENTRY alDopplerFactor(ALfloat value) +{ + ALCcontext *Context; + + Context = GetLockedContext(); + if(!Context) return; + + if(value >= 0.0f && isfinite(value)) + { + Context->DopplerFactor = value; + Context->UpdateSources = AL_TRUE; + } + else + alSetError(Context, AL_INVALID_VALUE); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alDopplerVelocity(ALfloat value) +{ + ALCcontext *Context; + + Context = GetLockedContext(); + if(!Context) return; + + if(value > 0.0f && isfinite(value)) + { + Context->DopplerVelocity=value; + Context->UpdateSources = AL_TRUE; + } + else + alSetError(Context, AL_INVALID_VALUE); + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alSpeedOfSound(ALfloat flSpeedOfSound) +{ + ALCcontext *pContext; + + pContext = GetLockedContext(); + if(!pContext) return; + + if(flSpeedOfSound > 0.0f && isfinite(flSpeedOfSound)) + { + pContext->flSpeedOfSound = flSpeedOfSound; + pContext->UpdateSources = AL_TRUE; + } + else + alSetError(pContext, AL_INVALID_VALUE); + + UnlockContext(pContext); +} + +AL_API ALvoid AL_APIENTRY alDistanceModel(ALenum value) +{ + ALCcontext *Context; + + Context = GetLockedContext(); + if(!Context) return; + + switch(value) + { + case AL_NONE: + case AL_INVERSE_DISTANCE: + case AL_INVERSE_DISTANCE_CLAMPED: + case AL_LINEAR_DISTANCE: + case AL_LINEAR_DISTANCE_CLAMPED: + case AL_EXPONENT_DISTANCE: + case AL_EXPONENT_DISTANCE_CLAMPED: + Context->DistanceModel = value; + Context->UpdateSources = AL_TRUE; + break; + + default: + alSetError(Context, AL_INVALID_VALUE); + break; + } + + UnlockContext(Context); +} + + +AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void) +{ + ALCcontext *Context; + + Context = GetLockedContext(); + if(!Context) return; + + if(!Context->DeferUpdates) + { + ALboolean UpdateSources; + ALsource **src, **src_end; + ALeffectslot *ALEffectSlot; + ALsizei e; + + Context->DeferUpdates = AL_TRUE; + + /* Make sure all pending updates are performed */ + UpdateSources = Context->UpdateSources; + Context->UpdateSources = AL_FALSE; + + src = Context->ActiveSources; + src_end = src + Context->ActiveSourceCount; + while(src != src_end) + { + if((*src)->state != AL_PLAYING) + { + Context->ActiveSourceCount--; + *src = *(--src_end); + continue; + } + + if((*src)->NeedsUpdate || UpdateSources) + { + (*src)->NeedsUpdate = AL_FALSE; + ALsource_Update(*src, Context); + } + src++; + } + + for(e = 0;e < Context->EffectSlotMap.size;e++) + { + ALEffectSlot = Context->EffectSlotMap.array[e].value; + if(ALEffectSlot->NeedsUpdate) + { + ALEffectSlot->NeedsUpdate = AL_FALSE; + ALEffect_Update(ALEffectSlot->EffectState, Context, ALEffectSlot); + } + } + } + + UnlockContext(Context); +} + +AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void) +{ + ALCcontext *Context; + + Context = GetLockedContext(); + if(!Context) return; + + if(Context->DeferUpdates) + { + ALsizei pos; + + Context->DeferUpdates = AL_FALSE; + + for(pos = 0;pos < Context->SourceMap.size;pos++) + { + ALsource *Source = Context->SourceMap.array[pos].value; + ALenum new_state; + + if(Source->lOffset != -1) + ApplyOffset(Source); + + new_state = Source->new_state; + Source->new_state = AL_NONE; + if(new_state) + SetSourceState(Source, Context, new_state); + } + } + + UnlockContext(Context); +} diff -r 000000000000 -r f9476ff7637e OpenAL32/alThunk.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenAL32/alThunk.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,93 @@ +/** + * 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 + +#include "alMain.h" +#include "alThunk.h" + + +static ALboolean *ThunkArray; +static ALuint ThunkArraySize; + +static CRITICAL_SECTION ThunkLock; + +void ThunkInit(void) +{ + InitializeCriticalSection(&ThunkLock); + ThunkArraySize = 1; + ThunkArray = calloc(1, ThunkArraySize * sizeof(*ThunkArray)); +} + +void ThunkExit(void) +{ + free(ThunkArray); + ThunkArray = NULL; + ThunkArraySize = 0; + DeleteCriticalSection(&ThunkLock); +} + +ALenum NewThunkEntry(ALuint *index) +{ + ALuint i; + + EnterCriticalSection(&ThunkLock); + + for(i = 0;i < ThunkArraySize;i++) + { + if(ThunkArray[i] == AL_FALSE) + break; + } + + if(i == ThunkArraySize) + { + ALboolean *NewList; + + NewList = realloc(ThunkArray, ThunkArraySize*2 * sizeof(*ThunkArray)); + if(!NewList) + { + LeaveCriticalSection(&ThunkLock); + ERR("Realloc failed to increase to %u enties!\n", ThunkArraySize*2); + return AL_OUT_OF_MEMORY; + } + memset(&NewList[ThunkArraySize], 0, ThunkArraySize*sizeof(*ThunkArray)); + ThunkArraySize *= 2; + ThunkArray = NewList; + } + + ThunkArray[i] = AL_TRUE; + *index = i+1; + + LeaveCriticalSection(&ThunkLock); + + return AL_NO_ERROR; +} + +void FreeThunkEntry(ALuint index) +{ + EnterCriticalSection(&ThunkLock); + + if(index > 0 && index <= ThunkArraySize) + ThunkArray[index-1] = AL_FALSE; + + LeaveCriticalSection(&ThunkLock); +} diff -r 000000000000 -r f9476ff7637e README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,53 @@ +Source Install +============== + +To install OpenAL Soft, use your favorite shell to go into the build/ +directory, and run: + +cmake .. + +Assuming configuration went well, you can then build it, typically using GNU +Make (KDevelop, MSVC, and others are possible depending on your system setup +and CMake configuration). + +Please Note: Double check that the appropriate backends were detected. Often, +complaints of no sound, crashing, and missing devices can be solved by making +sure the correct backends are being used. CMake's output will identify which +backends were enabled. + +For most systems, you will likely want to make sure ALSA, OSS, and PulseAudio +were detected (if your target system uses them). For Windows, make sure +DirectSound was detected. + + +Utilities +========= + +The source package comes with an informational utility, openal-info, and is +built by default. It prints out information provided by the ALC and AL sub- +systems, including discovered devices, version information, and extensions. + + +Configuration +============= + +OpenAL Soft can be configured on a per-user and per-system basis. This allows +users and sysadmins to control information provided to applications, as well +as application-agnostic behavior of the library. See alsoftrc.sample for +available settings. + + +Acknowledgements +================ + +Special thanks go to: + +Creative Labs for the original source code this is based off of. + +Christopher Fitzgerald for the current reverb effect implementation, and +helping with the low-pass filter. + +Christian Borss for the 3D panning code the current implementation is heavilly +based on. + +Ben Davis for the idea behind the current click-removal code. diff -r 000000000000 -r f9476ff7637e RecordAudioRenderer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RecordAudioRenderer.java Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,1183 @@ +package com.jme3.capture; + +import static org.lwjgl.openal.AL10.AL_BUFFER; +import static org.lwjgl.openal.AL10.AL_BUFFERS_PROCESSED; +import static org.lwjgl.openal.AL10.AL_CONE_INNER_ANGLE; +import static org.lwjgl.openal.AL10.AL_CONE_OUTER_ANGLE; +import static org.lwjgl.openal.AL10.AL_CONE_OUTER_GAIN; +import static org.lwjgl.openal.AL10.AL_DIRECTION; +import static org.lwjgl.openal.AL10.AL_FALSE; +import static org.lwjgl.openal.AL10.AL_FORMAT_MONO16; +import static org.lwjgl.openal.AL10.AL_FORMAT_MONO8; +import static org.lwjgl.openal.AL10.AL_FORMAT_STEREO16; +import static org.lwjgl.openal.AL10.AL_FORMAT_STEREO8; +import static org.lwjgl.openal.AL10.AL_GAIN; +import static org.lwjgl.openal.AL10.AL_LOOPING; +import static org.lwjgl.openal.AL10.AL_MAX_DISTANCE; +import static org.lwjgl.openal.AL10.AL_ORIENTATION; +import static org.lwjgl.openal.AL10.AL_PAUSED; +import static org.lwjgl.openal.AL10.AL_PITCH; +import static org.lwjgl.openal.AL10.AL_POSITION; +import static org.lwjgl.openal.AL10.AL_REFERENCE_DISTANCE; +import static org.lwjgl.openal.AL10.AL_RENDERER; +import static org.lwjgl.openal.AL10.AL_SOURCE_RELATIVE; +import static org.lwjgl.openal.AL10.AL_SOURCE_STATE; +import static org.lwjgl.openal.AL10.AL_STOPPED; +import static org.lwjgl.openal.AL10.AL_TRUE; +import static org.lwjgl.openal.AL10.AL_VELOCITY; +import static org.lwjgl.openal.AL10.AL_VENDOR; +import static org.lwjgl.openal.AL10.AL_VERSION; +import static org.lwjgl.openal.AL10.alBufferData; +import static org.lwjgl.openal.AL10.alDeleteBuffers; +import static org.lwjgl.openal.AL10.alDeleteSources; +import static org.lwjgl.openal.AL10.alGenBuffers; +import static org.lwjgl.openal.AL10.alGenSources; +import static org.lwjgl.openal.AL10.alGetError; +import static org.lwjgl.openal.AL10.alGetSourcei; +import static org.lwjgl.openal.AL10.alGetString; +import static org.lwjgl.openal.AL10.alListener; +import static org.lwjgl.openal.AL10.alListener3f; +import static org.lwjgl.openal.AL10.alListenerf; +import static org.lwjgl.openal.AL10.alSource3f; +import static org.lwjgl.openal.AL10.alSourcePause; +import static org.lwjgl.openal.AL10.alSourcePlay; +import static org.lwjgl.openal.AL10.alSourceQueueBuffers; +import static org.lwjgl.openal.AL10.alSourceStop; +import static org.lwjgl.openal.AL10.alSourceUnqueueBuffers; +import static org.lwjgl.openal.AL10.alSourcef; +import static org.lwjgl.openal.AL10.alSourcei; + +import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.Vector; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.lwjgl.LWJGLException; +import org.lwjgl.openal.AL; +import org.lwjgl.openal.AL11; +import org.lwjgl.openal.ALC10; +import org.lwjgl.openal.ALCdevice; +import org.lwjgl.openal.EFX10; +import org.lwjgl.openal.OpenALException; + +import com.jme3.audio.AudioBuffer; +import com.jme3.audio.AudioData; +import com.jme3.audio.AudioNode; +import com.jme3.audio.AudioNode.Status; +import com.jme3.audio.AudioParam; +import com.jme3.audio.AudioRenderer; +import com.jme3.audio.AudioStream; +import com.jme3.audio.Environment; +import com.jme3.audio.Filter; +import com.jme3.audio.Listener; +import com.jme3.audio.ListenerParam; +import com.jme3.audio.LowPassFilter; +import com.jme3.math.Vector3f; +import com.jme3.util.BufferUtils; + + + +public class RecordAudioRenderer implements AudioRenderer, Runnable { + + + + public static void getMainSamples(){ + + } + + + private static final Logger logger = Logger.getLogger(RecordAudioRenderer.class.getName()); + + // When multiplied by STREAMING_BUFFER_COUNT, will equal 44100 * 2 * 2 + // which is exactly 1 second of audio. + private static final int BUFFER_SIZE = 35280; + private static final int STREAMING_BUFFER_COUNT = 5; + + private final static int MAX_NUM_CHANNELS = 2; + private IntBuffer ib = BufferUtils.createIntBuffer(1); + private final FloatBuffer fb = BufferUtils.createVector3Buffer(2); + private final ByteBuffer nativeBuf = BufferUtils.createByteBuffer(BUFFER_SIZE); + private final byte[] arrayBuf = new byte[BUFFER_SIZE]; + + private int[] channels; + private AudioNode[] chanSrcs; + private int nextChan = 0; + private ArrayList freeChans = new ArrayList(); + + private Listener listener; + private boolean audioDisabled = false; + + private boolean supportEfx = false; + private int auxSends = 0; + private int reverbFx = -1; + private int reverbFxSlot = -1; + + // RLM: this is to call the native methods which require the OpenAL device ID. + // currently it is obtained through reflection. + private long deviceID; + + // Update audio 20 times per second + private static final float UPDATE_RATE = 0.05f; + + private final Thread audioThread = new Thread(this, "jME3 Audio Thread"); + private final AtomicBoolean threadLock = new AtomicBoolean(false); + + public RecordAudioRenderer(){ + } + + public static native void helloEveryone(); + + + public static native void nstep(long device); + public void step(){ + nstep(this.deviceID); + } + + + + public void getMainSamples(ByteBuffer buffer){ + ngetMainSamples(this.deviceID, buffer, buffer.position()); + } + public static native void ngetMainSamples(long device, ByteBuffer buffer, int position); + + + public void getAuxSamples(ByteBuffer buffer){ + ngetAuxSamples(this.deviceID, buffer, buffer.position()); + } + public static native void ngetAuxSamples(long device, ByteBuffer buffer, int position); + + + + public void initialize(){ + if (!audioThread.isAlive()){ + audioThread.setDaemon(true); + audioThread.setPriority(Thread.NORM_PRIORITY+1); + audioThread.start(); + }else{ + throw new IllegalStateException("Initialize already called"); + } + } + + private void checkDead(){ + if (audioThread.getState() == Thread.State.TERMINATED) + throw new IllegalStateException("Audio thread is terminated"); + } + + public void run(){ + initInThread(); + synchronized (threadLock){ + threadLock.set(true); + threadLock.notifyAll(); + } + + + helloEveryone(); + System.out.println("AudioRecorder: Trying to call native methods."); + System.out.println("our device ID is : " + this.deviceID); + + + + + long updateRateNanos = (long) (UPDATE_RATE * 1000000000); + mainloop: while (true){ + long startTime = System.nanoTime(); + + if (Thread.interrupted()) + break; + + synchronized (threadLock){ + updateInThread(UPDATE_RATE); + } + + long endTime = System.nanoTime(); + long diffTime = endTime - startTime; + + if (diffTime < updateRateNanos){ + long desiredEndTime = startTime + updateRateNanos; + while (System.nanoTime() < desiredEndTime){ + try{ + Thread.sleep(1); + }catch (InterruptedException ex){ + break mainloop; + } + } + } + } + + synchronized (threadLock){ + cleanupInThread(); + } + } + + public void initInThread(){ + try{ + if (!AL.isCreated()){ + AL.create("Aurellem", 44100, 15, false); + } + }catch (OpenALException ex){ + logger.log(Level.SEVERE, "Failed to load audio library", ex); + audioDisabled = true; + return; + }catch (LWJGLException ex){ + logger.log(Level.SEVERE, "Failed to load audio library", ex); + audioDisabled = true; + return; + } + + ALCdevice device = AL.getDevice(); + + // RLM: use reflection to grab the ID of our device for use later. + try { + Field deviceIDField; + deviceIDField = ALCdevice.class.getDeclaredField("device"); + deviceIDField.setAccessible(true); + try {deviceID = (Long)deviceIDField.get(device);} + catch (IllegalArgumentException e) {e.printStackTrace();} + catch (IllegalAccessException e) {e.printStackTrace();} + deviceIDField.setAccessible(false);} + catch (SecurityException e) {e.printStackTrace();} + catch (NoSuchFieldException e) {e.printStackTrace();} + + + + String deviceName = ALC10.alcGetString(device, ALC10.ALC_DEVICE_SPECIFIER); + + logger.log(Level.FINER, "Audio Device: {0}", deviceName); + logger.log(Level.FINER, "Audio Vendor: {0}", alGetString(AL_VENDOR)); + logger.log(Level.FINER, "Audio Renderer: {0}", alGetString(AL_RENDERER)); + logger.log(Level.FINER, "Audio Version: {0}", alGetString(AL_VERSION)); + + // Find maximum # of sources supported by this implementation + // RLM: this may not be wise -- exceeding the number of available channels + // can crash some versions of OpenAL + ArrayList channelList = new ArrayList(); + for (int i = 0; i < MAX_NUM_CHANNELS; i++){ + int chan = alGenSources(); + if (alGetError() != 0){ + break; + }else{ + channelList.add(chan); + } + } + + channels = new int[channelList.size()]; + for (int i = 0; i < channels.length; i++){ + channels[i] = channelList.get(i); + } + + ib = BufferUtils.createIntBuffer(channels.length); + chanSrcs = new AudioNode[channels.length]; + + logger.log(Level.INFO, "AudioRenderer supports {0} channels", channels.length); + + supportEfx = ALC10.alcIsExtensionPresent(device, "ALC_EXT_EFX"); + // RLM: disable this for now. + supportEfx = false; + logger.log(Level.FINER, "Audio EFX support: {0}", supportEfx); + + if (supportEfx){ + ib.position(0).limit(1); + ALC10.alcGetInteger(device, EFX10.ALC_EFX_MAJOR_VERSION, ib); + int major = ib.get(0); + ib.position(0).limit(1); + ALC10.alcGetInteger(device, EFX10.ALC_EFX_MINOR_VERSION, ib); + int minor = ib.get(0); + logger.log(Level.INFO, "Audio effect extension version: {0}.{1}", new Object[]{major, minor}); + + ALC10.alcGetInteger(device, EFX10.ALC_MAX_AUXILIARY_SENDS, ib); + auxSends = ib.get(0); + logger.log(Level.INFO, "Audio max auxilary sends: {0}", auxSends); + + // create slot + ib.position(0).limit(1); + EFX10.alGenAuxiliaryEffectSlots(ib); + reverbFxSlot = ib.get(0); + + // create effect + ib.position(0).limit(1); + EFX10.alGenEffects(ib); + reverbFx = ib.get(0); + EFX10.alEffecti(reverbFx, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_REVERB); + + // attach reverb effect to effect slot +// EFX10.alAuxiliaryEffectSloti(reverbFxSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbFx); + } + } + + public void cleanupInThread(){ + + + if (audioDisabled){ + AL.destroy(); + return; + } + + // delete channel-based sources + ib.clear(); + ib.put(channels); + ib.flip(); + alDeleteSources(ib); + + if (supportEfx){ + ib.position(0).limit(1); + ib.put(0, reverbFx); + EFX10.alDeleteEffects(ib); + + ib.position(0).limit(1); + ib.put(0, reverbFxSlot); + EFX10.alDeleteAuxiliaryEffectSlots(ib); + } + + // XXX: Delete other buffers/sources + AL.destroy(); + } + + public void cleanup(){ + // kill audio thread + + if (audioThread.isAlive()){ + audioThread.interrupt(); + } + + Byte[] data1 = new Byte[this.fullWaveData1.size()]; + data1 = this.fullWaveData1.toArray(data1); + + Byte[] data2 = new Byte[this.fullWaveData2.size()]; + data2 = this.fullWaveData2.toArray(data2); + System.out.println(this.fullWaveData1.size()); + System.out.println("Saving WAVE data!"); + /*for (int i = 0; i < data1.length;i++){ + System.out.print(data1[i]+","); + if (i%32 ==0){System.out.println();} + } + */ + + + StdAudio.save("/home/r/wave-output/data2.wav", data2); + StdAudio.save("/home/r/wave-output/data1.wav", data1); + } + + private void updateFilter(Filter f){ + int id = f.getId(); + if (id == -1){ + ib.position(0).limit(1); + EFX10.alGenFilters(ib); + id = ib.get(0); + f.setId(id); + } + + if (f instanceof LowPassFilter){ + LowPassFilter lpf = (LowPassFilter) f; + EFX10.alFilteri(id, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS); + EFX10.alFilterf(id, EFX10.AL_LOWPASS_GAIN, lpf.getVolume()); + EFX10.alFilterf(id, EFX10.AL_LOWPASS_GAINHF, lpf.getHighFreqVolume()); + }else{ + throw new UnsupportedOperationException("Filter type unsupported: "+ + f.getClass().getName()); + } + + f.clearUpdateNeeded(); + } + + public void updateSourceParam(AudioNode src, AudioParam param){ + checkDead(); + synchronized (threadLock){ + while (!threadLock.get()){ + try { + threadLock.wait(); + } catch (InterruptedException ex) { + } + } + if (audioDisabled) + return; + + // There is a race condition in AudioNode that can + // cause this to be called for a node that has been + // detached from its channel. For example, setVolume() + // called from the render thread may see that that AudioNode + // still has a channel value but the audio thread may + // clear that channel before setVolume() gets to call + // updateSourceParam() (because the audio stopped playing + // on its own right as the volume was set). In this case, + // it should be safe to just ignore the update + if (src.getChannel() < 0) + return; + + assert src.getChannel() >= 0; + + int id = channels[src.getChannel()]; + switch (param){ + case Position: + if (!src.isPositional()) + return; + + Vector3f pos = src.getWorldTranslation(); + alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); + break; + case Velocity: + if (!src.isPositional()) + return; + + Vector3f vel = src.getVelocity(); + alSource3f(id, AL_VELOCITY, vel.x, vel.y, vel.z); + break; + case MaxDistance: + if (!src.isPositional()) + return; + + alSourcef(id, AL_MAX_DISTANCE, src.getMaxDistance()); + break; + case RefDistance: + if (!src.isPositional()) + return; + + alSourcef(id, AL_REFERENCE_DISTANCE, src.getRefDistance()); + break; + case ReverbFilter: + if (!src.isPositional() || !src.isReverbEnabled()) + return; + + int filter = EFX10.AL_FILTER_NULL; + if (src.getReverbFilter() != null){ + Filter f = src.getReverbFilter(); + if (f.isUpdateNeeded()){ + updateFilter(f); + } + filter = f.getId(); + } + AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, reverbFxSlot, 0, filter); + break; + case ReverbEnabled: + if (!src.isPositional()) + return; + + if (src.isReverbEnabled()){ + updateSourceParam(src, AudioParam.ReverbFilter); + }else{ + AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, 0, 0, EFX10.AL_FILTER_NULL); + } + break; + case IsPositional: + if (!src.isPositional()){ + // play in headspace + alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE); + alSource3f(id, AL_POSITION, 0,0,0); + alSource3f(id, AL_VELOCITY, 0,0,0); + }else{ + alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); + updateSourceParam(src, AudioParam.Position); + updateSourceParam(src, AudioParam.Velocity); + updateSourceParam(src, AudioParam.MaxDistance); + updateSourceParam(src, AudioParam.RefDistance); + updateSourceParam(src, AudioParam.ReverbEnabled); + } + break; + case Direction: + if (!src.isDirectional()) + return; + + Vector3f dir = src.getDirection(); + alSource3f(id, AL_DIRECTION, dir.x, dir.y, dir.z); + break; + case InnerAngle: + if (!src.isDirectional()) + return; + + alSourcef(id, AL_CONE_INNER_ANGLE, src.getInnerAngle()); + break; + case OuterAngle: + if (!src.isDirectional()) + return; + + alSourcef(id, AL_CONE_OUTER_ANGLE, src.getOuterAngle()); + break; + case IsDirectional: + if (src.isDirectional()){ + updateSourceParam(src, AudioParam.Direction); + updateSourceParam(src, AudioParam.InnerAngle); + updateSourceParam(src, AudioParam.OuterAngle); + alSourcef(id, AL_CONE_OUTER_GAIN, 0); + }else{ + alSourcef(id, AL_CONE_INNER_ANGLE, 360); + alSourcef(id, AL_CONE_OUTER_ANGLE, 360); + alSourcef(id, AL_CONE_OUTER_GAIN, 1f); + } + break; + case DryFilter: + if (src.getDryFilter() != null){ + Filter f = src.getDryFilter(); + if (f.isUpdateNeeded()){ + updateFilter(f); + + // NOTE: must re-attach filter for changes to apply. + alSourcei(id, EFX10.AL_DIRECT_FILTER, f.getId()); + } + }else{ + alSourcei(id, EFX10.AL_DIRECT_FILTER, EFX10.AL_FILTER_NULL); + } + break; + case Looping: + if (src.isLooping()){ + if (!(src.getAudioData() instanceof AudioStream)){ + alSourcei(id, AL_LOOPING, AL_TRUE); + } + }else{ + alSourcei(id, AL_LOOPING, AL_FALSE); + } + break; + case Volume: + alSourcef(id, AL_GAIN, src.getVolume()); + break; + case Pitch: + alSourcef(id, AL_PITCH, src.getPitch()); + break; + } + } + } + + private void setSourceParams(int id, AudioNode src, boolean forceNonLoop){ + if (src.isPositional()){ + Vector3f pos = src.getWorldTranslation(); + Vector3f vel = src.getVelocity(); + alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); + alSource3f(id, AL_VELOCITY, vel.x, vel.y, vel.z); + alSourcef(id, AL_MAX_DISTANCE, src.getMaxDistance()); + alSourcef(id, AL_REFERENCE_DISTANCE, src.getRefDistance()); + alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); + + if (src.isReverbEnabled()){ + int filter = EFX10.AL_FILTER_NULL; + if (src.getReverbFilter() != null){ + Filter f = src.getReverbFilter(); + if (f.isUpdateNeeded()){ + updateFilter(f); + } + filter = f.getId(); + } + AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, reverbFxSlot, 0, filter); + } + }else{ + // play in headspace + alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE); + alSource3f(id, AL_POSITION, 0,0,0); + alSource3f(id, AL_VELOCITY, 0,0,0); + } + + if (src.getDryFilter() != null){ + Filter f = src.getDryFilter(); + if (f.isUpdateNeeded()){ + updateFilter(f); + + // NOTE: must re-attach filter for changes to apply. + alSourcei(id, EFX10.AL_DIRECT_FILTER, f.getId()); + } + } + + if (forceNonLoop){ + alSourcei(id, AL_LOOPING, AL_FALSE); + }else{ + alSourcei(id, AL_LOOPING, src.isLooping() ? AL_TRUE : AL_FALSE); + } + alSourcef(id, AL_GAIN, src.getVolume()); + alSourcef(id, AL_PITCH, src.getPitch()); + alSourcef(id, AL11.AL_SEC_OFFSET, src.getTimeOffset()); + + if (src.isDirectional()){ + Vector3f dir = src.getDirection(); + alSource3f(id, AL_DIRECTION, dir.x, dir.y, dir.z); + alSourcef(id, AL_CONE_INNER_ANGLE, src.getInnerAngle()); + alSourcef(id, AL_CONE_OUTER_ANGLE, src.getOuterAngle()); + alSourcef(id, AL_CONE_OUTER_GAIN, 0); + }else{ + alSourcef(id, AL_CONE_INNER_ANGLE, 360); + alSourcef(id, AL_CONE_OUTER_ANGLE, 360); + alSourcef(id, AL_CONE_OUTER_GAIN, 1f); + } + } + + public void updateListenerParam(Listener listener, ListenerParam param){ + checkDead(); + synchronized (threadLock){ + while (!threadLock.get()){ + try { + threadLock.wait(); + } catch (InterruptedException ex) { + } + } + if (audioDisabled) + return; + + switch (param){ + case Position: + Vector3f pos = listener.getLocation(); + alListener3f(AL_POSITION, pos.x, pos.y, pos.z); + break; + case Rotation: + Vector3f dir = listener.getDirection(); + Vector3f up = listener.getUp(); + fb.rewind(); + fb.put(dir.x).put(dir.y).put(dir.z); + fb.put(up.x).put(up.y).put(up.z); + fb.flip(); + alListener(AL_ORIENTATION, fb); + break; + case Velocity: + Vector3f vel = listener.getVelocity(); + alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z); + break; + case Volume: + alListenerf(AL_GAIN, listener.getVolume()); + break; + } + } + } + + private void setListenerParams(Listener listener){ + Vector3f pos = listener.getLocation(); + Vector3f vel = listener.getVelocity(); + Vector3f dir = listener.getDirection(); + Vector3f up = listener.getUp(); + + alListener3f(AL_POSITION, pos.x, pos.y, pos.z); + alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z); + fb.rewind(); + fb.put(dir.x).put(dir.y).put(dir.z); + fb.put(up.x).put(up.y).put(up.z); + fb.flip(); + alListener(AL_ORIENTATION, fb); + alListenerf(AL_GAIN, listener.getVolume()); + } + + private int newChannel(){ + if (freeChans.size() > 0) + return freeChans.remove(0); + else if (nextChan < channels.length){ + return nextChan++; + }else{ + return -1; + } + } + + private void freeChannel(int index){ + if (index == nextChan-1){ + nextChan--; + } else{ + freeChans.add(index); + } + } + + public void setEnvironment(Environment env){ + checkDead(); + synchronized (threadLock){ + while (!threadLock.get()){ + try { + threadLock.wait(); + } catch (InterruptedException ex) { + } + } + if (audioDisabled) + return; + + EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DENSITY, env.getDensity()); + EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DIFFUSION, env.getDiffusion()); + EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_GAIN, env.getGain()); + EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_GAINHF, env.getGainHf()); + EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DECAY_TIME, env.getDecayTime()); + EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DECAY_HFRATIO, env.getDecayHFRatio()); + EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_REFLECTIONS_GAIN, env.getReflectGain()); + EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_REFLECTIONS_DELAY, env.getReflectDelay()); + EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_LATE_REVERB_GAIN, env.getLateReverbGain()); + EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_LATE_REVERB_DELAY, env.getLateReverbDelay()); + EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_AIR_ABSORPTION_GAINHF, env.getAirAbsorbGainHf()); + EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_ROOM_ROLLOFF_FACTOR, env.getRoomRolloffFactor()); + + // attach effect to slot + EFX10.alAuxiliaryEffectSloti(reverbFxSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbFx); + } + } + + private boolean fillBuffer(AudioStream stream, int id){ + int size = 0; + int result; + + while (size < arrayBuf.length){ + result = stream.readSamples(arrayBuf, size, arrayBuf.length - size); + + if(result > 0){ + size += result; + }else{ + break; + } + } + + if(size == 0) + return false; + + nativeBuf.clear(); + nativeBuf.put(arrayBuf, 0, size); + nativeBuf.flip(); + + alBufferData(id, convertFormat(stream), nativeBuf, stream.getSampleRate()); + + return true; + } + + private boolean fillStreamingSource(int sourceId, AudioStream stream){ + if (!stream.isOpen()) + return false; + + boolean active = true; + int processed = alGetSourcei(sourceId, AL_BUFFERS_PROCESSED); + +// while((processed--) != 0){ + if (processed > 0){ + int buffer; + + ib.position(0).limit(1); + alSourceUnqueueBuffers(sourceId, ib); + buffer = ib.get(0); + + active = fillBuffer(stream, buffer); + + ib.position(0).limit(1); + ib.put(0, buffer); + alSourceQueueBuffers(sourceId, ib); + } + + if (!active && stream.isOpen()) + stream.close(); + + return active; + } + + private boolean attachStreamToSource(int sourceId, AudioStream stream){ + boolean active = true; + for (int id : stream.getIds()){ + active = fillBuffer(stream, id); + ib.position(0).limit(1); + ib.put(id).flip(); + alSourceQueueBuffers(sourceId, ib); + } + return active; + } + + private boolean attachBufferToSource(int sourceId, AudioBuffer buffer){ + alSourcei(sourceId, AL_BUFFER, buffer.getId()); + return true; + } + + private boolean attachAudioToSource(int sourceId, AudioData data){ + if (data instanceof AudioBuffer){ + return attachBufferToSource(sourceId, (AudioBuffer) data); + }else if (data instanceof AudioStream){ + return attachStreamToSource(sourceId, (AudioStream) data); + } + throw new UnsupportedOperationException(); + } + + private void clearChannel(int index){ + // make room at this channel + if (chanSrcs[index] != null){ + AudioNode src = chanSrcs[index]; + + int sourceId = channels[index]; + alSourceStop(sourceId); + + if (src.getAudioData() instanceof AudioStream){ + AudioStream str = (AudioStream) src.getAudioData(); + ib.position(0).limit(STREAMING_BUFFER_COUNT); + ib.put(str.getIds()).flip(); + alSourceUnqueueBuffers(sourceId, ib); + }else if (src.getAudioData() instanceof AudioBuffer){ + alSourcei(sourceId, AL_BUFFER, 0); + } + + if (src.getDryFilter() != null){ + // detach filter + alSourcei(sourceId, EFX10.AL_DIRECT_FILTER, EFX10.AL_FILTER_NULL); + } + if (src.isPositional()){ + AudioNode pas = (AudioNode) src; + if (pas.isReverbEnabled()) { + AL11.alSource3i(sourceId, EFX10.AL_AUXILIARY_SEND_FILTER, 0, 0, EFX10.AL_FILTER_NULL); + } + } + + chanSrcs[index] = null; + } + } + + public void update(float tpf){ + //ByteBuffer test = BufferUtils.createByteBuffer(1); + //AurellemTransport.getAuxSamples(AL.getDevice(), test); + } + + Vector fullWaveData1 = new Vector(); + Vector fullWaveData2 = new Vector(); + + public void updateInThread(float tpf){ + if (audioDisabled) + return; + + step(); + ByteBuffer test = BufferUtils.createByteBuffer(4096); + test.clear(); + this.getMainSamples(test); + byte[] waveData = new byte[4096]; + test.get(waveData, 0, 4096); + //System.out.println("J DATA:"); + /*for (int j = 0; j < 1; j++){ + for(int i = 64 * j; i < (64*j) + 64; i++){ + System.out.print(waveData[i]); + } + System.out.println(); + }*/ + + ByteBuffer test2 = BufferUtils.createByteBuffer(4096); + test2.clear(); + this.getAuxSamples(test2); + byte[] waveData2 = new byte[4096]; + test2.get(waveData2, 0, 4096); + //System.out.print("wave1:"); + //for (int j = 0; j< 32; j++){ + // System.out.print(waveData[j]+","); + // } + //System.out.println(); + //System.out.print("wave2:"); + // for (int j = 0; j< 4096; j++){ + // if (waveData2[j] != 0){ + // System.out.println("fucked at : " + j); + // } + + /* System.out.print(waveData2[j]+","); + if (0 == (j % 64)){System.out.println();}*/ + //} + //System.out.println(); + + for (byte b : waveData){ + this.fullWaveData1.add(b); + } + + for (byte b : waveData2){ + this.fullWaveData2.add(b); + } + + + for (int i = 0; i < channels.length; i++){ + AudioNode src = chanSrcs[i]; + if (src == null) + continue; + + int sourceId = channels[i]; + + // is the source bound to this channel + // if false, it's an instanced playback + boolean boundSource = i == src.getChannel(); + + // source's data is streaming + boolean streaming = src.getAudioData() instanceof AudioStream; + + // only buffered sources can be bound + assert (boundSource && streaming) || (!streaming); + + int state = alGetSourcei(sourceId, AL_SOURCE_STATE); + boolean wantPlaying = src.getStatus() == Status.Playing; + boolean stopped = state == AL_STOPPED; + + if (streaming && wantPlaying){ + AudioStream stream = (AudioStream) src.getAudioData(); + if (stream.isOpen()){ + fillStreamingSource(sourceId, stream); + if (stopped) + alSourcePlay(sourceId); + + }else{ + if (stopped){ + // became inactive + src.setStatus(Status.Stopped); + src.setChannel(-1); + clearChannel(i); + freeChannel(i); + + // And free the audio since it cannot be + // played again anyway. + deleteAudioData(stream); + } + } + }else if (!streaming){ + boolean paused = state == AL_PAUSED; + + // make sure OAL pause state & source state coincide + assert (src.getStatus() == Status.Paused && paused) || (!paused); + + if (stopped){ + if (boundSource){ + src.setStatus(Status.Stopped); + src.setChannel(-1); + } + clearChannel(i); + freeChannel(i); + } + } + } + } + + public void setListener(Listener listener) { + checkDead(); + synchronized (threadLock){ + while (!threadLock.get()){ + try { + threadLock.wait(); + } catch (InterruptedException ex) { + } + } + if (audioDisabled) + return; + + if (this.listener != null){ + // previous listener no longer associated with current + // renderer + this.listener.setRenderer(null); + } + + this.listener = listener; + this.listener.setRenderer(this); + setListenerParams(listener); + } + } + + public void playSourceInstance(AudioNode src){ + checkDead(); + synchronized (threadLock){ + while (!threadLock.get()){ + try { + threadLock.wait(); + } catch (InterruptedException ex) { + } + } + if (audioDisabled) + return; + + if (src.getAudioData() instanceof AudioStream) + throw new UnsupportedOperationException( + "Cannot play instances " + + "of audio streams. Use playSource() instead."); + + if (src.getAudioData().isUpdateNeeded()){ + updateAudioData(src.getAudioData()); + } + + // create a new index for an audio-channel + int index = newChannel(); + if (index == -1) + return; + + int sourceId = channels[index]; + + clearChannel(index); + + // set parameters, like position and max distance + setSourceParams(sourceId, src, true); + attachAudioToSource(sourceId, src.getAudioData()); + chanSrcs[index] = src; + + // play the channel + alSourcePlay(sourceId); + } + } + + + public void playSource(AudioNode src) { + checkDead(); + synchronized (threadLock){ + while (!threadLock.get()){ + try { + threadLock.wait(); + } catch (InterruptedException ex) { + } + } + if (audioDisabled) + return; + + //assert src.getStatus() == Status.Stopped || src.getChannel() == -1; + + if (src.getStatus() == Status.Playing){ + return; + }else if (src.getStatus() == Status.Stopped){ + + // allocate channel to this source + int index = newChannel(); + if (index == -1) { + logger.log(Level.WARNING, "No channel available to play {0}", src); + return; + } + clearChannel(index); + src.setChannel(index); + + AudioData data = src.getAudioData(); + if (data.isUpdateNeeded()) + updateAudioData(data); + + chanSrcs[index] = src; + setSourceParams(channels[index], src, false); + attachAudioToSource(channels[index], data); + } + + alSourcePlay(channels[src.getChannel()]); + src.setStatus(Status.Playing); + } + } + + + public void pauseSource(AudioNode src) { + checkDead(); + synchronized (threadLock){ + while (!threadLock.get()){ + try { + threadLock.wait(); + } catch (InterruptedException ex) { + } + } + if (audioDisabled) + return; + + if (src.getStatus() == Status.Playing){ + assert src.getChannel() != -1; + + alSourcePause(channels[src.getChannel()]); + src.setStatus(Status.Paused); + } + } + } + + + public void stopSource(AudioNode src) { + synchronized (threadLock){ + while (!threadLock.get()){ + try { + threadLock.wait(); + } catch (InterruptedException ex) { + } + } + if (audioDisabled) + return; + + if (src.getStatus() != Status.Stopped){ + int chan = src.getChannel(); + assert chan != -1; // if it's not stopped, must have id + + src.setStatus(Status.Stopped); + src.setChannel(-1); + clearChannel(chan); + freeChannel(chan); + + if (src.getAudioData() instanceof AudioStream) { + + AudioStream stream = (AudioStream)src.getAudioData(); + if (stream.isOpen()) { + stream.close(); + } + + // And free the audio since it cannot be + // played again anyway. + deleteAudioData(src.getAudioData()); + } + } + } + } + + private int convertFormat(AudioData ad){ + switch (ad.getBitsPerSample()){ + case 8: + if (ad.getChannels() == 1) + return AL_FORMAT_MONO8; + else if (ad.getChannels() == 2) + return AL_FORMAT_STEREO8; + + break; + case 16: + if (ad.getChannels() == 1) + return AL_FORMAT_MONO16; + else + return AL_FORMAT_STEREO16; + } + throw new UnsupportedOperationException("Unsupported channels/bits combination: "+ + "bits="+ad.getBitsPerSample()+", channels="+ad.getChannels()); + } + + private void updateAudioBuffer(AudioBuffer ab){ + int id = ab.getId(); + if (ab.getId() == -1){ + ib.position(0).limit(1); + alGenBuffers(ib); + id = ib.get(0); + ab.setId(id); + } + + ab.getData().clear(); + alBufferData(id, convertFormat(ab), ab.getData(), ab.getSampleRate()); + ab.clearUpdateNeeded(); + } + + private void updateAudioStream(AudioStream as){ + if (as.getIds() != null){ + deleteAudioData(as); + } + + int[] ids = new int[STREAMING_BUFFER_COUNT]; + ib.position(0).limit(STREAMING_BUFFER_COUNT); + alGenBuffers(ib); + ib.position(0).limit(STREAMING_BUFFER_COUNT); + ib.get(ids); + + as.setIds(ids); + as.clearUpdateNeeded(); + } + + private void updateAudioData(AudioData ad){ + if (ad instanceof AudioBuffer){ + updateAudioBuffer((AudioBuffer) ad); + }else if (ad instanceof AudioStream){ + updateAudioStream((AudioStream) ad); + } + } + + public void deleteAudioData(AudioData ad){ + synchronized (threadLock){ + while (!threadLock.get()){ + try { + threadLock.wait(); + } catch (InterruptedException ex) { + } + } + if (audioDisabled) + return; + + if (ad instanceof AudioBuffer){ + AudioBuffer ab = (AudioBuffer) ad; + int id = ab.getId(); + if (id != -1){ + ib.put(0,id); + ib.position(0).limit(1); + alDeleteBuffers(ib); + ab.resetObject(); + } + }else if (ad instanceof AudioStream){ + AudioStream as = (AudioStream) ad; + int[] ids = as.getIds(); + if (ids != null){ + ib.clear(); + ib.put(ids).flip(); + alDeleteBuffers(ib); + as.resetObject(); + } + } + } + } + +} diff -r 000000000000 -r f9476ff7637e XCompile.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/XCompile.txt Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,30 @@ +# Cross-compiling requires CMake 2.6 or newer. To use it from build/, call it +# like this: +# cmake .. -DCMAKE_TOOLCHAIN_FILE=../XCompile.txt -DHOST=i686-pc-mingw32 +# Where 'i686-pc-mingw32' is the host prefix for your cross-compiler. If you +# already have a toolchain file setup, you may use that instead of this file. + +# the name of the target operating system +SET(CMAKE_SYSTEM_NAME Windows) + +# which compilers to use for C and C++ +SET(CMAKE_C_COMPILER "${HOST}-gcc") +SET(CMAKE_CXX_COMPILER "${HOST}-g++") + +# here is the target environment located +SET(CMAKE_FIND_ROOT_PATH "/usr/${HOST}") + +# here is where stuff gets installed to +SET(CMAKE_INSTALL_PREFIX "${CMAKE_FIND_ROOT_PATH}/usr" CACHE STRING "Install path prefix, prepended onto install directories." FORCE) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# set env vars so that pkg-config will look in the appropriate directory for +# .pc files (as there seems to be no way to force using ${HOST}-pkg-config) +set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig") +set(ENV{PKG_CONFIG_PATH} "") diff -r 000000000000 -r f9476ff7637e alsoftrc.sample --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/alsoftrc.sample Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,277 @@ +# OpenAL config file. Options that are not under a block or are under the +# [general] block are for general, non-backend-specific options. Blocks may +# appear multiple times, and duplicated options will take the last value +# specified. +# The system-wide settings can be put in /etc/openal/alsoft.conf and user- +# specific override settings in ~/.alsoftrc. +# For Windows, these settings should go into %AppData%\alsoft.ini +# The environment variable ALSOFT_CONF can be used to specify another config +# override + +# Option and block names are case-insenstive. The supplied values are only +# hints and may not be honored (though generally it'll try to get as close as +# possible). Note: options that are left unset may default to app- or system- +# specified values. These are the current available settings: + +## format: +# Sets the output format. Can be one of: +# AL_FORMAT_MONO8 (8-bit mono) +# AL_FORMAT_STEREO8 (8-bit stereo) +# AL_FORMAT_QUAD8 (8-bit 4-channel) +# AL_FORMAT_51CHN8 (8-bit 5.1 output) +# AL_FORMAT_61CHN8 (8-bit 6.1 output) +# AL_FORMAT_71CHN8 (8-bit 7.1 output) +# AL_FORMAT_MONO16 (16-bit mono) +# AL_FORMAT_STEREO16 (16-bit stereo) +# AL_FORMAT_QUAD16 (16-bit 4-channel) +# AL_FORMAT_51CHN16 (16-bit 5.1 output) +# AL_FORMAT_61CHN16 (16-bit 6.1 output) +# AL_FORMAT_71CHN16 (16-bit 7.1 output) +# AL_FORMAT_MONO32 (32-bit float mono) +# AL_FORMAT_STEREO32 (32-bit float stereo) +# AL_FORMAT_QUAD32 (32-bit float 4-channel) +# AL_FORMAT_51CHN32 (32-bit float 5.1 output) +# AL_FORMAT_61CHN32 (32-bit float 6.1 output) +# AL_FORMAT_71CHN32 (32-bit float 7.1 output) +#format = AL_FORMAT_STEREO16 + +## hrtf: +# Enables HRTF filters. These filters provide for better sound spatialization +# while using headphones. The filters will only work when output is 44100hz +# stereo. While HRTF is active, the cf_level option is disabled. Default is +# disabled since stereo speaker output quality may suffer. +#hrtf = false + +## cf_level: +# Sets the crossfeed level for stereo output. Valid values are: +# 0 - No crossfeed +# 1 - Low crossfeed +# 2 - Middle crossfeed +# 3 - High crossfeed (virtual speakers are closer to itself) +# 4 - Low easy crossfeed +# 5 - Middle easy crossfeed +# 6 - High easy crossfeed +# Users of headphones may want to try various settings. Has no effect on non- +# stereo modes. +#cf_level = 0 + +## frequency: +# Sets the output frequency. +#frequency = 44100 + +## resampler: +# Selects the resampler used when mixing sources. Valid values are: +# 0 - None (nearest sample, no interpolation) +# 1 - Linear (extrapolates samples using a linear slope between samples) +# 2 - Cubic (extrapolates samples using a Catmull-Rom spline) +# Specifying other values will result in using the default (linear). +#resampler = 1 + +## rt-prio: +# Sets real-time priority for the mixing thread. Not all drivers may use this +# (eg. PortAudio) as they already control the priority of the mixing thread. +# 0 and negative values will disable it. Note that this may constitute a +# security risk since a real-time priority thread can indefinitely block +# normal-priority threads if it fails to wait. As such, the default is +# disabled. +#rt-prio = 0 + +## period_size: +# Sets the update period size, in frames. This is the number of frames needed +# for each mixing update. +#period_size = 1024 + +## periods: +# Sets the number of update periods. Higher values create a larger mix ahead, +# which helps protect against skips when the CPU is under load, but increases +# the delay between a sound getting mixed and being heard. +#periods = 4 + +## sources: +# Sets the maximum number of allocatable sources. Lower values may help for +# systems with apps that try to play more sounds than the CPU can handle. +#sources = 256 + +## stereodup: +# Sets whether to duplicate stereo sounds behind the listener for 4+ channel +# output. This provides a "fuller" playback quality for surround sound output +# modes, although each individual speaker will have a slight reduction in +# volume to compensate for the extra output speakers. True, yes, on, and non-0 +# values will duplicate stereo sources. 0 and anything else will cause stereo +# sounds to only play in front. This only has an effect when a suitable output +# format is used (ie. those that contain side and/or rear speakers). +#stereodup = true + +## drivers: +# Sets the backend driver list order, comma-seperated. Unknown backends and +# duplicated names are ignored. Unlisted backends won't be considered for use +# unless the list is ended with a comma (eg. 'oss,' will list OSS first +# followed by all other available backends, while 'oss' will list OSS only). +# Backends prepended with - won't be available for use (eg. '-oss,' will allow +# all available backends except OSS). An empty list means the default. +#drivers = pulse,alsa,core,oss,solaris,sndio,mmdevapi,dsound,winmm,port,opensl,null,wave + +## excludefx: +# Sets which effects to exclude, preventing apps from using them. This can +# help for apps that try to use effects which are too CPU intensive for the +# system to handle. Available effects are: eaxreverb,reverb,echo,modulator, +# dedicated +#excludefx = + +## slots: +# Sets the maximum number of Auxiliary Effect Slots an app can create. A slot +# can use a non-negligible amount of CPU time if an effect is set on it even +# if no sources are feeding it, so this may help when apps use more than the +# system can handle. +#slots = 4 + +## sends: +# Sets the number of auxiliary sends per source. When not specified (default), +# it allows the app to request how many it wants. The maximum value currently +# possible is 4. +#sends = + +## layout: +# Sets the virtual speaker layout. Values are specified in degrees, where 0 is +# straight in front, negative goes left, and positive goes right. Unspecified +# speakers will remain at their default positions (which are dependant on the +# output format). Available speakers are back-left(bl), side-left(sl), front- +# left(fl), front-center(fc), front-right(fr), side-right(sr), back-right(br), +# and back-center(bc). +#layout = + +## layout_*: +# Channel-specific layouts may be specified to override the layout option. The +# same speakers as the layout option are available, and the default settings +# are shown below. +#layout_STEREO = fl=-90, fr=90 +#layout_QUAD = fl=-45, fr=45, bl=-135, br=135 +#layout_51CHN = fl=-30, fr=30, fc=0, bl=-110, br=110 +#layout_61CHN = fl=-30, fr=30, fc=0, sl=-90, sr=90, bc=180 +#layout_71CHN = fl=-30, fr=30, fc=0, sl=-90, sr=90, bl=-150, br=150 + +## +## Reverb effect stuff (includes EAX reverb) +## +[reverb] + +## boost: +# A global amplification for reverb output, expressed in decibels. The value +# is logarithmic, so +6 will be a scale of (approximately) 2x, +12 will be a +# scale of 4x, etc. Similarly, -6 will be about half, and -12 about 1/4th. A +# value of 0 means no change. +#boost = 0 + +## emulate-eax: +# Allows the standard reverb effect to be used in place of EAX reverb. EAX +# reverb processing is a bit more CPU intensive than standard, so this option +# allows a simpler effect to be used at the loss of some quality. +#emulate-eax = false + +## +## ALSA backend stuff +## +[alsa] + +## device: +# Sets the device name for the default playback device. +#device = default + +## device-prefix: +# Sets the prefix used by the discovered (non-default) playback devices. This +# will be appended with "CARD=c,DEV=d", where c is the card id and d is the +# device index for the requested device name. +#device-prefix = plughw: + +## capture: +# Sets the device name for the default capture device. +#capture = default + +## capture-prefix: +# Sets the prefix used by the discovered (non-default) capture devices. This +# will be appended with "CARD=c,DEV=d", where c is the card number and d is +# the device number for the requested device name. +#capture-prefix = plughw: + +## mmap: +# Sets whether to try using mmap mode (helps reduce latencies and CPU +# consumption). If mmap isn't available, it will automatically fall back to +# non-mmap mode. True, yes, on, and non-0 values will attempt to use mmap. 0 +# and anything else will force mmap off. +#mmap = true + +## +## OSS backend stuff +## +[oss] + +## device: +# Sets the device name for OSS output. +#device = /dev/dsp + +## capture: +# Sets the device name for OSS capture. +#capture = /dev/dsp + +## +## Solaris backend stuff +## +[solaris] + +## device: +# Sets the device name for Solaris output. +#device = /dev/audio + +## +## MMDevApi backend stuff +## +[mmdevapi] + +## +## DirectSound backend stuff +## +[dsound] + +## +## Windows Multimedia backend stuff +## +[winmm] + +## +## PortAudio backend stuff +## +[port] + +## device: +# Sets the device index for output. Negative values will use the default as +# given by PortAudio itself. +#device = -1 + +## capture: +# Sets the device index for capture. Negative values will use the default as +# given by PortAudio itself. +#capture = -1 + +## +## PulseAudio backend stuff +## +[pulse] + +## spawn-server: +# Attempts to spawn a PulseAudio server when requesting to open a PulseAudio +# device. Note that some apps may open and probe all enumerated devices on +# startup, causing a server to spawn even if a PulseAudio device is not +# actually selected. Setting autospawn to false in Pulse's client.conf will +# still prevent autospawning even if this is set to true. +#spawn-server = false + +## +## Wave File Writer stuff +## +[wave] + +## file: +# Sets the filename of the wave file to write to. An empty name prevents the +# backend from opening, even when explicitly requested. +# THIS WILL OVERWRITE EXISTING FILES WITHOUT QUESTION! +#file = diff -r 000000000000 -r f9476ff7637e bugs-in-openal.org --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bugs-in-openal.org Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,50 @@ +* bugs in OpenAl-soft + +Wave files produced by wave writer do not have the correct header. + +something is wrong with + +#+begin_src conf +drivers = null, +#+end_src + +It only produces the single null option when queried with +ALC_ALL_DEVICES_SPECIFIER, where it should produce much more output. + +specifically, + +------------------------------ +#+begin_src conf +drivers = oss, +#+end_src + +#+begin_src text +0: OSS Software +1: PulseAudio Software +2: ALSA Software on default +3: ALSA Software on HDA Intel [ALC272 Analog] (hw:0,0) +4: ALSA Software on HDA Intel [ALC272 Digital] (hw:0,1) +5: ALSA Software on HDA Intel [INTEL HDMI] (hw:0,3) +6: PortAudio Software +7: Wave File Writer +#+end_src +------------------------------ +#+begin_src conf +drivers = pulse, +#+end_src + +#+begin_src text +Devices +0: Internal Audio Analog Stereo +#+end_src +------------------------------ +#+begin_src conf +drivers = wave, +#+end_src + +#+begin_src text +Devices +0: Wave File Writer +#+end_src +------------------------------ + diff -r 000000000000 -r f9476ff7637e cleanup-message.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cleanup-message.txt Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,100 @@ +My name is Robert McIntyre. I am seeking help packaging some changes +I've made to open-al. + +* tl;dr how do I distribute changes to open-al which involve adding a + new device? + +* Background / Motivation + +I'm working on an AI simulation involving multiple listeners, where +each listener is a separate AI entity. Since each AI entity can move +independently, I needed to add multiple listener functionality to +open-al. Furthermore, my entire simulation allows time to dilate +depending on how hard the entities are collectively "thinking," so +that the entities can keep up with the simulation. Therefore, I +needed to have a system that renders sound on-demand instead of trying +to render in real time as open-al does. + +I don't need any of the more advanced effects, just 3D positioning, +and I'm only using open-al from jMonkeyEngine3 that uses the LWJGL +bindings that importantly only allow access to one and only one +context. + +Under these constraints, I made a new device which renders sound in +small, user-defined increments. It must be explicitly told to render +sound or it won't do anything. It maintains a separate "auxiliary +context" for every additional listener, and syncs the sources from the +LWJGL context whenever it is about to render samples. I've tested it +and it works quite well for my purposes. So far, I've gotten 1,000 +separate listeners to work in a single simulation easily. + +The code is here: +http://aurellem.localhost/audio-send/html/send.html +No criticism is too harsh! + +Note that the java JNI bindings that are part of the device code right +now would be moved to a separate file the the same way that LWJGL does +it. I left them there for now to show how the device might be used. + +Although I made this for my own AI work, it's ideal for recording +perfect audio from a video game to create trailers/demos, since the +computer doesn't have to try to record the sound in real time. This +device could be used to record audio in any system that wraps open-al +and only exposes one context, which is what many wrappers do. + + +* Actual Question + +My question is about packaging --- how do you recommend I distribute +my new device? I got it to work by just grafting it on the open-al's +primitive object system, but this requires quite a few changes to main +open-al source files, and as far as I can tell requires me to +recompile open-al against my new device. + +I also don't want the user to be able to hide my devices presence +using their ~/.alsoftrc file, since that gets in the way of easy +recording when the system is wrapped several layers deep, and they've +already implicitly requested my device anyway by using my code in the +first place. + +The options I have thought of so far are: + +1.) Create my own C-artifact, compiled against open-al, which somehow +"hooks in" my device to open-al and forces open-al to use it to the +exclusion of all other devices. This new artifact would have bindings +for java, etc. I don't know how to do this, since there doesn't seem +to be any way to access the list of devices in Alc/ALc.c for example. +In order to add a new device to open-al I had to modify 5 separate +files, documented here: + +http://aurellem.localhost/audio-send/html/add-new-device.html + +and there doesn't seem to be a way to get the same effect +programmatically. + +2.) Strip down open-al to a minimal version that only has my device +and deal with selecting the right open-al library at a higher level, +depending on whether the user wants to record sound or actually hear +it. I don't like this because I can't easily benefit from +improvements in the main open-al distribution. It also would involve +more significant modification to jMonkeyEngine3's logic which selects +the appropriate C-artifact at runtime. + +3.) Get this new device added to open-al, and provide a java wrapper +for it in a separate artifact. Problem here is that this device does +not have the same semantics as the other devices --- it must be told +to render sound, doesn't support multiple user-created contexts, and +it exposes extra functions for retrieving the rendered sounds. It also +might be too "niche" for open-al proper. + +4.) Maybe abandon the device metaphor and use something better suited +to my problem that /can/ be done as in (1)? + + +I'm sure someone here knows enough about open-al's devices to give me +a better solution than these 4! All help would be most appreciated. + +sincerely, +--Robert McIntyre + + diff -r 000000000000 -r f9476ff7637e cmake/CheckCCompilerFlag.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake/CheckCCompilerFlag.cmake Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,59 @@ +# - Check if the C source code provided in the SOURCE argument compiles. +# CHECK_C_SOURCE_COMPILES(SOURCE VAR) +# +# FLAG - compiler flag to check +# VAR - variable to store whether the source code compiled +# +# The following variables may be set before calling this macro to +# modify the way the check is run: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# CMAKE_REQUIRED_LIBRARIES = list of libraries to link + +MACRO(CHECK_C_COMPILER_FLAG FLAG VAR) + IF("${VAR}" MATCHES "^${VAR}$") + SET(MACRO_CHECK_FUNCTION_DEFINITIONS + "${FLAG} ${CMAKE_REQUIRED_FLAGS}") + IF(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_C_COMPILER_FLAG_ADD_LIBRARIES + "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") + ELSE(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_C_COMPILER_FLAG_ADD_LIBRARIES) + ENDIF(CMAKE_REQUIRED_LIBRARIES) + IF(CMAKE_REQUIRED_INCLUDES) + SET(CHECK_C_COMPILER_FLAG_ADD_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + ELSE(CMAKE_REQUIRED_INCLUDES) + SET(CHECK_C_COMPILER_FLAG_ADD_INCLUDES) + ENDIF(CMAKE_REQUIRED_INCLUDES) + FILE(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c" + "int main() {return 0;}\n") + + MESSAGE(STATUS "Checking if C compiler supports ${FLAG}") + TRY_COMPILE(${VAR} + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + "${CHECK_C_COMPILER_FLAG_ADD_LIBRARIES}" + "${CHECK_C_COMPILER_FLAG_ADD_INCLUDES}" + OUTPUT_VARIABLE OUTPUT) + IF(${VAR}) + SET(${VAR} 1 CACHE INTERNAL "Test ${VAR}") + MESSAGE(STATUS "Checking if C compiler supports ${FLAG} - Success") + FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Performing C SOURCE FILE Test ${VAR} succeded with the following output:\n" + "${OUTPUT}\n" + "Source file was:\n${SOURCE}\n") + ELSE(${VAR}) + MESSAGE(STATUS "Checking if C compiler supports ${FLAG} - Failed") + SET(${VAR} "" CACHE INTERNAL "Test ${VAR}") + FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Performing C SOURCE FILE Test ${VAR} failed with the following output:\n" + "${OUTPUT}\n" + "Source file was:\n${SOURCE}\n") + ENDIF(${VAR}) + ENDIF("${VAR}" MATCHES "^${VAR}$") +ENDMACRO(CHECK_C_COMPILER_FLAG) diff -r 000000000000 -r f9476ff7637e cmake/CheckSharedFunctionExists.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake/CheckSharedFunctionExists.cmake Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,92 @@ +# - Check if a symbol exists as a function, variable, or macro +# CHECK_SYMBOL_EXISTS( ) +# +# Check that the is available after including given header +# and store the result in a . Specify the list +# of files in one argument as a semicolon-separated list. +# +# If the header files define the symbol as a macro it is considered +# available and assumed to work. If the header files declare the +# symbol as a function or variable then the symbol must also be +# available for linking. If the symbol is a type or enum value +# it will not be recognized (consider using CheckTypeSize or +# CheckCSourceCompiles). +# +# The following variables may be set before calling this macro to +# modify the way the check is run: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# CMAKE_REQUIRED_LIBRARIES = list of libraries to link + +#============================================================================= +# Copyright 2003-2011 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +MACRO(CHECK_SHARED_FUNCTION_EXISTS SYMBOL FILES LIBRARY LOCATION VARIABLE) + IF("${VARIABLE}" MATCHES "^${VARIABLE}$") + SET(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n") + SET(MACRO_CHECK_SYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS}) + IF(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_SYMBOL_EXISTS_LIBS + "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES};${LIBRARY}") + ELSE(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_SYMBOL_EXISTS_LIBS + "-DLINK_LIBRARIES:STRING=${LIBRARY}") + ENDIF(CMAKE_REQUIRED_LIBRARIES) + IF(CMAKE_REQUIRED_INCLUDES) + SET(CMAKE_SYMBOL_EXISTS_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + ELSE(CMAKE_REQUIRED_INCLUDES) + SET(CMAKE_SYMBOL_EXISTS_INCLUDES) + ENDIF(CMAKE_REQUIRED_INCLUDES) + FOREACH(FILE ${FILES}) + SET(CMAKE_CONFIGURABLE_FILE_CONTENT + "${CMAKE_CONFIGURABLE_FILE_CONTENT}#include <${FILE}>\n") + ENDFOREACH(FILE) + SET(CMAKE_CONFIGURABLE_FILE_CONTENT + "${CMAKE_CONFIGURABLE_FILE_CONTENT}\nvoid cmakeRequireSymbol(int dummy,...){(void)dummy;}\nint main()\n{\n cmakeRequireSymbol(0,&${SYMBOL});\n return 0;\n}\n") + + CONFIGURE_FILE("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in" + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c" @ONLY) + + MESSAGE(STATUS "Looking for ${SYMBOL} in ${LIBRARY}") + TRY_COMPILE(${VARIABLE} + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + CMAKE_FLAGS + -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_SYMBOL_EXISTS_FLAGS} + -DLINK_DIRECTORIES:STRING=${LOCATION} + "${CHECK_SYMBOL_EXISTS_LIBS}" + "${CMAKE_SYMBOL_EXISTS_INCLUDES}" + OUTPUT_VARIABLE OUTPUT) + IF(${VARIABLE}) + MESSAGE(STATUS "Looking for ${SYMBOL} in ${LIBRARY} - found") + SET(${VARIABLE} 1 CACHE INTERNAL "Have symbol ${SYMBOL} in ${LIBRARY}") + FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determining if the ${SYMBOL} " + "exist in ${LIBRARY} passed with the following output:\n" + "${OUTPUT}\nFile ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c:\n" + "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") + ELSE(${VARIABLE}) + MESSAGE(STATUS "Looking for ${SYMBOL} in ${LIBRARY} - not found.") + SET(${VARIABLE} "" CACHE INTERNAL "Have symbol ${SYMBOL} in ${LIBRARY}") + FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determining if the ${SYMBOL} " + "exist in ${LIBRARY} failed with the following output:\n" + "${OUTPUT}\nFile ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c:\n" + "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") + ENDIF(${VARIABLE}) + ENDIF("${VARIABLE}" MATCHES "^${VARIABLE}$") +ENDMACRO(CHECK_SHARED_FUNCTION_EXISTS) diff -r 000000000000 -r f9476ff7637e config.h.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.h.in Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,129 @@ +/* API declaration export attribute */ +#define AL_API ${EXPORT_DECL} +#define ALC_API ${EXPORT_DECL} + +/* Define to the library version */ +#define ALSOFT_VERSION "${LIB_VERSION}" + +/* Define if we have the ALSA backend */ +#cmakedefine HAVE_ALSA + +/* Define if we have the OSS backend */ +#cmakedefine HAVE_OSS + +/* Define if we have the Solaris backend */ +#cmakedefine HAVE_SOLARIS + +/* Define if we have the SndIO backend */ +#cmakedefine HAVE_SNDIO + +/* Define if we have the MMDevApi backend */ +#cmakedefine HAVE_MMDEVAPI + +/* Define if we have the DSound backend */ +#cmakedefine HAVE_DSOUND + +/* Define if we have the Windows Multimedia backend */ +#cmakedefine HAVE_WINMM + +/* Define if we have the PortAudio backend */ +#cmakedefine HAVE_PORTAUDIO + +/* Define if we have the PulseAudio backend */ +#cmakedefine HAVE_PULSEAUDIO + +/* Define if we have the CoreAudio backend */ +#cmakedefine HAVE_COREAUDIO + +/* Define if we have the OpenSL backend */ +#cmakedefine HAVE_OPENSL + +/* Define if we have the Wave Writer backend */ +#cmakedefine HAVE_WAVE + +/* RLM: add send backend */ +#cmakedefine HAVE_SEND + +/* Define if we have dlfcn.h */ +#cmakedefine HAVE_DLFCN_H + +/* Define if we have the stat function */ +#cmakedefine HAVE_STAT + +/* Define if we have the powf function */ +#cmakedefine HAVE_POWF + +/* Define if we have the sqrtf function */ +#cmakedefine HAVE_SQRTF + +/* Define if we have the acosf function */ +#cmakedefine HAVE_ACOSF + +/* Define if we have the atanf function */ +#cmakedefine HAVE_ATANF + +/* Define if we have the fabsf function */ +#cmakedefine HAVE_FABSF + +/* Define if we have the strtof function */ +#cmakedefine HAVE_STRTOF + +/* Define if we have stdint.h */ +#cmakedefine HAVE_STDINT_H + +/* Define if we have the __int64 type */ +#cmakedefine HAVE___INT64 + +/* Define to the size of a long int type */ +#cmakedefine SIZEOF_LONG ${SIZEOF_LONG} + +/* Define to the size of a long long int type */ +#cmakedefine SIZEOF_LONG_LONG ${SIZEOF_LONG_LONG} + +/* Define to the size of an unsigned int type */ +#cmakedefine SIZEOF_UINT ${SIZEOF_UINT} + +/* Define to the size of a void pointer type */ +#cmakedefine SIZEOF_VOIDP ${SIZEOF_VOIDP} + +/* Define if we have GCC's destructor attribute */ +#cmakedefine HAVE_GCC_DESTRUCTOR + +/* Define if we have GCC's format attribute */ +#cmakedefine HAVE_GCC_FORMAT + +/* Define if we have pthread_np.h */ +#cmakedefine HAVE_PTHREAD_NP_H + +/* Define if we have arm_neon.h */ +#cmakedefine HAVE_ARM_NEON_H + +/* Define if we have guiddef.h */ +#cmakedefine HAVE_GUIDDEF_H + +/* Define if we have guiddef.h */ +#cmakedefine HAVE_INITGUID_H + +/* Define if we have ieeefp.h */ +#cmakedefine HAVE_IEEEFP_H + +/* Define if we have float.h */ +#cmakedefine HAVE_FLOAT_H + +/* Define if we have fenv.h */ +#cmakedefine HAVE_FENV_H + +/* Define if we have fesetround() */ +#cmakedefine HAVE_FESETROUND + +/* Define if we have _controlfp() */ +#cmakedefine HAVE__CONTROLFP + +/* Define if we have pthread_setschedparam() */ +#cmakedefine HAVE_PTHREAD_SETSCHEDPARAM + +/* Define if we have the restrict keyword */ +#cmakedefine HAVE_RESTRICT + +/* Define if we have the __restrict keyword */ +#cmakedefine HAVE___RESTRICT diff -r 000000000000 -r f9476ff7637e device_details.org --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/device_details.org Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,37 @@ +* a device has a two names, + - *name*, which is the filename and which appears before all of the functions. + - *trueName*, which is a string that is used to summon the device. + + +Every device has the *_data* struct, which is where all the +device specific state is located. + +In the function *_open_playback*, the device is initialized, +here the trueName is set, and the devices data is initialized. + +The ALCdevice's ExtraData field must always be cast to to the actual +data struct that the device uses. + + + +the =open_playback= function is where everything is initialized, + + +but the =reset_playback= is where the actual thread which is the heart +of the device is started. + + +every device has a Proc function which takes a pointer to an +ALCdevice and is run in a separate thread. It is this procedure that +actually does stuff with sound data. + + +It is the ALContext which controls the particulars of a listener. + +The context contains the device. + +* so do we need a new context for each listener, alone with their own +devices? + +However, the device also maintains a *list* of contexts... + diff -r 000000000000 -r f9476ff7637e ear.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ear.html Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,266 @@ + + + + +The EARS! + + + + + + + + + + + + + +
+ + + +
+
+ +
+ +

aurellem

+ +
+ +

The EARS!

+
Written by Robert McIntyre
+ + + + + + + + + + +
+

Table of Contents

+
+ +
+
+ +
+

1 Ears!

+
+ + +

+I want to be able to place ears in a similiar manner to how I place +the eyes. I want to be able to place ears in a unique spatial +position, and recieve as output at every tick the FFT of whatever +signals are happening at that point. +

+ + + +
(ns body.ear)
+(use 'cortex.world)
+(use 'cortex.import)
+(use 'clojure.contrib.def)
+(cortex.import/mega-import-jme3)
+(rlm.rlm-commands/help)
+(import java.nio.ByteBuffer)
+(import java.awt.image.BufferedImage)
+(import java.awt.Color)
+(import java.awt.Dimension)
+(import java.awt.Graphics)
+(import java.awt.Graphics2D)
+(import java.awt.event.WindowAdapter)
+(import java.awt.event.WindowEvent)
+(import java.awt.image.BufferedImage)
+(import java.nio.ByteBuffer)
+(import javax.swing.JFrame)
+(import javax.swing.JPanel)
+(import javax.swing.SwingUtilities)
+(import javax.swing.ImageIcon)
+(import javax.swing.JOptionPane)
+(import java.awt.image.ImageObserver)
+
+ + + + +

+JMonkeyEngine3's audio system works as follows: +first, an appropiate audio renderer is created during initialization +and depending on the context. On my computer, this is the +LwjglAudioRenderer. +

+

+The LwjglAudioRenderer sets a few internal state variables depending +on what capabilities the audio system has. +

+

+may very well need to make my own AudioRenderer +

+ + + +
(in-ns 'body.ear)
+(import 'com.jme3.capture.SoundProcessor)
+
+
+(defn sound-processor
+  "deals with converting ByteBuffers into Arrays of bytes so that the
+  continuation functions can be defined in terms of immutable stuff."
+  [continuation]
+  (proxy [SoundProcessor] []
+    (cleanup [])
+    (process
+      [#^ByteBuffer audioSamples numSamples]
+      (no-exceptions
+       (let [byte-array (byte-array numSamples)]
+         (.get audioSamples byte-array 0 numSamples)
+         (continuation
+          (vec byte-array)))))))
+         
+
+(defn add-ear 
+  "add an ear to the world.  The continuation function will be called
+  on the FFT or the sounds which the ear hears in the given
+  timeframe. Sound is 3D."
+  [world listener continuation]
+  (let [renderer (.getAudioRenderer world)]
+    (.addListener renderer listener)
+    (.registerSoundProcessor renderer listener
+                             (sound-processor continuation))
+    listener))
+
+
+ + + + + + + +
(ns test.hearing)
+(use 'cortex.world)
+(use 'cortex.import)
+(use 'clojure.contrib.def)
+(use 'body.ear)
+(cortex.import/mega-import-jme3)
+(rlm.rlm-commands/help)
+
+(defn setup-fn [world]
+  (let [listener (Listener.)]
+    (add-ear world listener #(println (nth % 0)))))
+  
+(defn play-sound [node world value]
+  (if (not value)
+    (do
+      (.playSource (.getAudioRenderer world) node))))
+
+(defn test-world []
+  (let [node1 (AudioNode. (asset-manager) "Sounds/pure.wav" false false)]
+    (world
+     (Node.)
+     {"key-space" (partial play-sound node1)}
+     setup-fn
+     no-op
+     )))
+    
+
+
+ + + + + + + + +
+
+
+

Date: 2011-09-21 16:38:27 MDT

+

Author: Robert McIntyre

+

Org version 7.6 with Emacs version 23

+Validate XHTML 1.0 +
+
+ + diff -r 000000000000 -r f9476ff7637e ear.org --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ear.org Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,137 @@ +#+title: The EARS! +#+author: Robert McIntyre +#+email: rlm@mit.edu +#+MATHJAX: align:"left" mathml:t path:"../aurellem/src/MathJax/MathJax.js" +#+STYLE: +#+BABEL: :exports both :noweb yes :cache no :mkdirp yes +#+INCLUDE: ../aurellem/src/templates/level-0.org +#+description: Simulating multiple listeners and the sense of hearing in uMonkeyEngine3 + + + + +* Ears! + +I want to be able to place ears in a similiar manner to how I place +the eyes. I want to be able to place ears in a unique spatial +position, and recieve as output at every tick the FFT of whatever +signals are happening at that point. + +#+srcname: ear-header +#+begin_src clojure +(ns body.ear) +(use 'cortex.world) +(use 'cortex.import) +(use 'clojure.contrib.def) +(cortex.import/mega-import-jme3) +(rlm.rlm-commands/help) +(import java.nio.ByteBuffer) +(import java.awt.image.BufferedImage) +(import java.awt.Color) +(import java.awt.Dimension) +(import java.awt.Graphics) +(import java.awt.Graphics2D) +(import java.awt.event.WindowAdapter) +(import java.awt.event.WindowEvent) +(import java.awt.image.BufferedImage) +(import java.nio.ByteBuffer) +(import javax.swing.JFrame) +(import javax.swing.JPanel) +(import javax.swing.SwingUtilities) +(import javax.swing.ImageIcon) +(import javax.swing.JOptionPane) +(import java.awt.image.ImageObserver) +#+end_src + +JMonkeyEngine3's audio system works as follows: +first, an appropiate audio renderer is created during initialization +and depending on the context. On my computer, this is the +LwjglAudioRenderer. + +The LwjglAudioRenderer sets a few internal state variables depending +on what capabilities the audio system has. + +may very well need to make my own AudioRenderer + +#+srcname: ear-body-1 +#+begin_src clojure :results silent +(in-ns 'body.ear) +(import 'com.jme3.capture.SoundProcessor) + + +(defn sound-processor + "deals with converting ByteBuffers into Arrays of bytes so that the + continuation functions can be defined in terms of immutable stuff." + [continuation] + (proxy [SoundProcessor] [] + (cleanup []) + (process + [#^ByteBuffer audioSamples numSamples] + (no-exceptions + (let [byte-array (byte-array numSamples)] + (.get audioSamples byte-array 0 numSamples) + (continuation + (vec byte-array))))))) + + +(defn add-ear + "add an ear to the world. The continuation function will be called + on the FFT or the sounds which the ear hears in the given + timeframe. Sound is 3D." + [world listener continuation] + (let [renderer (.getAudioRenderer world)] + (.addListener renderer listener) + (.registerSoundProcessor renderer listener + (sound-processor continuation)) + listener)) + +#+end_src + + + +#+srcname: test-hearing +#+begin_src clojure :results silent +(ns test.hearing) +(use 'cortex.world) +(use 'cortex.import) +(use 'clojure.contrib.def) +(use 'body.ear) +(cortex.import/mega-import-jme3) +(rlm.rlm-commands/help) + +(defn setup-fn [world] + (let [listener (Listener.)] + (add-ear world listener #(println (nth % 0))))) + +(defn play-sound [node world value] + (if (not value) + (do + (.playSource (.getAudioRenderer world) node)))) + +(defn test-world [] + (let [node1 (AudioNode. (asset-manager) "Sounds/pure.wav" false false)] + (world + (Node.) + {"key-space" (partial play-sound node1)} + setup-fn + no-op + ))) + + +#+end_src + + + +* COMMENT Code Generation + +#+begin_src clojure :tangle /home/r/cortex/src/body/ear.clj +<> +<> +#+end_src + + +#+begin_src clojure :tangle /home/r/cortex/src/test/hearing.clj +<> +#+end_src + + diff -r 000000000000 -r f9476ff7637e html/add-new-device.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/html/add-new-device.html Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,301 @@ + + + + +all the steps to add a device to open-al + + + + + + + + + + + + + +
+ + + +
+
+ +
+ +

aurellem

+ +
+ +

all the steps to add a device to open-al

+
Written by Robert McIntyre
+ + + + + + + + + +
+

1 How to add a new backend device

+
+ + +
+ +
+ +
+

2 In Alc/backends/<your-device>.c

+
+ + + + + +
static const ALCchar <your-device>_device[] = <your-device-name>;
+
+ALCboolean alc_<your-device>_init(BackendFuncs *func_list)
+void alc_<your-device>_deinit(void)
+
+static ALCboolean <your-device>_open_playback(ALCdevice *device, const ALCchar *deviceName)
+static void <your-device>_close_playback(ALCdevice *device)
+static ALCboolean <your-device>_reset_playback(ALCdevice *device)
+static void <your-device>_stop_playback(ALCdevice *device)
+
+ + + + + +
void alc_<your-device>_probe(enum DevProbe type)
+{
+    switch(type)
+    {
+        case DEVICE_PROBE:
+            AppendDeviceList(<your-device>Device);
+            break;
+        case ALL_DEVICE_PROBE:
+            AppendAllDeviceList(<your-device>Device);
+            break;
+        case CAPTURE_DEVICE_PROBE:
+            break;
+    }
+}
+
+ + + + + +
static const BackendFuncs <your-device>_funcs = {
+    <your-device>_open_playback,
+    <your-device>_close_playback,
+    <your-device>_reset_playback,
+    <your-device>_stop_playback,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+ + + + + +
+ +
+ +
+

3 In OpenAL32/Include/alMain.h :

+
+ + + + + +
ALCboolean alc_<your-device>_init(BackendFuncs *func_list);
+void alc_<your-device>_deinit(void);
+void alc_<your-device>_probe(enum DevProbe type);
+
+
+ + + + +
+ +
+ +
+

4 In Alc/ALc.c :

+
+ + + + +
     { "<your-device>", alc_<your-device>_init, 
+         alc_<your-device>_deinit, alc_<your-device>_probe, EmptyFuncs },
+
+
+ + + + +
+ +
+ +
+

5 In CMakeLists.txt

+
+ + + + +
SET(ALC_OBJS  Alc/ALc.c
+              Alc/ALu.c
+              Alc/alcConfig.c
+              Alc/alcDedicated.c
+              Alc/alcEcho.c
+              Alc/alcModulator.c
+              Alc/alcReverb.c
+              Alc/alcRing.c
+              Alc/alcThread.c
+              Alc/bs2b.c
+              Alc/helpers.c
+              Alc/hrtf.c
+              Alc/mixer.c
+              Alc/panning.c
+              # Default backends, always available
+              Alc/backends/loopback.c
+              Alc/backends/null.c
+              # : add <your-device> device
+              Alc/backends/<your-device>.c
+)
+
+ + + + + +
+ +
+ +
+

6 In ~/.alsoftrc

+
+ + + + + +
drivers = <your-device>
+
+ + + +
+
+
+

Date: 2011-10-20 15:48:47 MST

+

Author: Robert McIntyre

+

Org version 7.6 with Emacs version 23

+Validate XHTML 1.0 +
+
+ + diff -r 000000000000 -r f9476ff7637e html/send.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/html/send.html Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,599 @@ + + + + +send.c + + + + + + + + + + + + + +
+ + + +
+
+ +
+ +

aurellem

+ +
+ +

send.c

+
Written by Robert McIntyre
+ + + + + + + +
#include "config.h"
+#include <stdlib.h>
+#include "alMain.h"
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alSource.h"
+#include <jni.h>
+
+//////////////////// Summary
+
+struct send_data;
+struct context_data;
+
+static void addContext(ALCdevice *, ALCcontext *);
+static void syncContexts(ALCcontext *master, ALCcontext *slave);
+static void syncSources(ALsource *master, ALsource *slave, 
+                        ALCcontext *masterCtx, ALCcontext *slaveCtx);
+
+static void syncSourcei(ALuint master, ALuint slave,
+                        ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param);
+static void syncSourcef(ALuint master, ALuint slave,
+                        ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param);
+static void syncSource3f(ALuint master, ALuint slave,
+                        ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param);
+
+static void swapInContext(ALCdevice *, struct context_data *);
+static void saveContext(ALCdevice *, struct context_data *);
+static void limitContext(ALCdevice *, ALCcontext *);
+static void unLimitContext(ALCdevice *);
+
+static void init(ALCdevice *);
+static void renderData(ALCdevice *, int samples);
+
+#define UNUSED(x)  (void)(x)
+
+////////////////////  State
+
+typedef struct context_data {
+  ALfloat ClickRemoval[MAXCHANNELS];
+  ALfloat PendingClicks[MAXCHANNELS];
+  ALvoid *renderBuffer;
+  ALCcontext *ctx;
+} context_data;
+
+typedef struct send_data {
+  ALuint size;
+  context_data **contexts;
+  ALuint numContexts;
+  ALuint maxContexts;
+} send_data;
+
+
+
+////////////////////  Context Creation / Synchronization
+
+#define _MAKE_SYNC(NAME, INIT_EXPR, GET_EXPR, SET_EXPR) \
+  void NAME (ALuint sourceID1, ALuint sourceID2,        \
+             ALCcontext *ctx1, ALCcontext *ctx2,        \
+             ALenum param){                             \
+    INIT_EXPR;                                          \
+    ALCcontext *current = alcGetCurrentContext();       \
+    alcMakeContextCurrent(ctx1);                        \
+    GET_EXPR;                                           \
+    alcMakeContextCurrent(ctx2);                        \
+    SET_EXPR;                                           \
+    alcMakeContextCurrent(current);                     \
+  }
+  
+#define MAKE_SYNC(NAME, TYPE, GET, SET)                 \
+  _MAKE_SYNC(NAME,                                      \
+             TYPE value,                                \
+             GET(sourceID1, param, &value),             \
+             SET(sourceID2, param, value))
+  
+#define MAKE_SYNC3(NAME, TYPE, GET, SET)                                \
+  _MAKE_SYNC(NAME,                                                      \
+             TYPE value1; TYPE value2; TYPE value3;,                    \
+             GET(sourceID1, param, &value1, &value2, &value3),          \
+             SET(sourceID2, param,  value1,  value2,  value3))
+
+MAKE_SYNC( syncSourcei,  ALint,   alGetSourcei,  alSourcei);
+MAKE_SYNC( syncSourcef,  ALfloat, alGetSourcef,  alSourcef);
+MAKE_SYNC3(syncSource3i, ALint,   alGetSource3i, alSource3i);
+MAKE_SYNC3(syncSource3f, ALfloat, alGetSource3f, alSource3f);
+  
+void syncSources(ALsource *masterSource, ALsource *slaveSource, 
+                 ALCcontext *masterCtx, ALCcontext *slaveCtx){
+  ALuint master = masterSource->source;
+  ALuint slave = slaveSource->source;
+  ALCcontext *current = alcGetCurrentContext();
+
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_PITCH);
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_GAIN);
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_DISTANCE);
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_ROLLOFF_FACTOR);
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_REFERENCE_DISTANCE);
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_MIN_GAIN);
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_GAIN);
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_GAIN);
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_INNER_ANGLE);
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_ANGLE);
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_SEC_OFFSET);
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_SAMPLE_OFFSET);
+  syncSourcef(master,slave,masterCtx,slaveCtx,AL_BYTE_OFFSET);
+    
+  syncSource3f(master,slave,masterCtx,slaveCtx,AL_POSITION);
+  syncSource3f(master,slave,masterCtx,slaveCtx,AL_VELOCITY);
+  syncSource3f(master,slave,masterCtx,slaveCtx,AL_DIRECTION);
+  
+  syncSourcei(master,slave,masterCtx,slaveCtx,AL_SOURCE_RELATIVE);
+  syncSourcei(master,slave,masterCtx,slaveCtx,AL_LOOPING);
+
+  alcMakeContextCurrent(masterCtx);
+  ALint source_type;
+  alGetSourcei(master, AL_SOURCE_TYPE, &source_type);
+
+  // Only static sources are currently synchronized! 
+  if (AL_STATIC == source_type){
+    ALint master_buffer;
+    ALint slave_buffer;
+    alGetSourcei(master, AL_BUFFER, &master_buffer);
+    alcMakeContextCurrent(slaveCtx);
+    alGetSourcei(slave, AL_BUFFER, &slave_buffer);
+    if (master_buffer != slave_buffer){
+      alSourcei(slave, AL_BUFFER, master_buffer);
+    }
+  }
+  
+  // Synchronize the state of the two sources.
+  alcMakeContextCurrent(masterCtx);
+  ALint masterState;
+  ALint slaveState;
+
+  alGetSourcei(master, AL_SOURCE_STATE, &masterState);
+  alcMakeContextCurrent(slaveCtx);
+  alGetSourcei(slave, AL_SOURCE_STATE, &slaveState);
+
+  if (masterState != slaveState){
+    switch (masterState){
+    case AL_INITIAL : alSourceRewind(slave); break;
+    case AL_PLAYING : alSourcePlay(slave);   break;
+    case AL_PAUSED  : alSourcePause(slave);  break;
+    case AL_STOPPED : alSourceStop(slave);   break;
+    }
+  }
+  // Restore whatever context was previously active.
+  alcMakeContextCurrent(current);
+}
+
+
+void syncContexts(ALCcontext *master, ALCcontext *slave){
+  /* If there aren't sufficient sources in slave to mirror 
+     the sources in master, create them. */
+  ALCcontext *current = alcGetCurrentContext();
+
+  UIntMap *masterSourceMap = &(master->SourceMap);
+  UIntMap *slaveSourceMap = &(slave->SourceMap);
+  ALuint numMasterSources = masterSourceMap->size;
+  ALuint numSlaveSources = slaveSourceMap->size;
+
+  alcMakeContextCurrent(slave);
+  if (numSlaveSources < numMasterSources){
+    ALuint numMissingSources = numMasterSources - numSlaveSources;
+    ALuint newSources[numMissingSources];
+    alGenSources(numMissingSources, newSources);
+  }
+
+  /* Now, slave is gauranteed to have at least as many sources
+     as master.  Sync each source from master to the corresponding
+     source in slave. */
+  int i;
+  for(i = 0; i < masterSourceMap->size; i++){
+    syncSources((ALsource*)masterSourceMap->array[i].value,
+                (ALsource*)slaveSourceMap->array[i].value,
+                master, slave);
+  }
+  alcMakeContextCurrent(current);
+}
+
+static void addContext(ALCdevice *Device, ALCcontext *context){
+  send_data *data = (send_data*)Device->ExtraData;
+  // expand array if necessary
+  if (data->numContexts >= data->maxContexts){
+    ALuint newMaxContexts = data->maxContexts*2 + 1;
+    data->contexts = realloc(data->contexts, newMaxContexts*sizeof(context_data));
+    data->maxContexts = newMaxContexts;
+  }
+  // create context_data and add it to the main array
+  context_data *ctxData;
+  ctxData = (context_data*)calloc(1, sizeof(*ctxData));
+  ctxData->renderBuffer = malloc(data->size);
+  ctxData->ctx = context;
+
+  data->contexts[data->numContexts] = ctxData;
+  data->numContexts++;
+}
+
+
+////////////////////  Context Switching 
+
+/* A device brings along with it two pieces of state
+ * which have to be swapped in and out with each context.
+ */
+static void swapInContext(ALCdevice *Device, context_data *ctxData){
+  memcpy(Device->ClickRemoval, ctxData->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS);
+  memcpy(Device->PendingClicks, ctxData->PendingClicks, sizeof(ALfloat)*MAXCHANNELS);
+}
+
+static void saveContext(ALCdevice *Device, context_data *ctxData){
+  memcpy(ctxData->ClickRemoval, Device->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS);
+  memcpy(ctxData->PendingClicks, Device->PendingClicks, sizeof(ALfloat)*MAXCHANNELS);
+}  
+
+static ALCcontext **currentContext;
+static ALuint currentNumContext;
+
+/* By default, all contexts are rendered at once for each call to aluMixData.
+ * This function uses the internals of the ALCdecice struct to temporarly 
+ * cause aluMixData to only render the chosen context.
+ */
+static void limitContext(ALCdevice *Device, ALCcontext *ctx){
+  currentContext  = Device->Contexts;
+  currentNumContext  = Device->NumContexts;
+  Device->Contexts = &ctx;
+  Device->NumContexts = 1;
+}
+
+static void unLimitContext(ALCdevice *Device){
+  Device->Contexts = currentContext;
+  Device->NumContexts = currentNumContext;
+}
+
+
+////////////////////   Main Device Loop
+
+/* Establish the LWJGL context as the main context, which will
+ * be synchronized to all the slave contexts
+ */
+static void init(ALCdevice *Device){
+  ALCcontext *masterContext = alcGetCurrentContext();
+  addContext(Device, masterContext);
+}
+
+
+static void renderData(ALCdevice *Device, int samples){
+  if(!Device->Connected){return;}
+  send_data *data = (send_data*)Device->ExtraData;
+  ALCcontext *current = alcGetCurrentContext();
+
+  ALuint i;
+  for (i = 1; i < data->numContexts; i++){
+    syncContexts(data->contexts[0]->ctx , data->contexts[i]->ctx);
+  }
+  
+  if ((uint) samples > Device->UpdateSize){
+    printf("exceeding internal buffer size; dropping samples\n");
+    printf("requested %d; available %d\n", samples, Device->UpdateSize);
+    samples = (int) Device->UpdateSize;
+  }
+
+  for (i = 0; i < data->numContexts; i++){
+    context_data *ctxData = data->contexts[i];
+    ALCcontext *ctx = ctxData->ctx;
+    alcMakeContextCurrent(ctx);
+    limitContext(Device, ctx);
+    swapInContext(Device, ctxData);
+    aluMixData(Device, ctxData->renderBuffer, samples);
+    saveContext(Device, ctxData);
+    unLimitContext(Device);
+  }
+  alcMakeContextCurrent(current);
+}
+
+
+////////////////////   JNI Methods
+
+#include "com_jme3_capture_AudioSend.h"
+
+/*
+ * Class:     com_jme3_capture_AudioSend
+ * Method:    nstep
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_capture_AudioSend_nstep
+(JNIEnv *env, jclass clazz, jlong device, jint samples){
+  UNUSED(env);UNUSED(clazz);UNUSED(device);
+  renderData((ALCdevice*)((intptr_t)device), samples);
+}
+
+/*
+ * Class:     com_jme3_capture_AudioSend
+ * Method:    ngetSamples
+ * Signature: (JLjava/nio/ByteBuffer;III)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_capture_AudioSend_ngetSamples
+(JNIEnv *env, jclass clazz, jlong device, jobject buffer, jint position, 
+ jint samples, jint n){
+  UNUSED(clazz);  
+
+  ALvoid *buffer_address = 
+    ((ALbyte *)(((char*)(*env)->GetDirectBufferAddress(env, buffer)) + position));
+  ALCdevice *recorder = (ALCdevice*) ((intptr_t)device);
+  send_data *data = (send_data*)recorder->ExtraData;
+  if ((ALuint)n > data->numContexts){return;}
+  if ((uint) samples > data->size){
+    samples = (int) data->size;
+  }
+  memcpy(buffer_address, data->contexts[n]->renderBuffer, samples*sizeof(ALfloat));
+}
+
+/*
+ * Class:     com_jme3_capture_AudioSend
+ * Method:    naddListener
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_capture_AudioSend_naddListener
+(JNIEnv *env, jclass clazz, jlong device){
+  UNUSED(env); UNUSED(clazz);
+  printf("creating new context via naddListener\n");
+  ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
+  ALCcontext *new = alcCreateContext(Device, NULL);
+  addContext(Device, new);
+}
+
+/*
+ * Class:     com_jme3_capture_AudioSend
+ * Method:    nsetNthListener3f
+ * Signature: (IFFFJI)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_capture_AudioSend_nsetNthListener3f
+  (JNIEnv *env, jclass clazz, jint param, 
+   jfloat v1, jfloat v2, jfloat v3, jlong device, jint contextNum){
+  UNUSED(env);UNUSED(clazz);
+
+  ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
+  send_data *data = (send_data*)Device->ExtraData;
+  
+  ALCcontext *current = alcGetCurrentContext();
+  if ((ALuint)contextNum > data->numContexts){return;}
+  alcMakeContextCurrent(data->contexts[contextNum]->ctx);
+  alListener3f(param, v1, v2, v3);
+  alcMakeContextCurrent(current);
+}
+
+/*
+ * Class:     com_jme3_capture_AudioSend
+ * Method:    nsetNthListenerf
+ * Signature: (IFJI)V
+ */
+JNIEXPORT void JNICALL Java_com_jme3_capture_AudioSend_nsetNthListenerf
+(JNIEnv *env, jclass clazz, jint param, jfloat v1, jlong device, 
+ jint contextNum){
+
+  UNUSED(env);UNUSED(clazz);
+  
+  ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
+  send_data *data = (send_data*)Device->ExtraData;
+  
+  ALCcontext *current = alcGetCurrentContext();
+  if ((ALuint)contextNum > data->numContexts){return;}
+  alcMakeContextCurrent(data->contexts[contextNum]->ctx);
+  alListenerf(param, v1);
+  alcMakeContextCurrent(current);
+}
+
+/*
+ * Class:     com_jme3_capture_AudioSend
+ * Method:    ninitDevice
+ * Signature: (J)V
+ */                     
+JNIEXPORT void JNICALL Java_com_jme3_capture_AudioSend_ninitDevice
+(JNIEnv *env, jclass clazz, jlong device){
+  UNUSED(env);UNUSED(clazz);
+  
+  ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
+  init(Device);
+
+}
+
+
+////////////////////   Device Initilization / Management
+
+static const ALCchar sendDevice[] = "Multiple Audio Send";
+
+static ALCboolean send_open_playback(ALCdevice *device, 
+ const ALCchar *deviceName)
+{
+  send_data *data;
+  // stop any buffering for stdout, so that I can 
+  // see the printf statements in my terminal immediatley
+  setbuf(stdout, NULL);
+
+  if(!deviceName)
+    deviceName = sendDevice;
+  else if(strcmp(deviceName, sendDevice) != 0)
+    return ALC_FALSE;
+  data = (send_data*)calloc(1, sizeof(*data));
+  device->szDeviceName = strdup(deviceName);
+  device->ExtraData = data;
+  return ALC_TRUE;
+}
+
+static void send_close_playback(ALCdevice *device)
+{
+  send_data *data = (send_data*)device->ExtraData;
+  alcMakeContextCurrent(NULL);
+  ALuint i;
+  // Destroy all slave contexts. LWJGL will take care of 
+  // its own context.
+  for (i = 1; i < data->numContexts; i++){
+    context_data *ctxData = data->contexts[i];
+    alcDestroyContext(ctxData->ctx);
+    free(ctxData->renderBuffer);
+    free(ctxData);
+  }
+  free(data);
+  device->ExtraData = NULL;
+}
+
+static ALCboolean send_reset_playback(ALCdevice *device)
+{
+  send_data *data = (send_data*)device->ExtraData;
+  ALuint channels=0, bits=0;
+  device->FmtType = DevFmtShort;
+  bits = BytesFromDevFmt(device->FmtType) * 8;
+  channels = ChannelsFromDevFmt(device->FmtChans);
+  data->size = device->UpdateSize * channels * bits / 8;
+
+  return ALC_TRUE;
+}
+
+static void send_stop_playback(ALCdevice *Device){
+  UNUSED(Device);
+}
+
+static const BackendFuncs send_funcs = {
+  send_open_playback,
+  send_close_playback,
+  send_reset_playback,
+  send_stop_playback,
+  NULL,
+  NULL,  /* These would be filled with functions to    */
+  NULL,  /* handle capturing audio if we we into that  */
+  NULL,  /* sort of thing...                           */
+  NULL,
+  NULL
+};
+
+ALCboolean alc_send_init(BackendFuncs *func_list){
+  *func_list = send_funcs;
+  return ALC_TRUE;
+}
+
+void alc_send_deinit(void){}
+
+void alc_send_probe(enum DevProbe type)
+{
+  switch(type)
+    {
+    case DEVICE_PROBE:
+      AppendDeviceList(sendDevice);
+      break;
+    case ALL_DEVICE_PROBE:
+      AppendAllDeviceList(sendDevice);
+      break;
+    case CAPTURE_DEVICE_PROBE:
+      break;
+    }
+}
+
+
+
+
+ + + + + +
+

Date: 2011-10-20 15:48:47 MST

+

Author: Robert McIntyre

+

Org version 7.6 with Emacs version 23

+Validate XHTML 1.0 +
+
+ + diff -r 000000000000 -r f9476ff7637e include/AL/al.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/AL/al.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,718 @@ +#ifndef AL_AL_H +#define AL_AL_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef AL_API + #if defined(AL_LIBTYPE_STATIC) + #define AL_API + #elif defined(_WIN32) + #define AL_API __declspec(dllimport) + #else + #define AL_API extern + #endif +#endif + +#if defined(_WIN32) + #define AL_APIENTRY __cdecl +#else + #define AL_APIENTRY +#endif + +#if defined(TARGET_OS_MAC) && TARGET_OS_MAC + #pragma export on +#endif + +/* + * The OPENAL, ALAPI, ALAPIENTRY, AL_INVALID, AL_ILLEGAL_ENUM, and + * AL_ILLEGAL_COMMAND macros are deprecated, but are included for + * applications porting code from AL 1.0 + */ +#define OPENAL +#define ALAPI AL_API +#define ALAPIENTRY AL_APIENTRY +#define AL_INVALID (-1) +#define AL_ILLEGAL_ENUM AL_INVALID_ENUM +#define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION + +#define AL_VERSION_1_0 +#define AL_VERSION_1_1 + + +/** 8-bit boolean */ +typedef char ALboolean; + +/** character */ +typedef char ALchar; + +/** signed 8-bit 2's complement integer */ +typedef signed char ALbyte; + +/** unsigned 8-bit integer */ +typedef unsigned char ALubyte; + +/** signed 16-bit 2's complement integer */ +typedef short ALshort; + +/** unsigned 16-bit integer */ +typedef unsigned short ALushort; + +/** signed 32-bit 2's complement integer */ +typedef int ALint; + +/** unsigned 32-bit integer */ +typedef unsigned int ALuint; + +/** non-negative 32-bit binary integer size */ +typedef int ALsizei; + +/** enumerated 32-bit value */ +typedef int ALenum; + +/** 32-bit IEEE754 floating-point */ +typedef float ALfloat; + +/** 64-bit IEEE754 floating-point */ +typedef double ALdouble; + +/** void type (for opaque pointers only) */ +typedef void ALvoid; + + +/* Enumerant values begin at column 50. No tabs. */ + +/* "no distance model" or "no buffer" */ +#define AL_NONE 0 + +/* Boolean False. */ +#define AL_FALSE 0 + +/** Boolean True. */ +#define AL_TRUE 1 + +/** Indicate Source has relative coordinates. */ +#define AL_SOURCE_RELATIVE 0x202 + + + +/** + * Directional source, inner cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_INNER_ANGLE 0x1001 + +/** + * Directional source, outer cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_OUTER_ANGLE 0x1002 + +/** + * Specify the pitch to be applied at source. + * Range: [0.5-2.0] + * Default: 1.0 + */ +#define AL_PITCH 0x1003 + +/** + * Specify the current location in three dimensional space. + * OpenAL, like OpenGL, uses a right handed coordinate system, + * where in a frontal default view X (thumb) points right, + * Y points up (index finger), and Z points towards the + * viewer/camera (middle finger). + * To switch from a left handed coordinate system, flip the + * sign on the Z coordinate. + * Listener position is always in the world coordinate system. + */ +#define AL_POSITION 0x1004 + +/** Specify the current direction. */ +#define AL_DIRECTION 0x1005 + +/** Specify the current velocity in three dimensional space. */ +#define AL_VELOCITY 0x1006 + +/** + * Indicate whether source is looping. + * Type: ALboolean? + * Range: [AL_TRUE, AL_FALSE] + * Default: FALSE. + */ +#define AL_LOOPING 0x1007 + +/** + * Indicate the buffer to provide sound samples. + * Type: ALuint. + * Range: any valid Buffer id. + */ +#define AL_BUFFER 0x1009 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_GAIN 0x100A + +/* + * Indicate minimum source attenuation + * Type: ALfloat + * Range: [0.0 - 1.0] + * + * Logarthmic + */ +#define AL_MIN_GAIN 0x100D + +/** + * Indicate maximum source attenuation + * Type: ALfloat + * Range: [0.0 - 1.0] + * + * Logarthmic + */ +#define AL_MAX_GAIN 0x100E + +/** + * Indicate listener orientation. + * + * at/up + */ +#define AL_ORIENTATION 0x100F + +/** + * Source state information. + */ +#define AL_SOURCE_STATE 0x1010 +#define AL_INITIAL 0x1011 +#define AL_PLAYING 0x1012 +#define AL_PAUSED 0x1013 +#define AL_STOPPED 0x1014 + +/** + * Buffer Queue params + */ +#define AL_BUFFERS_QUEUED 0x1015 +#define AL_BUFFERS_PROCESSED 0x1016 + +/** + * Source buffer position information + */ +#define AL_SEC_OFFSET 0x1024 +#define AL_SAMPLE_OFFSET 0x1025 +#define AL_BYTE_OFFSET 0x1026 + +/* + * Source type (Static, Streaming or undetermined) + * Source is Static if a Buffer has been attached using AL_BUFFER + * Source is Streaming if one or more Buffers have been attached using alSourceQueueBuffers + * Source is undetermined when it has the NULL buffer attached + */ +#define AL_SOURCE_TYPE 0x1027 +#define AL_STATIC 0x1028 +#define AL_STREAMING 0x1029 +#define AL_UNDETERMINED 0x1030 + +/** Sound samples: format specifier. */ +#define AL_FORMAT_MONO8 0x1100 +#define AL_FORMAT_MONO16 0x1101 +#define AL_FORMAT_STEREO8 0x1102 +#define AL_FORMAT_STEREO16 0x1103 + +/** + * source specific reference distance + * Type: ALfloat + * Range: 0.0 - +inf + * + * At 0.0, no distance attenuation occurs. Default is + * 1.0. + */ +#define AL_REFERENCE_DISTANCE 0x1020 + +/** + * source specific rolloff factor + * Type: ALfloat + * Range: 0.0 - +inf + * + */ +#define AL_ROLLOFF_FACTOR 0x1021 + +/** + * Directional source, outer cone gain. + * + * Default: 0.0 + * Range: [0.0 - 1.0] + * Logarithmic + */ +#define AL_CONE_OUTER_GAIN 0x1022 + +/** + * Indicate distance above which sources are not + * attenuated using the inverse clamped distance model. + * + * Default: +inf + * Type: ALfloat + * Range: 0.0 - +inf + */ +#define AL_MAX_DISTANCE 0x1023 + +/** + * Sound samples: frequency, in units of Hertz [Hz]. + * This is the number of samples per second. Half of the + * sample frequency marks the maximum significant + * frequency component. + */ +#define AL_FREQUENCY 0x2001 +#define AL_BITS 0x2002 +#define AL_CHANNELS 0x2003 +#define AL_SIZE 0x2004 + +/** + * Buffer state. + * + * Not supported for public use (yet). + */ +#define AL_UNUSED 0x2010 +#define AL_PENDING 0x2011 +#define AL_PROCESSED 0x2012 + + +/** Errors: No Error. */ +#define AL_NO_ERROR AL_FALSE + +/** + * Invalid Name paramater passed to AL call. + */ +#define AL_INVALID_NAME 0xA001 + +/** + * Invalid parameter passed to AL call. + */ +#define AL_INVALID_ENUM 0xA002 + +/** + * Invalid enum parameter value. + */ +#define AL_INVALID_VALUE 0xA003 + +/** + * Illegal call. + */ +#define AL_INVALID_OPERATION 0xA004 + + +/** + * No mojo. + */ +#define AL_OUT_OF_MEMORY 0xA005 + + +/** Context strings: Vendor Name. */ +#define AL_VENDOR 0xB001 +#define AL_VERSION 0xB002 +#define AL_RENDERER 0xB003 +#define AL_EXTENSIONS 0xB004 + +/** Global tweakage. */ + +/** + * Doppler scale. Default 1.0 + */ +#define AL_DOPPLER_FACTOR 0xC000 + +/** + * Tweaks speed of propagation. + */ +#define AL_DOPPLER_VELOCITY 0xC001 + +/** + * Speed of Sound in units per second + */ +#define AL_SPEED_OF_SOUND 0xC003 + +/** + * Distance models + * + * used in conjunction with DistanceModel + * + * implicit: NONE, which disances distance attenuation. + */ +#define AL_DISTANCE_MODEL 0xD000 +#define AL_INVERSE_DISTANCE 0xD001 +#define AL_INVERSE_DISTANCE_CLAMPED 0xD002 +#define AL_LINEAR_DISTANCE 0xD003 +#define AL_LINEAR_DISTANCE_CLAMPED 0xD004 +#define AL_EXPONENT_DISTANCE 0xD005 +#define AL_EXPONENT_DISTANCE_CLAMPED 0xD006 + +/* + * Renderer State management + */ +AL_API void AL_APIENTRY alEnable( ALenum capability ); + +AL_API void AL_APIENTRY alDisable( ALenum capability ); + +AL_API ALboolean AL_APIENTRY alIsEnabled( ALenum capability ); + + +/* + * State retrieval + */ +AL_API const ALchar* AL_APIENTRY alGetString( ALenum param ); + +AL_API void AL_APIENTRY alGetBooleanv( ALenum param, ALboolean* data ); + +AL_API void AL_APIENTRY alGetIntegerv( ALenum param, ALint* data ); + +AL_API void AL_APIENTRY alGetFloatv( ALenum param, ALfloat* data ); + +AL_API void AL_APIENTRY alGetDoublev( ALenum param, ALdouble* data ); + +AL_API ALboolean AL_APIENTRY alGetBoolean( ALenum param ); + +AL_API ALint AL_APIENTRY alGetInteger( ALenum param ); + +AL_API ALfloat AL_APIENTRY alGetFloat( ALenum param ); + +AL_API ALdouble AL_APIENTRY alGetDouble( ALenum param ); + + +/* + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +AL_API ALenum AL_APIENTRY alGetError( void ); + + +/* + * Extension support. + * Query for the presence of an extension, and obtain any appropriate + * function pointers and enum values. + */ +AL_API ALboolean AL_APIENTRY alIsExtensionPresent( const ALchar* extname ); + +AL_API void* AL_APIENTRY alGetProcAddress( const ALchar* fname ); + +AL_API ALenum AL_APIENTRY alGetEnumValue( const ALchar* ename ); + + +/* + * LISTENER + * Listener represents the location and orientation of the + * 'user' in 3D-space. + * + * Properties include: - + * + * Gain AL_GAIN ALfloat + * Position AL_POSITION ALfloat[3] + * Velocity AL_VELOCITY ALfloat[3] + * Orientation AL_ORIENTATION ALfloat[6] (Forward then Up vectors) +*/ + +/* + * Set Listener parameters + */ +AL_API void AL_APIENTRY alListenerf( ALenum param, ALfloat value ); + +AL_API void AL_APIENTRY alListener3f( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); + +AL_API void AL_APIENTRY alListenerfv( ALenum param, const ALfloat* values ); + +AL_API void AL_APIENTRY alListeneri( ALenum param, ALint value ); + +AL_API void AL_APIENTRY alListener3i( ALenum param, ALint value1, ALint value2, ALint value3 ); + +AL_API void AL_APIENTRY alListeneriv( ALenum param, const ALint* values ); + +/* + * Get Listener parameters + */ +AL_API void AL_APIENTRY alGetListenerf( ALenum param, ALfloat* value ); + +AL_API void AL_APIENTRY alGetListener3f( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 ); + +AL_API void AL_APIENTRY alGetListenerfv( ALenum param, ALfloat* values ); + +AL_API void AL_APIENTRY alGetListeneri( ALenum param, ALint* value ); + +AL_API void AL_APIENTRY alGetListener3i( ALenum param, ALint *value1, ALint *value2, ALint *value3 ); + +AL_API void AL_APIENTRY alGetListeneriv( ALenum param, ALint* values ); + + +/** + * SOURCE + * Sources represent individual sound objects in 3D-space. + * Sources take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial arrangement etc. + * + * Properties include: - + * + * Gain AL_GAIN ALfloat + * Min Gain AL_MIN_GAIN ALfloat + * Max Gain AL_MAX_GAIN ALfloat + * Position AL_POSITION ALfloat[3] + * Velocity AL_VELOCITY ALfloat[3] + * Direction AL_DIRECTION ALfloat[3] + * Head Relative Mode AL_SOURCE_RELATIVE ALint (AL_TRUE or AL_FALSE) + * Reference Distance AL_REFERENCE_DISTANCE ALfloat + * Max Distance AL_MAX_DISTANCE ALfloat + * RollOff Factor AL_ROLLOFF_FACTOR ALfloat + * Inner Angle AL_CONE_INNER_ANGLE ALint or ALfloat + * Outer Angle AL_CONE_OUTER_ANGLE ALint or ALfloat + * Cone Outer Gain AL_CONE_OUTER_GAIN ALint or ALfloat + * Pitch AL_PITCH ALfloat + * Looping AL_LOOPING ALint (AL_TRUE or AL_FALSE) + * MS Offset AL_MSEC_OFFSET ALint or ALfloat + * Byte Offset AL_BYTE_OFFSET ALint or ALfloat + * Sample Offset AL_SAMPLE_OFFSET ALint or ALfloat + * Attached Buffer AL_BUFFER ALint + * State (Query only) AL_SOURCE_STATE ALint + * Buffers Queued (Query only) AL_BUFFERS_QUEUED ALint + * Buffers Processed (Query only) AL_BUFFERS_PROCESSED ALint + */ + +/* Create Source objects */ +AL_API void AL_APIENTRY alGenSources( ALsizei n, ALuint* sources ); + +/* Delete Source objects */ +AL_API void AL_APIENTRY alDeleteSources( ALsizei n, const ALuint* sources ); + +/* Verify a handle is a valid Source */ +AL_API ALboolean AL_APIENTRY alIsSource( ALuint sid ); + +/* + * Set Source parameters + */ +AL_API void AL_APIENTRY alSourcef( ALuint sid, ALenum param, ALfloat value ); + +AL_API void AL_APIENTRY alSource3f( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); + +AL_API void AL_APIENTRY alSourcefv( ALuint sid, ALenum param, const ALfloat* values ); + +AL_API void AL_APIENTRY alSourcei( ALuint sid, ALenum param, ALint value ); + +AL_API void AL_APIENTRY alSource3i( ALuint sid, ALenum param, ALint value1, ALint value2, ALint value3 ); + +AL_API void AL_APIENTRY alSourceiv( ALuint sid, ALenum param, const ALint* values ); + +/* + * Get Source parameters + */ +AL_API void AL_APIENTRY alGetSourcef( ALuint sid, ALenum param, ALfloat* value ); + +AL_API void AL_APIENTRY alGetSource3f( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); + +AL_API void AL_APIENTRY alGetSourcefv( ALuint sid, ALenum param, ALfloat* values ); + +AL_API void AL_APIENTRY alGetSourcei( ALuint sid, ALenum param, ALint* value ); + +AL_API void AL_APIENTRY alGetSource3i( ALuint sid, ALenum param, ALint* value1, ALint* value2, ALint* value3); + +AL_API void AL_APIENTRY alGetSourceiv( ALuint sid, ALenum param, ALint* values ); + + +/* + * Source vector based playback calls + */ + +/* Play, replay, or resume (if paused) a list of Sources */ +AL_API void AL_APIENTRY alSourcePlayv( ALsizei ns, const ALuint *sids ); + +/* Stop a list of Sources */ +AL_API void AL_APIENTRY alSourceStopv( ALsizei ns, const ALuint *sids ); + +/* Rewind a list of Sources */ +AL_API void AL_APIENTRY alSourceRewindv( ALsizei ns, const ALuint *sids ); + +/* Pause a list of Sources */ +AL_API void AL_APIENTRY alSourcePausev( ALsizei ns, const ALuint *sids ); + +/* + * Source based playback calls + */ + +/* Play, replay, or resume a Source */ +AL_API void AL_APIENTRY alSourcePlay( ALuint sid ); + +/* Stop a Source */ +AL_API void AL_APIENTRY alSourceStop( ALuint sid ); + +/* Rewind a Source (set playback postiton to beginning) */ +AL_API void AL_APIENTRY alSourceRewind( ALuint sid ); + +/* Pause a Source */ +AL_API void AL_APIENTRY alSourcePause( ALuint sid ); + +/* + * Source Queuing + */ +AL_API void AL_APIENTRY alSourceQueueBuffers( ALuint sid, ALsizei numEntries, const ALuint *bids ); + +AL_API void AL_APIENTRY alSourceUnqueueBuffers( ALuint sid, ALsizei numEntries, ALuint *bids ); + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. One Buffer can be used + * by multiple Sources. + * + * Properties include: - + * + * Frequency (Query only) AL_FREQUENCY ALint + * Size (Query only) AL_SIZE ALint + * Bits (Query only) AL_BITS ALint + * Channels (Query only) AL_CHANNELS ALint + */ + +/* Create Buffer objects */ +AL_API void AL_APIENTRY alGenBuffers( ALsizei n, ALuint* buffers ); + +/* Delete Buffer objects */ +AL_API void AL_APIENTRY alDeleteBuffers( ALsizei n, const ALuint* buffers ); + +/* Verify a handle is a valid Buffer */ +AL_API ALboolean AL_APIENTRY alIsBuffer( ALuint bid ); + +/* Specify the data to be copied into a buffer */ +AL_API void AL_APIENTRY alBufferData( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq ); + +/* + * Set Buffer parameters + */ +AL_API void AL_APIENTRY alBufferf( ALuint bid, ALenum param, ALfloat value ); + +AL_API void AL_APIENTRY alBuffer3f( ALuint bid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); + +AL_API void AL_APIENTRY alBufferfv( ALuint bid, ALenum param, const ALfloat* values ); + +AL_API void AL_APIENTRY alBufferi( ALuint bid, ALenum param, ALint value ); + +AL_API void AL_APIENTRY alBuffer3i( ALuint bid, ALenum param, ALint value1, ALint value2, ALint value3 ); + +AL_API void AL_APIENTRY alBufferiv( ALuint bid, ALenum param, const ALint* values ); + +/* + * Get Buffer parameters + */ +AL_API void AL_APIENTRY alGetBufferf( ALuint bid, ALenum param, ALfloat* value ); + +AL_API void AL_APIENTRY alGetBuffer3f( ALuint bid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); + +AL_API void AL_APIENTRY alGetBufferfv( ALuint bid, ALenum param, ALfloat* values ); + +AL_API void AL_APIENTRY alGetBufferi( ALuint bid, ALenum param, ALint* value ); + +AL_API void AL_APIENTRY alGetBuffer3i( ALuint bid, ALenum param, ALint* value1, ALint* value2, ALint* value3); + +AL_API void AL_APIENTRY alGetBufferiv( ALuint bid, ALenum param, ALint* values ); + + +/* + * Global Parameters + */ +AL_API void AL_APIENTRY alDopplerFactor( ALfloat value ); + +AL_API void AL_APIENTRY alDopplerVelocity( ALfloat value ); + +AL_API void AL_APIENTRY alSpeedOfSound( ALfloat value ); + +AL_API void AL_APIENTRY alDistanceModel( ALenum distanceModel ); + +/* + * Pointer-to-function types, useful for dynamically getting AL entry points. + */ +typedef void (AL_APIENTRY *LPALENABLE)( ALenum capability ); +typedef void (AL_APIENTRY *LPALDISABLE)( ALenum capability ); +typedef ALboolean (AL_APIENTRY *LPALISENABLED)( ALenum capability ); +typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)( ALenum param ); +typedef void (AL_APIENTRY *LPALGETBOOLEANV)( ALenum param, ALboolean* data ); +typedef void (AL_APIENTRY *LPALGETINTEGERV)( ALenum param, ALint* data ); +typedef void (AL_APIENTRY *LPALGETFLOATV)( ALenum param, ALfloat* data ); +typedef void (AL_APIENTRY *LPALGETDOUBLEV)( ALenum param, ALdouble* data ); +typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)( ALenum param ); +typedef ALint (AL_APIENTRY *LPALGETINTEGER)( ALenum param ); +typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)( ALenum param ); +typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)( ALenum param ); +typedef ALenum (AL_APIENTRY *LPALGETERROR)( void ); +typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar* extname ); +typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)( const ALchar* fname ); +typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)( const ALchar* ename ); +typedef void (AL_APIENTRY *LPALLISTENERF)( ALenum param, ALfloat value ); +typedef void (AL_APIENTRY *LPALLISTENER3F)( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (AL_APIENTRY *LPALLISTENERFV)( ALenum param, const ALfloat* values ); +typedef void (AL_APIENTRY *LPALLISTENERI)( ALenum param, ALint value ); +typedef void (AL_APIENTRY *LPALLISTENER3I)( ALenum param, ALint value1, ALint value2, ALint value3 ); +typedef void (AL_APIENTRY *LPALLISTENERIV)( ALenum param, const ALint* values ); +typedef void (AL_APIENTRY *LPALGETLISTENERF)( ALenum param, ALfloat* value ); +typedef void (AL_APIENTRY *LPALGETLISTENER3F)( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 ); +typedef void (AL_APIENTRY *LPALGETLISTENERFV)( ALenum param, ALfloat* values ); +typedef void (AL_APIENTRY *LPALGETLISTENERI)( ALenum param, ALint* value ); +typedef void (AL_APIENTRY *LPALGETLISTENER3I)( ALenum param, ALint *value1, ALint *value2, ALint *value3 ); +typedef void (AL_APIENTRY *LPALGETLISTENERIV)( ALenum param, ALint* values ); +typedef void (AL_APIENTRY *LPALGENSOURCES)( ALsizei n, ALuint* sources ); +typedef void (AL_APIENTRY *LPALDELETESOURCES)( ALsizei n, const ALuint* sources ); +typedef ALboolean (AL_APIENTRY *LPALISSOURCE)( ALuint sid ); +typedef void (AL_APIENTRY *LPALSOURCEF)( ALuint sid, ALenum param, ALfloat value); +typedef void (AL_APIENTRY *LPALSOURCE3F)( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (AL_APIENTRY *LPALSOURCEFV)( ALuint sid, ALenum param, const ALfloat* values ); +typedef void (AL_APIENTRY *LPALSOURCEI)( ALuint sid, ALenum param, ALint value); +typedef void (AL_APIENTRY *LPALSOURCE3I)( ALuint sid, ALenum param, ALint value1, ALint value2, ALint value3 ); +typedef void (AL_APIENTRY *LPALSOURCEIV)( ALuint sid, ALenum param, const ALint* values ); +typedef void (AL_APIENTRY *LPALGETSOURCEF)( ALuint sid, ALenum param, ALfloat* value ); +typedef void (AL_APIENTRY *LPALGETSOURCE3F)( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); +typedef void (AL_APIENTRY *LPALGETSOURCEFV)( ALuint sid, ALenum param, ALfloat* values ); +typedef void (AL_APIENTRY *LPALGETSOURCEI)( ALuint sid, ALenum param, ALint* value ); +typedef void (AL_APIENTRY *LPALGETSOURCE3I)( ALuint sid, ALenum param, ALint* value1, ALint* value2, ALint* value3); +typedef void (AL_APIENTRY *LPALGETSOURCEIV)( ALuint sid, ALenum param, ALint* values ); +typedef void (AL_APIENTRY *LPALSOURCEPLAYV)( ALsizei ns, const ALuint *sids ); +typedef void (AL_APIENTRY *LPALSOURCESTOPV)( ALsizei ns, const ALuint *sids ); +typedef void (AL_APIENTRY *LPALSOURCEREWINDV)( ALsizei ns, const ALuint *sids ); +typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)( ALsizei ns, const ALuint *sids ); +typedef void (AL_APIENTRY *LPALSOURCEPLAY)( ALuint sid ); +typedef void (AL_APIENTRY *LPALSOURCESTOP)( ALuint sid ); +typedef void (AL_APIENTRY *LPALSOURCEREWIND)( ALuint sid ); +typedef void (AL_APIENTRY *LPALSOURCEPAUSE)( ALuint sid ); +typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, const ALuint *bids ); +typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, ALuint *bids ); +typedef void (AL_APIENTRY *LPALGENBUFFERS)( ALsizei n, ALuint* buffers ); +typedef void (AL_APIENTRY *LPALDELETEBUFFERS)( ALsizei n, const ALuint* buffers ); +typedef ALboolean (AL_APIENTRY *LPALISBUFFER)( ALuint bid ); +typedef void (AL_APIENTRY *LPALBUFFERDATA)( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq ); +typedef void (AL_APIENTRY *LPALBUFFERF)( ALuint bid, ALenum param, ALfloat value); +typedef void (AL_APIENTRY *LPALBUFFER3F)( ALuint bid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (AL_APIENTRY *LPALBUFFERFV)( ALuint bid, ALenum param, const ALfloat* values ); +typedef void (AL_APIENTRY *LPALBUFFERI)( ALuint bid, ALenum param, ALint value); +typedef void (AL_APIENTRY *LPALBUFFER3I)( ALuint bid, ALenum param, ALint value1, ALint value2, ALint value3 ); +typedef void (AL_APIENTRY *LPALBUFFERIV)( ALuint bid, ALenum param, const ALint* values ); +typedef void (AL_APIENTRY *LPALGETBUFFERF)( ALuint bid, ALenum param, ALfloat* value ); +typedef void (AL_APIENTRY *LPALGETBUFFER3F)( ALuint bid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); +typedef void (AL_APIENTRY *LPALGETBUFFERFV)( ALuint bid, ALenum param, ALfloat* values ); +typedef void (AL_APIENTRY *LPALGETBUFFERI)( ALuint bid, ALenum param, ALint* value ); +typedef void (AL_APIENTRY *LPALGETBUFFER3I)( ALuint bid, ALenum param, ALint* value1, ALint* value2, ALint* value3); +typedef void (AL_APIENTRY *LPALGETBUFFERIV)( ALuint bid, ALenum param, ALint* values ); +typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)( ALfloat value ); +typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)( ALfloat value ); +typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)( ALfloat value ); +typedef void (AL_APIENTRY *LPALDISTANCEMODEL)( ALenum distanceModel ); + +#if defined(TARGET_OS_MAC) && TARGET_OS_MAC + #pragma export off +#endif + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif /* AL_AL_H */ diff -r 000000000000 -r f9476ff7637e include/AL/alc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/AL/alc.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,280 @@ +#ifndef AL_ALC_H +#define AL_ALC_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef ALC_API + #if defined(AL_LIBTYPE_STATIC) + #define ALC_API + #elif defined(_WIN32) + #define ALC_API __declspec(dllimport) + #else + #define ALC_API extern + #endif +#endif + +#if defined(_WIN32) + #define ALC_APIENTRY __cdecl +#else + #define ALC_APIENTRY +#endif + +#if defined(TARGET_OS_MAC) && TARGET_OS_MAC + #pragma export on +#endif + +/* + * The ALCAPI, ALCAPIENTRY, and ALC_INVALID macros are deprecated, but are + * included for applications porting code from AL 1.0 + */ +#define ALCAPI ALC_API +#define ALCAPIENTRY ALC_APIENTRY +#define ALC_INVALID 0 + + +#define ALC_VERSION_0_1 1 + +typedef struct ALCdevice_struct ALCdevice; +typedef struct ALCcontext_struct ALCcontext; + + +/** 8-bit boolean */ +typedef char ALCboolean; + +/** character */ +typedef char ALCchar; + +/** signed 8-bit 2's complement integer */ +typedef signed char ALCbyte; + +/** unsigned 8-bit integer */ +typedef unsigned char ALCubyte; + +/** signed 16-bit 2's complement integer */ +typedef short ALCshort; + +/** unsigned 16-bit integer */ +typedef unsigned short ALCushort; + +/** signed 32-bit 2's complement integer */ +typedef int ALCint; + +/** unsigned 32-bit integer */ +typedef unsigned int ALCuint; + +/** non-negative 32-bit binary integer size */ +typedef int ALCsizei; + +/** enumerated 32-bit value */ +typedef int ALCenum; + +/** 32-bit IEEE754 floating-point */ +typedef float ALCfloat; + +/** 64-bit IEEE754 floating-point */ +typedef double ALCdouble; + +/** void type (for opaque pointers only) */ +typedef void ALCvoid; + + +/* Enumerant values begin at column 50. No tabs. */ + +/* Boolean False. */ +#define ALC_FALSE 0 + +/* Boolean True. */ +#define ALC_TRUE 1 + +/** + * followed by Hz + */ +#define ALC_FREQUENCY 0x1007 + +/** + * followed by Hz + */ +#define ALC_REFRESH 0x1008 + +/** + * followed by AL_TRUE, AL_FALSE + */ +#define ALC_SYNC 0x1009 + +/** + * followed by Num of requested Mono (3D) Sources + */ +#define ALC_MONO_SOURCES 0x1010 + +/** + * followed by Num of requested Stereo Sources + */ +#define ALC_STEREO_SOURCES 0x1011 + +/** + * errors + */ + +/** + * No error + */ +#define ALC_NO_ERROR ALC_FALSE + +/** + * No device + */ +#define ALC_INVALID_DEVICE 0xA001 + +/** + * invalid context ID + */ +#define ALC_INVALID_CONTEXT 0xA002 + +/** + * bad enum + */ +#define ALC_INVALID_ENUM 0xA003 + +/** + * bad value + */ +#define ALC_INVALID_VALUE 0xA004 + +/** + * Out of memory. + */ +#define ALC_OUT_OF_MEMORY 0xA005 + + +/** + * The Specifier string for default device + */ +#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 +#define ALC_DEVICE_SPECIFIER 0x1005 +#define ALC_EXTENSIONS 0x1006 + +#define ALC_MAJOR_VERSION 0x1000 +#define ALC_MINOR_VERSION 0x1001 + +#define ALC_ATTRIBUTES_SIZE 0x1002 +#define ALC_ALL_ATTRIBUTES 0x1003 + + +/** + * Capture extension + */ +#define ALC_EXT_CAPTURE 1 +#define ALC_CAPTURE_DEVICE_SPECIFIER 0x310 +#define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311 +#define ALC_CAPTURE_SAMPLES 0x312 + + +/** + * ALC_ENUMERATE_ALL_EXT enums + */ +#define ALC_ENUMERATE_ALL_EXT 1 +#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012 +#define ALC_ALL_DEVICES_SPECIFIER 0x1013 + + +/* + * Context Management + */ +ALC_API ALCcontext * ALC_APIENTRY alcCreateContext( ALCdevice *device, const ALCint* attrlist ); + +ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent( ALCcontext *context ); + +ALC_API void ALC_APIENTRY alcProcessContext( ALCcontext *context ); + +ALC_API void ALC_APIENTRY alcSuspendContext( ALCcontext *context ); + +ALC_API void ALC_APIENTRY alcDestroyContext( ALCcontext *context ); + +ALC_API ALCcontext * ALC_APIENTRY alcGetCurrentContext( void ); + +ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice( ALCcontext *context ); + + +/* + * Device Management + */ +ALC_API ALCdevice * ALC_APIENTRY alcOpenDevice( const ALCchar *devicename ); + +ALC_API ALCboolean ALC_APIENTRY alcCloseDevice( ALCdevice *device ); + + +/* + * Error support. + * Obtain the most recent Context error + */ +ALC_API ALCenum ALC_APIENTRY alcGetError( ALCdevice *device ); + + +/* + * Extension support. + * Query for the presence of an extension, and obtain any appropriate + * function pointers and enum values. + */ +ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent( ALCdevice *device, const ALCchar *extname ); + +ALC_API void * ALC_APIENTRY alcGetProcAddress( ALCdevice *device, const ALCchar *funcname ); + +ALC_API ALCenum ALC_APIENTRY alcGetEnumValue( ALCdevice *device, const ALCchar *enumname ); + + +/* + * Query functions + */ +ALC_API const ALCchar * ALC_APIENTRY alcGetString( ALCdevice *device, ALCenum param ); + +ALC_API void ALC_APIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *data ); + + +/* + * Capture functions + */ +ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); + +ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice( ALCdevice *device ); + +ALC_API void ALC_APIENTRY alcCaptureStart( ALCdevice *device ); + +ALC_API void ALC_APIENTRY alcCaptureStop( ALCdevice *device ); + +ALC_API void ALC_APIENTRY alcCaptureSamples( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); + +/* + * Pointer-to-function types, useful for dynamically getting ALC entry points. + */ +typedef ALCcontext * (ALC_APIENTRY *LPALCCREATECONTEXT) (ALCdevice *device, const ALCint *attrlist); +typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)( ALCcontext *context ); +typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)( ALCcontext *context ); +typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)( ALCcontext *context ); +typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)( ALCcontext *context ); +typedef ALCcontext * (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)( void ); +typedef ALCdevice * (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)( ALCcontext *context ); +typedef ALCdevice * (ALC_APIENTRY *LPALCOPENDEVICE)( const ALCchar *devicename ); +typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)( ALCdevice *device ); +typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)( ALCdevice *device ); +typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)( ALCdevice *device, const ALCchar *extname ); +typedef void * (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname ); +typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname ); +typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)( ALCdevice *device, ALCenum param ); +typedef void (ALC_APIENTRY *LPALCGETINTEGERV)( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *dest ); +typedef ALCdevice * (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); +typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)( ALCdevice *device ); +typedef void (ALC_APIENTRY *LPALCCAPTURESTART)( ALCdevice *device ); +typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)( ALCdevice *device ); +typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); + +#if defined(TARGET_OS_MAC) && TARGET_OS_MAC + #pragma export off +#endif + +#if defined(__cplusplus) +} +#endif + +#endif /* AL_ALC_H */ diff -r 000000000000 -r f9476ff7637e include/AL/alext.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/AL/alext.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,183 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2008 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 + */ + +#ifndef AL_ALEXT_H +#define AL_ALEXT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef AL_LOKI_IMA_ADPCM_format +#define AL_LOKI_IMA_ADPCM_format 1 +#define AL_FORMAT_IMA_ADPCM_MONO16_EXT 0x10000 +#define AL_FORMAT_IMA_ADPCM_STEREO16_EXT 0x10001 +#endif + +#ifndef AL_LOKI_WAVE_format +#define AL_LOKI_WAVE_format 1 +#define AL_FORMAT_WAVE_EXT 0x10002 +#endif + +#ifndef AL_EXT_vorbis +#define AL_EXT_vorbis 1 +#define AL_FORMAT_VORBIS_EXT 0x10003 +#endif + +#ifndef AL_LOKI_quadriphonic +#define AL_LOKI_quadriphonic 1 +#define AL_FORMAT_QUAD8_LOKI 0x10004 +#define AL_FORMAT_QUAD16_LOKI 0x10005 +#endif + +#ifndef AL_EXT_float32 +#define AL_EXT_float32 1 +#define AL_FORMAT_MONO_FLOAT32 0x10010 +#define AL_FORMAT_STEREO_FLOAT32 0x10011 +#endif + +#ifndef AL_EXT_double +#define AL_EXT_double 1 +#define AL_FORMAT_MONO_DOUBLE_EXT 0x10012 +#define AL_FORMAT_STEREO_DOUBLE_EXT 0x10013 +#endif + +#ifndef ALC_LOKI_audio_channel +#define ALC_LOKI_audio_channel 1 +#define ALC_CHAN_MAIN_LOKI 0x500001 +#define ALC_CHAN_PCM_LOKI 0x500002 +#define ALC_CHAN_CD_LOKI 0x500003 +#endif + +#ifndef AL_EXT_MCFORMATS +#define AL_EXT_MCFORMATS 1 +#define AL_FORMAT_QUAD8 0x1204 +#define AL_FORMAT_QUAD16 0x1205 +#define AL_FORMAT_QUAD32 0x1206 +#define AL_FORMAT_REAR8 0x1207 +#define AL_FORMAT_REAR16 0x1208 +#define AL_FORMAT_REAR32 0x1209 +#define AL_FORMAT_51CHN8 0x120A +#define AL_FORMAT_51CHN16 0x120B +#define AL_FORMAT_51CHN32 0x120C +#define AL_FORMAT_61CHN8 0x120D +#define AL_FORMAT_61CHN16 0x120E +#define AL_FORMAT_61CHN32 0x120F +#define AL_FORMAT_71CHN8 0x1210 +#define AL_FORMAT_71CHN16 0x1211 +#define AL_FORMAT_71CHN32 0x1212 +#endif + +#ifndef AL_EXT_MULAW_MCFORMATS +#define AL_EXT_MULAW_MCFORMATS 1 +#define AL_FORMAT_MONO_MULAW 0x10014 +#define AL_FORMAT_STEREO_MULAW 0x10015 +#define AL_FORMAT_QUAD_MULAW 0x10021 +#define AL_FORMAT_REAR_MULAW 0x10022 +#define AL_FORMAT_51CHN_MULAW 0x10023 +#define AL_FORMAT_61CHN_MULAW 0x10024 +#define AL_FORMAT_71CHN_MULAW 0x10025 +#endif + +#ifndef AL_EXT_IMA4 +#define AL_EXT_IMA4 1 +#define AL_FORMAT_MONO_IMA4 0x1300 +#define AL_FORMAT_STEREO_IMA4 0x1301 +#endif + +#ifndef AL_EXT_STATIC_BUFFER +#define AL_EXT_STATIC_BUFFER 1 +typedef ALvoid (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALint,ALenum,ALvoid*,ALsizei,ALsizei); +#ifdef AL_ALEXT_PROTOTYPES +AL_API ALvoid AL_APIENTRY alBufferDataStatic(const ALint buffer, ALenum format, ALvoid *data, ALsizei len, ALsizei freq); +#endif +#endif + +#ifndef ALC_EXT_EFX +#define ALC_EXT_EFX 1 +#include "efx.h" +#endif + +#ifndef ALC_EXT_disconnect +#define ALC_EXT_disconnect 1 +#define ALC_CONNECTED 0x313 +#endif + +#ifndef ALC_EXT_thread_local_context +#define ALC_EXT_thread_local_context 1 +typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context); +typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void); +#ifdef AL_ALEXT_PROTOTYPES +ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context); +ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void); +#endif +#endif + +#ifndef AL_EXT_source_distance_model +#define AL_EXT_source_distance_model 1 +#define AL_SOURCE_DISTANCE_MODEL 0x200 +#endif + +#ifndef AL_SOFT_buffer_sub_data +#define AL_SOFT_buffer_sub_data 1 +#define AL_BYTE_RW_OFFSETS_SOFT 0x1031 +#define AL_SAMPLE_RW_OFFSETS_SOFT 0x1032 +typedef ALvoid (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei); +#ifdef AL_ALEXT_PROTOTYPES +AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length); +#endif +#endif + +#ifndef AL_SOFT_loop_points +#define AL_SOFT_loop_points 1 +#define AL_LOOP_POINTS_SOFT 0x2015 +#endif + +#ifndef AL_EXT_FOLDBACK +#define AL_EXT_FOLDBACK 1 +#define AL_EXT_FOLDBACK_NAME "AL_EXT_FOLDBACK" +#define AL_FOLDBACK_EVENT_BLOCK 0x4112 +#define AL_FOLDBACK_EVENT_START 0x4111 +#define AL_FOLDBACK_EVENT_STOP 0x4113 +#define AL_FOLDBACK_MODE_MONO 0x4101 +#define AL_FOLDBACK_MODE_STEREO 0x4102 +typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei); +typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK); +typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void); +#ifdef AL_ALEXT_PROTOTYPES +AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback); +AL_API void AL_APIENTRY alRequestFoldbackStop(void); +#endif +#endif + +#ifndef ALC_EXT_DEDICATED +#define ALC_EXT_DEDICATED 1 +#define AL_DEDICATED_GAIN 0x0001 +#define AL_EFFECT_DEDICATED_DIALOGUE 0x9001 +#define AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT 0x9000 +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff -r 000000000000 -r f9476ff7637e include/AL/efx-creative.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/AL/efx-creative.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,3 @@ +/* The tokens that would be defined here are already defined in efx.h. This + * empty file is here to provide compatibility with Windows-based projects + * that would include it. */ diff -r 000000000000 -r f9476ff7637e include/AL/efx.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/AL/efx.h Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,758 @@ +#ifndef AL_EFX_H +#define AL_EFX_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ALC_EXT_EFX_NAME "ALC_EXT_EFX" + +#define ALC_EFX_MAJOR_VERSION 0x20001 +#define ALC_EFX_MINOR_VERSION 0x20002 +#define ALC_MAX_AUXILIARY_SENDS 0x20003 + + +/* Listener properties. */ +#define AL_METERS_PER_UNIT 0x20004 + +/* Source properties. */ +#define AL_DIRECT_FILTER 0x20005 +#define AL_AUXILIARY_SEND_FILTER 0x20006 +#define AL_AIR_ABSORPTION_FACTOR 0x20007 +#define AL_ROOM_ROLLOFF_FACTOR 0x20008 +#define AL_CONE_OUTER_GAINHF 0x20009 +#define AL_DIRECT_FILTER_GAINHF_AUTO 0x2000A +#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO 0x2000B +#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO 0x2000C + + +/* Effect properties. */ + +/* Reverb effect parameters */ +#define AL_REVERB_DENSITY 0x0001 +#define AL_REVERB_DIFFUSION 0x0002 +#define AL_REVERB_GAIN 0x0003 +#define AL_REVERB_GAINHF 0x0004 +#define AL_REVERB_DECAY_TIME 0x0005 +#define AL_REVERB_DECAY_HFRATIO 0x0006 +#define AL_REVERB_REFLECTIONS_GAIN 0x0007 +#define AL_REVERB_REFLECTIONS_DELAY 0x0008 +#define AL_REVERB_LATE_REVERB_GAIN 0x0009 +#define AL_REVERB_LATE_REVERB_DELAY 0x000A +#define AL_REVERB_AIR_ABSORPTION_GAINHF 0x000B +#define AL_REVERB_ROOM_ROLLOFF_FACTOR 0x000C +#define AL_REVERB_DECAY_HFLIMIT 0x000D + +/* EAX Reverb effect parameters */ +#define AL_EAXREVERB_DENSITY 0x0001 +#define AL_EAXREVERB_DIFFUSION 0x0002 +#define AL_EAXREVERB_GAIN 0x0003 +#define AL_EAXREVERB_GAINHF 0x0004 +#define AL_EAXREVERB_GAINLF 0x0005 +#define AL_EAXREVERB_DECAY_TIME 0x0006 +#define AL_EAXREVERB_DECAY_HFRATIO 0x0007 +#define AL_EAXREVERB_DECAY_LFRATIO 0x0008 +#define AL_EAXREVERB_REFLECTIONS_GAIN 0x0009 +#define AL_EAXREVERB_REFLECTIONS_DELAY 0x000A +#define AL_EAXREVERB_REFLECTIONS_PAN 0x000B +#define AL_EAXREVERB_LATE_REVERB_GAIN 0x000C +#define AL_EAXREVERB_LATE_REVERB_DELAY 0x000D +#define AL_EAXREVERB_LATE_REVERB_PAN 0x000E +#define AL_EAXREVERB_ECHO_TIME 0x000F +#define AL_EAXREVERB_ECHO_DEPTH 0x0010 +#define AL_EAXREVERB_MODULATION_TIME 0x0011 +#define AL_EAXREVERB_MODULATION_DEPTH 0x0012 +#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF 0x0013 +#define AL_EAXREVERB_HFREFERENCE 0x0014 +#define AL_EAXREVERB_LFREFERENCE 0x0015 +#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR 0x0016 +#define AL_EAXREVERB_DECAY_HFLIMIT 0x0017 + +/* Chorus effect parameters */ +#define AL_CHORUS_WAVEFORM 0x0001 +#define AL_CHORUS_PHASE 0x0002 +#define AL_CHORUS_RATE 0x0003 +#define AL_CHORUS_DEPTH 0x0004 +#define AL_CHORUS_FEEDBACK 0x0005 +#define AL_CHORUS_DELAY 0x0006 + +/* Distortion effect parameters */ +#define AL_DISTORTION_EDGE 0x0001 +#define AL_DISTORTION_GAIN 0x0002 +#define AL_DISTORTION_LOWPASS_CUTOFF 0x0003 +#define AL_DISTORTION_EQCENTER 0x0004 +#define AL_DISTORTION_EQBANDWIDTH 0x0005 + +/* Echo effect parameters */ +#define AL_ECHO_DELAY 0x0001 +#define AL_ECHO_LRDELAY 0x0002 +#define AL_ECHO_DAMPING 0x0003 +#define AL_ECHO_FEEDBACK 0x0004 +#define AL_ECHO_SPREAD 0x0005 + +/* Flanger effect parameters */ +#define AL_FLANGER_WAVEFORM 0x0001 +#define AL_FLANGER_PHASE 0x0002 +#define AL_FLANGER_RATE 0x0003 +#define AL_FLANGER_DEPTH 0x0004 +#define AL_FLANGER_FEEDBACK 0x0005 +#define AL_FLANGER_DELAY 0x0006 + +/* Frequency shifter effect parameters */ +#define AL_FREQUENCY_SHIFTER_FREQUENCY 0x0001 +#define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION 0x0002 +#define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION 0x0003 + +/* Vocal morpher effect parameters */ +#define AL_VOCAL_MORPHER_PHONEMEA 0x0001 +#define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING 0x0002 +#define AL_VOCAL_MORPHER_PHONEMEB 0x0003 +#define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING 0x0004 +#define AL_VOCAL_MORPHER_WAVEFORM 0x0005 +#define AL_VOCAL_MORPHER_RATE 0x0006 + +/* Pitchshifter effect parameters */ +#define AL_PITCH_SHIFTER_COARSE_TUNE 0x0001 +#define AL_PITCH_SHIFTER_FINE_TUNE 0x0002 + +/* Ringmodulator effect parameters */ +#define AL_RING_MODULATOR_FREQUENCY 0x0001 +#define AL_RING_MODULATOR_HIGHPASS_CUTOFF 0x0002 +#define AL_RING_MODULATOR_WAVEFORM 0x0003 + +/* Autowah effect parameters */ +#define AL_AUTOWAH_ATTACK_TIME 0x0001 +#define AL_AUTOWAH_RELEASE_TIME 0x0002 +#define AL_AUTOWAH_RESONANCE 0x0003 +#define AL_AUTOWAH_PEAK_GAIN 0x0004 + +/* Compressor effect parameters */ +#define AL_COMPRESSOR_ONOFF 0x0001 + +/* Equalizer effect parameters */ +#define AL_EQUALIZER_LOW_GAIN 0x0001 +#define AL_EQUALIZER_LOW_CUTOFF 0x0002 +#define AL_EQUALIZER_MID1_GAIN 0x0003 +#define AL_EQUALIZER_MID1_CENTER 0x0004 +#define AL_EQUALIZER_MID1_WIDTH 0x0005 +#define AL_EQUALIZER_MID2_GAIN 0x0006 +#define AL_EQUALIZER_MID2_CENTER 0x0007 +#define AL_EQUALIZER_MID2_WIDTH 0x0008 +#define AL_EQUALIZER_HIGH_GAIN 0x0009 +#define AL_EQUALIZER_HIGH_CUTOFF 0x000A + +/* Effect type */ +#define AL_EFFECT_FIRST_PARAMETER 0x0000 +#define AL_EFFECT_LAST_PARAMETER 0x8000 +#define AL_EFFECT_TYPE 0x8001 + +/* Effect types, used with the AL_EFFECT_TYPE property */ +#define AL_EFFECT_NULL 0x0000 +#define AL_EFFECT_REVERB 0x0001 +#define AL_EFFECT_CHORUS 0x0002 +#define AL_EFFECT_DISTORTION 0x0003 +#define AL_EFFECT_ECHO 0x0004 +#define AL_EFFECT_FLANGER 0x0005 +#define AL_EFFECT_FREQUENCY_SHIFTER 0x0006 +#define AL_EFFECT_VOCAL_MORPHER 0x0007 +#define AL_EFFECT_PITCH_SHIFTER 0x0008 +#define AL_EFFECT_RING_MODULATOR 0x0009 +#define AL_EFFECT_AUTOWAH 0x000A +#define AL_EFFECT_COMPRESSOR 0x000B +#define AL_EFFECT_EQUALIZER 0x000C +#define AL_EFFECT_EAXREVERB 0x8000 + +/* Auxiliary Effect Slot properties. */ +#define AL_EFFECTSLOT_EFFECT 0x0001 +#define AL_EFFECTSLOT_GAIN 0x0002 +#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO 0x0003 + +/* NULL Auxiliary Slot ID to disable a source send. */ +#define AL_EFFECTSLOT_NULL 0x0000 + + +/* Filter properties. */ + +/* Lowpass filter parameters */ +#define AL_LOWPASS_GAIN 0x0001 +#define AL_LOWPASS_GAINHF 0x0002 + +/* Highpass filter parameters */ +#define AL_HIGHPASS_GAIN 0x0001 +#define AL_HIGHPASS_GAINLF 0x0002 + +/* Bandpass filter parameters */ +#define AL_BANDPASS_GAIN 0x0001 +#define AL_BANDPASS_GAINLF 0x0002 +#define AL_BANDPASS_GAINHF 0x0003 + +/* Filter type */ +#define AL_FILTER_FIRST_PARAMETER 0x0000 +#define AL_FILTER_LAST_PARAMETER 0x8000 +#define AL_FILTER_TYPE 0x8001 + +/* Filter types, used with the AL_FILTER_TYPE property */ +#define AL_FILTER_NULL 0x0000 +#define AL_FILTER_LOWPASS 0x0001 +#define AL_FILTER_HIGHPASS 0x0002 +#define AL_FILTER_BANDPASS 0x0003 + + +/* Effect object function types. */ +typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, ALuint*); +typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint); +typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat); +typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*); + +/* Filter object function types. */ +typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, ALuint*); +typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint); +typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat); +typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*); + +/* Auxiliary Effect Slot object function types. */ +typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*); +typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*); + +#ifdef AL_ALEXT_PROTOTYPES +AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects); +AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, ALuint *effects); +AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect); +AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue); +AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue); +AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, ALfloat *pflValues); +AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue); +AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue); +AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues); + +AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters); +AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, ALuint *filters); +AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter); +AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue); +AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue); +AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, ALfloat *pflValues); +AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue); +AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue); +AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues); + +AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots); +AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots); +AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues); +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue); +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue); +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues); +#endif + +/* Filter ranges and defaults. */ + +/* Lowpass filter */ +#define AL_LOWPASS_MIN_GAIN (0.0f) +#define AL_LOWPASS_MAX_GAIN (1.0f) +#define AL_LOWPASS_DEFAULT_GAIN (1.0f) + +#define AL_LOWPASS_MIN_GAINHF (0.0f) +#define AL_LOWPASS_MAX_GAINHF (1.0f) +#define AL_LOWPASS_DEFAULT_GAINHF (1.0f) + +/* Highpass filter */ +#define AL_HIGHPASS_MIN_GAIN (0.0f) +#define AL_HIGHPASS_MAX_GAIN (1.0f) +#define AL_HIGHPASS_DEFAULT_GAIN (1.0f) + +#define AL_HIGHPASS_MIN_GAINLF (0.0f) +#define AL_HIGHPASS_MAX_GAINLF (1.0f) +#define AL_HIGHPASS_DEFAULT_GAINLF (1.0f) + +/* Bandpass filter */ +#define AL_BANDPASS_MIN_GAIN (0.0f) +#define AL_BANDPASS_MAX_GAIN (1.0f) +#define AL_BANDPASS_DEFAULT_GAIN (1.0f) + +#define AL_BANDPASS_MIN_GAINHF (0.0f) +#define AL_BANDPASS_MAX_GAINHF (1.0f) +#define AL_BANDPASS_DEFAULT_GAINHF (1.0f) + +#define AL_BANDPASS_MIN_GAINLF (0.0f) +#define AL_BANDPASS_MAX_GAINLF (1.0f) +#define AL_BANDPASS_DEFAULT_GAINLF (1.0f) + + +/* Effect parameter ranges and defaults. */ + +/* Standard reverb effect */ +#define AL_REVERB_MIN_DENSITY (0.0f) +#define AL_REVERB_MAX_DENSITY (1.0f) +#define AL_REVERB_DEFAULT_DENSITY (1.0f) + +#define AL_REVERB_MIN_DIFFUSION (0.0f) +#define AL_REVERB_MAX_DIFFUSION (1.0f) +#define AL_REVERB_DEFAULT_DIFFUSION (1.0f) + +#define AL_REVERB_MIN_GAIN (0.0f) +#define AL_REVERB_MAX_GAIN (1.0f) +#define AL_REVERB_DEFAULT_GAIN (0.32f) + +#define AL_REVERB_MIN_GAINHF (0.0f) +#define AL_REVERB_MAX_GAINHF (1.0f) +#define AL_REVERB_DEFAULT_GAINHF (0.89f) + +#define AL_REVERB_MIN_DECAY_TIME (0.1f) +#define AL_REVERB_MAX_DECAY_TIME (20.0f) +#define AL_REVERB_DEFAULT_DECAY_TIME (1.49f) + +#define AL_REVERB_MIN_DECAY_HFRATIO (0.1f) +#define AL_REVERB_MAX_DECAY_HFRATIO (2.0f) +#define AL_REVERB_DEFAULT_DECAY_HFRATIO (0.83f) + +#define AL_REVERB_MIN_REFLECTIONS_GAIN (0.0f) +#define AL_REVERB_MAX_REFLECTIONS_GAIN (3.16f) +#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN (0.05f) + +#define AL_REVERB_MIN_REFLECTIONS_DELAY (0.0f) +#define AL_REVERB_MAX_REFLECTIONS_DELAY (0.3f) +#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY (0.007f) + +#define AL_REVERB_MIN_LATE_REVERB_GAIN (0.0f) +#define AL_REVERB_MAX_LATE_REVERB_GAIN (10.0f) +#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN (1.26f) + +#define AL_REVERB_MIN_LATE_REVERB_DELAY (0.0f) +#define AL_REVERB_MAX_LATE_REVERB_DELAY (0.1f) +#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY (0.011f) + +#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f) +#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f) +#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f) + +#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f) +#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f) +#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) + +#define AL_REVERB_MIN_DECAY_HFLIMIT AL_FALSE +#define AL_REVERB_MAX_DECAY_HFLIMIT AL_TRUE +#define AL_REVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE + +/* EAX reverb effect */ +#define AL_EAXREVERB_MIN_DENSITY (0.0f) +#define AL_EAXREVERB_MAX_DENSITY (1.0f) +#define AL_EAXREVERB_DEFAULT_DENSITY (1.0f) + +#define AL_EAXREVERB_MIN_DIFFUSION (0.0f) +#define AL_EAXREVERB_MAX_DIFFUSION (1.0f) +#define AL_EAXREVERB_DEFAULT_DIFFUSION (1.0f) + +#define AL_EAXREVERB_MIN_GAIN (0.0f) +#define AL_EAXREVERB_MAX_GAIN (1.0f) +#define AL_EAXREVERB_DEFAULT_GAIN (0.32f) + +#define AL_EAXREVERB_MIN_GAINHF (0.0f) +#define AL_EAXREVERB_MAX_GAINHF (1.0f) +#define AL_EAXREVERB_DEFAULT_GAINHF (0.89f) + +#define AL_EAXREVERB_MIN_GAINLF (0.0f) +#define AL_EAXREVERB_MAX_GAINLF (1.0f) +#define AL_EAXREVERB_DEFAULT_GAINLF (1.0f) + +#define AL_EAXREVERB_MIN_DECAY_TIME (0.1f) +#define AL_EAXREVERB_MAX_DECAY_TIME (20.0f) +#define AL_EAXREVERB_DEFAULT_DECAY_TIME (1.49f) + +#define AL_EAXREVERB_MIN_DECAY_HFRATIO (0.1f) +#define AL_EAXREVERB_MAX_DECAY_HFRATIO (2.0f) +#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO (0.83f) + +#define AL_EAXREVERB_MIN_DECAY_LFRATIO (0.1f) +#define AL_EAXREVERB_MAX_DECAY_LFRATIO (2.0f) +#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO (1.0f) + +#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN (0.0f) +#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN (3.16f) +#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN (0.05f) + +#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY (0.0f) +#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY (0.3f) +#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY (0.007f) + +#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f) + +#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN (0.0f) +#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN (10.0f) +#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN (1.26f) + +#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY (0.0f) +#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY (0.1f) +#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY (0.011f) + +#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f) + +#define AL_EAXREVERB_MIN_ECHO_TIME (0.075f) +#define AL_EAXREVERB_MAX_ECHO_TIME (0.25f) +#define AL_EAXREVERB_DEFAULT_ECHO_TIME (0.25f) + +#define AL_EAXREVERB_MIN_ECHO_DEPTH (0.0f) +#define AL_EAXREVERB_MAX_ECHO_DEPTH (1.0f) +#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH (0.0f) + +#define AL_EAXREVERB_MIN_MODULATION_TIME (0.04f) +#define AL_EAXREVERB_MAX_MODULATION_TIME (4.0f) +#define AL_EAXREVERB_DEFAULT_MODULATION_TIME (0.25f) + +#define AL_EAXREVERB_MIN_MODULATION_DEPTH (0.0f) +#define AL_EAXREVERB_MAX_MODULATION_DEPTH (1.0f) +#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH (0.0f) + +#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f) +#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f) +#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f) + +#define AL_EAXREVERB_MIN_HFREFERENCE (1000.0f) +#define AL_EAXREVERB_MAX_HFREFERENCE (20000.0f) +#define AL_EAXREVERB_DEFAULT_HFREFERENCE (5000.0f) + +#define AL_EAXREVERB_MIN_LFREFERENCE (20.0f) +#define AL_EAXREVERB_MAX_LFREFERENCE (1000.0f) +#define AL_EAXREVERB_DEFAULT_LFREFERENCE (250.0f) + +#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f) +#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f) +#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) + +#define AL_EAXREVERB_MIN_DECAY_HFLIMIT AL_FALSE +#define AL_EAXREVERB_MAX_DECAY_HFLIMIT AL_TRUE +#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE + +/* Chorus effect */ +#define AL_CHORUS_WAVEFORM_SINUSOID (0) +#define AL_CHORUS_WAVEFORM_TRIANGLE (1) + +#define AL_CHORUS_MIN_WAVEFORM (0) +#define AL_CHORUS_MAX_WAVEFORM (1) +#define AL_CHORUS_DEFAULT_WAVEFORM (1) + +#define AL_CHORUS_MIN_PHASE (-180) +#define AL_CHORUS_MAX_PHASE (180) +#define AL_CHORUS_DEFAULT_PHASE (90) + +#define AL_CHORUS_MIN_RATE (0.0f) +#define AL_CHORUS_MAX_RATE (10.0f) +#define AL_CHORUS_DEFAULT_RATE (1.1f) + +#define AL_CHORUS_MIN_DEPTH (0.0f) +#define AL_CHORUS_MAX_DEPTH (1.0f) +#define AL_CHORUS_DEFAULT_DEPTH (0.1f) + +#define AL_CHORUS_MIN_FEEDBACK (-1.0f) +#define AL_CHORUS_MAX_FEEDBACK (1.0f) +#define AL_CHORUS_DEFAULT_FEEDBACK (0.25f) + +#define AL_CHORUS_MIN_DELAY (0.0f) +#define AL_CHORUS_MAX_DELAY (0.016f) +#define AL_CHORUS_DEFAULT_DELAY (0.016f) + +/* Distortion effect */ +#define AL_DISTORTION_MIN_EDGE (0.0f) +#define AL_DISTORTION_MAX_EDGE (1.0f) +#define AL_DISTORTION_DEFAULT_EDGE (0.2f) + +#define AL_DISTORTION_MIN_GAIN (0.01f) +#define AL_DISTORTION_MAX_GAIN (1.0f) +#define AL_DISTORTION_DEFAULT_GAIN (0.05f) + +#define AL_DISTORTION_MIN_LOWPASS_CUTOFF (80.0f) +#define AL_DISTORTION_MAX_LOWPASS_CUTOFF (24000.0f) +#define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF (8000.0f) + +#define AL_DISTORTION_MIN_EQCENTER (80.0f) +#define AL_DISTORTION_MAX_EQCENTER (24000.0f) +#define AL_DISTORTION_DEFAULT_EQCENTER (3600.0f) + +#define AL_DISTORTION_MIN_EQBANDWIDTH (80.0f) +#define AL_DISTORTION_MAX_EQBANDWIDTH (24000.0f) +#define AL_DISTORTION_DEFAULT_EQBANDWIDTH (3600.0f) + +/* Echo effect */ +#define AL_ECHO_MIN_DELAY (0.0f) +#define AL_ECHO_MAX_DELAY (0.207f) +#define AL_ECHO_DEFAULT_DELAY (0.1f) + +#define AL_ECHO_MIN_LRDELAY (0.0f) +#define AL_ECHO_MAX_LRDELAY (0.404f) +#define AL_ECHO_DEFAULT_LRDELAY (0.1f) + +#define AL_ECHO_MIN_DAMPING (0.0f) +#define AL_ECHO_MAX_DAMPING (0.99f) +#define AL_ECHO_DEFAULT_DAMPING (0.5f) + +#define AL_ECHO_MIN_FEEDBACK (0.0f) +#define AL_ECHO_MAX_FEEDBACK (1.0f) +#define AL_ECHO_DEFAULT_FEEDBACK (0.5f) + +#define AL_ECHO_MIN_SPREAD (-1.0f) +#define AL_ECHO_MAX_SPREAD (1.0f) +#define AL_ECHO_DEFAULT_SPREAD (-1.0f) + +/* Flanger effect */ +#define AL_FLANGER_WAVEFORM_SINUSOID (0) +#define AL_FLANGER_WAVEFORM_TRIANGLE (1) + +#define AL_FLANGER_MIN_WAVEFORM (0) +#define AL_FLANGER_MAX_WAVEFORM (1) +#define AL_FLANGER_DEFAULT_WAVEFORM (1) + +#define AL_FLANGER_MIN_PHASE (-180) +#define AL_FLANGER_MAX_PHASE (180) +#define AL_FLANGER_DEFAULT_PHASE (0) + +#define AL_FLANGER_MIN_RATE (0.0f) +#define AL_FLANGER_MAX_RATE (10.0f) +#define AL_FLANGER_DEFAULT_RATE (0.27f) + +#define AL_FLANGER_MIN_DEPTH (0.0f) +#define AL_FLANGER_MAX_DEPTH (1.0f) +#define AL_FLANGER_DEFAULT_DEPTH (1.0f) + +#define AL_FLANGER_MIN_FEEDBACK (-1.0f) +#define AL_FLANGER_MAX_FEEDBACK (1.0f) +#define AL_FLANGER_DEFAULT_FEEDBACK (-0.5f) + +#define AL_FLANGER_MIN_DELAY (0.0f) +#define AL_FLANGER_MAX_DELAY (0.004f) +#define AL_FLANGER_DEFAULT_DELAY (0.002f) + +/* Frequency shifter effect */ +#define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY (0.0f) +#define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY (24000.0f) +#define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY (0.0f) + +#define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION (0) +#define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION (2) +#define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION (0) + +#define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN (0) +#define AL_FREQUENCY_SHIFTER_DIRECTION_UP (1) +#define AL_FREQUENCY_SHIFTER_DIRECTION_OFF (2) + +#define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION (0) +#define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION (2) +#define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION (0) + +/* Vocal morpher effect */ +#define AL_VOCAL_MORPHER_MIN_PHONEMEA (0) +#define AL_VOCAL_MORPHER_MAX_PHONEMEA (29) +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA (0) + +#define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24) +#define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING (24) +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING (0) + +#define AL_VOCAL_MORPHER_MIN_PHONEMEB (0) +#define AL_VOCAL_MORPHER_MAX_PHONEMEB (29) +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB (10) + +#define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24) +#define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING (24) +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING (0) + +#define AL_VOCAL_MORPHER_PHONEME_A (0) +#define AL_VOCAL_MORPHER_PHONEME_E (1) +#define AL_VOCAL_MORPHER_PHONEME_I (2) +#define AL_VOCAL_MORPHER_PHONEME_O (3) +#define AL_VOCAL_MORPHER_PHONEME_U (4) +#define AL_VOCAL_MORPHER_PHONEME_AA (5) +#define AL_VOCAL_MORPHER_PHONEME_AE (6) +#define AL_VOCAL_MORPHER_PHONEME_AH (7) +#define AL_VOCAL_MORPHER_PHONEME_AO (8) +#define AL_VOCAL_MORPHER_PHONEME_EH (9) +#define AL_VOCAL_MORPHER_PHONEME_ER (10) +#define AL_VOCAL_MORPHER_PHONEME_IH (11) +#define AL_VOCAL_MORPHER_PHONEME_IY (12) +#define AL_VOCAL_MORPHER_PHONEME_UH (13) +#define AL_VOCAL_MORPHER_PHONEME_UW (14) +#define AL_VOCAL_MORPHER_PHONEME_B (15) +#define AL_VOCAL_MORPHER_PHONEME_D (16) +#define AL_VOCAL_MORPHER_PHONEME_F (17) +#define AL_VOCAL_MORPHER_PHONEME_G (18) +#define AL_VOCAL_MORPHER_PHONEME_J (19) +#define AL_VOCAL_MORPHER_PHONEME_K (20) +#define AL_VOCAL_MORPHER_PHONEME_L (21) +#define AL_VOCAL_MORPHER_PHONEME_M (22) +#define AL_VOCAL_MORPHER_PHONEME_N (23) +#define AL_VOCAL_MORPHER_PHONEME_P (24) +#define AL_VOCAL_MORPHER_PHONEME_R (25) +#define AL_VOCAL_MORPHER_PHONEME_S (26) +#define AL_VOCAL_MORPHER_PHONEME_T (27) +#define AL_VOCAL_MORPHER_PHONEME_V (28) +#define AL_VOCAL_MORPHER_PHONEME_Z (29) + +#define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID (0) +#define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE (1) +#define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH (2) + +#define AL_VOCAL_MORPHER_MIN_WAVEFORM (0) +#define AL_VOCAL_MORPHER_MAX_WAVEFORM (2) +#define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM (0) + +#define AL_VOCAL_MORPHER_MIN_RATE (0.0f) +#define AL_VOCAL_MORPHER_MAX_RATE (10.0f) +#define AL_VOCAL_MORPHER_DEFAULT_RATE (1.41f) + +/* Pitch shifter effect */ +#define AL_PITCH_SHIFTER_MIN_COARSE_TUNE (-12) +#define AL_PITCH_SHIFTER_MAX_COARSE_TUNE (12) +#define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE (12) + +#define AL_PITCH_SHIFTER_MIN_FINE_TUNE (-50) +#define AL_PITCH_SHIFTER_MAX_FINE_TUNE (50) +#define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE (0) + +/* Ring modulator effect */ +#define AL_RING_MODULATOR_MIN_FREQUENCY (0.0f) +#define AL_RING_MODULATOR_MAX_FREQUENCY (8000.0f) +#define AL_RING_MODULATOR_DEFAULT_FREQUENCY (440.0f) + +#define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF (0.0f) +#define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF (24000.0f) +#define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF (800.0f) + +#define AL_RING_MODULATOR_SINUSOID (0) +#define AL_RING_MODULATOR_SAWTOOTH (1) +#define AL_RING_MODULATOR_SQUARE (2) + +#define AL_RING_MODULATOR_MIN_WAVEFORM (0) +#define AL_RING_MODULATOR_MAX_WAVEFORM (2) +#define AL_RING_MODULATOR_DEFAULT_WAVEFORM (0) + +/* Autowah effect */ +#define AL_AUTOWAH_MIN_ATTACK_TIME (0.0001f) +#define AL_AUTOWAH_MAX_ATTACK_TIME (1.0f) +#define AL_AUTOWAH_DEFAULT_ATTACK_TIME (0.06f) + +#define AL_AUTOWAH_MIN_RELEASE_TIME (0.0001f) +#define AL_AUTOWAH_MAX_RELEASE_TIME (1.0f) +#define AL_AUTOWAH_DEFAULT_RELEASE_TIME (0.06f) + +#define AL_AUTOWAH_MIN_RESONANCE (2.0f) +#define AL_AUTOWAH_MAX_RESONANCE (1000.0f) +#define AL_AUTOWAH_DEFAULT_RESONANCE (1000.0f) + +#define AL_AUTOWAH_MIN_PEAK_GAIN (0.00003f) +#define AL_AUTOWAH_MAX_PEAK_GAIN (31621.0f) +#define AL_AUTOWAH_DEFAULT_PEAK_GAIN (11.22f) + +/* Compressor effect */ +#define AL_COMPRESSOR_MIN_ONOFF (0) +#define AL_COMPRESSOR_MAX_ONOFF (1) +#define AL_COMPRESSOR_DEFAULT_ONOFF (1) + +/* Equalizer effect */ +#define AL_EQUALIZER_MIN_LOW_GAIN (0.126f) +#define AL_EQUALIZER_MAX_LOW_GAIN (7.943f) +#define AL_EQUALIZER_DEFAULT_LOW_GAIN (1.0f) + +#define AL_EQUALIZER_MIN_LOW_CUTOFF (50.0f) +#define AL_EQUALIZER_MAX_LOW_CUTOFF (800.0f) +#define AL_EQUALIZER_DEFAULT_LOW_CUTOFF (200.0f) + +#define AL_EQUALIZER_MIN_MID1_GAIN (0.126f) +#define AL_EQUALIZER_MAX_MID1_GAIN (7.943f) +#define AL_EQUALIZER_DEFAULT_MID1_GAIN (1.0f) + +#define AL_EQUALIZER_MIN_MID1_CENTER (200.0f) +#define AL_EQUALIZER_MAX_MID1_CENTER (3000.0f) +#define AL_EQUALIZER_DEFAULT_MID1_CENTER (500.0f) + +#define AL_EQUALIZER_MIN_MID1_WIDTH (0.01f) +#define AL_EQUALIZER_MAX_MID1_WIDTH (1.0f) +#define AL_EQUALIZER_DEFAULT_MID1_WIDTH (1.0f) + +#define AL_EQUALIZER_MIN_MID2_GAIN (0.126f) +#define AL_EQUALIZER_MAX_MID2_GAIN (7.943f) +#define AL_EQUALIZER_DEFAULT_MID2_GAIN (1.0f) + +#define AL_EQUALIZER_MIN_MID2_CENTER (1000.0f) +#define AL_EQUALIZER_MAX_MID2_CENTER (8000.0f) +#define AL_EQUALIZER_DEFAULT_MID2_CENTER (3000.0f) + +#define AL_EQUALIZER_MIN_MID2_WIDTH (0.01f) +#define AL_EQUALIZER_MAX_MID2_WIDTH (1.0f) +#define AL_EQUALIZER_DEFAULT_MID2_WIDTH (1.0f) + +#define AL_EQUALIZER_MIN_HIGH_GAIN (0.126f) +#define AL_EQUALIZER_MAX_HIGH_GAIN (7.943f) +#define AL_EQUALIZER_DEFAULT_HIGH_GAIN (1.0f) + +#define AL_EQUALIZER_MIN_HIGH_CUTOFF (4000.0f) +#define AL_EQUALIZER_MAX_HIGH_CUTOFF (16000.0f) +#define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF (6000.0f) + + +/* Source parameter value ranges and defaults. */ +#define AL_MIN_AIR_ABSORPTION_FACTOR (0.0f) +#define AL_MAX_AIR_ABSORPTION_FACTOR (10.0f) +#define AL_DEFAULT_AIR_ABSORPTION_FACTOR (0.0f) + +#define AL_MIN_ROOM_ROLLOFF_FACTOR (0.0f) +#define AL_MAX_ROOM_ROLLOFF_FACTOR (10.0f) +#define AL_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) + +#define AL_MIN_CONE_OUTER_GAINHF (0.0f) +#define AL_MAX_CONE_OUTER_GAINHF (1.0f) +#define AL_DEFAULT_CONE_OUTER_GAINHF (1.0f) + +#define AL_MIN_DIRECT_FILTER_GAINHF_AUTO AL_FALSE +#define AL_MAX_DIRECT_FILTER_GAINHF_AUTO AL_TRUE +#define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO AL_TRUE + +#define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_FALSE +#define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE +#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE + +#define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE +#define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE +#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE + + +/* Listener parameter value ranges and defaults. */ +#define AL_MIN_METERS_PER_UNIT FLT_MIN +#define AL_MAX_METERS_PER_UNIT FLT_MAX +#define AL_DEFAULT_METERS_PER_UNIT (1.0f) + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AL_EFX_H */ diff -r 000000000000 -r f9476ff7637e openal.pc.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openal.pc.in Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: OpenAL +Description: OpenAL is a cross-platform 3D audio API +Requires: @PKG_CONFIG_REQUIRES@ +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -l@LIBNAME@ @PKG_CONFIG_LIBS@ +Cflags: -I${includedir} @PKG_CONFIG_CFLAGS@ diff -r 000000000000 -r f9476ff7637e org/add-new-device.org --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org/add-new-device.org Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,103 @@ +#+title: all the steps to add a device to open-al +#+author: Robert McIntyre +#+email: rlm@mit.edu +#+description: how to add a new device to open-al +#+SETUPFILE: ../../aurellem/org/setup.org +#+INCLUDE: ../../aurellem/org/level-0.org + + +* How to add a new backend device + +* In Alc/backends/.c + +#+begin_src C +static const ALCchar _device[] = ; + +ALCboolean alc__init(BackendFuncs *func_list) +void alc__deinit(void) + +static ALCboolean _open_playback(ALCdevice *device, const ALCchar *deviceName) +static void _close_playback(ALCdevice *device) +static ALCboolean _reset_playback(ALCdevice *device) +static void _stop_playback(ALCdevice *device) +#+end_src + +#+begin_src C +void alc__probe(enum DevProbe type) +{ + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(Device); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(Device); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} +#+end_src + +#+begin_src C +static const BackendFuncs _funcs = { + _open_playback, + _close_playback, + _reset_playback, + _stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; +#+end_src + + +* In OpenAL32/Include/alMain.h : + +#+begin_src C +ALCboolean alc__init(BackendFuncs *func_list); +void alc__deinit(void); +void alc__probe(enum DevProbe type); + +#+end_src + +* In Alc/ALc.c : + #+begin_src C + { "", alc__init, + alc__deinit, alc__probe, EmptyFuncs }, + + #+end_src + +* In CMakeLists.txt +#+begin_src cmake +SET(ALC_OBJS Alc/ALc.c + Alc/ALu.c + Alc/alcConfig.c + Alc/alcDedicated.c + Alc/alcEcho.c + Alc/alcModulator.c + Alc/alcReverb.c + Alc/alcRing.c + Alc/alcThread.c + Alc/bs2b.c + Alc/helpers.c + Alc/hrtf.c + Alc/mixer.c + Alc/panning.c + # Default backends, always available + Alc/backends/loopback.c + Alc/backends/null.c + # : add device + Alc/backends/.c +) +#+end_src + + +* In ~/.alsoftrc + +#+begin_src conf +drivers = +#+end_src diff -r 000000000000 -r f9476ff7637e org/send.org --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org/send.org Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,11 @@ +#+title: send.c +#+author: Robert McIntyre +#+email: rlm@mit.edu +#+description: some code I wrote to extend open-al +#+SETUPFILE: ../../aurellem/org/setup.org +#+INCLUDE: ../../aurellem/org/level-0.org + + +#+include /home/r/proj/audio-send/Alc/backends/send.c src C + + diff -r 000000000000 -r f9476ff7637e record.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/record.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,1199 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2010 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 +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" +#include "alSource.h" +#include + + +typedef struct record_data { + volatile int killNow; + ALvoid *thread; + ALuint robert; + + ALvoid *mainBuffer; + ALvoid *auxBuffer; + ALuint size; + + ALuint *unPauseIDs; + int unPauseCount; + ALboolean mainFull; + ALboolean auxFull; + ALboolean auxReady; + ALboolean mainReady; + +} record_data; + + + + +#define UNUSED(x) (void)(x) +#define RUNONLY(n) \ + {static int __runonce = n; \ + if (__runonce-- <= 0){return;}} + +#define RUNAT(n) \ + {static int __runat = n; \ + if (0 != __runat--){return;}} + +/// al error handling code prototypes + +char* GetALErrorString(ALenum err); +char* GetALCErrorString(ALenum err); +void printError(void); +void init(ALCdevice *); +void init2(ALCdevice *); +void later(ALCdevice *); +void every(ALCdevice *); +void every2(ALCdevice *); +void step(ALCdevice *); + + +//synchronization +struct record_data; +void syncContexts(ALCcontext *, ALCcontext *); +void syncSources(ALsource *, ALsource *, ALCcontext *, ALCcontext *); +void pauseMainContext(struct record_data *); +void unPauseMainContext(struct record_data *); +void pauseAllSources(ALCcontext *); +static void saveAux(ALCdevice *); +static void swapInAux(ALCdevice *); +static void saveMain(ALCdevice *); +static void swapInMain(ALCdevice *); +void aux_alListenerf(ALenum param, ALfloat value); +void aux_alListener3f(ALenum param, ALfloat v1, ALfloat v2, ALfloat v3); + + +////////////// + + + +// remove all the sources from all the auxContexts +// take the mainContext and for each context, COPY +// the sources from the main context. + +// render each context and make their data available. + +// =alGenSources= (and =alGetError=) +// attach each source to the shared buffer using =alSourcei= + + +// need to make sure in the AudioRender class that we don't do +// anything to sources while the audio is being rendered, so make sure +// to maintain an "isDone" state as well. + +// Special device which allows simulation of multiple listeners +// hearing the same set of sources. Provides access to the rendered +// data for each listener. Designed to work with LWJGL. + +// first, the mainContext is the only context that is controlled by +// LWJGL. + +// One context will be created for each listener that we want. + +// Then, at the start of each render loop, first PAUSE the sources on +// all other contexts except the mainContext (this takes care of +// sources in the mainContext being shut down.) +// we will iterate through all +// the active sources in our mainContext (the one which is controlled +// through LWJGL), and take their sourceIDs. For each of these IDs +// and for each other context, we'll check some internal structure to +// see if we've already created a corresponding source. If that +// source exits, we will SYNCH it with the source which belongs to the +// mainContext. If it DOESN'T exist, we will CLONE the source from the +// main context and establish it in the other context. + +// Each render loop consists of noting all of the active sources in +// the mainContext and storing their IDs. Then, for each context, +// the correspondig sources to the active sources in the mainContext +// will be set to PLAY, and all sources in all other contexts will be +// set to PAUSE. aluMixData will be called for each context in this +// way. + +// approved by KittyCat + + +// CAN get id appropiate for us in the sorce-repated openal functions +// by quering *source->source + +// first, do it with only two listeners, two contexts, automatically +// created. + +static ALCcontext* mainContext = NULL; +static ALCcontext* auxContext = NULL; + + +void aux_alListenerf(ALenum param, ALfloat value){ + ALCcontext *current = alcGetCurrentContext(); + alcMakeContextCurrent(auxContext); + alListenerf(param, value); + alcMakeContextCurrent(current); +} + +void aux_alListener3f(ALenum param, ALfloat v1, ALfloat v2, ALfloat v3){ + ALCcontext *current = alcGetCurrentContext(); + alcMakeContextCurrent(auxContext); + alListener3f(param, v1, v2, v3); + alcMakeContextCurrent(current); +} + + + +void pauseAllSources(ALCcontext *ctx){ + ALCcontext *current = alcGetCurrentContext(); + alcMakeContextCurrent(ctx); + ALsource **src, **src_end; + src = ctx->ActiveSources; + src_end = src + ctx->ActiveSourceCount; + while(src != src_end){ + if (AL_PLAYING == (*src)->state){ + //if (AL_TRUE){ + ALuint source_id = (*src)->source; + //printf("pausing ONE source\n"); + alSourcePause(source_id); + } + src++; + } + alcMakeContextCurrent(current); +} + + +void init2(ALCdevice *Device){ + UNUSED(Device); + RUNONLY(1) + // the mainContext only ever has a FIXED number of sources! + // duplicate them ALL into auxContext! + alcMakeContextCurrent(auxContext);{ + UIntMap source_map= mainContext->SourceMap; + ALuint num_sources = source_map.size; + ALuint newSources[num_sources]; + alGenSources(num_sources, newSources);} + alcMakeContextCurrent(mainContext); + + +} + + + + /* +void syncSourcei(ALuint sourceID1, ALuint sourceID2, + ALCcontext *ctx1, ALCcontext *ctx2, + int numParams, ALenum *params){ + ALint values[numParams]; + ALCcontext current = alcGetCurrentContext(); + // get values + printf("getting values from source1\n"); + alcMakeContextCurrent(ctx1); + int i; + for(i=0; isource; + ALuint ID2 = source2->source; + ALCcontext *current = alcGetCurrentContext(); + //printf("***************\n"); + //printf("SYNCHING source %d with source %d\n", ID1, ID2); + + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_PITCH); + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_GAIN); + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_MAX_DISTANCE); + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_ROLLOFF_FACTOR); + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_REFERENCE_DISTANCE); + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_MIN_GAIN); + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_MAX_GAIN); + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_CONE_OUTER_GAIN); + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_CONE_INNER_ANGLE); + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_CONE_OUTER_ANGLE); + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_SEC_OFFSET); + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_SAMPLE_OFFSET); + DsyncSourcef(ID1,ID2,ctx1,ctx2,AL_BYTE_OFFSET); + + DsyncSource3f(ID1,ID2,ctx1,ctx2,AL_POSITION); + DsyncSource3f(ID1,ID2,ctx1,ctx2,AL_VELOCITY); + DsyncSource3f(ID1,ID2,ctx1,ctx2,AL_DIRECTION); + + DsyncSourcei(ID1,ID2,ctx1,ctx2,AL_SOURCE_RELATIVE); + DsyncSourcei(ID1,ID2,ctx1,ctx2,AL_LOOPING); + + + + + + // first, copy buffer patterns over. + + //source2->lSourceType = source1->lSourceType; + //source2->NumChannels = source1->NumChannels; + //source2->SampleSize = source1->SampleSize; + //source2->Buffer = source1->Buffer; + // source2->queue = source1->queue; + // source2->BuffersInQueue = source1-> BuffersInQueue; + // source2->BuffersPlayed = source1 -> BuffersPlayed; + + // then, duplicate the state. + + // handle static sources + //printf("handling Buffers\n"); + alcMakeContextCurrent(ctx1); + ALint source_type; + alGetSourcei(ID1, AL_SOURCE_TYPE, &source_type); + //printError(); + + if (AL_STATIC == source_type){ + //printf("setting STATIC source\n"); + ALint buffer_id1; + ALint buffer_id2; + alGetSourcei(ID1, AL_BUFFER, &buffer_id1); + alcMakeContextCurrent(ctx2); + alGetSourcei(ID2, AL_BUFFER, &buffer_id2); + if (buffer_id1 != buffer_id2){ + //printf("setting source2's buffer from %d to %d\n", buffer_id2, buffer_id1); + alSourcei(ID2, AL_BUFFER, buffer_id1); + //printError(); + } + else { + //printf("Buffers are already the same, doing nothing.\n"); + } + } + else { + // printf("not a static source!\n"); + } + + + + + + + alcMakeContextCurrent(ctx2); + + + + + //printf("setting STATE\n"); + alcMakeContextCurrent(ctx1); + ALint state1; + alGetSourcei(ID1, AL_SOURCE_STATE, &state1); + //printError(); + + alcMakeContextCurrent(ctx2); + ALint state2; + alGetSourcei(ID2, AL_SOURCE_STATE, &state2); + //printError(); + if (state1 != state2){ + switch (state1){ + case AL_INITIAL : /*printf("INITIAL\n")*/;alSourceRewind(ID2);break; + case AL_PLAYING : /*printf("PLAYING\n")*/;alSourcePlay(ID2);break; + case AL_PAUSED : /*printf("PAUSED\n")*/;alSourcePause(ID2);break; + case AL_STOPPED : /*printf("STOPPED\n")*/;alSourceStop(ID2);break; + } + } + //printError(); + + + + alcMakeContextCurrent(current); + +} + + +void syncContexts(ALCcontext *ctx1, ALCcontext *ctx2){ + // if there aren't sufficient sources in ctx2 to mirror the sources + // in ctx1, create them. + ALCcontext *current = alcGetCurrentContext(); + + UIntMap *sourceMap1 = &(ctx1->SourceMap); + UIntMap *sourceMap2 = &(ctx2->SourceMap); + + + ALuint sources1 = sourceMap1->size; + ALuint sources2 = sourceMap2->size; + + //printf("ctx1 has %d sources; ctx2 has %d sources\n", sources1, sources2); + + alcMakeContextCurrent(ctx2); + if (sources2 < sources1){ + ALuint numSources = sources1 - sources2; + ALuint newSources[numSources]; + alGenSources(numSources, newSources); + printf("adjusting...\n"); + printf("now ctx1 has %d sources; ctx2 has %d sources\n", + sourceMap1->size, sourceMap2->size); + } + //printError(); + + + alcMakeContextCurrent(current); + + + // after this, ctx2 is gauranteed to have at least as many sources + // as ctx1. Now, sync each source from ctx1 to the corresponding + // source in ctx2. + + int i; + + + + for(i = 0; i < sourceMap1->size; i++){ + syncSources((ALsource*)sourceMap1->array[i].value, + (ALsource*)sourceMap2->array[i].value, + ctx1, ctx2); + } + + + + + +} + + + +void pauseMainContext(record_data *data){ + //printf("pausing MainContext\n"); + data->unPauseCount = 0; + data->unPauseIDs = + (ALuint*)realloc(data->unPauseIDs, + sizeof(ALuint) * mainContext->ActiveSourceCount); + + ALsource **src, **src_end; + src = mainContext->ActiveSources; + src_end = src + mainContext->ActiveSourceCount; + + while(src != src_end){ + + if (AL_PLAYING == (*src)->state){ + ALuint source_id = (*src)->source; + data->unPauseIDs[data->unPauseCount++] = source_id; + alSourcePause(source_id); + } + src++; + } +} + + + + +void unPauseMainContext(record_data *data){ + int i; + for(i=0;iunPauseCount;i++){ + alSourcePlay(data->unPauseIDs[i]); + } +} + + +// a device brings along with it multiple pieces of state +// which have to be swapped in and out with each context. + +//static ALfloat DryBufferMain[BUFFERSIZE][MAXCHANNELS]; +//static ALfloat DryBufferAux[BUFFERSIZE][MAXCHANNELS]; +//static ALfloat PanningLUTMain[LUT_NUM][MAXCHANNELS]; +//static ALfloat PanningLUTAux[LUT_NUM][MAXCHANNELS]; +static ALfloat ClickRemovalMain[MAXCHANNELS]; +static ALfloat ClickRemovalAux[MAXCHANNELS]; +static ALfloat PendingClicksMain[MAXCHANNELS]; +static ALfloat PendingClicksAux[MAXCHANNELS]; + + + +static void saveAux(ALCdevice *Device){ + //memcpy(DryBufferAux, Device->DryBuffer, sizeof(ALfloat)*BUFFERSIZE*MAXCHANNELS); + //memcpy(PanningLUTAux, Device->PanningLUT, sizeof(ALfloat)*LUT_NUM*MAXCHANNELS); + memcpy(ClickRemovalAux, Device->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS); + memcpy(PendingClicksAux, Device->PendingClicks, sizeof(ALfloat)*MAXCHANNELS); +} +static void swapInAux(ALCdevice *Device){ + //memcpy(Device->DryBuffer, DryBufferAux, sizeof(ALfloat)*BUFFERSIZE*MAXCHANNELS); + //memcpy(Device->PanningLUT, PanningLUTAux, sizeof(ALfloat)*LUT_NUM*MAXCHANNELS); + memcpy(Device->ClickRemoval, ClickRemovalAux, sizeof(ALfloat)*MAXCHANNELS); + memcpy(Device->PendingClicks, PendingClicksAux, sizeof(ALfloat)*MAXCHANNELS); +} + + +static void saveMain(ALCdevice *Device){ + //memcpy(DryBufferMain, Device->DryBuffer, sizeof(ALfloat)*BUFFERSIZE*MAXCHANNELS); + //memcpy(PanningLUTMain, Device->PanningLUT, sizeof(ALfloat)*LUT_NUM*MAXCHANNELS); + memcpy(ClickRemovalMain, Device->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS); + memcpy(PendingClicksMain, Device->PendingClicks, sizeof(ALfloat)*MAXCHANNELS); +} +static void swapInMain(ALCdevice *Device){ + //memcpy(Device->DryBuffer, DryBufferMain, sizeof(ALfloat)*BUFFERSIZE*MAXCHANNELS); + //memcpy(Device->PanningLUT, PanningLUTMain, sizeof(ALfloat)*LUT_NUM*MAXCHANNELS); + memcpy(Device->ClickRemoval, ClickRemovalMain, sizeof(ALfloat)*MAXCHANNELS); + memcpy(Device->PendingClicks, PendingClicksMain, sizeof(ALfloat)*MAXCHANNELS); +} + + +static ALCcontext **currentContext; +static ALuint currentNumContext; +static void unLimitContext(ALCdevice *Device){ + Device->Contexts = currentContext; + Device->NumContexts = currentNumContext; +} + +static void limitContext(ALCdevice *Device, ALCcontext *ctx){ + currentContext = Device->Contexts; + currentNumContext = Device->NumContexts; + Device->Contexts = &ctx; + Device->NumContexts = 1; +} + + +void every2(ALCdevice *Device){ + + record_data *data = (record_data*)Device->ExtraData; + + if (data->mainFull){ + printf("data has not yet been extracted!\n"); + return; + } + + + syncContexts(mainContext , auxContext); + + alcMakeContextCurrent(auxContext); + limitContext(Device, auxContext); + swapInAux(Device); + aluMixData(Device, data->auxBuffer, Device->UpdateSize); + saveAux(Device); + unLimitContext(Device); + + + alcMakeContextCurrent(mainContext); + limitContext(Device, mainContext); + swapInMain(Device); + aluMixData(Device, data->mainBuffer, Device->UpdateSize); + saveMain(Device); + unLimitContext(Device); + + data->mainFull = AL_TRUE; + data->auxFull = AL_TRUE; + +} + + + + + + +void every(ALCdevice *Device){ + UNUSED(Device); + // by the time every() is called, mainContext and auxContext will + // have been initiliazed. + printf("+++++++\nevery is called\n"); + // print sourceid for all sources. + ALsource **src, **src_end; + // LockDevice(Device); + // pauseAllSources(auxContext); + src = mainContext->ActiveSources; + src_end = src + mainContext->ActiveSourceCount; + UIntMap source_map ; + int i; + + source_map= mainContext->SourceMap; + printf("max sources in the mainContext is %d\n", source_map.maxsize); + printf("current sources: %d\n", source_map.size); + + printf("their ID's are:\n"); + for(i = 0; i < source_map.size; i++){ + printf("%d, ",source_map.array[i].key); + } + printf("\n"); + source_map= auxContext->SourceMap; + printf("max sources in the auxContext is %d\n", source_map.maxsize); + printf("current sources: %d\n", source_map.size); + + printf("their ID's are:\n"); + for(i = 0; i < source_map.size; i++){ + printf("%d, ",source_map.array[i].key); + } + printf("\n"); + + while(src != src_end){ + + if (AL_PLAYING == (*src)->state){ + ALuint source_id = (*src)->source; + printf("source %d is AL_PLAYING\n",source_id); + } + src++; + + } + + + // UnlockDevice(Device); +} + + + + + + + +// debug printing + +#define DEBUG 0 + +#define dprintf(expr) {if (DEBUG) {printf(expr);}} + + + + +static const ALCchar recordDevice[] = "Aurellem"; + + + + + + +struct ALsource * cloneSource(struct ALsource *source); + +//struct ALsource * cloneSource(struct ALsource *source){ +// ALuint[1] sourceID; + + +//} + +int lock = 0; + +/* +static ALuint RecordProc() +{ + if (0 == lock){return 0;} + ALCdevice *Device = deviceInstance; + dprintf("RLM: recordProc is begun!!\n"); + printError(); + static int MixCount = 0 ; + if (Device->Connected) + { + // the device does not seem to be "ready" until this point. + init(Device); + //init2(Device); + printf("\nMix Cycle %d\n", MixCount++); + every(Device); + every2(Device); + //aluMixData(Device, NULL, Device->UpdateSize); + } + lock = 1; + return 0; +} +*/ + + + /* +static ALuint RecordProc(ALvoid *ptr) +{ + + printf("RLM: recordProc is begun!!\n"); + ALCdevice *Device = (ALCdevice*)ptr; + //printError(); + record_data *data = (record_data*)Device->ExtraData; + ALuint now, start; + ALuint64 avail, done; + const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 / + Device->Frequency / 2; + + done = 0; + start = timeGetTime(); + //static int MixCount = 0 ; + + + if(!data->killNow && Device->Connected) + { + printf("sext level breakfast manuever!\n"); + now = timeGetTime(); + + avail = (ALuint64)(now-start) * Device->Frequency / 1000; + if(avail < done) + { + + avail += (ALuint64)0xFFFFFFFFu*Device->Frequency/1000 - done; + done = 0; + } + if(avail-done < Device->UpdateSize) + { + //Sleep(restTime); + //continue; + } + + while(avail-done >= Device->UpdateSize) + { + // the device does not seem to be "ready" until this point. + init(Device); + //init2(Device); + //printf("\nMix Cycle %d\n", MixCount++); + //every(Device); + every2(Device); + //later(Device); + //printError(); + //aluMixData(Device, NULL, Device->UpdateSize); + //Sleep(3000); + done += Device->UpdateSize; + } + } + else { + printf("WARNGING\n"); + } + + return 0; +} +*/ + + + +static ALuint RecordProc(ALCdevice *Device) +{ + + //printf("RLM: recordProc is begun!!\n"); + // ALCdevice *Device = (ALCdevice*)ptr; + + record_data *data = (record_data*)Device->ExtraData; + + if(!data->killNow && Device->Connected) + { + + if (AL_TRUE) + { + // the device does not seem to be "ready" until this point. + init(Device); + //init2(Device); + //printf("\nMix Cycle %d\n", MixCount++); + //every(Device); + every2(Device); + //later(Device); + //printError(); + //aluMixData(Device, NULL, Device->UpdateSize); + //Sleep(3000); + } + } + else { + printf("WARNGING\n"); + } + + return 0; +} + + +void init(ALCdevice *Device){ + RUNONLY(1); + printf("one time init\n"); + printError(); + printf("auxContext : %p\n", auxContext); + auxContext = alcCreateContext(Device,NULL); + printf("auxContext : %p\n", auxContext); + printError(); + printf("mainContext : %p\n", mainContext); + mainContext = alcGetCurrentContext(); + printf("mainContext : %p\n", mainContext); + printError(); + printf("setting listener properties\n"); + alcMakeContextCurrent(auxContext); + ALfloat val1; + ALfloat val2; + ALfloat val3; + alGetListener3f(AL_POSITION, &val1, &val2, &val3); + printf("location is [%f,%f,%f]\n", val1,val2,val3); + alGetListener3f(AL_VELOCITY, &val1, &val2, &val3); + printf("velocity is [%f,%f,%f]\n", val1,val2,val3); + saveAux(Device); + saveMain(Device); +} + +void later(ALCdevice *Device){ + // run only the third time it is called + UNUSED(Device); + RUNAT(3); + printf("Suspending main Context....\n"); + alcSuspendContext(mainContext); + printError(); + printf("Switching to aux Context...\n"); + alcMakeContextCurrent(auxContext); + printError(); +} + + + +static ALCboolean record_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + record_data *data; + // stop any buffering for stdout, so that I can + // see the damm printf statements in my terminal immediatley + setbuf(stdout, NULL); + + dprintf("open_playback is called.\n"); + if(!deviceName) + deviceName = recordDevice; + else if(strcmp(deviceName, recordDevice) != 0) + return ALC_FALSE; + + data = (record_data*)calloc(1, sizeof(*data)); + + device->szDeviceName = strdup(deviceName); + data->robert = 5; + device->ExtraData = data; + + return ALC_TRUE; +} + +static void record_close_playback(ALCdevice *device) +{ + record_data *data = (record_data*)device->ExtraData; + dprintf("RLM: close playback called\n"); + free(data); + device->ExtraData = NULL; +} + + + + + +static ALCboolean record_reset_playback(ALCdevice *device) +{ + record_data *data = (record_data*)device->ExtraData; + dprintf("RLM: reset playback called\n"); + + ALuint channels=0, bits=0; + + + + switch(device->FmtType) + { + case DevFmtByte: + device->FmtType = DevFmtUByte; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + break; + case DevFmtUByte: + case DevFmtShort: + case DevFmtFloat: + break; + } + bits = BytesFromDevFmt(device->FmtType) * 8; + channels = ChannelsFromDevFmt(device->FmtChans); + data->size = device->UpdateSize * channels * bits / 8; + data->auxBuffer = malloc(data->size); + data->mainBuffer = malloc(data->size); + data->mainFull = AL_FALSE; + data->auxFull = AL_FALSE; + data->mainReady = AL_TRUE; + data->auxReady = AL_TRUE; + + if(!data->mainBuffer || !data->auxBuffer) + { + ERR("Buffer malloc failed\n"); + return ALC_FALSE; + } + + //data->thread = StartThread(RecordProc, device); + //data->thread = StartThread(noop, device); + + //TODO: shoudl free everything somewhere else! + + /* + if(data->thread == NULL) + { + free(data->mainBuffer); + free(data->auxBuffer); + free(data->unPauseIDs); + data->auxBuffer = NULL; + data->mainBuffer = NULL; + + return ALC_FALSE; + } + */ + return ALC_TRUE; +} + + + +static void record_stop_playback(ALCdevice *device) +{ + record_data *data = (record_data*)device->ExtraData; + dprintf("RLM: stop playback called\n"); + printf("szName is %s \n", device->szDeviceName); + printf("robert is %d \n", data->robert); + + + if(!data->thread) + return; + + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; +} + + +static const BackendFuncs record_funcs = { + record_open_playback, + record_close_playback, + record_reset_playback, + record_stop_playback, + NULL, + NULL, /* These would be filled with functions to */ + NULL, /* handle capturing audio if we did that. */ + NULL, + NULL, + NULL +}; + +ALCboolean alc_record_init(BackendFuncs *func_list) +{ + dprintf("RECORD: I'm InIT111\n"); + *func_list = record_funcs; + return ALC_TRUE; +} + +void alc_record_deinit(void) +{ +} + +void alc_record_probe(enum DevProbe type) +{ + dprintf("RECORD: I'm being probed! :/\n"); + + switch(type) + { + case DEVICE_PROBE: + AppendDeviceList(recordDevice); + break; + case ALL_DEVICE_PROBE: + AppendAllDeviceList(recordDevice); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} + + + + +void printError(void){ + ALenum error = alGetError(); + printf("%s\n", GetALCErrorString(error)); + printf("%s\n", GetALErrorString(error)); +} + + + +char* GetALCErrorString(ALenum err) +{ + switch(err) + { + case ALC_NO_ERROR: + return "AL_NO_ERROR"; + break; + + case ALC_INVALID_DEVICE: + return "ALC_INVALID_DEVICE"; + break; + + case ALC_INVALID_CONTEXT: + return "ALC_INVALID_CONTEXT"; + break; + + case ALC_INVALID_ENUM: + return "ALC_INVALID_ENUM"; + break; + + case ALC_INVALID_VALUE: + return "ALC_INVALID_VALUE"; + break; + + case ALC_OUT_OF_MEMORY: + return "ALC_OUT_OF_MEMORY"; + break; + }; + return "UNknown error."; +} + + + + + + + +char* GetALErrorString(ALenum err) +{ + switch(err) + { + case AL_NO_ERROR: + return "AL_NO_ERROR"; + break; + + case AL_INVALID_NAME: + return "AL_INVALID_NAME"; + break; + + case AL_INVALID_ENUM: + return "AL_INVALID_ENUM"; + break; + + case AL_INVALID_VALUE: + return "AL_INVALID_VALUE"; + break; + + case AL_INVALID_OPERATION: + return "AL_INVALID_OPERATION"; + break; + + case AL_OUT_OF_MEMORY: + return "AL_OUT_OF_MEMORY"; + break; + }; + return "UNknown error."; +} + + + + + + + +#include "com_aurellem_audioPlay_TestCall.h" + +JNIEXPORT void JNICALL Java_com_aurellem_audioPlay_TestCall_nprintGarbage +(JNIEnv *env, jclass clazz){ + UNUSED(env);UNUSED(clazz); + printf("Native! method* zzz\n"); + return; +} + +#include "com_aurellem_audioPlay_WavCaptureMaybe.h" + +JNIEXPORT void JNICALL Java_com_aurellem_audioPlay_WavCaptureMaybe_nrecord_1whatever +(JNIEnv *env, jclass clazz){ + UNUSED(env);UNUSED(clazz); + printf("record_aurellem_whatever!"); +} + + + + + + + + + + + + + + + + +//////////////////////////// Real JNI stuff //////////////////////////////// +void getMainSamples(ALCvoid *buffer){ + UNUSED(buffer); + // memcpy(mainBuffer, (ALubyte*) buffer, samples); +} + +void getAuxSamples(ALCvoid *buffer){ + UNUSED(buffer); + // memcpy(auxBuffer, (ALubyte*) buffer, samples); +} + +#include "com_jme3_capture_RecordAudioRenderer.h" + +/* + * Class: com_jme3_capture_RecordAudioRenderer + * Method: helloEveryone + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_jme3_capture_RecordAudioRenderer_helloEveryone +(JNIEnv *env, jclass clazz){ + UNUSED(env);UNUSED(clazz); + printf("\n**************\nC from Java: I'm audioRecorder :)\n***********\n"); +} + + +/* + * Class: com_jme3_capture_RecordAudioRenderer + * Method: nstep + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_jme3_capture_RecordAudioRenderer_nstep +(JNIEnv *env, jclass clazz, jlong device){ + UNUSED(env);UNUSED(clazz);UNUSED(device); + //printf("C from Java: I'm audioRecorder -- nstep :)\n"); + RecordProc((ALCdevice*)((intptr_t)device)); +} + +/* + * Class: com_jme3_capture_RecordAudioRenderer + * Method: ngetMainSamples + * Signature: (JLjava/nio/ByteBuffer;I)V + */ +JNIEXPORT void JNICALL Java_com_jme3_capture_RecordAudioRenderer_ngetMainSamples +(JNIEnv *env, jclass clazz, jlong device, jobject buffer, jint position){ + UNUSED(clazz); + + ALvoid *buffer_address = + ((ALbyte *)(((char*)(*env)->GetDirectBufferAddress(env, buffer)) + position)); + ALCdevice *recorder = (ALCdevice*) ((intptr_t)device); + //printf("getMainSamples: device is %p\n", recorder); + record_data *data = (record_data*)recorder->ExtraData; + if (!data->mainFull){ + printf("data is not ready!\n"); + return; + } + memcpy(buffer_address, data->mainBuffer, data->size); + data->mainFull = AL_FALSE; +} + +/* + * Class: com_jme3_capture_RecordAudioRenderer + * Method: ngetAuxSamples + * Signature: (JLjava/nio/ByteBuffer;I)V + */ +JNIEXPORT void JNICALL Java_com_jme3_capture_RecordAudioRenderer_ngetAuxSamples + (JNIEnv *env, jclass clazz, jlong device, jobject buffer, jint position){ + UNUSED(clazz); + + ALvoid *buffer_address = + ((ALbyte *)(((char*)(*env)->GetDirectBufferAddress(env, buffer)) + position)); + ALCdevice *recorder = (ALCdevice*) ((intptr_t)device); + //printf("getMainSamples: device is %p\n", recorder); + record_data *data = (record_data*)recorder->ExtraData; + + memcpy(buffer_address, data->auxBuffer, data->size); + +} + + +/* + * Class: com_jme3_capture_RecordAudioRenderer + * Method: nsetAuxListener3f + * Signature: (IFFF)V + */ +JNIEXPORT void JNICALL Java_com_jme3_capture_RecordAudioRenderer_nsetAuxListener3f +(JNIEnv *env, jclass clazz, jint pname, jfloat v1, jfloat v2, jfloat v3){ + UNUSED(env);UNUSED(clazz); + aux_alListener3f(pname, v1, v2, v3); +} + +/* + * Class: com_jme3_capture_RecordAudioRenderer + * Method: nsetAuxListenerf + * Signature: (IF)V + */ +JNIEXPORT void JNICALL Java_com_jme3_capture_RecordAudioRenderer_nsetAuxListenerf +(JNIEnv *env, jclass clazz, jint pname, jfloat v1){ + UNUSED(env);UNUSED(clazz); + aux_alListenerf(pname, v1); +} + + + +/* +static void JNICALL Java_org_lwjgl_openal_ALC11_nalcCaptureSamples +(JNIEnv *env, jclass clazz, jlong device, jobject buffer, jint position, jint samples) { + ALvoid *buffer_address = + ((ALbyte *)(((char*)(*env)->GetDirectBufferAddress(env, buffer)) + position)); + alcCaptureSamples((ALCdevice*) ((intptr_t)device), buffer_address, samples); +} +*/ + + + + + + diff -r 000000000000 -r f9476ff7637e src/body/ear.clj --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/body/ear.clj Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,53 @@ + +(ns body.ear) +(use 'cortex.world) +(use 'cortex.import) +(use 'clojure.contrib.def) +(cortex.import/mega-import-jme3) +(rlm.rlm-commands/help) +(import java.nio.ByteBuffer) +(import java.awt.image.BufferedImage) +(import java.awt.Color) +(import java.awt.Dimension) +(import java.awt.Graphics) +(import java.awt.Graphics2D) +(import java.awt.event.WindowAdapter) +(import java.awt.event.WindowEvent) +(import java.awt.image.BufferedImage) +(import java.nio.ByteBuffer) +(import javax.swing.JFrame) +(import javax.swing.JPanel) +(import javax.swing.SwingUtilities) +(import javax.swing.ImageIcon) +(import javax.swing.JOptionPane) +(import java.awt.image.ImageObserver) +(in-ns 'body.ear) +(import 'com.jme3.capture.SoundProcessor) + + +(defn sound-processor + "deals with converting ByteBuffers into Vectors of Bytes so that the + continuation functions can be defined in terms of immutable stuff." + [continuation] + (proxy [SoundProcessor] [] + (cleanup []) + (process + [#^ByteBuffer audioSamples numSamples] + + (let [byte-array (make-array Byte numSamples)] + (.get audioSamples byte-array 0 numSamples) + + (continuation + (vec byte-array)))))) + + +(defn add-ear + "add an ear to the world. The continuation function will be called + on the FFT or the sounds which the ear hears in the given + timeframe. Sound is 3D." + [world ear continuation] + (let [listener (Listener.) + renderer (.getAudioRenderer world)] + (.addListener renderer listener) + (.registerSoundProcessor renderer listener (sound-processor continuation)) + listener)) diff -r 000000000000 -r f9476ff7637e todo.org --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/todo.org Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,53 @@ +* Tasks to do relating to the Aurellem recorder device. + + + +** Cleanup +- [ ] come up with a good name for the recording device. +- [X] shift stepping functions to =nstep= +- [ ] make a new project that Uses openal-soft but is not inside it +- [ ] transfer all of my stuff to this project (everntually, we'll hava a "record" artifact) +- [ ] put everything into an org file +- [ ] make a post on aurellem +- [ ] make a post on jMonkeyEngine forums + +** New Features +- [ ] enable multiple listeners / multiple data +- [ ] add support for streaming sources. + + +** Java Integration +- [ ] watch out for playSourceInstance and make sure it's handled correctly! +- [ ] figure out how to compile for all platforms +- [ ] get jMonkeyEngine to auto-load the new artifacts. +- [X] properly extend the AudioRenderer instead of copying everything over. +- [ ] combine with video recorder to make an easy-to-use thing for the main case. + +** Clojure Integration +- [ ] abstract to closure =(ear)= function. + +** Testing +- [X] make sure it works for effects (NOT GOING TO DO!), jMonkeyEngine doesn't do it well. +- [ ] make an automated test using two listeners that are in the exact same area. + + +** Conversion to multiple listeners. +- [ ] make every function call manage context switching automatically +- [ ] gather up all data necessary for context switching in a struct. +- [ ] nmake a map/list of these structures in the ExtraData device field. +- [ ] + + + + + + + + + + + + + + + diff -r 000000000000 -r f9476ff7637e update-jme.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/update-jme.sh Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,15 @@ +#!/bin/sh + + +ln -sf /home/r/proj/audio-send/build/libopenal.so \ + /home/r/proj/jMonkeyEngine3/lib/lwjgl/native/linux/audioSend/libopenal64.so; + +cp -v /home/r/proj/cortex/assets/Sounds/pure.wav /home/r/proj/jMonkeyEngine3/bin/Sound/Effects/pure.wav; + +cp -v /home/r/proj/cortex/assets/Sounds/dream.wav /home/r/proj/jMonkeyEngine3/bin/Sound/Effects/dream.wav; + +cp -v /home/r/proj/cortex/assets/Sounds/silence.wav /home/r/proj/jMonkeyEngine3/bin/Sound/Effects/silence.wav; + + +cd /home/r/proj/jMonkeyEngine3/lib/lwjgl; +jar uf jME3-lwjgl-natives.jar native/linux/audioSend/ diff -r 000000000000 -r f9476ff7637e utils/openal-info.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/utils/openal-info.c Tue Oct 25 13:02:31 2011 -0700 @@ -0,0 +1,365 @@ +/* + * OpenAL Info Utility + * + * Copyright (c) 2010 by Chris Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "AL/alc.h" +#include "AL/al.h" +#include "AL/alext.h" + +#ifndef ALC_ENUMERATE_ALL_EXT +#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012 +#define ALC_ALL_DEVICES_SPECIFIER 0x1013 +#endif + +#ifndef ALC_EXT_EFX +#define ALC_EFX_MAJOR_VERSION 0x20001 +#define ALC_EFX_MINOR_VERSION 0x20002 +#define ALC_MAX_AUXILIARY_SENDS 0x20003 +#define AL_FILTER_TYPE 0x8001 +#define AL_FILTER_NULL 0x0000 +#define AL_FILTER_LOWPASS 0x0001 +#define AL_FILTER_HIGHPASS 0x0002 +#define AL_FILTER_BANDPASS 0x0003 +#define AL_EFFECT_TYPE 0x8001 +#define AL_EFFECT_NULL 0x0000 +#define AL_EFFECT_EAXREVERB 0x8000 +#define AL_EFFECT_REVERB 0x0001 +#define AL_EFFECT_CHORUS 0x0002 +#define AL_EFFECT_DISTORTION 0x0003 +#define AL_EFFECT_ECHO 0x0004 +#define AL_EFFECT_FLANGER 0x0005 +#define AL_EFFECT_FREQUENCY_SHIFTER 0x0006 +#define AL_EFFECT_VOCAL_MORPHER 0x0007 +#define AL_EFFECT_PITCH_SHIFTER 0x0008 +#define AL_EFFECT_RING_MODULATOR 0x0009 +#define AL_EFFECT_AUTOWAH 0x000A +#define AL_EFFECT_COMPRESSOR 0x000B +#define AL_EFFECT_EQUALIZER 0x000C +typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint); +#endif +static LPALGENFILTERS palGenFilters; +static LPALDELETEFILTERS palDeleteFilters; +static LPALFILTERI palFilteri; +static LPALGENEFFECTS palGenEffects; +static LPALDELETEEFFECTS palDeleteEffects; +static LPALEFFECTI palEffecti; + +#ifndef ALC_EXT_DEDICATED +#define AL_EFFECT_DEDICATED_DIALOGUE 0x9001 +#define AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT 0x9000 +#endif + + +#define MAX_WIDTH 80 + +static void printList(const char *list, char separator) +{ + size_t col = MAX_WIDTH, len; + const char *indent = " "; + const char *next; + + if(!list || *list == '\0') + { + fprintf(stdout, "\n%s!!! none !!!\n", indent); + return; + } + + do { + next = strchr(list, separator); + if(next) + { + len = next-list; + do { + next++; + } while(*next == separator); + } + else + len = strlen(list); + + if(len + col + 2 >= MAX_WIDTH) + { + fprintf(stdout, "\n%s", indent); + col = strlen(indent); + } + else + { + fputc(' ', stdout); + col++; + } + + len = fwrite(list, 1, len, stdout); + col += len; + + if(!next || *next == '\0') + break; + fputc(',', stdout); + col++; + + list = next; + } while(1); + fputc('\n', stdout); +} + +static void printDeviceList(const char *list) +{ + if(!list || *list == '\0') + printf(" !!! none !!!\n"); + else do { + printf(" %s\n", list); + list += strlen(list) + 1; + } while(*list != '\0'); +} + + +static ALenum checkALErrors(int linenum) +{ + ALenum err = alGetError(); + if(err != AL_NO_ERROR) + printf("OpenAL Error: %s (0x%x), @ %d\n", alGetString(err), err, linenum); + return err; +} +#define checkALErrors() checkALErrors(__LINE__) + +static ALCenum checkALCErrors(ALCdevice *device, int linenum) +{ + ALCenum err = alcGetError(device); + if(err != ALC_NO_ERROR) + printf("ALC Error: %s (0x%x), @ %d\n", alcGetString(device, err), err, linenum); + return err; +} +#define checkALCErrors(x) checkALCErrors((x),__LINE__) + + +static void printALCInfo(ALCdevice *device) +{ + ALCint major, minor; + + alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &major); + alcGetIntegerv(device, ALC_MINOR_VERSION, 1, &minor); + if(checkALCErrors(device) == ALC_NO_ERROR) + printf("ALC version: %d.%d\n", major, minor); + if(device) + { + printf("ALC extensions:"); + printList(alcGetString(device, ALC_EXTENSIONS), ' '); + checkALCErrors(device); + } +} + +static void printALInfo(void) +{ + printf("OpenAL vendor string: %s\n", alGetString(AL_VENDOR)); + printf("OpenAL renderer string: %s\n", alGetString(AL_RENDERER)); + printf("OpenAL version string: %s\n", alGetString(AL_VERSION)); + printf("OpenAL extensions:"); + printList(alGetString(AL_EXTENSIONS), ' '); + checkALErrors(); +} + +static void printEFXInfo(ALCdevice *device) +{ + ALCint major, minor, sends; + ALuint obj; + int i; + const ALenum filters[] = { + AL_FILTER_LOWPASS, AL_FILTER_HIGHPASS, AL_FILTER_BANDPASS, + AL_FILTER_NULL + }; + char filterNames[] = "Low-pass,High-pass,Band-pass,"; + const ALenum effects[] = { + AL_EFFECT_EAXREVERB, AL_EFFECT_REVERB, AL_EFFECT_CHORUS, + AL_EFFECT_DISTORTION, AL_EFFECT_ECHO, AL_EFFECT_FLANGER, + AL_EFFECT_FREQUENCY_SHIFTER, AL_EFFECT_VOCAL_MORPHER, + AL_EFFECT_PITCH_SHIFTER, AL_EFFECT_RING_MODULATOR, AL_EFFECT_AUTOWAH, + AL_EFFECT_COMPRESSOR, AL_EFFECT_EQUALIZER, AL_EFFECT_NULL + }; + const ALenum dedeffects[] = { + AL_EFFECT_DEDICATED_DIALOGUE, + AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, AL_EFFECT_NULL + }; + char effectNames[] = "EAX Reverb,Reverb,Chorus,Distortion,Echo,Flanger," + "Frequency Shifter,Vocal Morpher,Pitch Shifter," + "Ring Modulator,Autowah,Compressor,Equalizer," + "Dedicated Dialog,Dedicated LFE,"; + char *current; + + if(alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_FALSE) + { + printf("EFX not available\n"); + return; + } + + alcGetIntegerv(device, ALC_EFX_MAJOR_VERSION, 1, &major); + alcGetIntegerv(device, ALC_EFX_MINOR_VERSION, 1, &minor); + if(checkALCErrors(device) == ALC_NO_ERROR) + printf("EFX version: %d.%d\n", major, minor); + alcGetIntegerv(device, ALC_MAX_AUXILIARY_SENDS, 1, &sends); + if(checkALCErrors(device) == ALC_NO_ERROR) + printf("Max auxiliary sends: %d\n", sends); + + palGenFilters = alGetProcAddress("alGenFilters"); + palDeleteFilters = alGetProcAddress("alDeleteFilters"); + palFilteri = alGetProcAddress("alFilteri"); + palGenEffects = alGetProcAddress("alGenEffects"); + palDeleteEffects = alGetProcAddress("alDeleteEffects"); + palEffecti = alGetProcAddress("alEffecti"); + if(checkALErrors() != AL_NO_ERROR || + !palGenFilters || !palDeleteFilters || !palFilteri || + !palGenEffects || !palDeleteEffects || !palEffecti) + { + printf("!!! Missing EFX functions !!!\n"); + return; + } + + palGenFilters(1, &obj); + if(checkALErrors() == AL_NO_ERROR) + { + current = filterNames; + for(i = 0;filters[i] != AL_FILTER_NULL;i++) + { + char *next = strchr(current, ','); + + palFilteri(obj, AL_FILTER_TYPE, filters[i]); + if(alGetError() == AL_NO_ERROR) + current = next+1; + else + memmove(current, next+1, strlen(next)); + } + palDeleteFilters(1, &obj); + checkALErrors(); + + printf("Supported filters:"); + printList(filterNames, ','); + } + + palGenEffects(1, &obj); + if(checkALErrors() == AL_NO_ERROR) + { + current = effectNames; + for(i = 0;effects[i] != AL_EFFECT_NULL;i++) + { + char *next = strchr(current, ','); + + palEffecti(obj, AL_EFFECT_TYPE, effects[i]); + if(alGetError() == AL_NO_ERROR) + current = next+1; + else + memmove(current, next+1, strlen(next)); + } + if(alcIsExtensionPresent(device, "ALC_EXT_DEDICATED")) + { + for(i = 0;dedeffects[i] != AL_EFFECT_NULL;i++) + { + char *next = strchr(current, ','); + + palEffecti(obj, AL_EFFECT_TYPE, dedeffects[i]); + if(alGetError() == AL_NO_ERROR) + current = next+1; + else + memmove(current, next+1, strlen(next)); + } + } + else + { + for(i = 0;dedeffects[i] != AL_EFFECT_NULL;i++) + { + char *next = strchr(current, ','); + memmove(current, next+1, strlen(next)); + } + } + palDeleteEffects(1, &obj); + checkALErrors(); + + printf("Supported effects:"); + printList(effectNames, ','); + } +} + +int main(int argc, char *argv[]) +{ + ALCdevice *device; + ALCcontext *context; + + if(argc > 1 && (strcmp(argv[1], "--help") == 0 || + strcmp(argv[1], "-h") == 0)) + { + printf("Usage: %s [playback device]\n", argv[0]); + return 0; + } + + printf("Available playback devices:\n"); + if(alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) + printDeviceList(alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER)); + else + printDeviceList(alcGetString(NULL, ALC_DEVICE_SPECIFIER)); + printf("Available capture devices:\n"); + printDeviceList(alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)); + + if(alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) + printf("Default playback device: %s\n", + alcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER)); + else + printf("Default playback device: %s\n", + alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER)); + printf("Default capture device: %s\n", + alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)); + + printALCInfo(NULL); + + device = alcOpenDevice((argc>1) ? argv[1] : NULL); + if(!device) + { + printf("\n!!! Failed to open %s !!!\n\n", ((argc>1) ? argv[1] : "default device")); + return 1; + } + + printf("\n** Info for device \"%s\" **\n", alcGetString(device, ALC_DEVICE_SPECIFIER)); + printALCInfo(device); + + context = alcCreateContext(device, NULL); + if(!context || alcMakeContextCurrent(context) == ALC_FALSE) + { + if(context) + alcDestroyContext(context); + alcCloseDevice(device); + printf("\n!!! Failed to set a context !!!\n\n"); + return 1; + } + + printALInfo(); + printEFXInfo(device); + + alcMakeContextCurrent(NULL); + alcDestroyContext(context); + alcCloseDevice(device); + + return 0; +}