diff --git a/libs/openal-soft/.gitignore b/libs/openal-soft/.gitignore new file mode 100644 index 00000000..9aa081c6 --- /dev/null +++ b/libs/openal-soft/.gitignore @@ -0,0 +1,7 @@ +build +winbuild +win64build +include/SLES +include/sndio.h +include/sys +openal-soft.kdev4 diff --git a/libs/openal-soft/Alc/ALc.c b/libs/openal-soft/Alc/ALc.c new file mode 100644 index 00000000..f7f7b63a --- /dev/null +++ b/libs/openal-soft/Alc/ALc.c @@ -0,0 +1,3746 @@ +/** + * 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 "alMain.h" +#include "alSource.h" +#include "alListener.h" +#include "alThunk.h" +#include "alSource.h" +#include "alBuffer.h" +#include "alAuxEffectSlot.h" +#include "alError.h" +#include "alMidi.h" +#include "bs2b.h" +#include "alu.h" + +#include "compat.h" +#include "threads.h" +#include "alstring.h" + +#include "backends/base.h" +#include "midi/base.h" + + +/************************************************ + * Backends + ************************************************/ +struct BackendInfo { + const char *name; + ALCbackendFactory* (*getFactory)(void); + ALCboolean (*Init)(BackendFuncs*); + void (*Deinit)(void); + void (*Probe)(enum DevProbe); + BackendFuncs Funcs; +}; + +#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +static struct BackendInfo BackendList[] = { +#ifdef HAVE_PULSEAUDIO + { "pulse", ALCpulseBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, +#endif +#ifdef HAVE_ALSA + { "alsa", ALCalsaBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, +#endif +#ifdef HAVE_COREAUDIO + { "core", NULL, alc_ca_init, alc_ca_deinit, alc_ca_probe, EmptyFuncs }, +#endif +#ifdef HAVE_OSS + { "oss", ALCossBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, +#endif +#ifdef HAVE_SOLARIS + { "solaris", NULL, alc_solaris_init, alc_solaris_deinit, alc_solaris_probe, EmptyFuncs }, +#endif +#ifdef HAVE_SNDIO + { "sndio", NULL, alc_sndio_init, alc_sndio_deinit, alc_sndio_probe, EmptyFuncs }, +#endif +#ifdef HAVE_QSA + { "qsa", NULL, alc_qsa_init, alc_qsa_deinit, alc_qsa_probe, EmptyFuncs }, +#endif +#ifdef HAVE_MMDEVAPI + { "mmdevapi", ALCmmdevBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, +#endif +#ifdef HAVE_DSOUND + { "dsound", ALCdsoundBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, +#endif +#ifdef HAVE_WINMM + { "winmm", NULL, alcWinMMInit, alcWinMMDeinit, alcWinMMProbe, EmptyFuncs }, +#endif +#ifdef HAVE_PORTAUDIO + { "port", NULL, alc_pa_init, alc_pa_deinit, alc_pa_probe, EmptyFuncs }, +#endif +#ifdef HAVE_OPENSL + { "opensl", NULL, alc_opensl_init, alc_opensl_deinit, alc_opensl_probe, EmptyFuncs }, +#endif + + { "null", ALCnullBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, +#ifdef HAVE_WAVE + { "wave", NULL, alc_wave_init, alc_wave_deinit, alc_wave_probe, EmptyFuncs }, +#endif + + { NULL, NULL, NULL, NULL, NULL, EmptyFuncs } +}; +#undef EmptyFuncs + +static struct BackendInfo PlaybackBackend; +static struct BackendInfo CaptureBackend; + + +/************************************************ + * Functions, enums, and errors + ************************************************/ +typedef struct ALCfunction { + const ALCchar *funcName; + ALCvoid *address; +} ALCfunction; + +typedef struct ALCenums { + const ALCchar *enumName; + ALCenum value; +} ALCenums; + +#define DECL(x) { #x, (ALCvoid*)(x) } +static const ALCfunction alcFunctions[] = { + DECL(alcCreateContext), + DECL(alcMakeContextCurrent), + DECL(alcProcessContext), + DECL(alcSuspendContext), + DECL(alcDestroyContext), + DECL(alcGetCurrentContext), + DECL(alcGetContextsDevice), + DECL(alcOpenDevice), + DECL(alcCloseDevice), + DECL(alcGetError), + DECL(alcIsExtensionPresent), + DECL(alcGetProcAddress), + DECL(alcGetEnumValue), + DECL(alcGetString), + DECL(alcGetIntegerv), + DECL(alcCaptureOpenDevice), + DECL(alcCaptureCloseDevice), + DECL(alcCaptureStart), + DECL(alcCaptureStop), + DECL(alcCaptureSamples), + + DECL(alcSetThreadContext), + DECL(alcGetThreadContext), + + DECL(alcLoopbackOpenDeviceSOFT), + DECL(alcIsRenderFormatSupportedSOFT), + DECL(alcRenderSamplesSOFT), + + DECL(alcDevicePauseSOFT), + DECL(alcDeviceResumeSOFT), + + DECL(alcGetInteger64vSOFT), + + DECL(alEnable), + DECL(alDisable), + DECL(alIsEnabled), + DECL(alGetString), + DECL(alGetBooleanv), + DECL(alGetIntegerv), + DECL(alGetFloatv), + DECL(alGetDoublev), + DECL(alGetBoolean), + DECL(alGetInteger), + DECL(alGetFloat), + DECL(alGetDouble), + DECL(alGetError), + DECL(alIsExtensionPresent), + DECL(alGetProcAddress), + DECL(alGetEnumValue), + DECL(alListenerf), + DECL(alListener3f), + DECL(alListenerfv), + DECL(alListeneri), + DECL(alListener3i), + DECL(alListeneriv), + DECL(alGetListenerf), + DECL(alGetListener3f), + DECL(alGetListenerfv), + DECL(alGetListeneri), + DECL(alGetListener3i), + DECL(alGetListeneriv), + DECL(alGenSources), + DECL(alDeleteSources), + DECL(alIsSource), + DECL(alSourcef), + DECL(alSource3f), + DECL(alSourcefv), + DECL(alSourcei), + DECL(alSource3i), + DECL(alSourceiv), + DECL(alGetSourcef), + DECL(alGetSource3f), + DECL(alGetSourcefv), + DECL(alGetSourcei), + DECL(alGetSource3i), + DECL(alGetSourceiv), + DECL(alSourcePlayv), + DECL(alSourceStopv), + DECL(alSourceRewindv), + DECL(alSourcePausev), + DECL(alSourcePlay), + DECL(alSourceStop), + DECL(alSourceRewind), + DECL(alSourcePause), + DECL(alSourceQueueBuffers), + DECL(alSourceUnqueueBuffers), + DECL(alGenBuffers), + DECL(alDeleteBuffers), + DECL(alIsBuffer), + DECL(alBufferData), + DECL(alBufferf), + DECL(alBuffer3f), + DECL(alBufferfv), + DECL(alBufferi), + DECL(alBuffer3i), + DECL(alBufferiv), + DECL(alGetBufferf), + DECL(alGetBuffer3f), + DECL(alGetBufferfv), + DECL(alGetBufferi), + DECL(alGetBuffer3i), + DECL(alGetBufferiv), + DECL(alDopplerFactor), + DECL(alDopplerVelocity), + DECL(alSpeedOfSound), + DECL(alDistanceModel), + + DECL(alGenFilters), + DECL(alDeleteFilters), + DECL(alIsFilter), + DECL(alFilteri), + DECL(alFilteriv), + DECL(alFilterf), + DECL(alFilterfv), + DECL(alGetFilteri), + DECL(alGetFilteriv), + DECL(alGetFilterf), + DECL(alGetFilterfv), + DECL(alGenEffects), + DECL(alDeleteEffects), + DECL(alIsEffect), + DECL(alEffecti), + DECL(alEffectiv), + DECL(alEffectf), + DECL(alEffectfv), + DECL(alGetEffecti), + DECL(alGetEffectiv), + DECL(alGetEffectf), + DECL(alGetEffectfv), + DECL(alGenAuxiliaryEffectSlots), + DECL(alDeleteAuxiliaryEffectSlots), + DECL(alIsAuxiliaryEffectSlot), + DECL(alAuxiliaryEffectSloti), + DECL(alAuxiliaryEffectSlotiv), + DECL(alAuxiliaryEffectSlotf), + DECL(alAuxiliaryEffectSlotfv), + DECL(alGetAuxiliaryEffectSloti), + DECL(alGetAuxiliaryEffectSlotiv), + DECL(alGetAuxiliaryEffectSlotf), + DECL(alGetAuxiliaryEffectSlotfv), + + DECL(alBufferSubDataSOFT), + + DECL(alBufferSamplesSOFT), + DECL(alBufferSubSamplesSOFT), + DECL(alGetBufferSamplesSOFT), + DECL(alIsBufferFormatSupportedSOFT), + + DECL(alDeferUpdatesSOFT), + DECL(alProcessUpdatesSOFT), + + DECL(alSourcedSOFT), + DECL(alSource3dSOFT), + DECL(alSourcedvSOFT), + DECL(alGetSourcedSOFT), + DECL(alGetSource3dSOFT), + DECL(alGetSourcedvSOFT), + DECL(alSourcei64SOFT), + DECL(alSource3i64SOFT), + DECL(alSourcei64vSOFT), + DECL(alGetSourcei64SOFT), + DECL(alGetSource3i64SOFT), + DECL(alGetSourcei64vSOFT), + + DECL(alGenSoundfontsSOFT), + DECL(alDeleteSoundfontsSOFT), + DECL(alIsSoundfontSOFT), + DECL(alGetSoundfontivSOFT), + DECL(alSoundfontPresetsSOFT), + DECL(alGenPresetsSOFT), + DECL(alDeletePresetsSOFT), + DECL(alIsPresetSOFT), + DECL(alPresetiSOFT), + DECL(alPresetivSOFT), + DECL(alGetPresetivSOFT), + DECL(alPresetFontsoundsSOFT), + DECL(alGenFontsoundsSOFT), + DECL(alDeleteFontsoundsSOFT), + DECL(alIsFontsoundSOFT), + DECL(alFontsoundiSOFT), + DECL(alFontsound2iSOFT), + DECL(alFontsoundivSOFT), + DECL(alGetFontsoundivSOFT), + DECL(alFontsoundModulatoriSOFT), + DECL(alGetFontsoundModulatorivSOFT), + DECL(alMidiSoundfontSOFT), + DECL(alMidiSoundfontvSOFT), + DECL(alMidiEventSOFT), + DECL(alMidiSysExSOFT), + DECL(alMidiPlaySOFT), + DECL(alMidiPauseSOFT), + DECL(alMidiStopSOFT), + DECL(alMidiResetSOFT), + DECL(alMidiGainSOFT), + DECL(alGetInteger64SOFT), + DECL(alGetInteger64vSOFT), + DECL(alLoadSoundfontSOFT), + + { NULL, NULL } +}; +#undef DECL + +#define DECL(x) { #x, (x) } +static const ALCenums enumeration[] = { + DECL(ALC_INVALID), + DECL(ALC_FALSE), + DECL(ALC_TRUE), + + DECL(ALC_MAJOR_VERSION), + DECL(ALC_MINOR_VERSION), + DECL(ALC_ATTRIBUTES_SIZE), + DECL(ALC_ALL_ATTRIBUTES), + DECL(ALC_DEFAULT_DEVICE_SPECIFIER), + DECL(ALC_DEVICE_SPECIFIER), + DECL(ALC_ALL_DEVICES_SPECIFIER), + DECL(ALC_DEFAULT_ALL_DEVICES_SPECIFIER), + DECL(ALC_EXTENSIONS), + DECL(ALC_FREQUENCY), + DECL(ALC_REFRESH), + DECL(ALC_SYNC), + DECL(ALC_MONO_SOURCES), + DECL(ALC_STEREO_SOURCES), + DECL(ALC_CAPTURE_DEVICE_SPECIFIER), + DECL(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER), + DECL(ALC_CAPTURE_SAMPLES), + DECL(ALC_CONNECTED), + + DECL(ALC_EFX_MAJOR_VERSION), + DECL(ALC_EFX_MINOR_VERSION), + DECL(ALC_MAX_AUXILIARY_SENDS), + + DECL(ALC_FORMAT_CHANNELS_SOFT), + DECL(ALC_FORMAT_TYPE_SOFT), + + DECL(ALC_MONO_SOFT), + DECL(ALC_STEREO_SOFT), + DECL(ALC_QUAD_SOFT), + DECL(ALC_5POINT1_SOFT), + DECL(ALC_6POINT1_SOFT), + DECL(ALC_7POINT1_SOFT), + + DECL(ALC_BYTE_SOFT), + DECL(ALC_UNSIGNED_BYTE_SOFT), + DECL(ALC_SHORT_SOFT), + DECL(ALC_UNSIGNED_SHORT_SOFT), + DECL(ALC_INT_SOFT), + DECL(ALC_UNSIGNED_INT_SOFT), + DECL(ALC_FLOAT_SOFT), + + DECL(ALC_NO_ERROR), + DECL(ALC_INVALID_DEVICE), + DECL(ALC_INVALID_CONTEXT), + DECL(ALC_INVALID_ENUM), + DECL(ALC_INVALID_VALUE), + DECL(ALC_OUT_OF_MEMORY), + + + DECL(AL_INVALID), + DECL(AL_NONE), + DECL(AL_FALSE), + DECL(AL_TRUE), + + DECL(AL_SOURCE_RELATIVE), + DECL(AL_CONE_INNER_ANGLE), + DECL(AL_CONE_OUTER_ANGLE), + DECL(AL_PITCH), + DECL(AL_POSITION), + DECL(AL_DIRECTION), + DECL(AL_VELOCITY), + DECL(AL_LOOPING), + DECL(AL_BUFFER), + DECL(AL_GAIN), + DECL(AL_MIN_GAIN), + DECL(AL_MAX_GAIN), + DECL(AL_ORIENTATION), + DECL(AL_REFERENCE_DISTANCE), + DECL(AL_ROLLOFF_FACTOR), + DECL(AL_CONE_OUTER_GAIN), + DECL(AL_MAX_DISTANCE), + DECL(AL_SEC_OFFSET), + DECL(AL_SAMPLE_OFFSET), + DECL(AL_SAMPLE_RW_OFFSETS_SOFT), + DECL(AL_BYTE_OFFSET), + DECL(AL_BYTE_RW_OFFSETS_SOFT), + DECL(AL_SOURCE_TYPE), + DECL(AL_STATIC), + DECL(AL_STREAMING), + DECL(AL_UNDETERMINED), + DECL(AL_METERS_PER_UNIT), + DECL(AL_DIRECT_CHANNELS_SOFT), + + DECL(AL_DIRECT_FILTER), + DECL(AL_AUXILIARY_SEND_FILTER), + DECL(AL_AIR_ABSORPTION_FACTOR), + DECL(AL_ROOM_ROLLOFF_FACTOR), + DECL(AL_CONE_OUTER_GAINHF), + DECL(AL_DIRECT_FILTER_GAINHF_AUTO), + DECL(AL_AUXILIARY_SEND_FILTER_GAIN_AUTO), + DECL(AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO), + + DECL(AL_SOURCE_STATE), + DECL(AL_INITIAL), + DECL(AL_PLAYING), + DECL(AL_PAUSED), + DECL(AL_STOPPED), + + DECL(AL_BUFFERS_QUEUED), + DECL(AL_BUFFERS_PROCESSED), + + DECL(AL_FORMAT_MONO8), + DECL(AL_FORMAT_MONO16), + DECL(AL_FORMAT_MONO_FLOAT32), + DECL(AL_FORMAT_MONO_DOUBLE_EXT), + DECL(AL_FORMAT_STEREO8), + DECL(AL_FORMAT_STEREO16), + DECL(AL_FORMAT_STEREO_FLOAT32), + DECL(AL_FORMAT_STEREO_DOUBLE_EXT), + DECL(AL_FORMAT_MONO_IMA4), + DECL(AL_FORMAT_STEREO_IMA4), + DECL(AL_FORMAT_MONO_MSADPCM_SOFT), + DECL(AL_FORMAT_STEREO_MSADPCM_SOFT), + DECL(AL_FORMAT_QUAD8_LOKI), + DECL(AL_FORMAT_QUAD16_LOKI), + DECL(AL_FORMAT_QUAD8), + DECL(AL_FORMAT_QUAD16), + DECL(AL_FORMAT_QUAD32), + DECL(AL_FORMAT_51CHN8), + DECL(AL_FORMAT_51CHN16), + DECL(AL_FORMAT_51CHN32), + DECL(AL_FORMAT_61CHN8), + DECL(AL_FORMAT_61CHN16), + DECL(AL_FORMAT_61CHN32), + DECL(AL_FORMAT_71CHN8), + DECL(AL_FORMAT_71CHN16), + DECL(AL_FORMAT_71CHN32), + DECL(AL_FORMAT_REAR8), + DECL(AL_FORMAT_REAR16), + DECL(AL_FORMAT_REAR32), + DECL(AL_FORMAT_MONO_MULAW), + DECL(AL_FORMAT_MONO_MULAW_EXT), + DECL(AL_FORMAT_STEREO_MULAW), + DECL(AL_FORMAT_STEREO_MULAW_EXT), + DECL(AL_FORMAT_QUAD_MULAW), + DECL(AL_FORMAT_51CHN_MULAW), + DECL(AL_FORMAT_61CHN_MULAW), + DECL(AL_FORMAT_71CHN_MULAW), + DECL(AL_FORMAT_REAR_MULAW), + DECL(AL_FORMAT_MONO_ALAW_EXT), + DECL(AL_FORMAT_STEREO_ALAW_EXT), + + DECL(AL_MONO8_SOFT), + DECL(AL_MONO16_SOFT), + DECL(AL_MONO32F_SOFT), + DECL(AL_STEREO8_SOFT), + DECL(AL_STEREO16_SOFT), + DECL(AL_STEREO32F_SOFT), + DECL(AL_QUAD8_SOFT), + DECL(AL_QUAD16_SOFT), + DECL(AL_QUAD32F_SOFT), + DECL(AL_REAR8_SOFT), + DECL(AL_REAR16_SOFT), + DECL(AL_REAR32F_SOFT), + DECL(AL_5POINT1_8_SOFT), + DECL(AL_5POINT1_16_SOFT), + DECL(AL_5POINT1_32F_SOFT), + DECL(AL_6POINT1_8_SOFT), + DECL(AL_6POINT1_16_SOFT), + DECL(AL_6POINT1_32F_SOFT), + DECL(AL_7POINT1_8_SOFT), + DECL(AL_7POINT1_16_SOFT), + DECL(AL_7POINT1_32F_SOFT), + + DECL(AL_MONO_SOFT), + DECL(AL_STEREO_SOFT), + DECL(AL_QUAD_SOFT), + DECL(AL_REAR_SOFT), + DECL(AL_5POINT1_SOFT), + DECL(AL_6POINT1_SOFT), + DECL(AL_7POINT1_SOFT), + + DECL(AL_BYTE_SOFT), + DECL(AL_UNSIGNED_BYTE_SOFT), + DECL(AL_SHORT_SOFT), + DECL(AL_UNSIGNED_SHORT_SOFT), + DECL(AL_INT_SOFT), + DECL(AL_UNSIGNED_INT_SOFT), + DECL(AL_FLOAT_SOFT), + DECL(AL_DOUBLE_SOFT), + DECL(AL_BYTE3_SOFT), + DECL(AL_UNSIGNED_BYTE3_SOFT), + + DECL(AL_FREQUENCY), + DECL(AL_BITS), + DECL(AL_CHANNELS), + DECL(AL_SIZE), + DECL(AL_INTERNAL_FORMAT_SOFT), + DECL(AL_BYTE_LENGTH_SOFT), + DECL(AL_SAMPLE_LENGTH_SOFT), + DECL(AL_SEC_LENGTH_SOFT), + DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT), + DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT), + + DECL(AL_UNUSED), + DECL(AL_PENDING), + DECL(AL_PROCESSED), + + DECL(AL_NO_ERROR), + DECL(AL_INVALID_NAME), + DECL(AL_INVALID_ENUM), + DECL(AL_INVALID_VALUE), + DECL(AL_INVALID_OPERATION), + DECL(AL_OUT_OF_MEMORY), + + DECL(AL_VENDOR), + DECL(AL_VERSION), + DECL(AL_RENDERER), + DECL(AL_EXTENSIONS), + + DECL(AL_DOPPLER_FACTOR), + DECL(AL_DOPPLER_VELOCITY), + DECL(AL_DISTANCE_MODEL), + DECL(AL_SPEED_OF_SOUND), + DECL(AL_SOURCE_DISTANCE_MODEL), + DECL(AL_DEFERRED_UPDATES_SOFT), + + DECL(AL_INVERSE_DISTANCE), + DECL(AL_INVERSE_DISTANCE_CLAMPED), + DECL(AL_LINEAR_DISTANCE), + DECL(AL_LINEAR_DISTANCE_CLAMPED), + DECL(AL_EXPONENT_DISTANCE), + DECL(AL_EXPONENT_DISTANCE_CLAMPED), + + DECL(AL_FILTER_TYPE), + DECL(AL_FILTER_NULL), + DECL(AL_FILTER_LOWPASS), + DECL(AL_FILTER_HIGHPASS), + DECL(AL_FILTER_BANDPASS), + + DECL(AL_LOWPASS_GAIN), + DECL(AL_LOWPASS_GAINHF), + + DECL(AL_HIGHPASS_GAIN), + DECL(AL_HIGHPASS_GAINLF), + + DECL(AL_BANDPASS_GAIN), + DECL(AL_BANDPASS_GAINHF), + DECL(AL_BANDPASS_GAINLF), + + DECL(AL_EFFECT_TYPE), + DECL(AL_EFFECT_NULL), + DECL(AL_EFFECT_REVERB), + DECL(AL_EFFECT_EAXREVERB), + DECL(AL_EFFECT_CHORUS), + DECL(AL_EFFECT_DISTORTION), + DECL(AL_EFFECT_ECHO), + DECL(AL_EFFECT_FLANGER), +#if 0 + DECL(AL_EFFECT_FREQUENCY_SHIFTER), + DECL(AL_EFFECT_VOCAL_MORPHER), + DECL(AL_EFFECT_PITCH_SHIFTER), +#endif + DECL(AL_EFFECT_RING_MODULATOR), +#if 0 + DECL(AL_EFFECT_AUTOWAH), +#endif + DECL(AL_EFFECT_COMPRESSOR), + DECL(AL_EFFECT_EQUALIZER), + DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT), + DECL(AL_EFFECT_DEDICATED_DIALOGUE), + + DECL(AL_EAXREVERB_DENSITY), + DECL(AL_EAXREVERB_DIFFUSION), + DECL(AL_EAXREVERB_GAIN), + DECL(AL_EAXREVERB_GAINHF), + DECL(AL_EAXREVERB_GAINLF), + DECL(AL_EAXREVERB_DECAY_TIME), + DECL(AL_EAXREVERB_DECAY_HFRATIO), + DECL(AL_EAXREVERB_DECAY_LFRATIO), + DECL(AL_EAXREVERB_REFLECTIONS_GAIN), + DECL(AL_EAXREVERB_REFLECTIONS_DELAY), + DECL(AL_EAXREVERB_REFLECTIONS_PAN), + DECL(AL_EAXREVERB_LATE_REVERB_GAIN), + DECL(AL_EAXREVERB_LATE_REVERB_DELAY), + DECL(AL_EAXREVERB_LATE_REVERB_PAN), + DECL(AL_EAXREVERB_ECHO_TIME), + DECL(AL_EAXREVERB_ECHO_DEPTH), + DECL(AL_EAXREVERB_MODULATION_TIME), + DECL(AL_EAXREVERB_MODULATION_DEPTH), + DECL(AL_EAXREVERB_AIR_ABSORPTION_GAINHF), + DECL(AL_EAXREVERB_HFREFERENCE), + DECL(AL_EAXREVERB_LFREFERENCE), + DECL(AL_EAXREVERB_ROOM_ROLLOFF_FACTOR), + DECL(AL_EAXREVERB_DECAY_HFLIMIT), + + DECL(AL_REVERB_DENSITY), + DECL(AL_REVERB_DIFFUSION), + DECL(AL_REVERB_GAIN), + DECL(AL_REVERB_GAINHF), + DECL(AL_REVERB_DECAY_TIME), + DECL(AL_REVERB_DECAY_HFRATIO), + DECL(AL_REVERB_REFLECTIONS_GAIN), + DECL(AL_REVERB_REFLECTIONS_DELAY), + DECL(AL_REVERB_LATE_REVERB_GAIN), + DECL(AL_REVERB_LATE_REVERB_DELAY), + DECL(AL_REVERB_AIR_ABSORPTION_GAINHF), + DECL(AL_REVERB_ROOM_ROLLOFF_FACTOR), + DECL(AL_REVERB_DECAY_HFLIMIT), + + DECL(AL_CHORUS_WAVEFORM), + DECL(AL_CHORUS_PHASE), + DECL(AL_CHORUS_RATE), + DECL(AL_CHORUS_DEPTH), + DECL(AL_CHORUS_FEEDBACK), + DECL(AL_CHORUS_DELAY), + + DECL(AL_DISTORTION_EDGE), + DECL(AL_DISTORTION_GAIN), + DECL(AL_DISTORTION_LOWPASS_CUTOFF), + DECL(AL_DISTORTION_EQCENTER), + DECL(AL_DISTORTION_EQBANDWIDTH), + + DECL(AL_ECHO_DELAY), + DECL(AL_ECHO_LRDELAY), + DECL(AL_ECHO_DAMPING), + DECL(AL_ECHO_FEEDBACK), + DECL(AL_ECHO_SPREAD), + + DECL(AL_FLANGER_WAVEFORM), + DECL(AL_FLANGER_PHASE), + DECL(AL_FLANGER_RATE), + DECL(AL_FLANGER_DEPTH), + DECL(AL_FLANGER_FEEDBACK), + DECL(AL_FLANGER_DELAY), + + DECL(AL_RING_MODULATOR_FREQUENCY), + DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF), + DECL(AL_RING_MODULATOR_WAVEFORM), + +#if 0 + DECL(AL_AUTOWAH_ATTACK_TIME), + DECL(AL_AUTOWAH_PEAK_GAIN), + DECL(AL_AUTOWAH_RELEASE_TIME), + DECL(AL_AUTOWAH_RESONANCE), +#endif + + DECL(AL_COMPRESSOR_ONOFF), + + DECL(AL_EQUALIZER_LOW_GAIN), + DECL(AL_EQUALIZER_LOW_CUTOFF), + DECL(AL_EQUALIZER_MID1_GAIN), + DECL(AL_EQUALIZER_MID1_CENTER), + DECL(AL_EQUALIZER_MID1_WIDTH), + DECL(AL_EQUALIZER_MID2_GAIN), + DECL(AL_EQUALIZER_MID2_CENTER), + DECL(AL_EQUALIZER_MID2_WIDTH), + DECL(AL_EQUALIZER_HIGH_GAIN), + DECL(AL_EQUALIZER_HIGH_CUTOFF), + + DECL(AL_DEDICATED_GAIN), + + { NULL, (ALCenum)0 } +}; +#undef DECL + +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"; + + +/************************************************ + * Global variables + ************************************************/ + +/* Enumerated device names */ +static const ALCchar alcDefaultName[] = "OpenAL Soft\0"; + +static al_string alcAllDevicesList; +static al_string alcCaptureDeviceList; + +/* Default is always the first in the list */ +static ALCchar *alcDefaultAllDevicesSpecifier; +static ALCchar *alcCaptureDefaultDeviceSpecifier; + +/* Default context extensions */ +static const ALchar alExtList[] = + "AL_EXT_ALAW 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_SOFT_block_alignment AL_SOFT_buffer_samples " + "AL_SOFT_buffer_sub_data AL_SOFT_deferred_updates AL_SOFT_direct_channels " + "AL_SOFT_loop_points AL_SOFT_MSADPCM AL_SOFT_source_latency " + "AL_SOFT_source_length"; + +static ATOMIC(ALCenum) LastNullDeviceError = ATOMIC_INIT_STATIC(ALC_NO_ERROR); + +/* Thread-local current context */ +static altss_t LocalContext; +/* Process-wide current context */ +static ATOMIC(ALCcontext*) GlobalContext = ATOMIC_INIT_STATIC(NULL); + +/* Mixing thread piority level */ +ALint RTPrioLevel; + +FILE *LogFile; +#ifdef _DEBUG +enum LogLevel LogLevel = LogWarning; +#else +enum LogLevel LogLevel = LogError; +#endif + +/* Flag to trap ALC device errors */ +static ALCboolean TrapALCError = ALC_FALSE; + +/* One-time configuration init control */ +static alonce_flag alc_config_once = AL_ONCE_FLAG_INIT; + +/* Default effect that applies to sources that don't have an effect on send 0 */ +static ALeffect DefaultEffect; + + +/************************************************ + * ALC information + ************************************************/ +static const ALCchar alcNoDeviceExtList[] = + "ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE " + "ALC_EXT_thread_local_context ALC_SOFT_loopback"; +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_device_clock ALC_SOFTX_HRTF " + "ALC_SOFT_loopback ALC_SOFTX_midi_interface ALC_SOFT_pause_device"; +static const ALCint alcMajorVersion = 1; +static const ALCint alcMinorVersion = 1; + +static const ALCint alcEFXMajorVersion = 1; +static const ALCint alcEFXMinorVersion = 0; + + +/************************************************ + * Device lists + ************************************************/ +static ATOMIC(ALCdevice*) DeviceList = ATOMIC_INIT_STATIC(NULL); + +static almtx_t ListLock; +static inline void LockLists(void) +{ + int lockret = almtx_lock(&ListLock); + assert(lockret == althrd_success); +} +static inline void UnlockLists(void) +{ + int unlockret = almtx_unlock(&ListLock); + assert(unlockret == althrd_success); +} + +/************************************************ + * Library initialization + ************************************************/ +#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(HINSTANCE hModule, DWORD reason, LPVOID lpReserved) +{ + switch(reason) + { + case DLL_PROCESS_ATTACH: + /* Pin the DLL so we won't get unloaded until the process terminates */ + GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + (WCHAR*)hModule, &hModule); + alc_init(); + break; + + case DLL_THREAD_DETACH: + 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 ReleaseThreadCtx(void *ptr); +static void alc_init(void) +{ + const char *str; + int ret; + + LogFile = stderr; + + AL_STRING_INIT(alcAllDevicesList); + AL_STRING_INIT(alcCaptureDeviceList); + + str = getenv("__ALSOFT_HALF_ANGLE_CONES"); + if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1)) + ConeScale *= 0.5f; + + str = getenv("__ALSOFT_REVERSE_Z"); + if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1)) + ZScale *= -1.0f; + + ret = altss_create(&LocalContext, ReleaseThreadCtx); + assert(ret == althrd_success); + + ret = almtx_init(&ListLock, almtx_recursive); + assert(ret == althrd_success); + + ThunkInit(); +} + +static void alc_initconfig(void) +{ + const char *devs, *str; + ALuint capfilter; + float valf; + int i, n; + + str = getenv("ALSOFT_LOGLEVEL"); + if(str) + { + long lvl = strtol(str, NULL, 0); + if(lvl >= NoLog && lvl <= LogRef) + LogLevel = lvl; + } + + str = getenv("ALSOFT_LOGFILE"); + if(str && str[0]) + { + FILE *logfile = al_fopen(str, "wt"); + if(logfile) LogFile = logfile; + else ERR("Failed to open log file '%s'\n", str); + } + + { + char buf[1024] = ""; + int len = snprintf(buf, sizeof(buf), "%s", BackendList[0].name); + for(i = 1;BackendList[i].name;i++) + len += snprintf(buf+len, sizeof(buf)-len, ", %s", BackendList[i].name); + TRACE("Supported backends: %s\n", buf); + } + ReadALConfig(); + + capfilter = 0; +#if defined(HAVE_SSE4_1) + capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE4_1; +#elif defined(HAVE_SSE2) + capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2; +#elif defined(HAVE_SSE) + capfilter |= CPU_CAP_SSE; +#endif +#ifdef HAVE_NEON + capfilter |= CPU_CAP_NEON; +#endif + if(ConfigValueStr(NULL, "disable-cpu-exts", &str)) + { + if(strcasecmp(str, "all") == 0) + capfilter = 0; + else + { + size_t len; + const char *next = str; + + do { + str = next; + while(isspace(str[0])) + str++; + next = strchr(str, ','); + + if(!str[0] || str[0] == ',') + continue; + + len = (next ? ((size_t)(next-str)) : strlen(str)); + while(len > 0 && isspace(str[len-1])) + len--; + if(len == 3 && strncasecmp(str, "sse", len) == 0) + capfilter &= ~CPU_CAP_SSE; + else if(len == 4 && strncasecmp(str, "sse2", len) == 0) + capfilter &= ~CPU_CAP_SSE2; + else if(len == 6 && strncasecmp(str, "sse4.1", len) == 0) + capfilter &= ~CPU_CAP_SSE4_1; + else if(len == 4 && strncasecmp(str, "neon", len) == 0) + capfilter &= ~CPU_CAP_NEON; + else + WARN("Invalid CPU extension \"%s\"\n", str); + } while(next++); + } + } + FillCPUCaps(capfilter); + +#ifdef _WIN32 + RTPrioLevel = 1; +#else + RTPrioLevel = 0; +#endif + ConfigValueInt(NULL, "rt-prio", &RTPrioLevel); + + if(ConfigValueStr(NULL, "resampler", &str)) + { + if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0) + DefaultResampler = PointResampler; + else if(strcasecmp(str, "linear") == 0) + DefaultResampler = LinearResampler; + else if(strcasecmp(str, "cubic") == 0) + DefaultResampler = CubicResampler; + else + { + char *end; + + n = strtol(str, &end, 0); + if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == CubicResampler)) + DefaultResampler = n; + else + WARN("Invalid resampler: %s\n", str); + } + } + + str = getenv("ALSOFT_TRAP_ERROR"); + if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1)) + { + TrapALError = AL_TRUE; + TrapALCError = AL_TRUE; + } + else + { + str = getenv("ALSOFT_TRAP_AL_ERROR"); + if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1)) + TrapALError = AL_TRUE; + TrapALError = GetConfigValueBool(NULL, "trap-al-error", TrapALError); + + str = getenv("ALSOFT_TRAP_ALC_ERROR"); + if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1)) + TrapALCError = ALC_TRUE; + TrapALCError = GetConfigValueBool(NULL, "trap-alc-error", TrapALCError); + } + + if(ConfigValueFloat("reverb", "boost", &valf)) + ReverbBoost *= powf(10.0f, valf / 20.0f); + + EmulateEAXReverb = GetConfigValueBool("reverb", "emulate-eax", AL_FALSE); + + if(((devs=getenv("ALSOFT_DRIVERS")) && devs[0]) || + ConfigValueStr(NULL, "drivers", &devs)) + { + int n; + size_t len; + const char *next = devs; + int endlist, delitem; + + i = 0; + do { + devs = next; + while(isspace(devs[0])) + 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)); + while(len > 0 && isspace(devs[len-1])) + len--; + for(n = i;BackendList[n].name;n++) + { + if(len == strlen(BackendList[n].name) && + strncmp(BackendList[n].name, devs, len) == 0) + { + if(delitem) + { + do { + BackendList[n] = BackendList[n+1]; + ++n; + } while(BackendList[n].name); + } + 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].getFactory = NULL; + BackendList[i].Init = NULL; + BackendList[i].Deinit = NULL; + BackendList[i].Probe = NULL; + } + } + + for(i = 0;(BackendList[i].Init || BackendList[i].getFactory) && (!PlaybackBackend.name || !CaptureBackend.name);i++) + { + if(BackendList[i].getFactory) + { + ALCbackendFactory *factory = BackendList[i].getFactory(); + if(!V0(factory,init)()) + { + WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name); + continue; + } + + TRACE("Initialized backend \"%s\"\n", BackendList[i].name); + if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback)) + { + PlaybackBackend = BackendList[i]; + TRACE("Added \"%s\" for playback\n", PlaybackBackend.name); + } + if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture)) + { + CaptureBackend = BackendList[i]; + TRACE("Added \"%s\" for capture\n", CaptureBackend.name); + } + + continue; + } + + if(!BackendList[i].Init(&BackendList[i].Funcs)) + { + WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name); + 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); + } + } + { + ALCbackendFactory *factory = ALCloopbackFactory_getFactory(); + V0(factory,init)(); + } + + if(ConfigValueStr(NULL, "excludefx", &str)) + { + 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++); + } + + InitEffectFactoryMap(); + + InitEffect(&DefaultEffect); + str = getenv("ALSOFT_DEFAULT_REVERB"); + if((str && str[0]) || ConfigValueStr(NULL, "default-reverb", &str)) + LoadReverbPreset(str, &DefaultEffect); +} +#define DO_INITCONFIG() alcall_once(&alc_config_once, alc_initconfig) + + +/************************************************ + * Library deinitialization + ************************************************/ +static void alc_cleanup(void) +{ + ALCdevice *dev; + + AL_STRING_DEINIT(alcAllDevicesList); + AL_STRING_DEINIT(alcCaptureDeviceList); + + free(alcDefaultAllDevicesSpecifier); + alcDefaultAllDevicesSpecifier = NULL; + free(alcCaptureDefaultDeviceSpecifier); + alcCaptureDefaultDeviceSpecifier = NULL; + + if((dev=ATOMIC_EXCHANGE(ALCdevice*, &DeviceList, NULL)) != NULL) + { + ALCuint num = 0; + do { + num++; + } while((dev=dev->next) != NULL); + ERR("%u device%s not closed\n", num, (num>1)?"s":""); + } + + DeinitEffectFactoryMap(); +} + +static void alc_deinit_safe(void) +{ + alc_cleanup(); + + FreeHrtfs(); + FreeALConfig(); + + ThunkExit(); + almtx_destroy(&ListLock); + altss_delete(LocalContext); + + if(LogFile != stderr) + fclose(LogFile); + LogFile = NULL; +} + +static void alc_deinit(void) +{ + int i; + + alc_cleanup(); + + memset(&PlaybackBackend, 0, sizeof(PlaybackBackend)); + memset(&CaptureBackend, 0, sizeof(CaptureBackend)); + + for(i = 0;BackendList[i].Deinit || BackendList[i].getFactory;i++) + { + if(!BackendList[i].getFactory) + BackendList[i].Deinit(); + else + { + ALCbackendFactory *factory = BackendList[i].getFactory(); + V0(factory,deinit)(); + } + } + { + ALCbackendFactory *factory = ALCloopbackFactory_getFactory(); + V0(factory,deinit)(); + } + + alc_deinit_safe(); +} + + +/************************************************ + * Device enumeration + ************************************************/ +static void ProbeDevices(al_string *list, enum DevProbe type) +{ + DO_INITCONFIG(); + + LockLists(); + al_string_clear(list); + + if(type == ALL_DEVICE_PROBE && (PlaybackBackend.Probe || PlaybackBackend.getFactory)) + { + if(!PlaybackBackend.getFactory) + PlaybackBackend.Probe(type); + else + { + ALCbackendFactory *factory = PlaybackBackend.getFactory(); + V(factory,probe)(type); + } + } + else if(type == CAPTURE_DEVICE_PROBE && (CaptureBackend.Probe || CaptureBackend.getFactory)) + { + if(!CaptureBackend.getFactory) + CaptureBackend.Probe(type); + else + { + ALCbackendFactory *factory = CaptureBackend.getFactory(); + V(factory,probe)(type); + } + } + UnlockLists(); +} +static void ProbeAllDevicesList(void) +{ ProbeDevices(&alcAllDevicesList, ALL_DEVICE_PROBE); } +static void ProbeCaptureDeviceList(void) +{ ProbeDevices(&alcCaptureDeviceList, CAPTURE_DEVICE_PROBE); } + +static void AppendDevice(const ALCchar *name, al_string *devnames) +{ + size_t len = strlen(name); + if(len > 0) + { + al_string_append_range(devnames, name, name+len); + al_string_append_char(devnames, '\0'); + } +} +void AppendAllDevicesList(const ALCchar *name) +{ AppendDevice(name, &alcAllDevicesList); } +void AppendCaptureDeviceList(const ALCchar *name) +{ AppendDevice(name, &alcCaptureDeviceList); } + + +/************************************************ + * Device format information + ************************************************/ +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 DevFmtInt: return "Signed Int"; + case DevFmtUInt: return "Unsigned Int"; + 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)"; +} + +extern inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type); +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 DevFmtInt: return sizeof(ALint); + case DevFmtUInt: return sizeof(ALuint); + 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; +} + +DECL_CONST static ALboolean DecomposeDevFormat(ALenum format, + enum DevFmtChannels *chans, enum DevFmtType *type) +{ + static const struct { + ALenum format; + enum DevFmtChannels channels; + enum DevFmtType type; + } list[] = { + { AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte }, + { AL_FORMAT_MONO16, DevFmtMono, DevFmtShort }, + { AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat }, + + { AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte }, + { AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort }, + { AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat }, + + { AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte }, + { AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort }, + { AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat }, + + { AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte }, + { AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort }, + { AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat }, + + { AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte }, + { AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort }, + { AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat }, + + { AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte }, + { AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort }, + { AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat }, + }; + ALuint i; + + for(i = 0;i < COUNTOF(list);i++) + { + if(list[i].format == format) + { + *chans = list[i].channels; + *type = list[i].type; + return AL_TRUE; + } + } + + return AL_FALSE; +} + +DECL_CONST static ALCboolean IsValidALCType(ALCenum type) +{ + switch(type) + { + case ALC_BYTE_SOFT: + case ALC_UNSIGNED_BYTE_SOFT: + case ALC_SHORT_SOFT: + case ALC_UNSIGNED_SHORT_SOFT: + case ALC_INT_SOFT: + case ALC_UNSIGNED_INT_SOFT: + case ALC_FLOAT_SOFT: + return ALC_TRUE; + } + return ALC_FALSE; +} + +DECL_CONST static ALCboolean IsValidALCChannels(ALCenum channels) +{ + switch(channels) + { + case ALC_MONO_SOFT: + case ALC_STEREO_SOFT: + case ALC_QUAD_SOFT: + case ALC_5POINT1_SOFT: + case ALC_6POINT1_SOFT: + case ALC_7POINT1_SOFT: + return ALC_TRUE; + } + return ALC_FALSE; +} + + +/************************************************ + * Miscellaneous ALC helpers + ************************************************/ +extern inline void LockContext(ALCcontext *context); +extern inline void UnlockContext(ALCcontext *context); + +ALint64 ALCdevice_GetLatencyDefault(ALCdevice *UNUSED(device)) +{ + return 0; +} + +ALint64 ALCdevice_GetLatency(ALCdevice *device) +{ + return V0(device->Backend,getLatency)(); +} + +void ALCdevice_Lock(ALCdevice *device) +{ + V0(device->Backend,lock)(); +} + +void ALCdevice_Unlock(ALCdevice *device) +{ + V0(device->Backend,unlock)(); +} + + +/* SetDefaultWFXChannelOrder + * + * Sets the default channel order used by WaveFormatEx. + */ +void SetDefaultWFXChannelOrder(ALCdevice *device) +{ + ALuint i; + + for(i = 0;i < MaxChannels;i++) + device->ChannelOffsets[i] = INVALID_OFFSET; + + switch(device->FmtChans) + { + case DevFmtMono: device->ChannelOffsets[FrontCenter] = 0; + break; + case DevFmtStereo: device->ChannelOffsets[FrontLeft] = 0; + device->ChannelOffsets[FrontRight] = 1; + break; + case DevFmtQuad: device->ChannelOffsets[FrontLeft] = 0; + device->ChannelOffsets[FrontRight] = 1; + device->ChannelOffsets[BackLeft] = 2; + device->ChannelOffsets[BackRight] = 3; + break; + case DevFmtX51: device->ChannelOffsets[FrontLeft] = 0; + device->ChannelOffsets[FrontRight] = 1; + device->ChannelOffsets[FrontCenter] = 2; + device->ChannelOffsets[LFE] = 3; + device->ChannelOffsets[BackLeft] = 4; + device->ChannelOffsets[BackRight] = 5; + break; + case DevFmtX51Side: device->ChannelOffsets[FrontLeft] = 0; + device->ChannelOffsets[FrontRight] = 1; + device->ChannelOffsets[FrontCenter] = 2; + device->ChannelOffsets[LFE] = 3; + device->ChannelOffsets[SideLeft] = 4; + device->ChannelOffsets[SideRight] = 5; + break; + case DevFmtX61: device->ChannelOffsets[FrontLeft] = 0; + device->ChannelOffsets[FrontRight] = 1; + device->ChannelOffsets[FrontCenter] = 2; + device->ChannelOffsets[LFE] = 3; + device->ChannelOffsets[BackCenter] = 4; + device->ChannelOffsets[SideLeft] = 5; + device->ChannelOffsets[SideRight] = 6; + break; + case DevFmtX71: device->ChannelOffsets[FrontLeft] = 0; + device->ChannelOffsets[FrontRight] = 1; + device->ChannelOffsets[FrontCenter] = 2; + device->ChannelOffsets[LFE] = 3; + device->ChannelOffsets[BackLeft] = 4; + device->ChannelOffsets[BackRight] = 5; + device->ChannelOffsets[SideLeft] = 6; + device->ChannelOffsets[SideRight] = 7; + break; + } +} + +/* SetDefaultChannelOrder + * + * Sets the default channel order used by most non-WaveFormatEx-based APIs. + */ +void SetDefaultChannelOrder(ALCdevice *device) +{ + ALuint i; + + for(i = 0;i < MaxChannels;i++) + device->ChannelOffsets[i] = INVALID_OFFSET; + + switch(device->FmtChans) + { + case DevFmtX51: device->ChannelOffsets[FrontLeft] = 0; + device->ChannelOffsets[FrontRight] = 1; + device->ChannelOffsets[BackLeft] = 2; + device->ChannelOffsets[BackRight] = 3; + device->ChannelOffsets[FrontCenter] = 4; + device->ChannelOffsets[LFE] = 5; + return; + case DevFmtX71: device->ChannelOffsets[FrontLeft] = 0; + device->ChannelOffsets[FrontRight] = 1; + device->ChannelOffsets[BackLeft] = 2; + device->ChannelOffsets[BackRight] = 3; + device->ChannelOffsets[FrontCenter] = 4; + device->ChannelOffsets[LFE] = 5; + device->ChannelOffsets[SideLeft] = 6; + device->ChannelOffsets[SideRight] = 7; + return; + + /* Same as WFX order */ + case DevFmtMono: + case DevFmtStereo: + case DevFmtQuad: + case DevFmtX51Side: + case DevFmtX61: + break; + } + SetDefaultWFXChannelOrder(device); +} + + +/* alcSetError + * + * Stores the latest ALC device error + */ +static void alcSetError(ALCdevice *device, ALCenum errorCode) +{ + if(TrapALCError) + { +#ifdef _WIN32 + /* DebugBreak() will cause an exception if there is no debugger */ + if(IsDebuggerPresent()) + DebugBreak(); +#elif defined(SIGTRAP) + raise(SIGTRAP); +#endif + } + + if(device) + ATOMIC_STORE(&device->LastError, errorCode); + else + ATOMIC_STORE(&LastNullDeviceError, errorCode); +} + + +/* UpdateClockBase + * + * Updates the device's base clock time with however many samples have been + * done. This is used so frequency changes on the device don't cause the time + * to jump forward or back. + */ +static inline void UpdateClockBase(ALCdevice *device) +{ + device->ClockBase += device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency; + device->SamplesDone = 0; +} + +/* UpdateDeviceParams + * + * Updates device parameters according to the attribute list (caller is + * responsible for holding the list lock). + */ +static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) +{ + ALCcontext *context; + enum DevFmtChannels oldChans; + enum DevFmtType oldType; + ALCuint oldFreq; + FPUCtl oldMode; + + // Check for attributes + if(device->Type == Loopback) + { + enum { + GotFreq = 1<<0, + GotChans = 1<<1, + GotType = 1<<2, + GotAll = GotFreq|GotChans|GotType + }; + ALCuint freq, numMono, numStereo, numSends, flags; + enum DevFmtChannels schans; + enum DevFmtType stype; + ALCuint attrIdx = 0; + ALCint gotFmt = 0; + + if(!attrList) + { + WARN("Missing attributes for loopback device\n"); + return ALC_INVALID_VALUE; + } + + numMono = device->NumMonoSources; + numStereo = device->NumStereoSources; + numSends = device->NumAuxSends; + schans = device->FmtChans; + stype = device->FmtType; + freq = device->Frequency; + flags = device->Flags; + + while(attrList[attrIdx]) + { + if(attrList[attrIdx] == ALC_FORMAT_CHANNELS_SOFT) + { + ALCint val = attrList[attrIdx + 1]; + if(!IsValidALCChannels(val) || !ChannelsFromDevFmt(val)) + return ALC_INVALID_VALUE; + schans = val; + gotFmt |= GotChans; + } + + if(attrList[attrIdx] == ALC_FORMAT_TYPE_SOFT) + { + ALCint val = attrList[attrIdx + 1]; + if(!IsValidALCType(val) || !BytesFromDevFmt(val)) + return ALC_INVALID_VALUE; + stype = val; + gotFmt |= GotType; + } + + if(attrList[attrIdx] == ALC_FREQUENCY) + { + freq = attrList[attrIdx + 1]; + if(freq < MIN_OUTPUT_RATE) + return ALC_INVALID_VALUE; + gotFmt |= GotFreq; + } + + 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) + numSends = attrList[attrIdx + 1]; + + if(attrList[attrIdx] == ALC_HRTF_SOFT) + { + if(attrList[attrIdx + 1] != ALC_FALSE) + flags |= DEVICE_HRTF_REQUEST; + else + flags &= ~DEVICE_HRTF_REQUEST; + } + + attrIdx += 2; + } + + if(gotFmt != GotAll) + { + WARN("Missing format for loopback device\n"); + return ALC_INVALID_VALUE; + } + + ConfigValueUInt(NULL, "sends", &numSends); + numSends = minu(MAX_SENDS, numSends); + + if((device->Flags&DEVICE_RUNNING)) + V0(device->Backend,stop)(); + device->Flags = (flags & ~DEVICE_RUNNING); + + if(freq != device->Frequency) + UpdateClockBase(device); + device->Frequency = freq; + device->FmtChans = schans; + device->FmtType = stype; + device->NumMonoSources = numMono; + device->NumStereoSources = numStereo; + device->NumAuxSends = numSends; + } + else if(attrList && attrList[0]) + { + ALCuint freq, numMono, numStereo, numSends; + ALCuint attrIdx = 0; + + /* If a context is already running on the device, stop playback so the + * device attributes can be updated. */ + if((device->Flags&DEVICE_RUNNING)) + V0(device->Backend,stop)(); + device->Flags &= ~DEVICE_RUNNING; + + freq = device->Frequency; + numMono = device->NumMonoSources; + numStereo = device->NumStereoSources; + numSends = device->NumAuxSends; + + while(attrList[attrIdx]) + { + if(attrList[attrIdx] == ALC_FREQUENCY) + { + freq = attrList[attrIdx + 1]; + 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) + numSends = attrList[attrIdx + 1]; + + if(attrList[attrIdx] == ALC_HRTF_SOFT) + { + if(attrList[attrIdx + 1] != ALC_FALSE) + device->Flags |= DEVICE_HRTF_REQUEST; + else + device->Flags &= ~DEVICE_HRTF_REQUEST; + } + + attrIdx += 2; + } + + ConfigValueUInt(NULL, "frequency", &freq); + freq = maxu(freq, MIN_OUTPUT_RATE); + + ConfigValueUInt(NULL, "sends", &numSends); + numSends = minu(MAX_SENDS, numSends); + + device->UpdateSize = (ALuint64)device->UpdateSize * freq / + device->Frequency; + /* SSE and Neon do best with the update size being a multiple of 4 */ + if((CPUCapFlags&(CPU_CAP_SSE|CPU_CAP_NEON)) != 0) + device->UpdateSize = (device->UpdateSize+3)&~3; + + if(freq != device->Frequency) + UpdateClockBase(device); + device->Frequency = freq; + device->NumMonoSources = numMono; + device->NumStereoSources = numStereo; + device->NumAuxSends = numSends; + } + + if((device->Flags&DEVICE_RUNNING)) + return ALC_NO_ERROR; + + UpdateClockBase(device); + + oldFreq = device->Frequency; + oldChans = device->FmtChans; + oldType = device->FmtType; + + TRACE("Pre-reset: %s%s, %s%s, %s%uhz, %u update size x%d\n", + (device->Flags&DEVICE_CHANNELS_REQUEST)?"*":"", + DevFmtChannelsString(device->FmtChans), + (device->Flags&DEVICE_SAMPLE_TYPE_REQUEST)?"*":"", + DevFmtTypeString(device->FmtType), + (device->Flags&DEVICE_FREQUENCY_REQUEST)?"*":"", + device->Frequency, + device->UpdateSize, device->NumUpdates); + + if(device->Type != Loopback) + { + bool usehrtf = !!(device->Flags&DEVICE_HRTF_REQUEST); + if(GetConfigValueBool(NULL, "hrtf", usehrtf)) + device->Flags |= DEVICE_HRTF_REQUEST; + else + device->Flags &= ~DEVICE_HRTF_REQUEST; + } + if((device->Flags&DEVICE_HRTF_REQUEST)) + { + enum DevFmtChannels chans = device->FmtChans; + ALCuint freq = device->Frequency; + if(FindHrtfFormat(&chans, &freq)) + { + if(device->Type != Loopback) + { + device->Frequency = freq; + device->FmtChans = chans; + device->Flags |= DEVICE_CHANNELS_REQUEST | + DEVICE_FREQUENCY_REQUEST; + } + else if(device->Frequency != freq || device->FmtChans != chans) + { + ERR("Requested format not HRTF compatible: %s, %uhz\n", + DevFmtChannelsString(device->FmtChans), device->Frequency); + device->Flags &= ~DEVICE_HRTF_REQUEST; + } + } + } + + if(V0(device->Backend,reset)() == ALC_FALSE) + return ALC_INVALID_DEVICE; + + if(device->FmtChans != oldChans && (device->Flags&DEVICE_CHANNELS_REQUEST)) + { + ERR("Failed to set %s, got %s instead\n", DevFmtChannelsString(oldChans), + DevFmtChannelsString(device->FmtChans)); + device->Flags &= ~DEVICE_CHANNELS_REQUEST; + } + if(device->FmtType != oldType && (device->Flags&DEVICE_SAMPLE_TYPE_REQUEST)) + { + ERR("Failed to set %s, got %s instead\n", DevFmtTypeString(oldType), + DevFmtTypeString(device->FmtType)); + device->Flags &= ~DEVICE_SAMPLE_TYPE_REQUEST; + } + if(device->Frequency != oldFreq && (device->Flags&DEVICE_FREQUENCY_REQUEST)) + { + ERR("Failed to set %uhz, got %uhz instead\n", oldFreq, device->Frequency); + device->Flags &= ~DEVICE_FREQUENCY_REQUEST; + } + + TRACE("Post-reset: %s, %s, %uhz, %u update size x%d\n", + DevFmtChannelsString(device->FmtChans), + DevFmtTypeString(device->FmtType), device->Frequency, + device->UpdateSize, device->NumUpdates); + + aluInitPanning(device); + + V(device->Synth,update)(device); + + device->Hrtf = NULL; + if((device->Flags&DEVICE_HRTF_REQUEST)) + { + device->Hrtf = GetHrtf(device->FmtChans, device->Frequency); + if(!device->Hrtf) + device->Flags &= ~DEVICE_HRTF_REQUEST; + } + TRACE("HRTF %s\n", device->Hrtf?"enabled":"disabled"); + + if(!device->Hrtf && device->Bs2bLevel > 0 && device->Bs2bLevel <= 6) + { + 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_WIDE_STEREO; + if(device->Type != Loopback && !device->Hrtf && GetConfigValueBool(NULL, "wide-stereo", AL_FALSE)) + device->Flags |= DEVICE_WIDE_STEREO; + + if(!device->Hrtf && (device->UpdateSize&3)) + { + if((CPUCapFlags&CPU_CAP_SSE)) + WARN("SSE performs best with multiple of 4 update sizes (%u)\n", device->UpdateSize); + if((CPUCapFlags&CPU_CAP_NEON)) + WARN("NEON performs best with multiple of 4 update sizes (%u)\n", device->UpdateSize); + } + + SetMixerFPUMode(&oldMode); + ALCdevice_Lock(device); + context = ATOMIC_LOAD(&device->ContextList); + while(context) + { + ALsizei pos; + + ATOMIC_STORE(&context->UpdateSources, AL_FALSE); + LockUIntMapRead(&context->EffectSlotMap); + for(pos = 0;pos < context->EffectSlotMap.size;pos++) + { + ALeffectslot *slot = context->EffectSlotMap.array[pos].value; + + if(V(slot->EffectState,deviceUpdate)(device) == AL_FALSE) + { + UnlockUIntMapRead(&context->EffectSlotMap); + ALCdevice_Unlock(device); + RestoreFPUMode(&oldMode); + return ALC_INVALID_DEVICE; + } + ATOMIC_STORE(&slot->NeedsUpdate, AL_FALSE); + V(slot->EffectState,update)(device, slot); + } + UnlockUIntMapRead(&context->EffectSlotMap); + + LockUIntMapRead(&context->SourceMap); + 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) + DecrementRef(&source->Send[s].Slot->ref); + source->Send[s].Slot = NULL; + source->Send[s].Gain = 1.0f; + source->Send[s].GainHF = 1.0f; + s++; + } + ATOMIC_STORE(&source->NeedsUpdate, AL_TRUE); + } + UnlockUIntMapRead(&context->SourceMap); + + for(pos = 0;pos < context->ActiveSourceCount;pos++) + { + ALactivesource *src = context->ActiveSources[pos]; + ALsource *source = src->Source; + ALuint s = device->NumAuxSends; + while(s < MAX_SENDS) + { + src->Send[s].Moving = AL_FALSE; + src->Send[s].Counter = 0; + s++; + } + + src->Update(src, context); + ATOMIC_STORE(&source->NeedsUpdate, AL_FALSE); + } + + context = context->next; + } + if(device->DefaultSlot) + { + ALeffectslot *slot = device->DefaultSlot; + + if(V(slot->EffectState,deviceUpdate)(device) == AL_FALSE) + { + ALCdevice_Unlock(device); + RestoreFPUMode(&oldMode); + return ALC_INVALID_DEVICE; + } + ATOMIC_STORE(&slot->NeedsUpdate, AL_FALSE); + V(slot->EffectState,update)(device, slot); + } + ALCdevice_Unlock(device); + RestoreFPUMode(&oldMode); + + if(!(device->Flags&DEVICE_PAUSED)) + { + if(V0(device->Backend,start)() == ALC_FALSE) + return ALC_INVALID_DEVICE; + device->Flags |= DEVICE_RUNNING; + } + + return ALC_NO_ERROR; +} + +/* FreeDevice + * + * Frees the device structure, and destroys any objects the app failed to + * delete. Called once there's no more references on the device. + */ +static ALCvoid FreeDevice(ALCdevice *device) +{ + TRACE("%p\n", device); + + V0(device->Backend,close)(); + DELETE_OBJ(device->Backend); + device->Backend = NULL; + + DELETE_OBJ(device->Synth); + device->Synth = NULL; + + if(device->DefaultSlot) + { + ALeffectState *state = device->DefaultSlot->EffectState; + device->DefaultSlot = NULL; + DELETE_OBJ(state); + } + + if(device->DefaultSfont) + ALsoundfont_deleteSoundfont(device->DefaultSfont, device); + device->DefaultSfont = NULL; + + if(device->BufferMap.size > 0) + { + WARN("(%p) Deleting %d Buffer(s)\n", device, device->BufferMap.size); + ReleaseALBuffers(device); + } + ResetUIntMap(&device->BufferMap); + + if(device->EffectMap.size > 0) + { + WARN("(%p) Deleting %d Effect(s)\n", device, device->EffectMap.size); + ReleaseALEffects(device); + } + ResetUIntMap(&device->EffectMap); + + if(device->FilterMap.size > 0) + { + WARN("(%p) Deleting %d Filter(s)\n", device, device->FilterMap.size); + ReleaseALFilters(device); + } + ResetUIntMap(&device->FilterMap); + + if(device->SfontMap.size > 0) + { + WARN("(%p) Deleting %d Soundfont(s)\n", device, device->SfontMap.size); + ReleaseALSoundfonts(device); + } + ResetUIntMap(&device->SfontMap); + + if(device->PresetMap.size > 0) + { + WARN("(%p) Deleting %d Preset(s)\n", device, device->PresetMap.size); + ReleaseALPresets(device); + } + ResetUIntMap(&device->PresetMap); + + if(device->FontsoundMap.size > 0) + { + WARN("(%p) Deleting %d Fontsound(s)\n", device, device->FontsoundMap.size); + ReleaseALFontsounds(device); + } + ResetUIntMap(&device->FontsoundMap); + + free(device->Bs2b); + device->Bs2b = NULL; + + AL_STRING_DEINIT(device->DeviceName); + + al_free(device); +} + + +void ALCdevice_IncRef(ALCdevice *device) +{ + uint ref; + ref = IncrementRef(&device->ref); + TRACEREF("%p increasing refcount to %u\n", device, ref); +} + +void ALCdevice_DecRef(ALCdevice *device) +{ + uint ref; + ref = DecrementRef(&device->ref); + TRACEREF("%p decreasing refcount to %u\n", device, ref); + if(ref == 0) FreeDevice(device); +} + +/* VerifyDevice + * + * Checks if the device handle is valid, and increments its ref count if so. + */ +static ALCdevice *VerifyDevice(ALCdevice *device) +{ + ALCdevice *tmpDevice; + + if(!device) + return NULL; + + LockLists(); + tmpDevice = ATOMIC_LOAD(&DeviceList); + while(tmpDevice && tmpDevice != device) + tmpDevice = tmpDevice->next; + + if(tmpDevice) + ALCdevice_IncRef(tmpDevice); + UnlockLists(); + return tmpDevice; +} + + +/* InitContext + * + * Initializes context fields + */ +static ALvoid InitContext(ALCcontext *Context) +{ + ALint i, j; + + //Initialise listener + Context->Listener->Gain = 1.0f; + Context->Listener->MetersPerUnit = 1.0f; + Context->Listener->Position[0] = 0.0f; + Context->Listener->Position[1] = 0.0f; + Context->Listener->Position[2] = 0.0f; + Context->Listener->Velocity[0] = 0.0f; + Context->Listener->Velocity[1] = 0.0f; + Context->Listener->Velocity[2] = 0.0f; + Context->Listener->Forward[0] = 0.0f; + Context->Listener->Forward[1] = 0.0f; + Context->Listener->Forward[2] = -1.0f; + Context->Listener->Up[0] = 0.0f; + Context->Listener->Up[1] = 1.0f; + Context->Listener->Up[2] = 0.0f; + for(i = 0;i < 4;i++) + { + for(j = 0;j < 4;j++) + Context->Listener->Params.Matrix[i][j] = ((i==j) ? 1.0f : 0.0f); + } + for(i = 0;i < 3;i++) + Context->Listener->Params.Velocity[i] = 0.0f; + + //Validate Context + ATOMIC_INIT(&Context->LastError, AL_NO_ERROR); + ATOMIC_INIT(&Context->UpdateSources, AL_FALSE); + Context->ActiveSourceCount = 0; + InitUIntMap(&Context->SourceMap, Context->Device->MaxNoOfSources); + InitUIntMap(&Context->EffectSlotMap, Context->Device->AuxiliaryEffectSlotMax); + + //Set globals + Context->DistanceModel = DefaultDistanceModel; + Context->SourceDistanceModel = AL_FALSE; + Context->DopplerFactor = 1.0f; + Context->DopplerVelocity = 1.0f; + Context->SpeedOfSound = SPEEDOFSOUNDMETRESPERSEC; + Context->DeferUpdates = AL_FALSE; + + Context->ExtensionList = alExtList; +} + + +/* FreeContext + * + * Cleans up the context, and destroys any remaining objects the app failed to + * delete. Called once there's no more references on the context. + */ +static ALCvoid FreeContext(ALCcontext *context) +{ + ALsizei i; + + TRACE("%p\n", context); + + if(context->SourceMap.size > 0) + { + WARN("(%p) Deleting %d Source(s)\n", context, context->SourceMap.size); + ReleaseALSources(context); + } + ResetUIntMap(&context->SourceMap); + + if(context->EffectSlotMap.size > 0) + { + WARN("(%p) Deleting %d AuxiliaryEffectSlot(s)\n", context, context->EffectSlotMap.size); + ReleaseALAuxiliaryEffectSlots(context); + } + ResetUIntMap(&context->EffectSlotMap); + + for(i = 0;i < context->MaxActiveSources;i++) + { + al_free(context->ActiveSources[i]); + context->ActiveSources[i] = NULL; + } + free(context->ActiveSources); + context->ActiveSources = NULL; + context->ActiveSourceCount = 0; + context->MaxActiveSources = 0; + + VECTOR_DEINIT(context->ActiveAuxSlots); + + ALCdevice_DecRef(context->Device); + context->Device = NULL; + + //Invalidate context + memset(context, 0, sizeof(ALCcontext)); + free(context); +} + +/* ReleaseContext + * + * Removes the context reference from the given device and removes it from + * being current on the running thread or globally. + */ +static void ReleaseContext(ALCcontext *context, ALCdevice *device) +{ + ALCcontext *nextctx; + ALCcontext *origctx; + + if(altss_get(LocalContext) == context) + { + WARN("%p released while current on thread\n", context); + altss_set(LocalContext, NULL); + ALCcontext_DecRef(context); + } + + origctx = context; + if(ATOMIC_COMPARE_EXCHANGE_STRONG(ALCcontext*, &GlobalContext, &origctx, NULL)) + ALCcontext_DecRef(context); + + ALCdevice_Lock(device); + origctx = context; + nextctx = context->next; + if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCcontext*, &device->ContextList, &origctx, nextctx)) + { + ALCcontext *list; + do { + list = origctx; + origctx = context; + } while(!COMPARE_EXCHANGE(&list->next, &origctx, nextctx)); + } + ALCdevice_Unlock(device); + + ALCcontext_DecRef(context); +} + +void ALCcontext_IncRef(ALCcontext *context) +{ + uint ref; + ref = IncrementRef(&context->ref); + TRACEREF("%p increasing refcount to %u\n", context, ref); +} + +void ALCcontext_DecRef(ALCcontext *context) +{ + uint ref; + ref = DecrementRef(&context->ref); + TRACEREF("%p decreasing refcount to %u\n", context, ref); + if(ref == 0) FreeContext(context); +} + +static void ReleaseThreadCtx(void *ptr) +{ + WARN("%p current for thread being destroyed\n", ptr); + ALCcontext_DecRef(ptr); +} + +/* VerifyContext + * + * Checks that the given context is valid, and increments its reference count. + */ +static ALCcontext *VerifyContext(ALCcontext *context) +{ + ALCdevice *dev; + + LockLists(); + dev = ATOMIC_LOAD(&DeviceList); + while(dev) + { + ALCcontext *ctx = ATOMIC_LOAD(&dev->ContextList); + while(ctx) + { + if(ctx == context) + { + ALCcontext_IncRef(ctx); + UnlockLists(); + return ctx; + } + ctx = ctx->next; + } + dev = dev->next; + } + UnlockLists(); + + return NULL; +} + + +/* GetContextRef + * + * Returns the currently active context for this thread, and adds a reference + * without locking it. + */ +ALCcontext *GetContextRef(void) +{ + ALCcontext *context; + + context = altss_get(LocalContext); + if(context) + ALCcontext_IncRef(context); + else + { + LockLists(); + context = ATOMIC_LOAD(&GlobalContext); + if(context) + ALCcontext_IncRef(context); + UnlockLists(); + } + + return context; +} + + +/************************************************ + * Standard ALC functions + ************************************************/ + +/* alcGetError + * + * Return last ALC generated error code for the given device +*/ +ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) +{ + ALCenum errorCode; + + if(VerifyDevice(device)) + { + errorCode = ATOMIC_EXCHANGE(ALCenum, &device->LastError, ALC_NO_ERROR); + ALCdevice_DecRef(device); + } + else + errorCode = ATOMIC_EXCHANGE(ALCenum, &LastNullDeviceError, ALC_NO_ERROR); + + return errorCode; +} + + +/* alcSuspendContext + * + * Not functional + */ +ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *UNUSED(context)) +{ +} + +/* alcProcessContext + * + * Not functional + */ +ALC_API ALCvoid ALC_APIENTRY alcProcessContext(ALCcontext *UNUSED(context)) +{ +} + + +/* alcGetString + * + * Returns information about the device, and error strings + */ +ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, 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: + value = alcDefaultName; + break; + + case ALC_ALL_DEVICES_SPECIFIER: + if(VerifyDevice(Device)) + { + value = al_string_get_cstr(Device->DeviceName); + ALCdevice_DecRef(Device); + } + else + { + ProbeAllDevicesList(); + value = al_string_get_cstr(alcAllDevicesList); + } + break; + + case ALC_CAPTURE_DEVICE_SPECIFIER: + if(VerifyDevice(Device)) + { + value = al_string_get_cstr(Device->DeviceName); + ALCdevice_DecRef(Device); + } + else + { + ProbeCaptureDeviceList(); + value = al_string_get_cstr(alcCaptureDeviceList); + } + break; + + /* Default devices are always first in the list */ + case ALC_DEFAULT_DEVICE_SPECIFIER: + value = alcDefaultName; + break; + + case ALC_DEFAULT_ALL_DEVICES_SPECIFIER: + if(al_string_empty(alcAllDevicesList)) + ProbeAllDevicesList(); + + Device = VerifyDevice(Device); + + free(alcDefaultAllDevicesSpecifier); + alcDefaultAllDevicesSpecifier = strdup(al_string_get_cstr(alcAllDevicesList)); + if(!alcDefaultAllDevicesSpecifier) + alcSetError(Device, ALC_OUT_OF_MEMORY); + + value = alcDefaultAllDevicesSpecifier; + if(Device) ALCdevice_DecRef(Device); + break; + + case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER: + if(al_string_empty(alcCaptureDeviceList)) + ProbeCaptureDeviceList(); + + Device = VerifyDevice(Device); + + free(alcCaptureDefaultDeviceSpecifier); + alcCaptureDefaultDeviceSpecifier = strdup(al_string_get_cstr(alcAllDevicesList)); + if(!alcCaptureDefaultDeviceSpecifier) + alcSetError(Device, ALC_OUT_OF_MEMORY); + + value = alcCaptureDefaultDeviceSpecifier; + if(Device) ALCdevice_DecRef(Device); + break; + + case ALC_EXTENSIONS: + if(!VerifyDevice(Device)) + value = alcNoDeviceExtList; + else + { + value = alcExtensionList; + ALCdevice_DecRef(Device); + } + break; + + default: + Device = VerifyDevice(Device); + alcSetError(Device, ALC_INVALID_ENUM); + if(Device) ALCdevice_DecRef(Device); + break; + } + + return value; +} + + +static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) +{ + ALCsizei i; + + if(size <= 0 || values == NULL) + { + alcSetError(device, ALC_INVALID_VALUE); + return 0; + } + + if(!device) + { + switch(param) + { + case ALC_MAJOR_VERSION: + values[0] = alcMajorVersion; + return 1; + case ALC_MINOR_VERSION: + values[0] = alcMinorVersion; + return 1; + + 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); + return 0; + + default: + alcSetError(NULL, ALC_INVALID_ENUM); + return 0; + } + return 0; + } + + if(device->Type == Capture) + { + switch(param) + { + case ALC_CAPTURE_SAMPLES: + ALCdevice_Lock(device); + values[0] = V0(device->Backend,availableSamples)(); + ALCdevice_Unlock(device); + return 1; + + case ALC_CONNECTED: + values[0] = device->Connected; + return 1; + + default: + alcSetError(device, ALC_INVALID_ENUM); + return 0; + } + return 0; + } + + /* render device */ + switch(param) + { + case ALC_MAJOR_VERSION: + values[0] = alcMajorVersion; + return 1; + + case ALC_MINOR_VERSION: + values[0] = alcMinorVersion; + return 1; + + case ALC_EFX_MAJOR_VERSION: + values[0] = alcEFXMajorVersion; + return 1; + + case ALC_EFX_MINOR_VERSION: + values[0] = alcEFXMinorVersion; + return 1; + + case ALC_ATTRIBUTES_SIZE: + values[0] = 15; + return 1; + + case ALC_ALL_ATTRIBUTES: + if(size < 15) + { + alcSetError(device, ALC_INVALID_VALUE); + return 0; + } + + i = 0; + values[i++] = ALC_FREQUENCY; + values[i++] = device->Frequency; + + if(device->Type != Loopback) + { + values[i++] = ALC_REFRESH; + values[i++] = device->Frequency / device->UpdateSize; + + values[i++] = ALC_SYNC; + values[i++] = ALC_FALSE; + } + else + { + values[i++] = ALC_FORMAT_CHANNELS_SOFT; + values[i++] = device->FmtChans; + + values[i++] = ALC_FORMAT_TYPE_SOFT; + values[i++] = device->FmtType; + } + + values[i++] = ALC_MONO_SOURCES; + values[i++] = device->NumMonoSources; + + values[i++] = ALC_STEREO_SOURCES; + values[i++] = device->NumStereoSources; + + values[i++] = ALC_MAX_AUXILIARY_SENDS; + values[i++] = device->NumAuxSends; + + values[i++] = ALC_HRTF_SOFT; + values[i++] = (device->Hrtf ? ALC_TRUE : ALC_FALSE); + + values[i++] = 0; + return i; + + case ALC_FREQUENCY: + values[0] = device->Frequency; + return 1; + + case ALC_REFRESH: + if(device->Type == Loopback) + { + alcSetError(device, ALC_INVALID_DEVICE); + return 0; + } + values[0] = device->Frequency / device->UpdateSize; + return 1; + + case ALC_SYNC: + if(device->Type == Loopback) + { + alcSetError(device, ALC_INVALID_DEVICE); + return 0; + } + values[0] = ALC_FALSE; + return 1; + + case ALC_FORMAT_CHANNELS_SOFT: + if(device->Type != Loopback) + { + alcSetError(device, ALC_INVALID_DEVICE); + return 0; + } + values[0] = device->FmtChans; + return 1; + + case ALC_FORMAT_TYPE_SOFT: + if(device->Type != Loopback) + { + alcSetError(device, ALC_INVALID_DEVICE); + return 0; + } + values[0] = device->FmtType; + return 1; + + case ALC_MONO_SOURCES: + values[0] = device->NumMonoSources; + return 1; + + case ALC_STEREO_SOURCES: + values[0] = device->NumStereoSources; + return 1; + + case ALC_MAX_AUXILIARY_SENDS: + values[0] = device->NumAuxSends; + return 1; + + case ALC_CONNECTED: + values[0] = device->Connected; + return 1; + + case ALC_HRTF_SOFT: + values[0] = (device->Hrtf ? ALC_TRUE : ALC_FALSE); + return 1; + + default: + alcSetError(device, ALC_INVALID_ENUM); + return 0; + } + return 0; +} + +/* alcGetIntegerv + * + * Returns information about the device and the version of OpenAL + */ +ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) +{ + device = VerifyDevice(device); + if(size <= 0 || values == NULL) + alcSetError(device, ALC_INVALID_VALUE); + else + GetIntegerv(device, param, size, values); + if(device) ALCdevice_DecRef(device); +} + +ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALCsizei size, ALCint64SOFT *values) +{ + ALCint *ivals; + ALsizei i; + + device = VerifyDevice(device); + if(size <= 0 || values == NULL) + alcSetError(device, ALC_INVALID_VALUE); + else if(!device || device->Type == Capture) + { + ivals = malloc(size * sizeof(ALCint)); + size = GetIntegerv(device, pname, size, ivals); + for(i = 0;i < size;i++) + values[i] = ivals[i]; + free(ivals); + } + else /* render device */ + { + switch(pname) + { + case ALC_ATTRIBUTES_SIZE: + *values = 17; + break; + + case ALC_ALL_ATTRIBUTES: + if(size < 17) + alcSetError(device, ALC_INVALID_VALUE); + else + { + int i = 0; + + V0(device->Backend,lock)(); + values[i++] = ALC_FREQUENCY; + values[i++] = device->Frequency; + + if(device->Type != Loopback) + { + values[i++] = ALC_REFRESH; + values[i++] = device->Frequency / device->UpdateSize; + + values[i++] = ALC_SYNC; + values[i++] = ALC_FALSE; + } + else + { + values[i++] = ALC_FORMAT_CHANNELS_SOFT; + values[i++] = device->FmtChans; + + values[i++] = ALC_FORMAT_TYPE_SOFT; + values[i++] = device->FmtType; + } + + values[i++] = ALC_MONO_SOURCES; + values[i++] = device->NumMonoSources; + + values[i++] = ALC_STEREO_SOURCES; + values[i++] = device->NumStereoSources; + + values[i++] = ALC_MAX_AUXILIARY_SENDS; + values[i++] = device->NumAuxSends; + + values[i++] = ALC_HRTF_SOFT; + values[i++] = (device->Hrtf ? ALC_TRUE : ALC_FALSE); + + values[i++] = ALC_DEVICE_CLOCK_SOFT; + values[i++] = device->ClockBase + + (device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency); + + values[i++] = 0; + V0(device->Backend,unlock)(); + } + break; + + case ALC_DEVICE_CLOCK_SOFT: + V0(device->Backend,lock)(); + *values = device->ClockBase + + (device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency); + V0(device->Backend,unlock)(); + break; + + default: + ivals = malloc(size * sizeof(ALCint)); + size = GetIntegerv(device, pname, size, ivals); + for(i = 0;i < size;i++) + values[i] = ivals[i]; + free(ivals); + break; + } + } + if(device) + ALCdevice_DecRef(device); +} + + +/* 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; + + device = VerifyDevice(device); + + if(!extName) + alcSetError(device, ALC_INVALID_VALUE); + else + { + size_t len = strlen(extName); + const char *ptr = (device ? alcExtensionList : alcNoDeviceExtList); + 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)); + } + } + } + if(device) + ALCdevice_DecRef(device); + return bResult; +} + + +/* alcGetProcAddress + * + * Retrieves the function address for a particular extension function + */ +ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName) +{ + ALCvoid *ptr = NULL; + + if(!funcName) + { + device = VerifyDevice(device); + alcSetError(device, ALC_INVALID_VALUE); + if(device) ALCdevice_DecRef(device); + } + else + { + ALsizei i = 0; + while(alcFunctions[i].funcName && strcmp(alcFunctions[i].funcName, funcName) != 0) + i++; + ptr = alcFunctions[i].address; + } + + return ptr; +} + + +/* alcGetEnumValue + * + * Get the value for a particular ALC enumeration name + */ +ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName) +{ + ALCenum val = 0; + + if(!enumName) + { + device = VerifyDevice(device); + alcSetError(device, ALC_INVALID_VALUE); + if(device) ALCdevice_DecRef(device); + } + else + { + ALsizei i = 0; + while(enumeration[i].enumName && strcmp(enumeration[i].enumName, enumName) != 0) + i++; + val = enumeration[i].value; + } + + return val; +} + + +/* alcCreateContext + * + * Create and attach a context to the given device. + */ +ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList) +{ + ALCcontext *ALContext; + ALCenum err; + + LockLists(); + if(!(device=VerifyDevice(device)) || device->Type == Capture || !device->Connected) + { + UnlockLists(); + alcSetError(device, ALC_INVALID_DEVICE); + if(device) ALCdevice_DecRef(device); + return NULL; + } + + ATOMIC_STORE(&device->LastError, ALC_NO_ERROR); + + if((err=UpdateDeviceParams(device, attrList)) != ALC_NO_ERROR) + { + UnlockLists(); + alcSetError(device, err); + if(err == ALC_INVALID_DEVICE) + { + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); + } + ALCdevice_DecRef(device); + return NULL; + } + + ALContext = calloc(1, sizeof(ALCcontext)+sizeof(ALlistener)); + if(ALContext) + { + InitRef(&ALContext->ref, 1); + ALContext->Listener = (ALlistener*)ALContext->_listener_mem; + + VECTOR_INIT(ALContext->ActiveAuxSlots); + + ALContext->MaxActiveSources = 256; + ALContext->ActiveSources = calloc(ALContext->MaxActiveSources, + sizeof(ALContext->ActiveSources[0])); + } + if(!ALContext || !ALContext->ActiveSources) + { + if(!ATOMIC_LOAD(&device->ContextList)) + { + V0(device->Backend,stop)(); + device->Flags &= ~DEVICE_RUNNING; + } + UnlockLists(); + + if(ALContext) + { + free(ALContext->ActiveSources); + ALContext->ActiveSources = NULL; + + VECTOR_DEINIT(ALContext->ActiveAuxSlots); + + free(ALContext); + ALContext = NULL; + } + + alcSetError(device, ALC_OUT_OF_MEMORY); + ALCdevice_DecRef(device); + return NULL; + } + + ALContext->Device = device; + ALCdevice_IncRef(device); + InitContext(ALContext); + + { + ALCcontext *head = ATOMIC_LOAD(&device->ContextList); + do { + ALContext->next = head; + } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCcontext*, &device->ContextList, &head, ALContext)); + } + UnlockLists(); + + ALCdevice_DecRef(device); + + TRACE("Created context %p\n", ALContext); + return ALContext; +} + +/* alcDestroyContext + * + * Remove a context from its device + */ +ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context) +{ + ALCdevice *Device; + + LockLists(); + /* alcGetContextsDevice sets an error for invalid contexts */ + Device = alcGetContextsDevice(context); + if(Device) + { + ReleaseContext(context, Device); + if(!ATOMIC_LOAD(&Device->ContextList)) + { + V0(Device->Backend,stop)(); + Device->Flags &= ~DEVICE_RUNNING; + } + } + UnlockLists(); +} + + +/* alcGetCurrentContext + * + * Returns the currently active context on the calling thread + */ +ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) +{ + ALCcontext *Context = altss_get(LocalContext); + if(!Context) Context = ATOMIC_LOAD(&GlobalContext); + return Context; +} + +/* alcGetThreadContext + * + * Returns the currently active thread-local context + */ +ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) +{ + return altss_get(LocalContext); +} + + +/* alcMakeContextCurrent + * + * Makes the given context the active process-wide context, and removes the + * thread-local context for the calling thread. + */ +ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) +{ + /* context must be valid or NULL */ + if(context && !(context=VerifyContext(context))) + { + alcSetError(NULL, ALC_INVALID_CONTEXT); + return ALC_FALSE; + } + /* context's reference count is already incremented */ + context = ATOMIC_EXCHANGE(ALCcontext*, &GlobalContext, context); + if(context) ALCcontext_DecRef(context); + + if((context=altss_get(LocalContext)) != NULL) + { + altss_set(LocalContext, NULL); + ALCcontext_DecRef(context); + } + + return ALC_TRUE; +} + +/* alcSetThreadContext + * + * Makes the given context the active context for the current thread + */ +ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) +{ + ALCcontext *old; + + /* context must be valid or NULL */ + if(context && !(context=VerifyContext(context))) + { + alcSetError(NULL, ALC_INVALID_CONTEXT); + return ALC_FALSE; + } + /* context's reference count is already incremented */ + old = altss_get(LocalContext); + altss_set(LocalContext, context); + if(old) ALCcontext_DecRef(old); + + return ALC_TRUE; +} + + +/* alcGetContextsDevice + * + * Returns the device that a particular context is attached to + */ +ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context) +{ + ALCdevice *Device; + + if(!(Context=VerifyContext(Context))) + { + alcSetError(NULL, ALC_INVALID_CONTEXT); + return NULL; + } + Device = Context->Device; + ALCcontext_DecRef(Context); + + return Device; +} + + +/* alcOpenDevice + * + * Opens the named device. + */ +ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) +{ + const ALCchar *fmt; + ALCdevice *device; + ALCenum err; + + DO_INITCONFIG(); + + if(!PlaybackBackend.name) + { + alcSetError(NULL, ALC_INVALID_VALUE); + return NULL; + } + + if(deviceName && (!deviceName[0] || strcasecmp(deviceName, alcDefaultName) == 0 || strcasecmp(deviceName, "openal-soft") == 0)) + deviceName = NULL; + + device = al_calloc(16, sizeof(ALCdevice)+sizeof(ALeffectslot)); + if(!device) + { + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + + //Validate device + InitRef(&device->ref, 1); + device->Connected = ALC_TRUE; + device->Type = Playback; + ATOMIC_INIT(&device->LastError, ALC_NO_ERROR); + + device->Flags = 0; + device->Bs2b = NULL; + device->Bs2bLevel = 0; + AL_STRING_INIT(device->DeviceName); + + ATOMIC_INIT(&device->ContextList, NULL); + + device->ClockBase = 0; + device->SamplesDone = 0; + + device->MaxNoOfSources = 256; + device->AuxiliaryEffectSlotMax = 4; + device->NumAuxSends = MAX_SENDS; + + InitUIntMap(&device->BufferMap, ~0); + InitUIntMap(&device->EffectMap, ~0); + InitUIntMap(&device->FilterMap, ~0); + InitUIntMap(&device->SfontMap, ~0); + InitUIntMap(&device->PresetMap, ~0); + InitUIntMap(&device->FontsoundMap, ~0); + + //Set output format + device->FmtChans = DevFmtChannelsDefault; + device->FmtType = DevFmtTypeDefault; + device->Frequency = DEFAULT_OUTPUT_RATE; + device->NumUpdates = 4; + device->UpdateSize = 1024; + + if(!PlaybackBackend.getFactory) + device->Backend = create_backend_wrapper(device, &PlaybackBackend.Funcs, + ALCbackend_Playback); + else + { + ALCbackendFactory *factory = PlaybackBackend.getFactory(); + device->Backend = V(factory,createBackend)(device, ALCbackend_Playback); + } + if(!device->Backend) + { + al_free(device); + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + + + if(ConfigValueStr(NULL, "channels", &fmt)) + { + static const struct { + const char name[16]; + enum DevFmtChannels chans; + } chanlist[] = { + { "mono", DevFmtMono }, + { "stereo", DevFmtStereo }, + { "quad", DevFmtQuad }, + { "surround51", DevFmtX51 }, + { "surround61", DevFmtX61 }, + { "surround71", DevFmtX71 }, + }; + size_t i; + + for(i = 0;i < COUNTOF(chanlist);i++) + { + if(strcasecmp(chanlist[i].name, fmt) == 0) + { + device->FmtChans = chanlist[i].chans; + device->Flags |= DEVICE_CHANNELS_REQUEST; + break; + } + } + if(i == COUNTOF(chanlist)) + ERR("Unsupported channels: %s\n", fmt); + } + if(ConfigValueStr(NULL, "sample-type", &fmt)) + { + static const struct { + const char name[16]; + enum DevFmtType type; + } typelist[] = { + { "int8", DevFmtByte }, + { "uint8", DevFmtUByte }, + { "int16", DevFmtShort }, + { "uint16", DevFmtUShort }, + { "int32", DevFmtInt }, + { "uint32", DevFmtUInt }, + { "float32", DevFmtFloat }, + }; + size_t i; + + for(i = 0;i < COUNTOF(typelist);i++) + { + if(strcasecmp(typelist[i].name, fmt) == 0) + { + device->FmtType = typelist[i].type; + device->Flags |= DEVICE_SAMPLE_TYPE_REQUEST; + break; + } + } + if(i == COUNTOF(typelist)) + ERR("Unsupported sample-type: %s\n", fmt); + } +#define DEVICE_FORMAT_REQUEST (DEVICE_CHANNELS_REQUEST|DEVICE_SAMPLE_TYPE_REQUEST) + if((device->Flags&DEVICE_FORMAT_REQUEST) != DEVICE_FORMAT_REQUEST && + ConfigValueStr(NULL, "format", &fmt)) + { + static const struct { + const char name[32]; + enum DevFmtChannels channels; + enum DevFmtType type; + } formats[] = { + { "AL_FORMAT_MONO32", DevFmtMono, DevFmtFloat }, + { "AL_FORMAT_STEREO32", DevFmtStereo, DevFmtFloat }, + { "AL_FORMAT_QUAD32", DevFmtQuad, DevFmtFloat }, + { "AL_FORMAT_51CHN32", DevFmtX51, DevFmtFloat }, + { "AL_FORMAT_61CHN32", DevFmtX61, DevFmtFloat }, + { "AL_FORMAT_71CHN32", DevFmtX71, DevFmtFloat }, + + { "AL_FORMAT_MONO16", DevFmtMono, DevFmtShort }, + { "AL_FORMAT_STEREO16", DevFmtStereo, DevFmtShort }, + { "AL_FORMAT_QUAD16", DevFmtQuad, DevFmtShort }, + { "AL_FORMAT_51CHN16", DevFmtX51, DevFmtShort }, + { "AL_FORMAT_61CHN16", DevFmtX61, DevFmtShort }, + { "AL_FORMAT_71CHN16", DevFmtX71, DevFmtShort }, + + { "AL_FORMAT_MONO8", DevFmtMono, DevFmtByte }, + { "AL_FORMAT_STEREO8", DevFmtStereo, DevFmtByte }, + { "AL_FORMAT_QUAD8", DevFmtQuad, DevFmtByte }, + { "AL_FORMAT_51CHN8", DevFmtX51, DevFmtByte }, + { "AL_FORMAT_61CHN8", DevFmtX61, DevFmtByte }, + { "AL_FORMAT_71CHN8", DevFmtX71, DevFmtByte } + }; + size_t i; + + ERR("Option 'format' is deprecated, please use 'channels' and 'sample-type'\n"); + for(i = 0;i < COUNTOF(formats);i++) + { + if(strcasecmp(fmt, formats[i].name) == 0) + { + if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) + device->FmtChans = formats[i].channels; + if(!(device->Flags&DEVICE_SAMPLE_TYPE_REQUEST)) + device->FmtType = formats[i].type; + device->Flags |= DEVICE_FORMAT_REQUEST; + break; + } + } + if(i == COUNTOF(formats)) + ERR("Unsupported format: %s\n", fmt); + } +#undef DEVICE_FORMAT_REQUEST + + if(ConfigValueUInt(NULL, "frequency", &device->Frequency)) + { + device->Flags |= DEVICE_FREQUENCY_REQUEST; + if(device->Frequency < MIN_OUTPUT_RATE) + ERR("%uhz request clamped to %uhz minimum\n", device->Frequency, MIN_OUTPUT_RATE); + device->Frequency = maxu(device->Frequency, MIN_OUTPUT_RATE); + } + + ConfigValueUInt(NULL, "periods", &device->NumUpdates); + device->NumUpdates = clampu(device->NumUpdates, 2, 16); + + ConfigValueUInt(NULL, "period_size", &device->UpdateSize); + device->UpdateSize = clampu(device->UpdateSize, 64, 8192); + if((CPUCapFlags&CPU_CAP_SSE)) + device->UpdateSize = (device->UpdateSize+3)&~3; + + ConfigValueUInt(NULL, "sources", &device->MaxNoOfSources); + if(device->MaxNoOfSources == 0) device->MaxNoOfSources = 256; + + ConfigValueUInt(NULL, "slots", &device->AuxiliaryEffectSlotMax); + if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 4; + + ConfigValueUInt(NULL, "sends", &device->NumAuxSends); + if(device->NumAuxSends > MAX_SENDS) device->NumAuxSends = MAX_SENDS; + + ConfigValueInt(NULL, "cf_level", &device->Bs2bLevel); + + device->NumStereoSources = 1; + device->NumMonoSources = device->MaxNoOfSources - device->NumStereoSources; + + device->Synth = SynthCreate(device); + if(!device->Synth) + { + DELETE_OBJ(device->Backend); + al_free(device); + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + + // Find a playback device to open + if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR) + { + DELETE_OBJ(device->Synth); + DELETE_OBJ(device->Backend); + al_free(device); + alcSetError(NULL, err); + return NULL; + } + + if(DefaultEffect.type != AL_EFFECT_NULL) + { + device->DefaultSlot = (ALeffectslot*)device->_slot_mem; + if(InitEffectSlot(device->DefaultSlot) != AL_NO_ERROR) + { + device->DefaultSlot = NULL; + ERR("Failed to initialize the default effect slot\n"); + } + else if(InitializeEffect(device, device->DefaultSlot, &DefaultEffect) != AL_NO_ERROR) + { + ALeffectState *state = device->DefaultSlot->EffectState; + device->DefaultSlot = NULL; + DELETE_OBJ(state); + ERR("Failed to initialize the default effect\n"); + } + } + + { + ALCdevice *head = ATOMIC_LOAD(&DeviceList); + do { + device->next = head; + } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device)); + } + + TRACE("Created device %p, \"%s\"\n", device, al_string_get_cstr(device->DeviceName)); + return device; +} + +/* alcCloseDevice + * + * Closes the given device. + */ +ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) +{ + ALCdevice *list, *origdev, *nextdev; + ALCcontext *ctx; + + LockLists(); + list = ATOMIC_LOAD(&DeviceList); + do { + if(list == device) + break; + } while((list=list->next) != NULL); + if(!list || list->Type == Capture) + { + alcSetError(list, ALC_INVALID_DEVICE); + UnlockLists(); + return ALC_FALSE; + } + + origdev = device; + nextdev = device->next; + if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCdevice*, &DeviceList, &origdev, nextdev)) + { + do { + list = origdev; + origdev = device; + } while(!COMPARE_EXCHANGE(&list->next, &origdev, nextdev)); + } + UnlockLists(); + + ctx = ATOMIC_LOAD(&device->ContextList); + while(ctx != NULL) + { + ALCcontext *next = ctx->next; + WARN("Releasing context %p\n", ctx); + ReleaseContext(ctx, device); + ctx = next; + } + if((device->Flags&DEVICE_RUNNING)) + V0(device->Backend,stop)(); + device->Flags &= ~DEVICE_RUNNING; + + ALCdevice_DecRef(device); + + return ALC_TRUE; +} + + +/************************************************ + * ALC capture functions + ************************************************/ +ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples) +{ + ALCdevice *device = NULL; + ALCenum err; + + DO_INITCONFIG(); + + if(!CaptureBackend.name) + { + alcSetError(NULL, ALC_INVALID_VALUE); + return NULL; + } + + if(samples <= 0) + { + alcSetError(NULL, ALC_INVALID_VALUE); + return NULL; + } + + if(deviceName && (!deviceName[0] || strcasecmp(deviceName, alcDefaultName) == 0 || strcasecmp(deviceName, "openal-soft") == 0)) + deviceName = NULL; + + device = al_calloc(16, sizeof(ALCdevice)); + if(!device) + { + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + + //Validate device + InitRef(&device->ref, 1); + device->Connected = ALC_TRUE; + device->Type = Capture; + + AL_STRING_INIT(device->DeviceName); + + InitUIntMap(&device->BufferMap, ~0); + InitUIntMap(&device->EffectMap, ~0); + InitUIntMap(&device->FilterMap, ~0); + InitUIntMap(&device->SfontMap, ~0); + InitUIntMap(&device->PresetMap, ~0); + InitUIntMap(&device->FontsoundMap, ~0); + + if(!CaptureBackend.getFactory) + device->Backend = create_backend_wrapper(device, &CaptureBackend.Funcs, + ALCbackend_Capture); + else + { + ALCbackendFactory *factory = CaptureBackend.getFactory(); + device->Backend = V(factory,createBackend)(device, ALCbackend_Capture); + } + if(!device->Backend) + { + al_free(device); + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + + device->Flags |= DEVICE_FREQUENCY_REQUEST; + device->Frequency = frequency; + + device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_SAMPLE_TYPE_REQUEST; + if(DecomposeDevFormat(format, &device->FmtChans, &device->FmtType) == AL_FALSE) + { + al_free(device); + alcSetError(NULL, ALC_INVALID_ENUM); + return NULL; + } + + device->UpdateSize = samples; + device->NumUpdates = 1; + + if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR) + { + al_free(device); + alcSetError(NULL, err); + return NULL; + } + + { + ALCdevice *head = ATOMIC_LOAD(&DeviceList); + do { + device->next = head; + } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device)); + } + + TRACE("Created device %p, \"%s\"\n", device, al_string_get_cstr(device->DeviceName)); + return device; +} + +ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) +{ + ALCdevice *list, *next, *nextdev; + + LockLists(); + list = ATOMIC_LOAD(&DeviceList); + do { + if(list == device) + break; + } while((list=list->next) != NULL); + if(!list || list->Type != Capture) + { + alcSetError(list, ALC_INVALID_DEVICE); + UnlockLists(); + return ALC_FALSE; + } + + next = device; + nextdev = device->next; + if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCdevice*, &DeviceList, &next, nextdev)) + { + do { + list = next; + next = device; + } while(!COMPARE_EXCHANGE(&list->next, &next, nextdev)); + } + UnlockLists(); + + ALCdevice_DecRef(device); + + return ALC_TRUE; +} + +ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) +{ + if(!(device=VerifyDevice(device)) || device->Type != Capture) + alcSetError(device, ALC_INVALID_DEVICE); + else + { + ALCdevice_Lock(device); + if(device->Connected) + { + if(!(device->Flags&DEVICE_RUNNING)) + V0(device->Backend,start)(); + device->Flags |= DEVICE_RUNNING; + } + ALCdevice_Unlock(device); + } + + if(device) ALCdevice_DecRef(device); +} + +ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) +{ + if(!(device=VerifyDevice(device)) || device->Type != Capture) + alcSetError(device, ALC_INVALID_DEVICE); + else + { + ALCdevice_Lock(device); + if((device->Flags&DEVICE_RUNNING)) + V0(device->Backend,stop)(); + device->Flags &= ~DEVICE_RUNNING; + ALCdevice_Unlock(device); + } + + if(device) ALCdevice_DecRef(device); +} + +ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) +{ + if(!(device=VerifyDevice(device)) || device->Type != Capture) + alcSetError(device, ALC_INVALID_DEVICE); + else + { + ALCenum err = ALC_INVALID_VALUE; + + ALCdevice_Lock(device); + if(samples >= 0 && V0(device->Backend,availableSamples)() >= (ALCuint)samples) + err = V(device->Backend,captureSamples)(buffer, samples); + ALCdevice_Unlock(device); + + if(err != ALC_NO_ERROR) + alcSetError(device, err); + } + if(device) ALCdevice_DecRef(device); +} + + +/************************************************ + * ALC loopback functions + ************************************************/ + +/* alcLoopbackOpenDeviceSOFT + * + * Open a loopback device, for manual rendering. + */ +ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName) +{ + ALCbackendFactory *factory; + ALCdevice *device; + + DO_INITCONFIG(); + + /* Make sure the device name, if specified, is us. */ + if(deviceName && strcmp(deviceName, alcDefaultName) != 0) + { + alcSetError(NULL, ALC_INVALID_VALUE); + return NULL; + } + + device = al_calloc(16, sizeof(ALCdevice)); + if(!device) + { + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + + //Validate device + InitRef(&device->ref, 1); + device->Connected = ALC_TRUE; + device->Type = Loopback; + ATOMIC_INIT(&device->LastError, ALC_NO_ERROR); + + device->Flags = 0; + device->Bs2b = NULL; + device->Bs2bLevel = 0; + AL_STRING_INIT(device->DeviceName); + + ATOMIC_INIT(&device->ContextList, NULL); + + device->ClockBase = 0; + device->SamplesDone = 0; + + device->MaxNoOfSources = 256; + device->AuxiliaryEffectSlotMax = 4; + device->NumAuxSends = MAX_SENDS; + + InitUIntMap(&device->BufferMap, ~0); + InitUIntMap(&device->EffectMap, ~0); + InitUIntMap(&device->FilterMap, ~0); + InitUIntMap(&device->SfontMap, ~0); + InitUIntMap(&device->PresetMap, ~0); + InitUIntMap(&device->FontsoundMap, ~0); + + factory = ALCloopbackFactory_getFactory(); + device->Backend = V(factory,createBackend)(device, ALCbackend_Loopback); + if(!device->Backend) + { + al_free(device); + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + + //Set output format + device->NumUpdates = 0; + device->UpdateSize = 0; + + device->Frequency = DEFAULT_OUTPUT_RATE; + device->FmtChans = DevFmtChannelsDefault; + device->FmtType = DevFmtTypeDefault; + + ConfigValueUInt(NULL, "sources", &device->MaxNoOfSources); + if(device->MaxNoOfSources == 0) device->MaxNoOfSources = 256; + + ConfigValueUInt(NULL, "slots", &device->AuxiliaryEffectSlotMax); + if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 4; + + ConfigValueUInt(NULL, "sends", &device->NumAuxSends); + if(device->NumAuxSends > MAX_SENDS) device->NumAuxSends = MAX_SENDS; + + device->NumStereoSources = 1; + device->NumMonoSources = device->MaxNoOfSources - device->NumStereoSources; + + device->Synth = SynthCreate(device); + if(!device->Synth) + { + DELETE_OBJ(device->Backend); + al_free(device); + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + + // Open the "backend" + V(device->Backend,open)("Loopback"); + + { + ALCdevice *head = ATOMIC_LOAD(&DeviceList); + do { + device->next = head; + } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device)); + } + + TRACE("Created device %p\n", device); + return device; +} + +/* alcIsRenderFormatSupportedSOFT + * + * Determines if the loopback device supports the given format for rendering. + */ +ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type) +{ + ALCboolean ret = ALC_FALSE; + + if(!(device=VerifyDevice(device)) || device->Type != Loopback) + alcSetError(device, ALC_INVALID_DEVICE); + else if(freq <= 0) + alcSetError(device, ALC_INVALID_VALUE); + else + { + if(IsValidALCType(type) && BytesFromDevFmt(type) > 0 && + IsValidALCChannels(channels) && ChannelsFromDevFmt(channels) > 0 && + freq >= MIN_OUTPUT_RATE) + ret = ALC_TRUE; + } + if(device) ALCdevice_DecRef(device); + + return ret; +} + +/* alcRenderSamplesSOFT + * + * Renders some samples into a buffer, using the format last set by the + * attributes given to alcCreateContext. + */ +FORCE_ALIGN ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) +{ + if(!(device=VerifyDevice(device)) || device->Type != Loopback) + alcSetError(device, ALC_INVALID_DEVICE); + else if(samples < 0 || (samples > 0 && buffer == NULL)) + alcSetError(device, ALC_INVALID_VALUE); + else + aluMixData(device, buffer, samples); + if(device) ALCdevice_DecRef(device); +} + + +/************************************************ + * ALC DSP pause/resume functions + ************************************************/ + +/* alcDevicePauseSOFT + * + * Pause the DSP to stop audio processing. + */ +ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) +{ + if(!(device=VerifyDevice(device)) || device->Type != Playback) + alcSetError(device, ALC_INVALID_DEVICE); + else + { + LockLists(); + if((device->Flags&DEVICE_RUNNING)) + V0(device->Backend,stop)(); + device->Flags &= ~DEVICE_RUNNING; + device->Flags |= DEVICE_PAUSED; + UnlockLists(); + } + if(device) ALCdevice_DecRef(device); +} + +/* alcDeviceResumeSOFT + * + * Resume the DSP to restart audio processing. + */ +ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) +{ + if(!(device=VerifyDevice(device)) || device->Type != Playback) + alcSetError(device, ALC_INVALID_DEVICE); + else + { + LockLists(); + if((device->Flags&DEVICE_PAUSED)) + { + device->Flags &= ~DEVICE_PAUSED; + if(ATOMIC_LOAD(&device->ContextList) != NULL) + { + if(V0(device->Backend,start)() != ALC_FALSE) + device->Flags |= DEVICE_RUNNING; + else + { + alcSetError(device, ALC_INVALID_DEVICE); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); + } + } + } + UnlockLists(); + } + if(device) ALCdevice_DecRef(device); +} diff --git a/libs/openal-soft/Alc/ALu.c b/libs/openal-soft/Alc/ALu.c new file mode 100644 index 00000000..a85e1aa0 --- /dev/null +++ b/libs/openal-soft/Alc/ALu.c @@ -0,0 +1,1321 @@ +/** + * 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 "alBuffer.h" +#include "alListener.h" +#include "alAuxEffectSlot.h" +#include "alu.h" +#include "bs2b.h" +#include "hrtf.h" +#include "static_assert.h" + +#include "midi/base.h" + + +static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE, + "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!"); + +struct ChanMap { + enum Channel channel; + ALfloat angle; +}; + +/* Cone scalar */ +ALfloat ConeScale = 1.0f; + +/* Localized Z scalar for mono sources */ +ALfloat ZScale = 1.0f; + +extern inline ALfloat minf(ALfloat a, ALfloat b); +extern inline ALfloat maxf(ALfloat a, ALfloat b); +extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max); + +extern inline ALdouble mind(ALdouble a, ALdouble b); +extern inline ALdouble maxd(ALdouble a, ALdouble b); +extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max); + +extern inline ALuint minu(ALuint a, ALuint b); +extern inline ALuint maxu(ALuint a, ALuint b); +extern inline ALuint clampu(ALuint val, ALuint min, ALuint max); + +extern inline ALint mini(ALint a, ALint b); +extern inline ALint maxi(ALint a, ALint b); +extern inline ALint clampi(ALint val, ALint min, ALint max); + +extern inline ALint64 mini64(ALint64 a, ALint64 b); +extern inline ALint64 maxi64(ALint64 a, ALint64 b); +extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max); + +extern inline ALuint64 minu64(ALuint64 a, ALuint64 b); +extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b); +extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max); + +extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu); +extern inline ALfloat cubic(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat mu); + + +static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector) +{ + outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1]; + outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2]; + outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0]; +} + +static inline ALfloat aluDotproduct(const ALfloat *inVector1, const ALfloat *inVector2) +{ + return inVector1[0]*inVector2[0] + inVector1[1]*inVector2[1] + + inVector1[2]*inVector2[2]; +} + +static inline void aluNormalize(ALfloat *inVector) +{ + ALfloat lengthsqr = aluDotproduct(inVector, inVector); + if(lengthsqr > 0.0f) + { + ALfloat inv_length = 1.0f/sqrtf(lengthsqr); + inVector[0] *= inv_length; + inVector[1] *= inv_length; + inVector[2] *= inv_length; + } +} + +static inline ALvoid aluMatrixVector(ALfloat *vector, ALfloat w, ALfloat (*restrict matrix)[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]; +} + + +static ALvoid CalcListenerParams(ALlistener *Listener) +{ + ALfloat N[3], V[3], U[3], P[3]; + + /* AT then UP */ + N[0] = Listener->Forward[0]; + N[1] = Listener->Forward[1]; + N[2] = Listener->Forward[2]; + aluNormalize(N); + V[0] = Listener->Up[0]; + V[1] = Listener->Up[1]; + V[2] = Listener->Up[2]; + aluNormalize(V); + /* Build and normalize right-vector */ + aluCrossproduct(N, V, U); + aluNormalize(U); + + Listener->Params.Matrix[0][0] = U[0]; + Listener->Params.Matrix[0][1] = V[0]; + Listener->Params.Matrix[0][2] = -N[0]; + Listener->Params.Matrix[0][3] = 0.0f; + Listener->Params.Matrix[1][0] = U[1]; + Listener->Params.Matrix[1][1] = V[1]; + Listener->Params.Matrix[1][2] = -N[1]; + Listener->Params.Matrix[1][3] = 0.0f; + Listener->Params.Matrix[2][0] = U[2]; + Listener->Params.Matrix[2][1] = V[2]; + Listener->Params.Matrix[2][2] = -N[2]; + Listener->Params.Matrix[2][3] = 0.0f; + Listener->Params.Matrix[3][0] = 0.0f; + Listener->Params.Matrix[3][1] = 0.0f; + Listener->Params.Matrix[3][2] = 0.0f; + Listener->Params.Matrix[3][3] = 1.0f; + + P[0] = Listener->Position[0]; + P[1] = Listener->Position[1]; + P[2] = Listener->Position[2]; + aluMatrixVector(P, 1.0f, Listener->Params.Matrix); + Listener->Params.Matrix[3][0] = -P[0]; + Listener->Params.Matrix[3][1] = -P[1]; + Listener->Params.Matrix[3][2] = -P[2]; + + Listener->Params.Velocity[0] = Listener->Velocity[0]; + Listener->Params.Velocity[1] = Listener->Velocity[1]; + Listener->Params.Velocity[2] = Listener->Velocity[2]; + aluMatrixVector(Listener->Params.Velocity, 0.0f, Listener->Params.Matrix); +} + +ALvoid CalcNonAttnSourceParams(ALactivesource *src, const ALCcontext *ALContext) +{ + static const struct ChanMap MonoMap[1] = { { FrontCenter, 0.0f } }; + static const struct ChanMap StereoMap[2] = { + { FrontLeft, DEG2RAD(-30.0f) }, + { FrontRight, DEG2RAD( 30.0f) } + }; + static const struct ChanMap StereoWideMap[2] = { + { FrontLeft, DEG2RAD(-90.0f) }, + { FrontRight, DEG2RAD( 90.0f) } + }; + static const struct ChanMap RearMap[2] = { + { BackLeft, DEG2RAD(-150.0f) }, + { BackRight, DEG2RAD( 150.0f) } + }; + static const struct ChanMap QuadMap[4] = { + { FrontLeft, DEG2RAD( -45.0f) }, + { FrontRight, DEG2RAD( 45.0f) }, + { BackLeft, DEG2RAD(-135.0f) }, + { BackRight, DEG2RAD( 135.0f) } + }; + static const struct ChanMap X51Map[6] = { + { FrontLeft, DEG2RAD( -30.0f) }, + { FrontRight, DEG2RAD( 30.0f) }, + { FrontCenter, DEG2RAD( 0.0f) }, + { LFE, 0.0f }, + { BackLeft, DEG2RAD(-110.0f) }, + { BackRight, DEG2RAD( 110.0f) } + }; + static const struct ChanMap X61Map[7] = { + { FrontLeft, DEG2RAD(-30.0f) }, + { FrontRight, DEG2RAD( 30.0f) }, + { FrontCenter, DEG2RAD( 0.0f) }, + { LFE, 0.0f }, + { BackCenter, DEG2RAD(180.0f) }, + { SideLeft, DEG2RAD(-90.0f) }, + { SideRight, DEG2RAD( 90.0f) } + }; + static const struct ChanMap X71Map[8] = { + { FrontLeft, DEG2RAD( -30.0f) }, + { FrontRight, DEG2RAD( 30.0f) }, + { FrontCenter, DEG2RAD( 0.0f) }, + { LFE, 0.0f }, + { BackLeft, DEG2RAD(-150.0f) }, + { BackRight, DEG2RAD( 150.0f) }, + { SideLeft, DEG2RAD( -90.0f) }, + { SideRight, DEG2RAD( 90.0f) } + }; + + ALCdevice *Device = ALContext->Device; + const ALsource *ALSource = src->Source; + ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume; + ALbufferlistitem *BufferListItem; + enum FmtChannels Channels; + ALfloat DryGain, DryGainHF, DryGainLF; + ALfloat WetGain[MAX_SENDS]; + ALfloat WetGainHF[MAX_SENDS]; + ALfloat WetGainLF[MAX_SENDS]; + ALint NumSends, Frequency; + const struct ChanMap *chans = NULL; + ALint num_channels = 0; + ALboolean DirectChannels; + ALfloat hwidth = 0.0f; + ALfloat Pitch; + ALint i, j, c; + + /* Get device properties */ + NumSends = Device->NumAuxSends; + Frequency = Device->Frequency; + + /* Get listener properties */ + ListenerGain = ALContext->Listener->Gain; + + /* Get source properties */ + SourceVolume = ALSource->Gain; + MinVolume = ALSource->MinGain; + MaxVolume = ALSource->MaxGain; + Pitch = ALSource->Pitch; + DirectChannels = ALSource->DirectChannels; + + src->Direct.OutBuffer = Device->DryBuffer; + for(i = 0;i < NumSends;i++) + { + ALeffectslot *Slot = ALSource->Send[i].Slot; + if(!Slot && i == 0) + Slot = Device->DefaultSlot; + if(!Slot || Slot->EffectType == AL_EFFECT_NULL) + src->Send[i].OutBuffer = NULL; + else + src->Send[i].OutBuffer = Slot->WetBuffer; + } + + /* Calculate the stepping value */ + Channels = FmtMono; + BufferListItem = ATOMIC_LOAD(&ALSource->queue); + while(BufferListItem != NULL) + { + ALbuffer *ALBuffer; + if((ALBuffer=BufferListItem->buffer) != NULL) + { + Pitch = Pitch * ALBuffer->Frequency / Frequency; + if(Pitch > (ALfloat)MAX_PITCH) + src->Step = MAX_PITCH<Step = fastf2i(Pitch*FRACTIONONE); + if(src->Step == 0) + src->Step = 1; + } + + Channels = ALBuffer->FmtChannels; + break; + } + BufferListItem = BufferListItem->next; + } + + /* Calculate gains */ + DryGain = clampf(SourceVolume, MinVolume, MaxVolume); + DryGain *= ALSource->Direct.Gain * ListenerGain; + DryGainHF = ALSource->Direct.GainHF; + DryGainLF = ALSource->Direct.GainLF; + for(i = 0;i < NumSends;i++) + { + WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume); + WetGain[i] *= ALSource->Send[i].Gain * ListenerGain; + WetGainHF[i] = ALSource->Send[i].GainHF; + WetGainLF[i] = ALSource->Send[i].GainLF; + } + + switch(Channels) + { + case FmtMono: + chans = MonoMap; + num_channels = 1; + break; + + case FmtStereo: + if(!(Device->Flags&DEVICE_WIDE_STEREO)) + { + /* HACK: Place the stereo channels at +/-90 degrees when using non- + * HRTF stereo output. This helps reduce the "monoization" caused + * by them panning towards the center. */ + if(Device->FmtChans == DevFmtStereo && !Device->Hrtf) + chans = StereoWideMap; + else + chans = StereoMap; + } + else + { + chans = StereoWideMap; + hwidth = DEG2RAD(60.0f); + } + num_channels = 2; + break; + + case FmtRear: + chans = RearMap; + num_channels = 2; + break; + + case FmtQuad: + chans = QuadMap; + num_channels = 4; + break; + + case FmtX51: + chans = X51Map; + num_channels = 6; + break; + + case FmtX61: + chans = X61Map; + num_channels = 7; + break; + + case FmtX71: + chans = X71Map; + num_channels = 8; + break; + } + + if(DirectChannels != AL_FALSE) + { + for(c = 0;c < num_channels;c++) + { + MixGains *gains = src->Direct.Mix.Gains[c]; + for(j = 0;j < MaxChannels;j++) + gains[j].Target = 0.0f; + } + + for(c = 0;c < num_channels;c++) + { + MixGains *gains = src->Direct.Mix.Gains[c]; + for(i = 0;i < (ALint)Device->NumChan;i++) + { + enum Channel chan = Device->Speaker2Chan[i]; + if(chan == chans[c].channel) + { + gains[chan].Target = DryGain; + break; + } + } + } + + if(!src->Direct.Moving) + { + for(i = 0;i < num_channels;i++) + { + MixGains *gains = src->Direct.Mix.Gains[i]; + for(j = 0;j < MaxChannels;j++) + { + gains[j].Current = gains[j].Target; + gains[j].Step = 1.0f; + } + } + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + } + else + { + for(i = 0;i < num_channels;i++) + { + MixGains *gains = src->Direct.Mix.Gains[i]; + for(j = 0;j < MaxChannels;j++) + { + ALfloat cur = maxf(gains[j].Current, FLT_EPSILON); + ALfloat trg = maxf(gains[j].Target, FLT_EPSILON); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + gains[j].Step = powf(trg/cur, 1.0f/64.0f); + else + gains[j].Step = 1.0f; + gains[j].Current = cur; + } + } + src->Direct.Counter = 64; + } + + src->IsHrtf = AL_FALSE; + } + else if(Device->Hrtf) + { + for(c = 0;c < num_channels;c++) + { + if(chans[c].channel == LFE) + { + /* Skip LFE */ + src->Direct.Mix.Hrtf.Params[c].Delay[0] = 0; + src->Direct.Mix.Hrtf.Params[c].Delay[1] = 0; + for(i = 0;i < HRIR_LENGTH;i++) + { + src->Direct.Mix.Hrtf.Params[c].Coeffs[i][0] = 0.0f; + src->Direct.Mix.Hrtf.Params[c].Coeffs[i][1] = 0.0f; + } + } + else + { + /* Get the static HRIR coefficients and delays for this + * channel. */ + GetLerpedHrtfCoeffs(Device->Hrtf, + 0.0f, chans[c].angle, 1.0f, DryGain, + src->Direct.Mix.Hrtf.Params[c].Coeffs, + src->Direct.Mix.Hrtf.Params[c].Delay); + } + } + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + src->Direct.Mix.Hrtf.IrSize = GetHrtfIrSize(Device->Hrtf); + + src->IsHrtf = AL_TRUE; + } + else + { + for(i = 0;i < num_channels;i++) + { + MixGains *gains = src->Direct.Mix.Gains[i]; + for(j = 0;j < MaxChannels;j++) + gains[j].Target = 0.0f; + } + + DryGain *= lerp(1.0f, 1.0f/sqrtf((float)Device->NumChan), hwidth/F_PI); + for(c = 0;c < num_channels;c++) + { + MixGains *gains = src->Direct.Mix.Gains[c]; + ALfloat Target[MaxChannels]; + + /* Special-case LFE */ + if(chans[c].channel == LFE) + { + gains[chans[c].channel].Target = DryGain; + continue; + } + ComputeAngleGains(Device, chans[c].angle, hwidth, DryGain, Target); + for(i = 0;i < MaxChannels;i++) + gains[i].Target = Target[i]; + } + + if(!src->Direct.Moving) + { + for(i = 0;i < num_channels;i++) + { + MixGains *gains = src->Direct.Mix.Gains[i]; + for(j = 0;j < MaxChannels;j++) + { + gains[j].Current = gains[j].Target; + gains[j].Step = 1.0f; + } + } + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + } + else + { + for(i = 0;i < num_channels;i++) + { + MixGains *gains = src->Direct.Mix.Gains[i]; + for(j = 0;j < MaxChannels;j++) + { + ALfloat trg = maxf(gains[j].Target, FLT_EPSILON); + ALfloat cur = maxf(gains[j].Current, FLT_EPSILON); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + gains[j].Step = powf(trg/cur, 1.0f/64.0f); + else + gains[j].Step = 1.0f; + gains[j].Current = cur; + } + } + src->Direct.Counter = 64; + } + + src->IsHrtf = AL_FALSE; + } + for(i = 0;i < NumSends;i++) + { + src->Send[i].Gain.Target = WetGain[i]; + if(!src->Send[i].Moving) + { + src->Send[i].Gain.Current = src->Send[i].Gain.Target; + src->Send[i].Gain.Step = 1.0f; + src->Send[i].Counter = 0; + src->Send[i].Moving = AL_TRUE; + } + else + { + ALfloat cur = maxf(src->Send[i].Gain.Current, FLT_EPSILON); + ALfloat trg = maxf(src->Send[i].Gain.Target, FLT_EPSILON); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + src->Send[i].Gain.Step = powf(trg/cur, 1.0f/64.0f); + else + src->Send[i].Gain.Step = 1.0f; + src->Send[i].Gain.Current = cur; + src->Send[i].Counter = 64; + } + } + + { + ALfloat gainhf = maxf(0.01f, DryGainHF); + ALfloat gainlf = maxf(0.01f, DryGainLF); + ALfloat hfscale = ALSource->Direct.HFReference / Frequency; + ALfloat lfscale = ALSource->Direct.LFReference / Frequency; + for(c = 0;c < num_channels;c++) + { + src->Direct.Filters[c].ActiveType = AF_None; + if(gainhf != 1.0f) src->Direct.Filters[c].ActiveType |= AF_LowPass; + if(gainlf != 1.0f) src->Direct.Filters[c].ActiveType |= AF_HighPass; + ALfilterState_setParams( + &src->Direct.Filters[c].LowPass, ALfilterType_HighShelf, gainhf, + hfscale, 0.0f + ); + ALfilterState_setParams( + &src->Direct.Filters[c].HighPass, ALfilterType_LowShelf, gainlf, + lfscale, 0.0f + ); + } + } + for(i = 0;i < NumSends;i++) + { + ALfloat gainhf = maxf(0.01f, WetGainHF[i]); + ALfloat gainlf = maxf(0.01f, WetGainLF[i]); + ALfloat hfscale = ALSource->Send[i].HFReference / Frequency; + ALfloat lfscale = ALSource->Send[i].LFReference / Frequency; + for(c = 0;c < num_channels;c++) + { + src->Send[i].Filters[c].ActiveType = AF_None; + if(gainhf != 1.0f) src->Send[i].Filters[c].ActiveType |= AF_LowPass; + if(gainlf != 1.0f) src->Send[i].Filters[c].ActiveType |= AF_HighPass; + ALfilterState_setParams( + &src->Send[i].Filters[c].LowPass, ALfilterType_HighShelf, gainhf, + hfscale, 0.0f + ); + ALfilterState_setParams( + &src->Send[i].Filters[c].HighPass, ALfilterType_LowShelf, gainlf, + lfscale, 0.0f + ); + } + } +} + +ALvoid CalcSourceParams(ALactivesource *src, const ALCcontext *ALContext) +{ + ALCdevice *Device = ALContext->Device; + const ALsource *ALSource = src->Source; + ALfloat Velocity[3],Direction[3],Position[3],SourceToListener[3]; + ALfloat InnerAngle,OuterAngle,Angle,Distance,ClampedDist; + ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff; + ALfloat ConeVolume,ConeHF,SourceVolume,ListenerGain; + ALfloat DopplerFactor, SpeedOfSound; + ALfloat AirAbsorptionFactor; + ALfloat RoomAirAbsorption[MAX_SENDS]; + ALbufferlistitem *BufferListItem; + ALfloat Attenuation; + ALfloat RoomAttenuation[MAX_SENDS]; + ALfloat MetersPerUnit; + ALfloat RoomRolloffBase; + ALfloat RoomRolloff[MAX_SENDS]; + ALfloat DecayDistance[MAX_SENDS]; + ALfloat DryGain; + ALfloat DryGainHF; + ALfloat DryGainLF; + ALboolean DryGainHFAuto; + ALfloat WetGain[MAX_SENDS]; + ALfloat WetGainHF[MAX_SENDS]; + ALfloat WetGainLF[MAX_SENDS]; + ALboolean WetGainAuto; + ALboolean WetGainHFAuto; + ALfloat Pitch; + ALuint Frequency; + ALint NumSends; + ALint i, j; + + DryGainHF = 1.0f; + DryGainLF = 1.0f; + for(i = 0;i < MAX_SENDS;i++) + { + WetGainHF[i] = 1.0f; + WetGainLF[i] = 1.0f; + } + + /* Get context/device properties */ + DopplerFactor = ALContext->DopplerFactor * ALSource->DopplerFactor; + SpeedOfSound = ALContext->SpeedOfSound * ALContext->DopplerVelocity; + NumSends = Device->NumAuxSends; + Frequency = Device->Frequency; + + /* Get listener properties */ + ListenerGain = ALContext->Listener->Gain; + MetersPerUnit = ALContext->Listener->MetersPerUnit; + + /* Get source properties */ + SourceVolume = ALSource->Gain; + MinVolume = ALSource->MinGain; + MaxVolume = ALSource->MaxGain; + Pitch = ALSource->Pitch; + Position[0] = ALSource->Position[0]; + Position[1] = ALSource->Position[1]; + Position[2] = ALSource->Position[2]; + Direction[0] = ALSource->Orientation[0]; + Direction[1] = ALSource->Orientation[1]; + Direction[2] = ALSource->Orientation[2]; + Velocity[0] = ALSource->Velocity[0]; + Velocity[1] = ALSource->Velocity[1]; + Velocity[2] = ALSource->Velocity[2]; + MinDist = ALSource->RefDistance; + MaxDist = ALSource->MaxDistance; + Rolloff = ALSource->RollOffFactor; + InnerAngle = ALSource->InnerAngle; + OuterAngle = ALSource->OuterAngle; + AirAbsorptionFactor = ALSource->AirAbsorptionFactor; + DryGainHFAuto = ALSource->DryGainHFAuto; + WetGainAuto = ALSource->WetGainAuto; + WetGainHFAuto = ALSource->WetGainHFAuto; + RoomRolloffBase = ALSource->RoomRolloffFactor; + + src->Direct.OutBuffer = Device->DryBuffer; + for(i = 0;i < NumSends;i++) + { + ALeffectslot *Slot = ALSource->Send[i].Slot; + + if(!Slot && i == 0) + Slot = Device->DefaultSlot; + if(!Slot || Slot->EffectType == AL_EFFECT_NULL) + { + Slot = NULL; + RoomRolloff[i] = 0.0f; + DecayDistance[i] = 0.0f; + RoomAirAbsorption[i] = 1.0f; + } + else if(Slot->AuxSendAuto) + { + RoomRolloff[i] = RoomRolloffBase; + if(IsReverbEffect(Slot->EffectType)) + { + RoomRolloff[i] += Slot->EffectProps.Reverb.RoomRolloffFactor; + DecayDistance[i] = Slot->EffectProps.Reverb.DecayTime * + SPEEDOFSOUNDMETRESPERSEC; + RoomAirAbsorption[i] = Slot->EffectProps.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; + } + + if(!Slot || Slot->EffectType == AL_EFFECT_NULL) + src->Send[i].OutBuffer = NULL; + else + src->Send[i].OutBuffer = Slot->WetBuffer; + } + + /* Transform source to listener space (convert to head relative) */ + if(ALSource->HeadRelative == AL_FALSE) + { + ALfloat (*restrict Matrix)[4] = ALContext->Listener->Params.Matrix; + /* Transform source vectors */ + aluMatrixVector(Position, 1.0f, Matrix); + aluMatrixVector(Direction, 0.0f, Matrix); + aluMatrixVector(Velocity, 0.0f, Matrix); + } + else + { + const ALfloat *ListenerVel = ALContext->Listener->Params.Velocity; + /* Offset the source velocity to be relative of the listener velocity */ + Velocity[0] += ListenerVel[0]; + Velocity[1] += ListenerVel[1]; + Velocity[2] += ListenerVel[2]; + } + + SourceToListener[0] = -Position[0]; + SourceToListener[1] = -Position[1]; + SourceToListener[2] = -Position[2]; + aluNormalize(SourceToListener); + aluNormalize(Direction); + + /* Calculate distance attenuation */ + Distance = sqrtf(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 = powf(ClampedDist/MinDist, -Rolloff); + for(i = 0;i < NumSends;i++) + RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]); + } + break; + + case DisableDistance: + ClampedDist = MinDist; + break; + } + + /* Source Gain + Attenuation */ + DryGain = SourceVolume * Attenuation; + for(i = 0;i < NumSends;i++) + WetGain[i] = SourceVolume * RoomAttenuation[i]; + + /* Distance-based air absorption */ + if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist) + { + ALfloat meters = maxf(ClampedDist-MinDist, 0.0f) * MetersPerUnit; + DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters); + for(i = 0;i < NumSends;i++) + WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters); + } + + if(WetGainAuto) + { + ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f; + + /* Apply a decay-time transformation to the wet path, based on the + * attenuation of the dry path. + * + * Using the apparent distance, based on the distance attenuation, 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] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]); + } + } + + /* Calculate directional soundcones */ + Angle = RAD2DEG(acosf(aluDotproduct(Direction,SourceToListener)) * ConeScale) * 2.0f; + if(Angle > InnerAngle && Angle <= OuterAngle) + { + ALfloat scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle); + ConeVolume = lerp(1.0f, ALSource->OuterGain, scale); + ConeHF = lerp(1.0f, ALSource->OuterGainHF, scale); + } + else if(Angle > OuterAngle) + { + ConeVolume = ALSource->OuterGain; + 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 gain and frequency filters */ + DryGain *= ALSource->Direct.Gain * ListenerGain; + DryGainHF *= ALSource->Direct.GainHF; + DryGainLF *= ALSource->Direct.GainLF; + for(i = 0;i < NumSends;i++) + { + WetGain[i] *= ALSource->Send[i].Gain * ListenerGain; + WetGainHF[i] *= ALSource->Send[i].GainHF; + WetGainLF[i] *= ALSource->Send[i].GainLF; + } + + /* Calculate velocity-based doppler effect */ + if(DopplerFactor > 0.0f) + { + const ALfloat *ListenerVel = ALContext->Listener->Params.Velocity; + ALfloat VSS, VLS; + + if(SpeedOfSound < 1.0f) + { + DopplerFactor *= 1.0f/SpeedOfSound; + SpeedOfSound = 1.0f; + } + + VSS = aluDotproduct(Velocity, SourceToListener) * DopplerFactor; + VLS = aluDotproduct(ListenerVel, SourceToListener) * DopplerFactor; + + Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) / + clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f); + } + + BufferListItem = ATOMIC_LOAD(&ALSource->queue); + while(BufferListItem != NULL) + { + ALbuffer *ALBuffer; + if((ALBuffer=BufferListItem->buffer) != NULL) + { + /* Calculate fixed-point stepping value, based on the pitch, buffer + * frequency, and output frequency. */ + Pitch = Pitch * ALBuffer->Frequency / Frequency; + if(Pitch > (ALfloat)MAX_PITCH) + src->Step = MAX_PITCH<Step = fastf2i(Pitch*FRACTIONONE); + if(src->Step == 0) + src->Step = 1; + } + + break; + } + BufferListItem = BufferListItem->next; + } + + if(Device->Hrtf) + { + /* Use a binaural HRTF algorithm for stereo headphone playback */ + ALfloat delta, ev = 0.0f, az = 0.0f; + ALfloat radius = ALSource->Radius; + ALfloat dirfact = 1.0f; + + if(Distance > FLT_EPSILON) + { + 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. Also, clamp Y in case FP precision errors + * cause it to land outside of -1..+1. */ + ev = asinf(clampf(Position[1], -1.0f, 1.0f)); + az = atan2f(Position[0], -Position[2]*ZScale); + } + if(radius > Distance) + dirfact *= Distance / radius; + + /* Check to see if the HRIR is already moving. */ + if(src->Direct.Moving) + { + /* Calculate the normalized HRTF transition factor (delta). */ + delta = CalcHrtfDelta(src->Direct.Mix.Hrtf.Gain, DryGain, + src->Direct.Mix.Hrtf.Dir, Position); + /* If the delta is large enough, get the moving HRIR target + * coefficients, target delays, steppping values, and counter. */ + if(delta > 0.001f) + { + ALuint counter = GetMovingHrtfCoeffs(Device->Hrtf, + ev, az, dirfact, DryGain, delta, src->Direct.Counter, + src->Direct.Mix.Hrtf.Params[0].Coeffs, src->Direct.Mix.Hrtf.Params[0].Delay, + src->Direct.Mix.Hrtf.Params[0].CoeffStep, src->Direct.Mix.Hrtf.Params[0].DelayStep + ); + src->Direct.Counter = counter; + src->Direct.Mix.Hrtf.Gain = DryGain; + src->Direct.Mix.Hrtf.Dir[0] = Position[0]; + src->Direct.Mix.Hrtf.Dir[1] = Position[1]; + src->Direct.Mix.Hrtf.Dir[2] = Position[2]; + } + } + else + { + /* Get the initial (static) HRIR coefficients and delays. */ + GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, dirfact, DryGain, + src->Direct.Mix.Hrtf.Params[0].Coeffs, + src->Direct.Mix.Hrtf.Params[0].Delay); + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + src->Direct.Mix.Hrtf.Gain = DryGain; + src->Direct.Mix.Hrtf.Dir[0] = Position[0]; + src->Direct.Mix.Hrtf.Dir[1] = Position[1]; + src->Direct.Mix.Hrtf.Dir[2] = Position[2]; + } + src->Direct.Mix.Hrtf.IrSize = GetHrtfIrSize(Device->Hrtf); + + src->IsHrtf = AL_TRUE; + } + else + { + MixGains *gains = src->Direct.Mix.Gains[0]; + ALfloat DirGain = 0.0f; + ALfloat AmbientGain; + + for(j = 0;j < MaxChannels;j++) + gains[j].Target = 0.0f; + + /* Normalize the length, and compute panned gains. */ + if(Distance > FLT_EPSILON) + { + ALfloat radius = ALSource->Radius; + ALfloat Target[MaxChannels]; + ALfloat invlen = 1.0f/maxf(Distance, radius); + Position[0] *= invlen; + Position[1] *= invlen; + Position[2] *= invlen; + + DirGain = sqrtf(Position[0]*Position[0] + Position[2]*Position[2]); + ComputeAngleGains(Device, atan2f(Position[0], -Position[2]*ZScale), 0.0f, + DryGain*DirGain, Target); + for(j = 0;j < MaxChannels;j++) + gains[j].Target = Target[j]; + } + + /* Adjustment for vertical offsets. Not the greatest, but simple + * enough. */ + AmbientGain = DryGain * sqrtf(1.0f/Device->NumChan) * (1.0f-DirGain); + for(i = 0;i < (ALint)Device->NumChan;i++) + { + enum Channel chan = Device->Speaker2Chan[i]; + gains[chan].Target = maxf(gains[chan].Target, AmbientGain); + } + + if(!src->Direct.Moving) + { + for(j = 0;j < MaxChannels;j++) + { + gains[j].Current = gains[j].Target; + gains[j].Step = 1.0f; + } + src->Direct.Counter = 0; + src->Direct.Moving = AL_TRUE; + } + else + { + for(j = 0;j < MaxChannels;j++) + { + ALfloat cur = maxf(gains[j].Current, FLT_EPSILON); + ALfloat trg = maxf(gains[j].Target, FLT_EPSILON); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + gains[j].Step = powf(trg/cur, 1.0f/64.0f); + else + gains[j].Step = 1.0f; + gains[j].Current = cur; + } + src->Direct.Counter = 64; + } + + src->IsHrtf = AL_FALSE; + } + for(i = 0;i < NumSends;i++) + { + src->Send[i].Gain.Target = WetGain[i]; + if(!src->Send[i].Moving) + { + src->Send[i].Gain.Current = src->Send[i].Gain.Target; + src->Send[i].Gain.Step = 1.0f; + src->Send[i].Counter = 0; + src->Send[i].Moving = AL_TRUE; + } + else + { + ALfloat cur = maxf(src->Send[i].Gain.Current, FLT_EPSILON); + ALfloat trg = maxf(src->Send[i].Gain.Target, FLT_EPSILON); + if(fabs(trg - cur) >= GAIN_SILENCE_THRESHOLD) + src->Send[i].Gain.Step = powf(trg/cur, 1.0f/64.0f); + else + src->Send[i].Gain.Step = 1.0f; + src->Send[i].Gain.Current = cur; + src->Send[i].Counter = 64; + } + } + + { + ALfloat gainhf = maxf(0.01f, DryGainHF); + ALfloat gainlf = maxf(0.01f, DryGainLF); + ALfloat hfscale = ALSource->Direct.HFReference / Frequency; + ALfloat lfscale = ALSource->Direct.LFReference / Frequency; + src->Direct.Filters[0].ActiveType = AF_None; + if(gainhf != 1.0f) src->Direct.Filters[0].ActiveType |= AF_LowPass; + if(gainlf != 1.0f) src->Direct.Filters[0].ActiveType |= AF_HighPass; + ALfilterState_setParams( + &src->Direct.Filters[0].LowPass, ALfilterType_HighShelf, gainhf, + hfscale, 0.0f + ); + ALfilterState_setParams( + &src->Direct.Filters[0].HighPass, ALfilterType_LowShelf, gainlf, + lfscale, 0.0f + ); + } + for(i = 0;i < NumSends;i++) + { + ALfloat gainhf = maxf(0.01f, WetGainHF[i]); + ALfloat gainlf = maxf(0.01f, WetGainLF[i]); + ALfloat hfscale = ALSource->Send[i].HFReference / Frequency; + ALfloat lfscale = ALSource->Send[i].LFReference / Frequency; + src->Send[i].Filters[0].ActiveType = AF_None; + if(gainhf != 1.0f) src->Send[i].Filters[0].ActiveType |= AF_LowPass; + if(gainlf != 1.0f) src->Send[i].Filters[0].ActiveType |= AF_HighPass; + ALfilterState_setParams( + &src->Send[i].Filters[0].LowPass, ALfilterType_HighShelf, gainhf, + hfscale, 0.0f + ); + ALfilterState_setParams( + &src->Send[i].Filters[0].HighPass, ALfilterType_LowShelf, gainlf, + lfscale, 0.0f + ); + } +} + + +static inline ALint aluF2I25(ALfloat val) +{ + /* Clamp the value between -1 and +1. This handles that with only a single branch. */ + if(fabsf(val) > 1.0f) + val = (ALfloat)((0.0f < val) - (val < 0.0f)); + /* Convert to a signed integer, between -16777215 and +16777215. */ + return fastf2i(val*16777215.0f); +} + +static inline ALfloat aluF2F(ALfloat val) +{ return val; } +static inline ALint aluF2I(ALfloat val) +{ return aluF2I25(val)<<7; } +static inline ALuint aluF2UI(ALfloat val) +{ return aluF2I(val)+2147483648u; } +static inline ALshort aluF2S(ALfloat val) +{ return aluF2I25(val)>>9; } +static inline ALushort aluF2US(ALfloat val) +{ return aluF2S(val)+32768; } +static inline ALbyte aluF2B(ALfloat val) +{ return aluF2I25(val)>>17; } +static inline ALubyte aluF2UB(ALfloat val) +{ return aluF2B(val)+128; } + +#define DECL_TEMPLATE(T, func) \ +static void Write_##T(ALCdevice *device, ALvoid **buffer, ALuint SamplesToDo) \ +{ \ + ALfloat (*restrict DryBuffer)[BUFFERSIZE] = device->DryBuffer; \ + const ALuint numchans = ChannelsFromDevFmt(device->FmtChans); \ + const ALuint *offsets = device->ChannelOffsets; \ + ALuint i, j; \ + \ + for(j = 0;j < MaxChannels;j++) \ + { \ + T *restrict out; \ + \ + if(offsets[j] == INVALID_OFFSET) \ + continue; \ + \ + out = (T*)(*buffer) + offsets[j]; \ + for(i = 0;i < SamplesToDo;i++) \ + out[i*numchans] = func(DryBuffer[j][i]); \ + } \ + *buffer = (char*)(*buffer) + SamplesToDo*numchans*sizeof(T); \ +} + +DECL_TEMPLATE(ALfloat, aluF2F) +DECL_TEMPLATE(ALuint, aluF2UI) +DECL_TEMPLATE(ALint, aluF2I) +DECL_TEMPLATE(ALushort, aluF2US) +DECL_TEMPLATE(ALshort, aluF2S) +DECL_TEMPLATE(ALubyte, aluF2UB) +DECL_TEMPLATE(ALbyte, aluF2B) + +#undef DECL_TEMPLATE + + +ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) +{ + ALuint SamplesToDo; + ALeffectslot **slot, **slot_end; + ALactivesource **src, **src_end; + ALCcontext *ctx; + FPUCtl oldMode; + ALuint i, c; + + SetMixerFPUMode(&oldMode); + + while(size > 0) + { + IncrementRef(&device->MixCount); + + SamplesToDo = minu(size, BUFFERSIZE); + for(c = 0;c < MaxChannels;c++) + memset(device->DryBuffer[c], 0, SamplesToDo*sizeof(ALfloat)); + + ALCdevice_Lock(device); + V(device->Synth,process)(SamplesToDo, device->DryBuffer); + + ctx = ATOMIC_LOAD(&device->ContextList); + while(ctx) + { + ALenum DeferUpdates = ctx->DeferUpdates; + ALenum UpdateSources = AL_FALSE; + + if(!DeferUpdates) + UpdateSources = ATOMIC_EXCHANGE(ALenum, &ctx->UpdateSources, AL_FALSE); + + if(UpdateSources) + CalcListenerParams(ctx->Listener); + + /* source processing */ + src = ctx->ActiveSources; + src_end = src + ctx->ActiveSourceCount; + while(src != src_end) + { + ALsource *source = (*src)->Source; + + if(source->state != AL_PLAYING && source->state != AL_PAUSED) + { + ALactivesource *temp = *(--src_end); + *src_end = *src; + *src = temp; + --(ctx->ActiveSourceCount); + continue; + } + + if(!DeferUpdates && (ATOMIC_EXCHANGE(ALenum, &source->NeedsUpdate, AL_FALSE) || + UpdateSources)) + (*src)->Update(*src, ctx); + + if(source->state != AL_PAUSED) + MixSource(*src, device, SamplesToDo); + src++; + } + + /* effect slot processing */ + slot = VECTOR_ITER_BEGIN(ctx->ActiveAuxSlots); + slot_end = VECTOR_ITER_END(ctx->ActiveAuxSlots); + while(slot != slot_end) + { + if(!DeferUpdates && ATOMIC_EXCHANGE(ALenum, &(*slot)->NeedsUpdate, AL_FALSE)) + V((*slot)->EffectState,update)(device, *slot); + + V((*slot)->EffectState,process)(SamplesToDo, (*slot)->WetBuffer[0], + device->DryBuffer); + + for(i = 0;i < SamplesToDo;i++) + (*slot)->WetBuffer[0][i] = 0.0f; + + slot++; + } + + ctx = ctx->next; + } + + slot = &device->DefaultSlot; + if(*slot != NULL) + { + if(ATOMIC_EXCHANGE(ALenum, &(*slot)->NeedsUpdate, AL_FALSE)) + V((*slot)->EffectState,update)(device, *slot); + + V((*slot)->EffectState,process)(SamplesToDo, (*slot)->WetBuffer[0], + device->DryBuffer); + + for(i = 0;i < SamplesToDo;i++) + (*slot)->WetBuffer[0][i] = 0.0f; + } + + /* Increment the clock time. Every second's worth of samples is + * converted and added to clock base so that large sample counts don't + * overflow during conversion. This also guarantees an exact, stable + * conversion. */ + device->SamplesDone += SamplesToDo; + device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES; + device->SamplesDone %= device->Frequency; + ALCdevice_Unlock(device); + + if(device->Bs2b) + { + /* Apply binaural/crossfeed filter */ + for(i = 0;i < SamplesToDo;i++) + { + float samples[2]; + samples[0] = device->DryBuffer[FrontLeft][i]; + samples[1] = device->DryBuffer[FrontRight][i]; + bs2b_cross_feed(device->Bs2b, samples); + device->DryBuffer[FrontLeft][i] = samples[0]; + device->DryBuffer[FrontRight][i] = samples[1]; + } + } + + 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 DevFmtInt: + Write_ALint(device, &buffer, SamplesToDo); + break; + case DevFmtUInt: + Write_ALuint(device, &buffer, SamplesToDo); + break; + case DevFmtFloat: + Write_ALfloat(device, &buffer, SamplesToDo); + break; + } + } + + size -= SamplesToDo; + IncrementRef(&device->MixCount); + } + + RestoreFPUMode(&oldMode); +} + + +ALvoid aluHandleDisconnect(ALCdevice *device) +{ + ALCcontext *Context; + + device->Connected = ALC_FALSE; + + Context = ATOMIC_LOAD(&device->ContextList); + while(Context) + { + ALactivesource **src, **src_end; + + src = Context->ActiveSources; + src_end = src + Context->ActiveSourceCount; + while(src != src_end) + { + ALsource *source = (*src)->Source; + if(source->state == AL_PLAYING) + { + source->state = AL_STOPPED; + ATOMIC_STORE(&source->current_buffer, NULL); + source->position = 0; + source->position_fraction = 0; + } + src++; + } + Context->ActiveSourceCount = 0; + + Context = Context->next; + } +} diff --git a/libs/openal-soft/Alc/alcConfig.c b/libs/openal-soft/Alc/alcConfig.c new file mode 100644 index 00000000..2c9aef41 --- /dev/null +++ b/libs/openal-soft/Alc/alcConfig.c @@ -0,0 +1,515 @@ +/** + * 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 __MINGW32__ +#define _WIN32_IE 0x501 +#else +#define _WIN32_IE 0x400 +#endif +#endif + +#include "config.h" + +#include +#include +#include +#include +#ifdef _WIN32_IE +#include +#endif + +#include "alMain.h" +#include "compat.h" + + +typedef struct ConfigEntry { + char *key; + char *value; +} ConfigEntry; + +typedef struct ConfigBlock { + ConfigEntry *entries; + unsigned int entryCount; +} ConfigBlock; +static ConfigBlock cfgBlock; + + +static char *lstrip(char *line) +{ + while(isspace(line[0])) + line++; + return line; +} + +static char *rstrip(char *line) +{ + size_t len = strlen(line); + while(len > 0 && isspace(line[len-1])) + len--; + line[len] = 0; + return line; +} + +static int readline(FILE *f, char **output, size_t *maxlen) +{ + size_t len = 0; + int c; + + while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n')) + ; + if(c == EOF) + return 0; + + do { + if(len+1 >= *maxlen) + { + void *temp = NULL; + size_t newmax; + + newmax = (*maxlen ? (*maxlen)<<1 : 32); + if(newmax > *maxlen) + temp = realloc(*output, newmax); + if(!temp) + { + ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen); + return 0; + } + + *output = temp; + *maxlen = newmax; + } + (*output)[len++] = c; + (*output)[len] = '\0'; + } while((c=fgetc(f)) != EOF && c != '\r' && c != '\n'); + + return 1; +} + + +static char *expdup(const char *str) +{ + char *output = NULL; + size_t maxlen = 0; + size_t len = 0; + + while(*str != '\0') + { + const char *addstr; + size_t addstrlen; + size_t i; + + if(str[0] != '$') + { + const char *next = strchr(str, '$'); + addstr = str; + addstrlen = next ? (size_t)(next-str) : strlen(str); + + str += addstrlen; + } + else + { + str++; + if(*str == '$') + { + const char *next = strchr(str+1, '$'); + addstr = str; + addstrlen = next ? (size_t)(next-str) : strlen(str); + + str += addstrlen; + } + else + { + char envname[1024]; + size_t k = 0; + + while((isalnum(*str) || *str == '_') && k < sizeof(envname)-1) + envname[k++] = *(str++); + envname[k++] = '\0'; + + if((addstr=getenv(envname)) == NULL) + continue; + addstrlen = strlen(addstr); + } + } + if(addstrlen == 0) + continue; + + if(addstrlen >= maxlen-len) + { + void *temp = NULL; + size_t newmax; + + newmax = len+addstrlen+1; + if(newmax > maxlen) + temp = realloc(output, newmax); + if(!temp) + { + ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, maxlen); + return output; + } + + output = temp; + maxlen = newmax; + } + + for(i = 0;i < addstrlen;i++) + output[len++] = addstr[i]; + output[len] = '\0'; + } + + return output ? output : calloc(1, 1); +} + + +static void LoadConfigFromFile(FILE *f) +{ + char curSection[128] = ""; + char *buffer = NULL; + size_t maxlen = 0; + ConfigEntry *ent; + + while(readline(f, &buffer, &maxlen)) + { + char *line, *comment; + char key[256] = ""; + char value[256] = ""; + + comment = strchr(buffer, '#'); + if(comment) *(comment++) = 0; + + line = rstrip(lstrip(buffer)); + if(!line[0]) + continue; + + if(line[0] == '[') + { + char *section = line+1; + char *endsection; + + endsection = strchr(section, ']'); + if(!endsection || section == endsection || endsection[1] != 0) + { + ERR("config parse error: bad line \"%s\"\n", line); + continue; + } + *endsection = 0; + + if(strcasecmp(section, "general") == 0) + curSection[0] = 0; + else + { + strncpy(curSection, section, sizeof(curSection)-1); + curSection[sizeof(curSection)-1] = 0; + } + + continue; + } + + if(sscanf(line, "%255[^=] = \"%255[^\"]\"", key, value) == 2 || + sscanf(line, "%255[^=] = '%255[^\']'", key, value) == 2 || + sscanf(line, "%255[^=] = %255[^\n]", key, value) == 2) + { + /* sscanf doesn't handle '' or "" as empty values, so clip it + * manually. */ + if(strcmp(value, "\"\"") == 0 || strcmp(value, "''") == 0) + value[0] = 0; + } + else if(sscanf(line, "%255[^=] %255[=]", key, value) == 2) + { + /* Special case for 'key =' */ + value[0] = 0; + } + else + { + ERR("config parse error: malformed option line: \"%s\"\n\n", line); + continue; + } + rstrip(key); + + if(curSection[0] != 0) + { + size_t len = strlen(curSection); + memmove(&key[len+1], key, sizeof(key)-1-len); + key[len] = '/'; + memcpy(key, curSection, len); + } + + /* Check if we already have this option set */ + ent = cfgBlock.entries; + while((unsigned int)(ent-cfgBlock.entries) < cfgBlock.entryCount) + { + if(strcasecmp(ent->key, key) == 0) + break; + ent++; + } + + if((unsigned int)(ent-cfgBlock.entries) >= cfgBlock.entryCount) + { + /* Allocate a new option entry */ + ent = realloc(cfgBlock.entries, (cfgBlock.entryCount+1)*sizeof(ConfigEntry)); + if(!ent) + { + ERR("config parse error: error reallocating config entries\n"); + continue; + } + cfgBlock.entries = ent; + ent = cfgBlock.entries + cfgBlock.entryCount; + cfgBlock.entryCount++; + + ent->key = strdup(key); + ent->value = NULL; + } + + free(ent->value); + ent->value = expdup(value); + + TRACE("found '%s' = '%s'\n", ent->key, ent->value); + } + + free(buffer); +} + +#ifdef _WIN32 +void ReadALConfig(void) +{ + WCHAR buffer[PATH_MAX]; + const WCHAR *str; + FILE *f; + + if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE) + { + size_t p = lstrlenW(buffer); + _snwprintf(buffer+p, PATH_MAX-p, L"\\alsoft.ini"); + + TRACE("Loading config %ls...\n", buffer); + f = _wfopen(buffer, L"rt"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + } + + if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str) + { + TRACE("Loading config %ls...\n", str); + f = _wfopen(str, L"rt"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + } +} +#else +void ReadALConfig(void) +{ + char buffer[PATH_MAX]; + const char *str; + FILE *f; + + str = "/etc/openal/alsoft.conf"; + + TRACE("Loading config %s...\n", str); + f = al_fopen(str, "r"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + + if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0) + str = "/etc/xdg"; + strncpy(buffer, str, sizeof(buffer)-1); + buffer[sizeof(buffer)-1] = 0; + /* Go through the list in reverse, since "the order of base directories + * denotes their importance; the first directory listed is the most + * important". Ergo, we need to load the settings from the later dirs + * first so that the settings in the earlier dirs override them. + */ + while(1) + { + char *next = strrchr(buffer, ':'); + if(next) *(next++) = 0; + else next = buffer; + + if(next[0] != '/') + WARN("Ignoring XDG config dir: %s\n", next); + else + { + size_t len = strlen(next); + strncpy(next+len, "/alsoft.conf", buffer+sizeof(buffer)-next-len); + buffer[sizeof(buffer)-1] = 0; + + TRACE("Loading config %s...\n", next); + f = al_fopen(next, "r"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + } + if(next == buffer) + break; + } + + if((str=getenv("HOME")) != NULL && *str) + { + snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", str); + + TRACE("Loading config %s...\n", buffer); + f = al_fopen(buffer, "r"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + } + + if((str=getenv("XDG_CONFIG_HOME")) != NULL && str[0] != 0) + snprintf(buffer, sizeof(buffer), "%s/%s", str, "alsoft.conf"); + else + { + buffer[0] = 0; + if((str=getenv("HOME")) != NULL && str[0] != 0) + snprintf(buffer, sizeof(buffer), "%s/.config/%s", str, "alsoft.conf"); + } + if(buffer[0] != 0) + { + TRACE("Loading config %s...\n", buffer); + f = al_fopen(buffer, "r"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + } + + if((str=getenv("ALSOFT_CONF")) != NULL && *str) + { + TRACE("Loading config %s...\n", str); + f = al_fopen(str, "r"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + } +} +#endif + +void FreeALConfig(void) +{ + unsigned int i; + + for(i = 0;i < cfgBlock.entryCount;i++) + { + free(cfgBlock.entries[i].key); + free(cfgBlock.entries[i].value); + } + free(cfgBlock.entries); +} + +const char *GetConfigValue(const char *blockName, const char *keyName, const char *def) +{ + unsigned int i; + char key[256]; + + if(!keyName) + return def; + + if(blockName && strcasecmp(blockName, "general") != 0) + snprintf(key, sizeof(key), "%s/%s", blockName, keyName); + else + { + strncpy(key, keyName, sizeof(key)-1); + key[sizeof(key)-1] = 0; + } + + for(i = 0;i < cfgBlock.entryCount;i++) + { + if(strcasecmp(cfgBlock.entries[i].key, key) == 0) + { + TRACE("Found %s = \"%s\"\n", key, cfgBlock.entries[i].value); + if(cfgBlock.entries[i].value[0]) + return cfgBlock.entries[i].value; + return def; + } + } + + TRACE("Key %s not found\n", key); + return def; +} + +int ConfigValueExists(const char *blockName, const char *keyName) +{ + const char *val = GetConfigValue(blockName, keyName, ""); + return !!val[0]; +} + +int ConfigValueStr(const char *blockName, const char *keyName, const char **ret) +{ + const char *val = GetConfigValue(blockName, keyName, ""); + if(!val[0]) return 0; + + *ret = val; + return 1; +} + +int ConfigValueInt(const char *blockName, const char *keyName, int *ret) +{ + const char *val = GetConfigValue(blockName, keyName, ""); + if(!val[0]) return 0; + + *ret = strtol(val, NULL, 0); + return 1; +} + +int ConfigValueUInt(const char *blockName, const char *keyName, unsigned int *ret) +{ + const char *val = GetConfigValue(blockName, keyName, ""); + if(!val[0]) return 0; + + *ret = strtoul(val, NULL, 0); + return 1; +} + +int ConfigValueFloat(const char *blockName, const char *keyName, float *ret) +{ + const char *val = GetConfigValue(blockName, keyName, ""); + if(!val[0]) return 0; + +#ifdef HAVE_STRTOF + *ret = strtof(val, NULL); +#else + *ret = (float)strtod(val, NULL); +#endif + return 1; +} + +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 --git a/libs/openal-soft/Alc/alcRing.c b/libs/openal-soft/Alc/alcRing.c new file mode 100644 index 00000000..9b5d8214 --- /dev/null +++ b/libs/openal-soft/Alc/alcRing.c @@ -0,0 +1,129 @@ +/** + * 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" +#include "threads.h" +#include "compat.h" + + +struct RingBuffer { + ALubyte *mem; + + ALsizei frame_size; + ALsizei length; + ALint read_pos; + ALint write_pos; + + almtx_t mtx; +}; + + +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; + + almtx_init(&ring->mtx, almtx_plain); + } + return ring; +} + +void DestroyRingBuffer(RingBuffer *ring) +{ + if(ring) + { + almtx_destroy(&ring->mtx); + free(ring); + } +} + +ALsizei RingBufferSize(RingBuffer *ring) +{ + ALsizei s; + + almtx_lock(&ring->mtx); + s = (ring->write_pos-ring->read_pos+ring->length) % ring->length; + almtx_unlock(&ring->mtx); + + return s; +} + +void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len) +{ + int remain; + + almtx_lock(&ring->mtx); + + 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; + } + + almtx_unlock(&ring->mtx); +} + +void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len) +{ + int remain; + + almtx_lock(&ring->mtx); + + 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; + + almtx_unlock(&ring->mtx); +} diff --git a/libs/openal-soft/Alc/alstring.h b/libs/openal-soft/Alc/alstring.h new file mode 100644 index 00000000..32f4280a --- /dev/null +++ b/libs/openal-soft/Alc/alstring.h @@ -0,0 +1,45 @@ +#ifndef ALSTRING_H +#define ALSTRING_H + +#include + +#include "vector.h" + + +typedef char al_string_char_type; +TYPEDEF_VECTOR(al_string_char_type, al_string) + +inline void al_string_deinit(al_string *str) +{ VECTOR_DEINIT(*str); } +#define AL_STRING_INIT(_x) do { (_x) = (al_string)NULL; } while(0) +#define AL_STRING_INIT_STATIC() ((al_string)NULL) +#define AL_STRING_DEINIT(_x) al_string_deinit(&(_x)) + +inline ALsizei al_string_length(const_al_string str) +{ return VECTOR_SIZE(str); } + +inline ALboolean al_string_empty(const_al_string str) +{ return al_string_length(str) == 0; } + +inline const al_string_char_type *al_string_get_cstr(const_al_string str) +{ return str ? &VECTOR_FRONT(str) : ""; } + +void al_string_clear(al_string *str); + +int al_string_cmp(const_al_string str1, const_al_string str2); +int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2); + +void al_string_copy(al_string *str, const_al_string from); +void al_string_copy_cstr(al_string *str, const al_string_char_type *from); + +void al_string_append_char(al_string *str, const al_string_char_type c); +void al_string_append_cstr(al_string *str, const al_string_char_type *from); +void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to); + +#ifdef _WIN32 +#include +/* Windows-only methods to deal with WideChar strings. */ +void al_string_copy_wcstr(al_string *str, const wchar_t *from); +#endif + +#endif /* ALSTRING_H */ diff --git a/libs/openal-soft/Alc/backends/alsa.c b/libs/openal-soft/Alc/backends/alsa.c new file mode 100644 index 00000000..d5174da8 --- /dev/null +++ b/libs/openal-soft/Alc/backends/alsa.c @@ -0,0 +1,1387 @@ +/** + * 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 "alu.h" +#include "threads.h" +#include "compat.h" + +#include "backends/base.h" + +#include + + +static const ALCchar alsaDevice[] = "ALSA Default"; + + +#ifdef HAVE_DYNLOAD +#define ALSA_FUNCS(MAGIC) \ + MAGIC(snd_strerror); \ + MAGIC(snd_pcm_open); \ + MAGIC(snd_pcm_close); \ + MAGIC(snd_pcm_nonblock); \ + MAGIC(snd_pcm_frames_to_bytes); \ + MAGIC(snd_pcm_bytes_to_frames); \ + MAGIC(snd_pcm_hw_params_malloc); \ + MAGIC(snd_pcm_hw_params_free); \ + MAGIC(snd_pcm_hw_params_any); \ + MAGIC(snd_pcm_hw_params_current); \ + MAGIC(snd_pcm_hw_params_set_access); \ + MAGIC(snd_pcm_hw_params_set_format); \ + MAGIC(snd_pcm_hw_params_set_channels); \ + MAGIC(snd_pcm_hw_params_set_periods_near); \ + MAGIC(snd_pcm_hw_params_set_rate_near); \ + MAGIC(snd_pcm_hw_params_set_rate); \ + MAGIC(snd_pcm_hw_params_set_rate_resample); \ + MAGIC(snd_pcm_hw_params_set_buffer_time_near); \ + MAGIC(snd_pcm_hw_params_set_period_time_near); \ + MAGIC(snd_pcm_hw_params_set_buffer_size_near); \ + MAGIC(snd_pcm_hw_params_set_period_size_near); \ + MAGIC(snd_pcm_hw_params_set_buffer_size_min); \ + MAGIC(snd_pcm_hw_params_get_buffer_time_min); \ + MAGIC(snd_pcm_hw_params_get_buffer_time_max); \ + MAGIC(snd_pcm_hw_params_get_period_time_min); \ + MAGIC(snd_pcm_hw_params_get_period_time_max); \ + MAGIC(snd_pcm_hw_params_get_buffer_size); \ + MAGIC(snd_pcm_hw_params_get_period_size); \ + MAGIC(snd_pcm_hw_params_get_access); \ + MAGIC(snd_pcm_hw_params_get_periods); \ + MAGIC(snd_pcm_hw_params_test_format); \ + MAGIC(snd_pcm_hw_params_test_channels); \ + MAGIC(snd_pcm_hw_params); \ + MAGIC(snd_pcm_sw_params_malloc); \ + MAGIC(snd_pcm_sw_params_current); \ + MAGIC(snd_pcm_sw_params_set_avail_min); \ + MAGIC(snd_pcm_sw_params_set_stop_threshold); \ + MAGIC(snd_pcm_sw_params); \ + MAGIC(snd_pcm_sw_params_free); \ + MAGIC(snd_pcm_prepare); \ + MAGIC(snd_pcm_start); \ + MAGIC(snd_pcm_resume); \ + MAGIC(snd_pcm_reset); \ + MAGIC(snd_pcm_wait); \ + MAGIC(snd_pcm_delay); \ + MAGIC(snd_pcm_state); \ + MAGIC(snd_pcm_avail_update); \ + MAGIC(snd_pcm_areas_silence); \ + MAGIC(snd_pcm_mmap_begin); \ + MAGIC(snd_pcm_mmap_commit); \ + MAGIC(snd_pcm_readi); \ + MAGIC(snd_pcm_writei); \ + MAGIC(snd_pcm_drain); \ + MAGIC(snd_pcm_drop); \ + MAGIC(snd_pcm_recover); \ + MAGIC(snd_pcm_info_malloc); \ + MAGIC(snd_pcm_info_free); \ + MAGIC(snd_pcm_info_set_device); \ + MAGIC(snd_pcm_info_set_subdevice); \ + MAGIC(snd_pcm_info_set_stream); \ + MAGIC(snd_pcm_info_get_name); \ + MAGIC(snd_ctl_pcm_next_device); \ + MAGIC(snd_ctl_pcm_info); \ + MAGIC(snd_ctl_open); \ + MAGIC(snd_ctl_close); \ + MAGIC(snd_ctl_card_info_malloc); \ + MAGIC(snd_ctl_card_info_free); \ + MAGIC(snd_ctl_card_info); \ + MAGIC(snd_ctl_card_info_get_name); \ + MAGIC(snd_ctl_card_info_get_id); \ + MAGIC(snd_card_next); \ + MAGIC(snd_config_update_free_global) + +static void *alsa_handle; +#define MAKE_FUNC(f) static __typeof(f) * p##f +ALSA_FUNCS(MAKE_FUNC); +#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_current psnd_pcm_hw_params_current +#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_time_min psnd_pcm_hw_params_get_buffer_time_min +#define snd_pcm_hw_params_get_buffer_time_max psnd_pcm_hw_params_get_buffer_time_max +#define snd_pcm_hw_params_get_period_time_min psnd_pcm_hw_params_get_period_time_min +#define snd_pcm_hw_params_get_period_time_max psnd_pcm_hw_params_get_period_time_max +#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_test_format psnd_pcm_hw_params_test_format +#define snd_pcm_hw_params_test_channels psnd_pcm_hw_params_test_channels +#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_set_stop_threshold psnd_pcm_sw_params_set_stop_threshold +#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_reset psnd_pcm_reset +#define snd_pcm_wait psnd_pcm_wait +#define snd_pcm_delay psnd_pcm_delay +#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_drop psnd_pcm_drop +#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 +#define snd_config_update_free_global psnd_config_update_free_global +#endif + + +static ALCboolean alsa_load(void) +{ + ALCboolean error = ALC_FALSE; + +#ifdef HAVE_DYNLOAD + if(!alsa_handle) + { + alsa_handle = LoadLib("libasound.so.2"); + if(!alsa_handle) + return ALC_FALSE; + + error = ALC_FALSE; +#define LOAD_FUNC(f) do { \ + p##f = GetSymbol(alsa_handle, #f); \ + if(p##f == NULL) { \ + error = ALC_TRUE; \ + } \ +} while(0) + ALSA_FUNCS(LOAD_FUNC); +#undef LOAD_FUNC + + if(error) + { + CloseLib(alsa_handle); + alsa_handle = NULL; + return ALC_FALSE; + } + } +#endif + + return !error; +} + + +typedef struct { + al_string name; + al_string device_name; +} DevMap; +TYPEDEF_VECTOR(DevMap, vector_DevMap) + +static vector_DevMap PlaybackDevices; +static vector_DevMap CaptureDevices; + +static void clear_devlist(vector_DevMap *devlist) +{ + DevMap *iter, *end; + + iter = VECTOR_ITER_BEGIN(*devlist); + end = VECTOR_ITER_END(*devlist); + for(;iter != end;iter++) + { + AL_STRING_DEINIT(iter->name); + AL_STRING_DEINIT(iter->device_name); + } + VECTOR_RESIZE(*devlist, 0); +} + + +static const char *prefix_name(snd_pcm_stream_t stream) +{ + assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE); + return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix"; +} + +static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList) +{ + const char *main_prefix = "plughw:"; + snd_ctl_t *handle; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + int card, err, dev; + DevMap entry; + + clear_devlist(DeviceList); + + snd_ctl_card_info_malloc(&info); + snd_pcm_info_malloc(&pcminfo); + + AL_STRING_INIT(entry.name); + AL_STRING_INIT(entry.device_name); + al_string_copy_cstr(&entry.name, alsaDevice); + al_string_copy_cstr(&entry.device_name, GetConfigValue("alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? + "device" : "capture", "default")); + VECTOR_PUSH_BACK(*DeviceList, entry); + + card = -1; + if((err=snd_card_next(&card)) < 0) + ERR("Failed to find a card: %s\n", snd_strerror(err)); + ConfigValueStr("alsa", prefix_name(stream), &main_prefix); + while(card >= 0) + { + const char *card_prefix = main_prefix; + const char *cardname, *cardid; + char name[256]; + + snprintf(name, sizeof(name), "hw:%d", card); + if((err = snd_ctl_open(&handle, name, 0)) < 0) + { + ERR("control open (hw:%d): %s\n", card, snd_strerror(err)); + goto next_card; + } + if((err = snd_ctl_card_info(handle, info)) < 0) + { + ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err)); + snd_ctl_close(handle); + goto next_card; + } + + cardname = snd_ctl_card_info_get_name(info); + cardid = snd_ctl_card_info_get_id(info); + + snprintf(name, sizeof(name), "%s-%s", prefix_name(stream), cardid); + ConfigValueStr("alsa", name, &card_prefix); + + dev = -1; + while(1) + { + const char *device_prefix = card_prefix; + const char *devname; + char device[128]; + + 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 (hw:%d): %s\n", card, snd_strerror(err)); + continue; + } + + devname = snd_pcm_info_get_name(pcminfo); + + snprintf(name, sizeof(name), "%s-%s-%d", prefix_name(stream), cardid, dev); + ConfigValueStr("alsa", name, &device_prefix); + + snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)", + cardname, devname, cardid, dev); + snprintf(device, sizeof(device), "%sCARD=%s,DEV=%d", + device_prefix, cardid, dev); + + TRACE("Got device \"%s\", \"%s\"\n", name, device); + AL_STRING_INIT(entry.name); + AL_STRING_INIT(entry.device_name); + al_string_copy_cstr(&entry.name, name); + al_string_copy_cstr(&entry.device_name, device); + VECTOR_PUSH_BACK(*DeviceList, entry); + } + 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); +} + + +static int verify_state(snd_pcm_t *handle) +{ + snd_pcm_state_t state = snd_pcm_state(handle); + int err; + + switch(state) + { + case SND_PCM_STATE_OPEN: + case SND_PCM_STATE_SETUP: + case SND_PCM_STATE_PREPARED: + case SND_PCM_STATE_RUNNING: + case SND_PCM_STATE_DRAINING: + case SND_PCM_STATE_PAUSED: + /* All Okay */ + break; + + case SND_PCM_STATE_XRUN: + if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0) + return err; + break; + case SND_PCM_STATE_SUSPENDED: + if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0) + return err; + break; + case SND_PCM_STATE_DISCONNECTED: + return -ENODEV; + } + + return state; +} + + +typedef struct ALCplaybackAlsa { + DERIVE_FROM_TYPE(ALCbackend); + + snd_pcm_t *pcmHandle; + + ALvoid *buffer; + ALsizei size; + + volatile int killNow; + althrd_t thread; +} ALCplaybackAlsa; + +static int ALCplaybackAlsa_mixerProc(void *ptr); +static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr); + +static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device); +static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, Destruct) +static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name); +static void ALCplaybackAlsa_close(ALCplaybackAlsa *self); +static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self); +static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self); +static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self); +static DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples) +static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self); +static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa) + +DEFINE_ALCBACKEND_VTABLE(ALCplaybackAlsa); + + +static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCplaybackAlsa, ALCbackend, self); +} + + +static int ALCplaybackAlsa_mixerProc(void *ptr) +{ + ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + const snd_pcm_channel_area_t *areas = NULL; + snd_pcm_uframes_t update_size, num_updates; + snd_pcm_sframes_t avail, commitres; + snd_pcm_uframes_t offset, frames; + char *WritePtr; + int err; + + SetRTPriority(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + update_size = device->UpdateSize; + num_updates = device->NumUpdates; + while(!self->killNow) + { + int state = verify_state(self->pcmHandle); + if(state < 0) + { + ERR("Invalid state detected: %s\n", snd_strerror(state)); + ALCplaybackAlsa_lock(self); + aluHandleDisconnect(device); + ALCplaybackAlsa_unlock(self); + break; + } + + avail = snd_pcm_avail_update(self->pcmHandle); + if(avail < 0) + { + ERR("available update failed: %s\n", snd_strerror(avail)); + continue; + } + + if((snd_pcm_uframes_t)avail > update_size*(num_updates+1)) + { + WARN("available samples exceeds the buffer size\n"); + snd_pcm_reset(self->pcmHandle); + continue; + } + + // make sure there's frames to process + if((snd_pcm_uframes_t)avail < update_size) + { + if(state != SND_PCM_STATE_RUNNING) + { + err = snd_pcm_start(self->pcmHandle); + if(err < 0) + { + ERR("start failed: %s\n", snd_strerror(err)); + continue; + } + } + if(snd_pcm_wait(self->pcmHandle, 1000) == 0) + ERR("Wait timeout... buffer size too low?\n"); + continue; + } + avail -= avail%update_size; + + // it is possible that contiguous areas are smaller, thus we use a loop + ALCplaybackAlsa_lock(self); + while(avail > 0) + { + frames = avail; + + err = snd_pcm_mmap_begin(self->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(device, WritePtr, frames); + + commitres = snd_pcm_mmap_commit(self->pcmHandle, offset, frames); + if(commitres < 0 || (commitres-frames) != 0) + { + ERR("mmap commit error: %s\n", + snd_strerror(commitres >= 0 ? -EPIPE : commitres)); + break; + } + + avail -= frames; + } + ALCplaybackAlsa_unlock(self); + } + + return 0; +} + +static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr) +{ + ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + snd_pcm_uframes_t update_size, num_updates; + snd_pcm_sframes_t avail; + char *WritePtr; + int err; + + SetRTPriority(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + update_size = device->UpdateSize; + num_updates = device->NumUpdates; + while(!self->killNow) + { + int state = verify_state(self->pcmHandle); + if(state < 0) + { + ERR("Invalid state detected: %s\n", snd_strerror(state)); + ALCplaybackAlsa_lock(self); + aluHandleDisconnect(device); + ALCplaybackAlsa_unlock(self); + break; + } + + avail = snd_pcm_avail_update(self->pcmHandle); + if(avail < 0) + { + ERR("available update failed: %s\n", snd_strerror(avail)); + continue; + } + + if((snd_pcm_uframes_t)avail > update_size*num_updates) + { + WARN("available samples exceeds the buffer size\n"); + snd_pcm_reset(self->pcmHandle); + continue; + } + + if((snd_pcm_uframes_t)avail < update_size) + { + if(state != SND_PCM_STATE_RUNNING) + { + err = snd_pcm_start(self->pcmHandle); + if(err < 0) + { + ERR("start failed: %s\n", snd_strerror(err)); + continue; + } + } + if(snd_pcm_wait(self->pcmHandle, 1000) == 0) + ERR("Wait timeout... buffer size too low?\n"); + continue; + } + + ALCplaybackAlsa_lock(self); + WritePtr = self->buffer; + avail = snd_pcm_bytes_to_frames(self->pcmHandle, self->size); + aluMixData(device, WritePtr, avail); + + while(avail > 0) + { + int ret = snd_pcm_writei(self->pcmHandle, WritePtr, avail); + switch (ret) + { + case -EAGAIN: + continue; + case -ESTRPIPE: + case -EPIPE: + case -EINTR: + ret = snd_pcm_recover(self->pcmHandle, ret, 1); + if(ret < 0) + avail = 0; + break; + default: + if (ret >= 0) + { + WritePtr += snd_pcm_frames_to_bytes(self->pcmHandle, ret); + avail -= ret; + } + break; + } + if (ret < 0) + { + ret = snd_pcm_prepare(self->pcmHandle); + if(ret < 0) + break; + } + } + ALCplaybackAlsa_unlock(self); + } + + return 0; +} + + +static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + const char *driver = NULL; + int err; + + if(name) + { + const DevMap *iter; + + if(VECTOR_SIZE(PlaybackDevices) == 0) + probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices); + +#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0) + VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME); +#undef MATCH_NAME + if(iter == VECTOR_ITER_END(PlaybackDevices)) + return ALC_INVALID_VALUE; + driver = al_string_get_cstr(iter->device_name); + } + else + { + name = alsaDevice; + driver = GetConfigValue("alsa", "device", "default"); + } + + TRACE("Opening device \"%s\"\n", driver); + err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + if(err < 0) + { + ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(err)); + return ALC_OUT_OF_MEMORY; + } + + /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */ + snd_config_update_free_global(); + + al_string_copy_cstr(&device->DeviceName, name); + + return ALC_NO_ERROR; +} + +static void ALCplaybackAlsa_close(ALCplaybackAlsa *self) +{ + snd_pcm_close(self->pcmHandle); +} + +static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + snd_pcm_uframes_t periodSizeInFrames; + unsigned int periodLen, bufferLen; + snd_pcm_sw_params_t *sp = NULL; + snd_pcm_hw_params_t *hp = NULL; + snd_pcm_format_t format = -1; + snd_pcm_access_t access; + unsigned int periods; + unsigned int rate; + const char *funcerr; + int allowmmap; + int err; + + 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 DevFmtInt: + format = SND_PCM_FORMAT_S32; + break; + case DevFmtUInt: + format = SND_PCM_FORMAT_U32; + 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; + + snd_pcm_hw_params_malloc(&hp); +#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error + CHECK(snd_pcm_hw_params_any(self->pcmHandle, hp)); + /* set interleaved access */ + if(!allowmmap || snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) + { + /* No mmap */ + CHECK(snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED)); + } + /* test and set format (implicitly sets sample bits) */ + if(snd_pcm_hw_params_test_format(self->pcmHandle, hp, format) < 0) + { + static const struct { + snd_pcm_format_t format; + enum DevFmtType fmttype; + } formatlist[] = { + { SND_PCM_FORMAT_FLOAT, DevFmtFloat }, + { SND_PCM_FORMAT_S32, DevFmtInt }, + { SND_PCM_FORMAT_U32, DevFmtUInt }, + { SND_PCM_FORMAT_S16, DevFmtShort }, + { SND_PCM_FORMAT_U16, DevFmtUShort }, + { SND_PCM_FORMAT_S8, DevFmtByte }, + { SND_PCM_FORMAT_U8, DevFmtUByte }, + }; + size_t k; + + for(k = 0;k < COUNTOF(formatlist);k++) + { + format = formatlist[k].format; + if(snd_pcm_hw_params_test_format(self->pcmHandle, hp, format) >= 0) + { + device->FmtType = formatlist[k].fmttype; + break; + } + } + } + CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format)); + /* test and set channels (implicitly sets frame bits) */ + if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0) + { + static const enum DevFmtChannels channellist[] = { + DevFmtStereo, + DevFmtQuad, + DevFmtX51, + DevFmtX71, + DevFmtMono, + }; + size_t k; + + for(k = 0;k < COUNTOF(channellist);k++) + { + if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0) + { + device->FmtChans = channellist[k]; + break; + } + } + } + CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans))); + /* set rate (implicitly constrains period/buffer parameters) */ + if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0) + ERR("Failed to disable ALSA resampler\n"); + CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL)); + /* set buffer time (implicitly constrains period/buffer parameters) */ + if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0) + ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err)); + /* set period time (implicitly sets buffer size/bytes/time and period size/bytes) */ + if((err=snd_pcm_hw_params_set_period_time_near(self->pcmHandle, hp, &periodLen, NULL)) < 0) + ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err)); + /* install and prepare hardware configuration */ + CHECK(snd_pcm_hw_params(self->pcmHandle, hp)); + /* retrieve configuration info */ + CHECK(snd_pcm_hw_params_get_access(hp, &access)); + CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL)); + CHECK(snd_pcm_hw_params_get_periods(hp, &periods, NULL)); + + snd_pcm_hw_params_free(hp); + hp = NULL; + snd_pcm_sw_params_malloc(&sp); + + CHECK(snd_pcm_sw_params_current(self->pcmHandle, sp)); + CHECK(snd_pcm_sw_params_set_avail_min(self->pcmHandle, sp, periodSizeInFrames)); + CHECK(snd_pcm_sw_params_set_stop_threshold(self->pcmHandle, sp, periodSizeInFrames*periods)); + CHECK(snd_pcm_sw_params(self->pcmHandle, sp)); +#undef CHECK + snd_pcm_sw_params_free(sp); + sp = NULL; + + device->NumUpdates = periods; + device->UpdateSize = periodSizeInFrames; + device->Frequency = rate; + + SetDefaultChannelOrder(device); + + return ALC_TRUE; + +error: + ERR("%s failed: %s\n", funcerr, snd_strerror(err)); + if(hp) snd_pcm_hw_params_free(hp); + if(sp) snd_pcm_sw_params_free(sp); + return ALC_FALSE; +} + +static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + int (*thread_func)(void*) = NULL; + snd_pcm_hw_params_t *hp = NULL; + snd_pcm_access_t access; + const char *funcerr; + int err; + + snd_pcm_hw_params_malloc(&hp); +#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error + CHECK(snd_pcm_hw_params_current(self->pcmHandle, hp)); + /* retrieve configuration info */ + CHECK(snd_pcm_hw_params_get_access(hp, &access)); +#undef CHECK + snd_pcm_hw_params_free(hp); + hp = NULL; + + self->size = snd_pcm_frames_to_bytes(self->pcmHandle, device->UpdateSize); + if(access == SND_PCM_ACCESS_RW_INTERLEAVED) + { + self->buffer = malloc(self->size); + if(!self->buffer) + { + ERR("buffer malloc failed\n"); + return ALC_FALSE; + } + thread_func = ALCplaybackAlsa_mixerNoMMapProc; + } + else + { + err = snd_pcm_prepare(self->pcmHandle); + if(err < 0) + { + ERR("snd_pcm_prepare(data->pcmHandle) failed: %s\n", snd_strerror(err)); + return ALC_FALSE; + } + thread_func = ALCplaybackAlsa_mixerProc; + } + self->killNow = 0; + if(althrd_create(&self->thread, thread_func, self) != althrd_success) + { + ERR("Could not create playback thread\n"); + free(self->buffer); + self->buffer = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; + +error: + ERR("%s failed: %s\n", funcerr, snd_strerror(err)); + if(hp) snd_pcm_hw_params_free(hp); + return ALC_FALSE; +} + +static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self) +{ + int res; + + if(self->killNow) + return; + + self->killNow = 1; + althrd_join(self->thread, &res); + + free(self->buffer); + self->buffer = NULL; +} + +static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + snd_pcm_sframes_t delay = 0; + int err; + + if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0) + { + ERR("Failed to get pcm delay: %s\n", snd_strerror(err)); + return 0; + } + return maxi64((ALint64)delay*1000000000/device->Frequency, 0); +} + + +typedef struct ALCcaptureAlsa { + DERIVE_FROM_TYPE(ALCbackend); + + snd_pcm_t *pcmHandle; + + ALvoid *buffer; + ALsizei size; + + ALboolean doCapture; + RingBuffer *ring; + + snd_pcm_sframes_t last_avail; +} ALCcaptureAlsa; + +static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device); +static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, Destruct) +static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name); +static void ALCcaptureAlsa_close(ALCcaptureAlsa *self); +static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, ALCboolean, reset) +static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self); +static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self); +static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples); +static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self); +static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self); +static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa) + +DEFINE_ALCBACKEND_VTABLE(ALCcaptureAlsa); + + +static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCcaptureAlsa, ALCbackend, self); +} + + +static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + const char *driver = NULL; + snd_pcm_hw_params_t *hp; + snd_pcm_uframes_t bufferSizeInFrames; + snd_pcm_uframes_t periodSizeInFrames; + ALboolean needring = AL_FALSE; + snd_pcm_format_t format = -1; + const char *funcerr; + int err; + + if(name) + { + const DevMap *iter; + + if(VECTOR_SIZE(CaptureDevices) == 0) + probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices); + +#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0) + VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME); +#undef MATCH_NAME + if(iter == VECTOR_ITER_END(CaptureDevices)) + return ALC_INVALID_VALUE; + driver = al_string_get_cstr(iter->device_name); + } + else + { + name = alsaDevice; + driver = GetConfigValue("alsa", "capture", "default"); + } + + TRACE("Opening device \"%s\"\n", driver); + err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + if(err < 0) + { + ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(err)); + return ALC_INVALID_VALUE; + } + + /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */ + snd_config_update_free_global(); + + 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 DevFmtInt: + format = SND_PCM_FORMAT_S32; + break; + case DevFmtUInt: + format = SND_PCM_FORMAT_U32; + break; + case DevFmtFloat: + format = SND_PCM_FORMAT_FLOAT; + break; + } + + funcerr = NULL; + bufferSizeInFrames = maxu(device->UpdateSize*device->NumUpdates, + 100*device->Frequency/1000); + periodSizeInFrames = minu(bufferSizeInFrames, 25*device->Frequency/1000); + + snd_pcm_hw_params_malloc(&hp); +#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error + CHECK(snd_pcm_hw_params_any(self->pcmHandle, hp)); + /* set interleaved access */ + CHECK(snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED)); + /* set format (implicitly sets sample bits) */ + CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format)); + /* set channels (implicitly sets frame bits) */ + CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans))); + /* set rate (implicitly constrains period/buffer parameters) */ + CHECK(snd_pcm_hw_params_set_rate(self->pcmHandle, hp, device->Frequency, 0)); + /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */ + if(snd_pcm_hw_params_set_buffer_size_min(self->pcmHandle, hp, &bufferSizeInFrames) < 0) + { + TRACE("Buffer too large, using intermediate ring buffer\n"); + needring = AL_TRUE; + CHECK(snd_pcm_hw_params_set_buffer_size_near(self->pcmHandle, hp, &bufferSizeInFrames)); + } + /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */ + CHECK(snd_pcm_hw_params_set_period_size_near(self->pcmHandle, hp, &periodSizeInFrames, NULL)); + /* install and prepare hardware configuration */ + CHECK(snd_pcm_hw_params(self->pcmHandle, hp)); + /* retrieve configuration info */ + CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL)); +#undef CHECK + snd_pcm_hw_params_free(hp); + hp = NULL; + + if(needring) + { + self->ring = CreateRingBuffer(FrameSizeFromDevFmt(device->FmtChans, device->FmtType), + device->UpdateSize*device->NumUpdates); + if(!self->ring) + { + ERR("ring buffer create failed\n"); + goto error2; + } + + self->size = snd_pcm_frames_to_bytes(self->pcmHandle, periodSizeInFrames); + self->buffer = malloc(self->size); + if(!self->buffer) + { + ERR("buffer malloc failed\n"); + goto error2; + } + } + + al_string_copy_cstr(&device->DeviceName, name); + + return ALC_NO_ERROR; + +error: + ERR("%s failed: %s\n", funcerr, snd_strerror(err)); + if(hp) snd_pcm_hw_params_free(hp); + +error2: + free(self->buffer); + self->buffer = NULL; + DestroyRingBuffer(self->ring); + self->ring = NULL; + snd_pcm_close(self->pcmHandle); + + return ALC_INVALID_VALUE; +} + +static void ALCcaptureAlsa_close(ALCcaptureAlsa *self) +{ + snd_pcm_close(self->pcmHandle); + DestroyRingBuffer(self->ring); + + free(self->buffer); + self->buffer = NULL; +} + +static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self) +{ + int err = snd_pcm_start(self->pcmHandle); + if(err < 0) + { + ERR("start failed: %s\n", snd_strerror(err)); + aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice); + return ALC_FALSE; + } + + self->doCapture = AL_TRUE; + return ALC_TRUE; +} + +static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self) +{ + ALCuint avail; + int err; + + /* OpenAL requires access to unread audio after stopping, but ALSA's + * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's + * available now so it'll be available later after the drop. */ + avail = ALCcaptureAlsa_availableSamples(self); + if(!self->ring && avail > 0) + { + /* The ring buffer implicitly captures when checking availability. + * Direct access needs to explicitly capture it into temp storage. */ + ALsizei size; + void *ptr; + + size = snd_pcm_frames_to_bytes(self->pcmHandle, avail); + ptr = malloc(size); + if(ptr) + { + ALCcaptureAlsa_captureSamples(self, ptr, avail); + free(self->buffer); + self->buffer = ptr; + self->size = size; + } + } + err = snd_pcm_drop(self->pcmHandle); + if(err < 0) + ERR("drop failed: %s\n", snd_strerror(err)); + self->doCapture = AL_FALSE; +} + +static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + + if(self->ring) + { + ReadRingBuffer(self->ring, buffer, samples); + return ALC_NO_ERROR; + } + + self->last_avail -= samples; + while(device->Connected && samples > 0) + { + snd_pcm_sframes_t amt = 0; + + if(self->size > 0) + { + /* First get any data stored from the last stop */ + amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size); + if((snd_pcm_uframes_t)amt > samples) amt = samples; + + amt = snd_pcm_frames_to_bytes(self->pcmHandle, amt); + memcpy(buffer, self->buffer, amt); + + if(self->size > amt) + { + memmove(self->buffer, self->buffer+amt, self->size - amt); + self->size -= amt; + } + else + { + free(self->buffer); + self->buffer = NULL; + self->size = 0; + } + amt = snd_pcm_bytes_to_frames(self->pcmHandle, amt); + } + else if(self->doCapture) + amt = snd_pcm_readi(self->pcmHandle, buffer, samples); + if(amt < 0) + { + ERR("read error: %s\n", snd_strerror(amt)); + + if(amt == -EAGAIN) + continue; + if((amt=snd_pcm_recover(self->pcmHandle, amt, 1)) >= 0) + { + amt = snd_pcm_start(self->pcmHandle); + if(amt >= 0) + amt = snd_pcm_avail_update(self->pcmHandle); + } + if(amt < 0) + { + ERR("restore error: %s\n", snd_strerror(amt)); + aluHandleDisconnect(device); + break; + } + /* If the amount available is less than what's asked, we lost it + * during recovery. So just give silence instead. */ + if((snd_pcm_uframes_t)amt < samples) + break; + continue; + } + + buffer = (ALbyte*)buffer + amt; + samples -= amt; + } + if(samples > 0) + memset(buffer, ((device->FmtType == DevFmtUByte) ? 0x80 : 0), + snd_pcm_frames_to_bytes(self->pcmHandle, samples)); + + return ALC_NO_ERROR; +} + +static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + snd_pcm_sframes_t avail = 0; + + if(device->Connected && self->doCapture) + avail = snd_pcm_avail_update(self->pcmHandle); + if(avail < 0) + { + ERR("avail update failed: %s\n", snd_strerror(avail)); + + if((avail=snd_pcm_recover(self->pcmHandle, avail, 1)) >= 0) + { + if(self->doCapture) + avail = snd_pcm_start(self->pcmHandle); + if(avail >= 0) + avail = snd_pcm_avail_update(self->pcmHandle); + } + if(avail < 0) + { + ERR("restore error: %s\n", snd_strerror(avail)); + aluHandleDisconnect(device); + } + } + + if(!self->ring) + { + if(avail < 0) avail = 0; + avail += snd_pcm_bytes_to_frames(self->pcmHandle, self->size); + if(avail > self->last_avail) self->last_avail = avail; + return self->last_avail; + } + + while(avail > 0) + { + snd_pcm_sframes_t amt; + + amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size); + if(avail < amt) amt = avail; + + amt = snd_pcm_readi(self->pcmHandle, self->buffer, amt); + if(amt < 0) + { + ERR("read error: %s\n", snd_strerror(amt)); + + if(amt == -EAGAIN) + continue; + if((amt=snd_pcm_recover(self->pcmHandle, amt, 1)) >= 0) + { + if(self->doCapture) + amt = snd_pcm_start(self->pcmHandle); + if(amt >= 0) + amt = snd_pcm_avail_update(self->pcmHandle); + } + if(amt < 0) + { + ERR("restore error: %s\n", snd_strerror(amt)); + aluHandleDisconnect(device); + break; + } + avail = amt; + continue; + } + + WriteRingBuffer(self->ring, self->buffer, amt); + avail -= amt; + } + + return RingBufferSize(self->ring); +} + +static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + snd_pcm_sframes_t delay = 0; + int err; + + if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0) + { + ERR("Failed to get pcm delay: %s\n", snd_strerror(err)); + return 0; + } + return maxi64((ALint64)delay*1000000000/device->Frequency, 0); +} + + +static inline void AppendAllDevicesList2(const DevMap *entry) +{ AppendAllDevicesList(al_string_get_cstr(entry->name)); } +static inline void AppendCaptureDeviceList2(const DevMap *entry) +{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); } + +typedef struct ALCalsaBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCalsaBackendFactory; +#define ALCALSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCalsaBackendFactory, ALCbackendFactory) } } + +static ALCboolean ALCalsaBackendFactory_init(ALCalsaBackendFactory* UNUSED(self)) +{ + VECTOR_INIT(PlaybackDevices); + VECTOR_INIT(CaptureDevices); + + if(!alsa_load()) + return ALC_FALSE; + return ALC_TRUE; +} + +static void ALCalsaBackendFactory_deinit(ALCalsaBackendFactory* UNUSED(self)) +{ + clear_devlist(&PlaybackDevices); + VECTOR_DEINIT(PlaybackDevices); + + clear_devlist(&CaptureDevices); + VECTOR_DEINIT(CaptureDevices); + +#ifdef HAVE_DYNLOAD + if(alsa_handle) + CloseLib(alsa_handle); + alsa_handle = NULL; +#endif +} + +static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback || type == ALCbackend_Capture) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type) +{ + switch(type) + { + case ALL_DEVICE_PROBE: + probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices); + VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2); + break; + + case CAPTURE_DEVICE_PROBE: + probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices); + VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2); + break; + } +} + +static ALCbackend* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCplaybackAlsa *backend; + + backend = ALCplaybackAlsa_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCplaybackAlsa_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + if(type == ALCbackend_Capture) + { + ALCcaptureAlsa *backend; + + backend = ALCcaptureAlsa_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCcaptureAlsa_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} + +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCalsaBackendFactory); + + +ALCbackendFactory *ALCalsaBackendFactory_getFactory(void) +{ + static ALCalsaBackendFactory factory = ALCALSABACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} diff --git a/libs/openal-soft/Alc/backends/base.c b/libs/openal-soft/Alc/backends/base.c new file mode 100644 index 00000000..37e4ccc9 --- /dev/null +++ b/libs/openal-soft/Alc/backends/base.c @@ -0,0 +1,232 @@ + +#include "config.h" + +#include + +#include "alMain.h" + +#include "backends/base.h" + + +/* Base ALCbackend method implementations. */ +void ALCbackend_Construct(ALCbackend *self, ALCdevice *device) +{ + int ret; + self->mDevice = device; + ret = almtx_init(&self->mMutex, almtx_recursive); + assert(ret == althrd_success); +} + +void ALCbackend_Destruct(ALCbackend *self) +{ + almtx_destroy(&self->mMutex); +} + +ALCboolean ALCbackend_reset(ALCbackend* UNUSED(self)) +{ + return ALC_FALSE; +} + +ALCenum ALCbackend_captureSamples(ALCbackend* UNUSED(self), void* UNUSED(buffer), ALCuint UNUSED(samples)) +{ + return ALC_INVALID_DEVICE; +} + +ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self)) +{ + return 0; +} + +ALint64 ALCbackend_getLatency(ALCbackend* UNUSED(self)) +{ + return 0; +} + +void ALCbackend_lock(ALCbackend *self) +{ + int ret = almtx_lock(&self->mMutex); + assert(ret == althrd_success); +} + +void ALCbackend_unlock(ALCbackend *self) +{ + int ret = almtx_unlock(&self->mMutex); + assert(ret == althrd_success); +} + + +/* Base ALCbackendFactory method implementations. */ +void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self)) +{ +} + + +/* Wrappers to use an old-style backend with the new interface. */ +typedef struct PlaybackWrapper { + DERIVE_FROM_TYPE(ALCbackend); + + const BackendFuncs *Funcs; +} PlaybackWrapper; + +static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs); +static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct) +static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name); +static void PlaybackWrapper_close(PlaybackWrapper *self); +static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self); +static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self); +static void PlaybackWrapper_stop(PlaybackWrapper *self); +static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples) +static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self); +static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock) +static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper) +DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper); + +static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(PlaybackWrapper, ALCbackend, self); + + self->Funcs = funcs; +} + +static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return self->Funcs->OpenPlayback(device, name); +} + +static void PlaybackWrapper_close(PlaybackWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + self->Funcs->ClosePlayback(device); +} + +static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return self->Funcs->ResetPlayback(device); +} + +static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return self->Funcs->StartPlayback(device); +} + +static void PlaybackWrapper_stop(PlaybackWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + self->Funcs->StopPlayback(device); +} + +static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return self->Funcs->GetLatency(device); +} + + +typedef struct CaptureWrapper { + DERIVE_FROM_TYPE(ALCbackend); + + const BackendFuncs *Funcs; +} CaptureWrapper; + +static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs); +static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct) +static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name); +static void CaptureWrapper_close(CaptureWrapper *self); +static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset) +static ALCboolean CaptureWrapper_start(CaptureWrapper *self); +static void CaptureWrapper_stop(CaptureWrapper *self); +static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples); +static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self); +static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self); +static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock) +static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper) +DEFINE_ALCBACKEND_VTABLE(CaptureWrapper); + + +static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(CaptureWrapper, ALCbackend, self); + + self->Funcs = funcs; +} + +static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return self->Funcs->OpenCapture(device, name); +} + +static void CaptureWrapper_close(CaptureWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + self->Funcs->CloseCapture(device); +} + +static ALCboolean CaptureWrapper_start(CaptureWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + self->Funcs->StartCapture(device); + return ALC_TRUE; +} + +static void CaptureWrapper_stop(CaptureWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + self->Funcs->StopCapture(device); +} + +static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return self->Funcs->CaptureSamples(device, buffer, samples); +} + +static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return self->Funcs->AvailableSamples(device); +} + +static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return self->Funcs->GetLatency(device); +} + + +ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + PlaybackWrapper *backend; + + backend = PlaybackWrapper_New(sizeof(*backend)); + if(!backend) return NULL; + + PlaybackWrapper_Construct(backend, device, funcs); + + return STATIC_CAST(ALCbackend, backend); + } + + if(type == ALCbackend_Capture) + { + CaptureWrapper *backend; + + backend = CaptureWrapper_New(sizeof(*backend)); + if(!backend) return NULL; + + CaptureWrapper_Construct(backend, device, funcs); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} diff --git a/libs/openal-soft/Alc/backends/base.h b/libs/openal-soft/Alc/backends/base.h new file mode 100644 index 00000000..5573c178 --- /dev/null +++ b/libs/openal-soft/Alc/backends/base.h @@ -0,0 +1,133 @@ +#ifndef AL_BACKENDS_BASE_H +#define AL_BACKENDS_BASE_H + +#include "alMain.h" +#include "threads.h" + + +struct ALCbackendVtable; + +typedef struct ALCbackend { + const struct ALCbackendVtable *vtbl; + + ALCdevice *mDevice; + + almtx_t mMutex; +} ALCbackend; + +void ALCbackend_Construct(ALCbackend *self, ALCdevice *device); +void ALCbackend_Destruct(ALCbackend *self); +ALCboolean ALCbackend_reset(ALCbackend *self); +ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples); +ALCuint ALCbackend_availableSamples(ALCbackend *self); +ALint64 ALCbackend_getLatency(ALCbackend *self); +void ALCbackend_lock(ALCbackend *self); +void ALCbackend_unlock(ALCbackend *self); + +struct ALCbackendVtable { + void (*const Destruct)(ALCbackend*); + + ALCenum (*const open)(ALCbackend*, const ALCchar*); + void (*const close)(ALCbackend*); + + ALCboolean (*const reset)(ALCbackend*); + ALCboolean (*const start)(ALCbackend*); + void (*const stop)(ALCbackend*); + + ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint); + ALCuint (*const availableSamples)(ALCbackend*); + + ALint64 (*const getLatency)(ALCbackend*); + + void (*const lock)(ALCbackend*); + void (*const unlock)(ALCbackend*); + + void (*const Delete)(void*); +}; + +#define DEFINE_ALCBACKEND_VTABLE(T) \ +DECLARE_THUNK(T, ALCbackend, void, Destruct) \ +DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \ +DECLARE_THUNK(T, ALCbackend, void, close) \ +DECLARE_THUNK(T, ALCbackend, ALCboolean, reset) \ +DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \ +DECLARE_THUNK(T, ALCbackend, void, stop) \ +DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint) \ +DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \ +DECLARE_THUNK(T, ALCbackend, ALint64, getLatency) \ +DECLARE_THUNK(T, ALCbackend, void, lock) \ +DECLARE_THUNK(T, ALCbackend, void, unlock) \ +static void T##_ALCbackend_Delete(void *ptr) \ +{ T##_Delete(STATIC_UPCAST(T, ALCbackend, (ALCbackend*)ptr)); } \ + \ +static const struct ALCbackendVtable T##_ALCbackend_vtable = { \ + T##_ALCbackend_Destruct, \ + \ + T##_ALCbackend_open, \ + T##_ALCbackend_close, \ + T##_ALCbackend_reset, \ + T##_ALCbackend_start, \ + T##_ALCbackend_stop, \ + T##_ALCbackend_captureSamples, \ + T##_ALCbackend_availableSamples, \ + T##_ALCbackend_getLatency, \ + T##_ALCbackend_lock, \ + T##_ALCbackend_unlock, \ + \ + T##_ALCbackend_Delete, \ +} + + +typedef enum ALCbackend_Type { + ALCbackend_Playback, + ALCbackend_Capture, + ALCbackend_Loopback +} ALCbackend_Type; + + +struct ALCbackendFactoryVtable; + +typedef struct ALCbackendFactory { + const struct ALCbackendFactoryVtable *vtbl; +} ALCbackendFactory; + +void ALCbackendFactory_deinit(ALCbackendFactory *self); + +struct ALCbackendFactoryVtable { + ALCboolean (*const init)(ALCbackendFactory *self); + void (*const deinit)(ALCbackendFactory *self); + + ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type); + + void (*const probe)(ALCbackendFactory *self, enum DevProbe type); + + ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type); +}; + +#define DEFINE_ALCBACKENDFACTORY_VTABLE(T) \ +DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init) \ +DECLARE_THUNK(T, ALCbackendFactory, void, deinit) \ +DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \ +DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe) \ +DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \ + \ +static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \ + T##_ALCbackendFactory_init, \ + T##_ALCbackendFactory_deinit, \ + T##_ALCbackendFactory_querySupport, \ + T##_ALCbackendFactory_probe, \ + T##_ALCbackendFactory_createBackend, \ +} + + +ALCbackendFactory *ALCpulseBackendFactory_getFactory(void); +ALCbackendFactory *ALCalsaBackendFactory_getFactory(void); +ALCbackendFactory *ALCossBackendFactory_getFactory(void); +ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void); +ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void); +ALCbackendFactory *ALCnullBackendFactory_getFactory(void); +ALCbackendFactory *ALCloopbackFactory_getFactory(void); + +ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type); + +#endif /* AL_BACKENDS_BASE_H */ diff --git a/libs/openal-soft/Alc/backends/coreaudio.c b/libs/openal-soft/Alc/backends/coreaudio.c new file mode 100644 index 00000000..b3583ffd --- /dev/null +++ b/libs/openal-soft/Alc/backends/coreaudio.c @@ -0,0 +1,707 @@ +/** + * 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 "alu.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 ALCenum 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_INVALID_VALUE; + + /* 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_INVALID_VALUE; + } + + data = calloc(1, sizeof(*data)); + + err = OpenAComponent(comp, &data->audioUnit); + if(err != noErr) + { + ERR("OpenAComponent failed\n"); + free(data); + return ALC_INVALID_VALUE; + } + + /* init and start the default audio unit... */ + err = AudioUnitInitialize(data->audioUnit); + if(err != noErr) + { + ERR("AudioUnitInitialize failed\n"); + CloseComponent(data->audioUnit); + free(data); + return ALC_INVALID_VALUE; + } + + al_string_copy_cstr(&device->DeviceName, deviceName); + device->ExtraData = data; + return ALC_NO_ERROR; +} + +static void ca_close_playback(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + + AudioUnitUninitialize(data->audioUnit); + 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; + + err = AudioUnitUninitialize(data->audioUnit); + if(err != noErr) + ERR("-- AudioUnitUninitialize failed.\n"); + + /* 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) + { + 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: + device->FmtChans = DevFmtMono; + break; + case 2: + device->FmtChans = DevFmtStereo; + break; + case 4: + device->FmtChans = DevFmtQuad; + break; + case 6: + device->FmtChans = DevFmtX51; + break; + case 7: + device->FmtChans = DevFmtX61; + break; + case 8: + device->FmtChans = DevFmtX71; + break; + default: + ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame); + 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; + streamFormat.mFormatFlags = 0; + switch(device->FmtType) + { + case DevFmtUByte: + device->FmtType = DevFmtByte; + /* fall-through */ + case DevFmtByte: + streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; + streamFormat.mBitsPerChannel = 8; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; + streamFormat.mBitsPerChannel = 16; + break; + case DevFmtUInt: + device->FmtType = DevFmtInt; + /* fall-through */ + case DevFmtInt: + streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; + streamFormat.mBitsPerChannel = 32; + break; + case DevFmtFloat: + streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat; + streamFormat.mBitsPerChannel = 32; + break; + } + streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame * + streamFormat.mBitsPerChannel / 8; + streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame; + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags |= 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; + } + + /* init the default audio unit... */ + err = AudioUnitInitialize(data->audioUnit); + if(err != noErr) + { + ERR("AudioUnitInitialize failed\n"); + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static ALCboolean ca_start_playback(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + OSStatus err; + + err = AudioOutputUnitStart(data->audioUnit); + if(err != noErr) + { + ERR("AudioOutputUnitStart failed\n"); + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void ca_stop_playback(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + OSStatus err; + + err = AudioOutputUnitStop(data->audioUnit); + if(err != noErr) + ERR("AudioOutputUnitStop failed\n"); +} + +static ALCenum 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_INVALID_VALUE; + } + + 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 DevFmtInt: + requestedFormat.mBitsPerChannel = 32; + requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; + break; + case DevFmtFloat: + requestedFormat.mBitsPerChannel = 32; + requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked; + break; + case DevFmtByte: + case DevFmtUShort: + case DevFmtUInt: + 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) + goto error; + + data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates); + if(data->ring == NULL) + goto error; + + al_string_copy_cstr(&device->DeviceName, deviceName); + + return ALC_NO_ERROR; + +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_INVALID_VALUE; +} + +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 ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) +{ + ca_data *data = (ca_data*)device->ExtraData; + AudioBufferList *list; + UInt32 frameCount; + OSStatus err; + + // If no samples are requested, just return + if(samples == 0) + return ALC_NO_ERROR; + + // 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); + return ALC_INVALID_VALUE; + } + return ALC_NO_ERROR; +} + +static ALCuint ca_available_samples(ALCdevice *device) +{ + ca_data *data = device->ExtraData; + return RingBufferSize(data->ring) / data->sampleRateRatio; +} + + +static const BackendFuncs ca_funcs = { + ca_open_playback, + ca_close_playback, + ca_reset_playback, + ca_start_playback, + ca_stop_playback, + ca_open_capture, + ca_close_capture, + ca_start_capture, + ca_stop_capture, + ca_capture_samples, + ca_available_samples, + ALCdevice_GetLatencyDefault +}; + +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 ALL_DEVICE_PROBE: + AppendAllDevicesList(ca_device); + break; + case CAPTURE_DEVICE_PROBE: + AppendCaptureDeviceList(ca_device); + break; + } +} diff --git a/libs/openal-soft/Alc/backends/dsound.c b/libs/openal-soft/Alc/backends/dsound.c new file mode 100644 index 00000000..2229347e --- /dev/null +++ b/libs/openal-soft/Alc/backends/dsound.c @@ -0,0 +1,1054 @@ +/** + * 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 +#ifndef _WAVEFORMATEXTENSIBLE_ +#include +#include +#endif + +#include "alMain.h" +#include "alu.h" +#include "threads.h" +#include "compat.h" +#include "alstring.h" + +#include "backends/base.h" + +#ifndef DSSPEAKER_5POINT1 +# define DSSPEAKER_5POINT1 0x00000006 +#endif +#ifndef DSSPEAKER_7POINT1 +# define DSSPEAKER_7POINT1 0x00000007 +#endif +#ifndef DSSPEAKER_7POINT1_SURROUND +# define DSSPEAKER_7POINT1_SURROUND 0x00000008 +#endif +#ifndef DSSPEAKER_5POINT1_SURROUND +# define DSSPEAKER_5POINT1_SURROUND 0x00000009 +#endif + + +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); + + +#ifdef HAVE_DYNLOAD +static void *ds_handle; +static HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter); +static HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext); +static HRESULT (WINAPI *pDirectSoundCaptureCreate)(const GUID *pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter); +static HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext); + +#define DirectSoundCreate pDirectSoundCreate +#define DirectSoundEnumerateW pDirectSoundEnumerateW +#define DirectSoundCaptureCreate pDirectSoundCaptureCreate +#define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW +#endif + + +static ALCboolean DSoundLoad(void) +{ +#ifdef HAVE_DYNLOAD + if(!ds_handle) + { + ds_handle = LoadLib("dsound.dll"); + if(ds_handle == NULL) + { + ERR("Failed to load dsound.dll\n"); + return ALC_FALSE; + } + +#define LOAD_FUNC(f) do { \ + p##f = GetSymbol(ds_handle, #f); \ + if(p##f == NULL) { \ + CloseLib(ds_handle); \ + ds_handle = NULL; \ + return ALC_FALSE; \ + } \ +} while(0) + LOAD_FUNC(DirectSoundCreate); + LOAD_FUNC(DirectSoundEnumerateW); + LOAD_FUNC(DirectSoundCaptureCreate); + LOAD_FUNC(DirectSoundCaptureEnumerateW); +#undef LOAD_FUNC + } +#endif + return ALC_TRUE; +} + + +#define MAX_UPDATES 128 + +typedef struct { + al_string name; + GUID guid; +} DevMap; +TYPEDEF_VECTOR(DevMap, vector_DevMap) + +static vector_DevMap PlaybackDevices; +static vector_DevMap CaptureDevices; + +static void clear_devlist(vector_DevMap *list) +{ +#define DEINIT_STR(i) AL_STRING_DEINIT((i)->name) + VECTOR_FOR_EACH(DevMap, *list, DEINIT_STR); +#undef DEINIT_STR + VECTOR_RESIZE(*list, 0); +} + +static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR* UNUSED(drvname), void *data) +{ + vector_DevMap *devices = data; + OLECHAR *guidstr = NULL; + DevMap *iter, *end; + DevMap entry; + HRESULT hr; + int count; + + if(!guid) + return TRUE; + + AL_STRING_INIT(entry.name); + + count = 0; + do { + al_string_copy_wcstr(&entry.name, desc); + if(count != 0) + { + char str[64]; + snprintf(str, sizeof(str), " #%d", count+1); + al_string_append_cstr(&entry.name, str); + } + count++; + + iter = VECTOR_ITER_BEGIN(*devices); + end = VECTOR_ITER_END(*devices); + for(;iter != end;++iter) + { + if(al_string_cmp(entry.name, iter->name) == 0) + break; + } + } while(iter != end); + entry.guid = *guid; + + hr = StringFromCLSID(guid, &guidstr); + if(SUCCEEDED(hr)) + { + TRACE("Got device \"%s\", GUID \"%ls\"\n", al_string_get_cstr(entry.name), guidstr); + CoTaskMemFree(guidstr); + } + + VECTOR_PUSH_BACK(*devices, entry); + + return TRUE; +} + + +typedef struct ALCdsoundPlayback { + DERIVE_FROM_TYPE(ALCbackend); + + IDirectSound *DS; + IDirectSoundBuffer *PrimaryBuffer; + IDirectSoundBuffer *Buffer; + IDirectSoundNotify *Notifies; + HANDLE NotifyEvent; + + volatile int killNow; + althrd_t thread; +} ALCdsoundPlayback; + +static int ALCdsoundPlayback_mixerProc(void *ptr); + +static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device); +static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, Destruct) +static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *name); +static void ALCdsoundPlayback_close(ALCdsoundPlayback *self); +static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self); +static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self); +static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self); +static DECLARE_FORWARD2(ALCdsoundPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALCuint, availableSamples) +static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback) + +DEFINE_ALCBACKEND_VTABLE(ALCdsoundPlayback); + + +static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCdsoundPlayback, ALCbackend, self); +} + + +FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr) +{ + ALCdsoundPlayback *self = ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + 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(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + memset(&DSBCaps, 0, sizeof(DSBCaps)); + DSBCaps.dwSize = sizeof(DSBCaps); + err = IDirectSoundBuffer_GetCaps(self->Buffer, &DSBCaps); + if(FAILED(err)) + { + ERR("Failed to get buffer caps: 0x%lx\n", err); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); + return 1; + } + + FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + FragSize = device->UpdateSize * FrameSize; + + IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &LastCursor, NULL); + while(!self->killNow) + { + // Get current play cursor + IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &PlayCursor, NULL); + avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes; + + if(avail < FragSize) + { + if(!Playing) + { + err = IDirectSoundBuffer_Play(self->Buffer, 0, 0, DSBPLAY_LOOPING); + if(FAILED(err)) + { + ERR("Failed to play buffer: 0x%lx\n", err); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); + return 1; + } + Playing = TRUE; + } + + avail = WaitForSingleObjectEx(self->NotifyEvent, 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(self->Buffer, 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(self->Buffer); + if(SUCCEEDED(err)) + { + Playing = FALSE; + LastCursor = 0; + err = IDirectSoundBuffer_Lock(self->Buffer, 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(device, WritePtr1, WriteCnt1/FrameSize); + aluMixData(device, WritePtr2, WriteCnt2/FrameSize); + + // Unlock output buffer only when successfully locked + IDirectSoundBuffer_Unlock(self->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2); + } + else + { + ERR("Buffer lock error: %#lx\n", err); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); + return 1; + } + + // Update old write cursor location + LastCursor += WriteCnt1+WriteCnt2; + LastCursor %= DSBCaps.dwBufferBytes; + } + + return 0; +} + +static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *deviceName) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + const GUID *guid = NULL; + HRESULT hr, hrcom; + + if(VECTOR_SIZE(PlaybackDevices) == 0) + { + /* Initialize COM to prevent name truncation */ + hrcom = CoInitialize(NULL); + hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices); + if(FAILED(hr)) + ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr); + if(SUCCEEDED(hrcom)) + CoUninitialize(); + } + + if(!deviceName && VECTOR_SIZE(PlaybackDevices) > 0) + { + deviceName = al_string_get_cstr(VECTOR_FRONT(PlaybackDevices).name); + guid = &VECTOR_FRONT(PlaybackDevices).guid; + } + else + { + const DevMap *iter; + +#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0) + VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME); +#undef MATCH_NAME + if(iter == VECTOR_ITER_END(PlaybackDevices)) + return ALC_INVALID_VALUE; + guid = &iter->guid; + } + + hr = DS_OK; + self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(self->NotifyEvent == NULL) + hr = E_FAIL; + + //DirectSound Init code + if(SUCCEEDED(hr)) + hr = DirectSoundCreate(guid, &self->DS, NULL); + if(SUCCEEDED(hr)) + hr = IDirectSound_SetCooperativeLevel(self->DS, GetForegroundWindow(), DSSCL_PRIORITY); + if(FAILED(hr)) + { + if(self->DS) + IDirectSound_Release(self->DS); + self->DS = NULL; + if(self->NotifyEvent) + CloseHandle(self->NotifyEvent); + self->NotifyEvent = NULL; + + ERR("Device init failed: 0x%08lx\n", hr); + return ALC_INVALID_VALUE; + } + + al_string_copy_cstr(&device->DeviceName, deviceName); + + return ALC_NO_ERROR; +} + +static void ALCdsoundPlayback_close(ALCdsoundPlayback *self) +{ + if(self->Notifies) + IDirectSoundNotify_Release(self->Notifies); + self->Notifies = NULL; + if(self->Buffer) + IDirectSoundBuffer_Release(self->Buffer); + self->Buffer = NULL; + if(self->PrimaryBuffer != NULL) + IDirectSoundBuffer_Release(self->PrimaryBuffer); + self->PrimaryBuffer = NULL; + + IDirectSound_Release(self->DS); + self->DS = NULL; + CloseHandle(self->NotifyEvent); + self->NotifyEvent = NULL; +} + +static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + DSBUFFERDESC DSBDescription; + WAVEFORMATEXTENSIBLE OutputType; + DWORD speakers; + HRESULT hr; + + memset(&OutputType, 0, sizeof(OutputType)); + + if(self->Notifies) + IDirectSoundNotify_Release(self->Notifies); + self->Notifies = NULL; + if(self->Buffer) + IDirectSoundBuffer_Release(self->Buffer); + self->Buffer = NULL; + if(self->PrimaryBuffer != NULL) + IDirectSoundBuffer_Release(self->PrimaryBuffer); + self->PrimaryBuffer = NULL; + + switch(device->FmtType) + { + case DevFmtByte: + device->FmtType = DevFmtUByte; + break; + case DevFmtFloat: + if((device->Flags&DEVICE_SAMPLE_TYPE_REQUEST)) + break; + /* fall-through */ + case DevFmtUShort: + device->FmtType = DevFmtShort; + break; + case DevFmtUInt: + device->FmtType = DevFmtInt; + break; + case DevFmtUByte: + case DevFmtShort: + case DevFmtInt: + break; + } + + hr = IDirectSound_GetSpeakerConfig(self->DS, &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 || speakers == DSSPEAKER_5POINT1_SURROUND) + device->FmtChans = DevFmtX51; + else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND) + 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; + } + +retry_open: + hr = S_OK; + 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; + + if(self->PrimaryBuffer) + IDirectSoundBuffer_Release(self->PrimaryBuffer); + self->PrimaryBuffer = NULL; + } + else + { + if(SUCCEEDED(hr) && !self->PrimaryBuffer) + { + memset(&DSBDescription,0,sizeof(DSBUFFERDESC)); + DSBDescription.dwSize=sizeof(DSBUFFERDESC); + DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER; + hr = IDirectSound_CreateSoundBuffer(self->DS, &DSBDescription, &self->PrimaryBuffer, NULL); + } + if(SUCCEEDED(hr)) + hr = IDirectSoundBuffer_SetFormat(self->PrimaryBuffer,&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(self->DS, &DSBDescription, &self->Buffer, NULL); + if(FAILED(hr) && device->FmtType == DevFmtFloat) + { + device->FmtType = DevFmtShort; + goto retry_open; + } + } + + if(SUCCEEDED(hr)) + { + hr = IDirectSoundBuffer_QueryInterface(self->Buffer, &IID_IDirectSoundNotify, (void**)&self->Notifies); + 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 = self->NotifyEvent; + } + if(IDirectSoundNotify_SetNotificationPositions(self->Notifies, device->NumUpdates, notifies) != DS_OK) + hr = E_FAIL; + } + } + + if(FAILED(hr)) + { + if(self->Notifies != NULL) + IDirectSoundNotify_Release(self->Notifies); + self->Notifies = NULL; + if(self->Buffer != NULL) + IDirectSoundBuffer_Release(self->Buffer); + self->Buffer = NULL; + if(self->PrimaryBuffer != NULL) + IDirectSoundBuffer_Release(self->PrimaryBuffer); + self->PrimaryBuffer = NULL; + return ALC_FALSE; + } + + ResetEvent(self->NotifyEvent); + SetDefaultWFXChannelOrder(device); + + return ALC_TRUE; +} + +static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self) +{ + self->killNow = 0; + if(althrd_create(&self->thread, ALCdsoundPlayback_mixerProc, self) != althrd_success) + return ALC_FALSE; + + return ALC_TRUE; +} + +static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self) +{ + int res; + + if(self->killNow) + return; + + self->killNow = 1; + althrd_join(self->thread, &res); + + IDirectSoundBuffer_Stop(self->Buffer); +} + + + +typedef struct ALCdsoundCapture { + DERIVE_FROM_TYPE(ALCbackend); + + IDirectSoundCapture *DSC; + IDirectSoundCaptureBuffer *DSCbuffer; + DWORD BufferBytes; + DWORD Cursor; + RingBuffer *Ring; +} ALCdsoundCapture; + +static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device); +static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, Destruct) +static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *name); +static void ALCdsoundCapture_close(ALCdsoundCapture *self); +static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALCboolean, reset) +static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self); +static void ALCdsoundCapture_stop(ALCdsoundCapture *self); +static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples); +static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self); +static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture) + +DEFINE_ALCBACKEND_VTABLE(ALCdsoundCapture); + +static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCdsoundCapture, ALCbackend, self); +} + + +static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *deviceName) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + WAVEFORMATEXTENSIBLE InputType; + DSCBUFFERDESC DSCBDescription; + const GUID *guid = NULL; + HRESULT hr, hrcom; + ALuint samples; + + if(VECTOR_SIZE(CaptureDevices) == 0) + { + /* Initialize COM to prevent name truncation */ + hrcom = CoInitialize(NULL); + hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices); + if(FAILED(hr)) + ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr); + if(SUCCEEDED(hrcom)) + CoUninitialize(); + } + + if(!deviceName && VECTOR_SIZE(CaptureDevices) > 0) + { + deviceName = al_string_get_cstr(VECTOR_FRONT(CaptureDevices).name); + guid = &VECTOR_FRONT(CaptureDevices).guid; + } + else + { + const DevMap *iter; + +#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0) + VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME); +#undef MATCH_NAME + if(iter == VECTOR_ITER_END(CaptureDevices)) + return ALC_INVALID_VALUE; + guid = &iter->guid; + } + + switch(device->FmtType) + { + case DevFmtByte: + case DevFmtUShort: + case DevFmtUInt: + WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType)); + return ALC_INVALID_ENUM; + + case DevFmtUByte: + case DevFmtShort: + case DevFmtInt: + case DevFmtFloat: + break; + } + + //DirectSoundCapture Init code + hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL); + if(SUCCEEDED(hr)) + { + memset(&InputType, 0, sizeof(InputType)); + + switch(device->FmtChans) + { + case DevFmtMono: + InputType.dwChannelMask = SPEAKER_FRONT_CENTER; + break; + case DevFmtStereo: + InputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT; + break; + case DevFmtQuad: + InputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT; + break; + case DevFmtX51: + InputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT; + break; + case DevFmtX51Side: + InputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT; + break; + case DevFmtX61: + InputType.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: + InputType.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; + } + + InputType.Format.wFormatTag = WAVE_FORMAT_PCM; + InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans); + InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8; + InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8; + InputType.Format.nSamplesPerSec = device->Frequency; + InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign; + InputType.Format.cbSize = 0; + + if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat) + { + InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample; + if(device->FmtType == DevFmtFloat) + InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + else + InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + } + + samples = device->UpdateSize * device->NumUpdates; + samples = maxu(samples, 100 * device->Frequency / 1000); + + memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC)); + DSCBDescription.dwSize = sizeof(DSCBUFFERDESC); + DSCBDescription.dwFlags = 0; + DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign; + DSCBDescription.lpwfxFormat = &InputType.Format; + + hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL); + } + if(SUCCEEDED(hr)) + { + self->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates); + if(self->Ring == NULL) + hr = DSERR_OUTOFMEMORY; + } + + if(FAILED(hr)) + { + ERR("Device init failed: 0x%08lx\n", hr); + + DestroyRingBuffer(self->Ring); + self->Ring = NULL; + if(self->DSCbuffer != NULL) + IDirectSoundCaptureBuffer_Release(self->DSCbuffer); + self->DSCbuffer = NULL; + if(self->DSC) + IDirectSoundCapture_Release(self->DSC); + self->DSC = NULL; + + return ALC_INVALID_VALUE; + } + + self->BufferBytes = DSCBDescription.dwBufferBytes; + SetDefaultWFXChannelOrder(device); + + al_string_copy_cstr(&device->DeviceName, deviceName); + + return ALC_NO_ERROR; +} + +static void ALCdsoundCapture_close(ALCdsoundCapture *self) +{ + DestroyRingBuffer(self->Ring); + self->Ring = NULL; + + if(self->DSCbuffer != NULL) + { + IDirectSoundCaptureBuffer_Stop(self->DSCbuffer); + IDirectSoundCaptureBuffer_Release(self->DSCbuffer); + self->DSCbuffer = NULL; + } + + IDirectSoundCapture_Release(self->DSC); + self->DSC = NULL; +} + +static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self) +{ + HRESULT hr; + + hr = IDirectSoundCaptureBuffer_Start(self->DSCbuffer, DSCBSTART_LOOPING); + if(FAILED(hr)) + { + ERR("start failed: 0x%08lx\n", hr); + aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice); + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void ALCdsoundCapture_stop(ALCdsoundCapture *self) +{ + HRESULT hr; + + hr = IDirectSoundCaptureBuffer_Stop(self->DSCbuffer); + if(FAILED(hr)) + { + ERR("stop failed: 0x%08lx\n", hr); + aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice); + } +} + +static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples) +{ + ReadRingBuffer(self->Ring, buffer, samples); + return ALC_NO_ERROR; +} + +static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + DWORD ReadCursor, LastCursor, BufferBytes, NumBytes; + void *ReadPtr1, *ReadPtr2; + DWORD ReadCnt1, ReadCnt2; + DWORD FrameSize; + HRESULT hr; + + if(!device->Connected) + goto done; + + FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + BufferBytes = self->BufferBytes; + LastCursor = self->Cursor; + + hr = IDirectSoundCaptureBuffer_GetCurrentPosition(self->DSCbuffer, NULL, &ReadCursor); + if(SUCCEEDED(hr)) + { + NumBytes = (ReadCursor-LastCursor + BufferBytes) % BufferBytes; + if(NumBytes == 0) + goto done; + hr = IDirectSoundCaptureBuffer_Lock(self->DSCbuffer, LastCursor, NumBytes, + &ReadPtr1, &ReadCnt1, + &ReadPtr2, &ReadCnt2, 0); + } + if(SUCCEEDED(hr)) + { + WriteRingBuffer(self->Ring, ReadPtr1, ReadCnt1/FrameSize); + if(ReadPtr2 != NULL) + WriteRingBuffer(self->Ring, ReadPtr2, ReadCnt2/FrameSize); + hr = IDirectSoundCaptureBuffer_Unlock(self->DSCbuffer, + ReadPtr1, ReadCnt1, + ReadPtr2, ReadCnt2); + self->Cursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes; + } + + if(FAILED(hr)) + { + ERR("update failed: 0x%08lx\n", hr); + aluHandleDisconnect(device); + } + +done: + return RingBufferSize(self->Ring); +} + + +static inline void AppendAllDevicesList2(const DevMap *entry) +{ AppendAllDevicesList(al_string_get_cstr(entry->name)); } +static inline void AppendCaptureDeviceList2(const DevMap *entry) +{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); } + +typedef struct ALCdsoundBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCdsoundBackendFactory; +#define ALCDSOUNDBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCdsoundBackendFactory, ALCbackendFactory) } } + +ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void); + +static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory *self); +static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory *self); +static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory *self, ALCbackend_Type type); +static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory); + + +ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void) +{ + static ALCdsoundBackendFactory factory = ALCDSOUNDBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} + + +static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory* UNUSED(self)) +{ + VECTOR_INIT(PlaybackDevices); + VECTOR_INIT(CaptureDevices); + + if(!DSoundLoad()) + return ALC_FALSE; + return ALC_TRUE; +} + +static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory* UNUSED(self)) +{ + clear_devlist(&PlaybackDevices); + VECTOR_DEINIT(PlaybackDevices); + + clear_devlist(&CaptureDevices); + VECTOR_DEINIT(CaptureDevices); + +#ifdef HAVE_DYNLOAD + if(ds_handle) + CloseLib(ds_handle); + ds_handle = NULL; +#endif +} + +static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback || type == ALCbackend_Capture) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type) +{ + HRESULT hr, hrcom; + + /* Initialize COM to prevent name truncation */ + hrcom = CoInitialize(NULL); + switch(type) + { + case ALL_DEVICE_PROBE: + clear_devlist(&PlaybackDevices); + hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices); + if(FAILED(hr)) + ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr); + VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2); + break; + + case CAPTURE_DEVICE_PROBE: + clear_devlist(&CaptureDevices); + hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices); + if(FAILED(hr)) + ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr); + VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2); + break; + } + if(SUCCEEDED(hrcom)) + CoUninitialize(); +} + +static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCdsoundPlayback *backend; + + backend = ALCdsoundPlayback_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCdsoundPlayback_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + if(type == ALCbackend_Capture) + { + ALCdsoundCapture *backend; + + backend = ALCdsoundCapture_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCdsoundCapture_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} diff --git a/libs/openal-soft/Alc/backends/loopback.c b/libs/openal-soft/Alc/backends/loopback.c new file mode 100644 index 00000000..505dfacf --- /dev/null +++ b/libs/openal-soft/Alc/backends/loopback.c @@ -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 "alu.h" + +#include "backends/base.h" + + +typedef struct ALCloopback { + DERIVE_FROM_TYPE(ALCbackend); +} ALCloopback; + +static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device); +static DECLARE_FORWARD(ALCloopback, ALCbackend, void, Destruct) +static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name); +static void ALCloopback_close(ALCloopback *self); +static ALCboolean ALCloopback_reset(ALCloopback *self); +static ALCboolean ALCloopback_start(ALCloopback *self); +static void ALCloopback_stop(ALCloopback *self); +static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples) +static DECLARE_FORWARD(ALCloopback, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCloopback) +DEFINE_ALCBACKEND_VTABLE(ALCloopback); + + +static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCloopback, ALCbackend, self); +} + + +static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + + al_string_copy_cstr(&device->DeviceName, name); + return ALC_NO_ERROR; +} + +static void ALCloopback_close(ALCloopback* UNUSED(self)) +{ +} + +static ALCboolean ALCloopback_reset(ALCloopback *self) +{ + SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice); + return ALC_TRUE; +} + +static ALCboolean ALCloopback_start(ALCloopback* UNUSED(self)) +{ + return ALC_TRUE; +} + +static void ALCloopback_stop(ALCloopback* UNUSED(self)) +{ +} + + +typedef struct ALCloopbackFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCloopbackFactory; +#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCloopbackFactory, ALCbackendFactory) } } + +ALCbackendFactory *ALCloopbackFactory_getFactory(void); +static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self); +static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit) +static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type); +static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type); +static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory); + + +ALCbackendFactory *ALCloopbackFactory_getFactory(void) +{ + static ALCloopbackFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} + +static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory* UNUSED(self)) +{ + return ALC_TRUE; +} + +static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Loopback) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type)) +{ +} + +static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Loopback) + { + ALCloopback *backend; + + backend = ALCloopback_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCloopback_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} diff --git a/libs/openal-soft/Alc/backends/mmdevapi.c b/libs/openal-soft/Alc/backends/mmdevapi.c new file mode 100644 index 00000000..430e1cb8 --- /dev/null +++ b/libs/openal-soft/Alc/backends/mmdevapi.c @@ -0,0 +1,1183 @@ +/** + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _WAVEFORMATEXTENSIBLE_ +#include +#include +#endif + +#include "alMain.h" +#include "alu.h" +#include "threads.h" +#include "compat.h" +#include "alstring.h" + +#include "backends/base.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_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14); + +#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 { + al_string name; + WCHAR *devid; +} DevMap; +TYPEDEF_VECTOR(DevMap, vector_DevMap) + +static void clear_devlist(vector_DevMap *list) +{ + DevMap *iter, *end; + + iter = VECTOR_ITER_BEGIN(*list); + end = VECTOR_ITER_END(*list); + for(;iter != end;iter++) + { + AL_STRING_DEINIT(iter->name); + free(iter->devid); + } + VECTOR_RESIZE(*list, 0); +} + +static vector_DevMap PlaybackDevices; +static vector_DevMap CaptureDevices; + + +static HANDLE ThreadHdl; +static DWORD ThreadID; + +typedef struct { + HANDLE FinishedEvt; + HRESULT result; +} ThreadRequest; + +#define WM_USER_First (WM_USER+0) +#define WM_USER_OpenDevice (WM_USER+0) +#define WM_USER_ResetDevice (WM_USER+1) +#define WM_USER_StartDevice (WM_USER+2) +#define WM_USER_StopDevice (WM_USER+3) +#define WM_USER_CloseDevice (WM_USER+4) +#define WM_USER_Enumerate (WM_USER+5) +#define WM_USER_Last (WM_USER+5) + +static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res) +{ + req->result = res; + SetEvent(req->FinishedEvt); +} + +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 void get_device_name(IMMDevice *device, al_string *name) +{ + IPropertyStore *ps; + PROPVARIANT pvname; + HRESULT hr; + + hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps); + if(FAILED(hr)) + { + WARN("OpenPropertyStore failed: 0x%08lx\n", hr); + return; + } + + PropVariantInit(&pvname); + + hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname); + if(FAILED(hr)) + WARN("GetValue failed: 0x%08lx\n", hr); + else + al_string_copy_wcstr(name, pvname.pwszVal); + + PropVariantClear(&pvname); + IPropertyStore_Release(ps); +} + +static void add_device(IMMDevice *device, vector_DevMap *list) +{ + LPWSTR devid; + HRESULT hr; + + hr = IMMDevice_GetId(device, &devid); + if(SUCCEEDED(hr)) + { + DevMap entry; + AL_STRING_INIT(entry.name); + + entry.devid = strdupW(devid); + get_device_name(device, &entry.name); + + CoTaskMemFree(devid); + + TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), entry.devid); + VECTOR_PUSH_BACK(*list, entry); + } +} + +static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, vector_DevMap *list) +{ + IMMDeviceCollection *coll; + IMMDevice *defdev = NULL; + HRESULT hr; + UINT count; + UINT i; + + hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll); + if(FAILED(hr)) + { + ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr); + return hr; + } + + count = 0; + hr = IMMDeviceCollection_GetCount(coll, &count); + if(SUCCEEDED(hr) && count > 0) + { + clear_devlist(list); + if(!VECTOR_RESERVE(*list, count+1)) + { + IMMDeviceCollection_Release(coll); + return E_OUTOFMEMORY; + } + + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir, + eMultimedia, &defdev); + } + if(SUCCEEDED(hr) && defdev != NULL) + add_device(defdev, list); + + for(i = 0;i < count;++i) + { + IMMDevice *device; + + if(FAILED(IMMDeviceCollection_Item(coll, i, &device))) + continue; + + if(device != defdev) + add_device(device, list); + + IMMDevice_Release(device); + } + + if(defdev) IMMDevice_Release(defdev); + IMMDeviceCollection_Release(coll); + + return S_OK; +} + + +/* Proxy interface used by the message handler. */ +struct ALCmmdevProxyVtable; + +typedef struct ALCmmdevProxy { + const struct ALCmmdevProxyVtable *vtbl; +} ALCmmdevProxy; + +struct ALCmmdevProxyVtable { + HRESULT (*const openProxy)(ALCmmdevProxy*); + void (*const closeProxy)(ALCmmdevProxy*); + + HRESULT (*const resetProxy)(ALCmmdevProxy*); + HRESULT (*const startProxy)(ALCmmdevProxy*); + void (*const stopProxy)(ALCmmdevProxy*); +}; + +#define DEFINE_ALCMMDEVPROXY_VTABLE(T) \ +DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \ +DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \ +DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \ +DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \ +DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \ + \ +static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \ + T##_ALCmmdevProxy_openProxy, \ + T##_ALCmmdevProxy_closeProxy, \ + T##_ALCmmdevProxy_resetProxy, \ + T##_ALCmmdevProxy_startProxy, \ + T##_ALCmmdevProxy_stopProxy, \ +} + +static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { } +static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { } + +static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr) +{ + ThreadRequest *req = ptr; + IMMDeviceEnumerator *Enumerator; + ALuint deviceCount = 0; + ALCmmdevProxy *proxy; + HRESULT hr, cohr; + MSG msg; + + TRACE("Starting message thread\n"); + + cohr = CoInitialize(NULL); + if(FAILED(cohr)) + { + WARN("Failed to initialize COM: 0x%08lx\n", cohr); + ReturnMsgResponse(req, cohr); + 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(); + ReturnMsgResponse(req, hr); + return 0; + } + Enumerator = ptr; + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + + CoUninitialize(); + + /* HACK: Force Windows to create a message queue for this thread before + * returning success, otherwise PostThreadMessage may fail if it gets + * called before GetMessage. + */ + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + TRACE("Message thread initialization complete\n"); + ReturnMsgResponse(req, S_OK); + + TRACE("Starting message loop\n"); + while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last)) + { + TRACE("Got message %u\n", msg.message); + switch(msg.message) + { + case WM_USER_OpenDevice: + req = (ThreadRequest*)msg.wParam; + proxy = (ALCmmdevProxy*)msg.lParam; + + hr = cohr = S_OK; + if(++deviceCount == 1) + hr = cohr = CoInitialize(NULL); + if(SUCCEEDED(hr)) + hr = V0(proxy,openProxy)(); + if(FAILED(hr)) + { + if(--deviceCount == 0 && SUCCEEDED(cohr)) + CoUninitialize(); + } + + ReturnMsgResponse(req, hr); + continue; + + case WM_USER_ResetDevice: + req = (ThreadRequest*)msg.wParam; + proxy = (ALCmmdevProxy*)msg.lParam; + + hr = V0(proxy,resetProxy)(); + ReturnMsgResponse(req, hr); + continue; + + case WM_USER_StartDevice: + req = (ThreadRequest*)msg.wParam; + proxy = (ALCmmdevProxy*)msg.lParam; + + hr = V0(proxy,startProxy)(); + ReturnMsgResponse(req, hr); + continue; + + case WM_USER_StopDevice: + req = (ThreadRequest*)msg.wParam; + proxy = (ALCmmdevProxy*)msg.lParam; + + V0(proxy,stopProxy)(); + ReturnMsgResponse(req, S_OK); + continue; + + case WM_USER_CloseDevice: + req = (ThreadRequest*)msg.wParam; + proxy = (ALCmmdevProxy*)msg.lParam; + + V0(proxy,closeProxy)(); + if(--deviceCount == 0) + CoUninitialize(); + + ReturnMsgResponse(req, S_OK); + continue; + + case WM_USER_Enumerate: + req = (ThreadRequest*)msg.wParam; + + hr = cohr = S_OK; + if(++deviceCount == 1) + hr = cohr = CoInitialize(NULL); + if(SUCCEEDED(hr)) + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); + if(SUCCEEDED(hr)) + { + Enumerator = ptr; + + if(msg.lParam == ALL_DEVICE_PROBE) + hr = probe_devices(Enumerator, eRender, &PlaybackDevices); + else if(msg.lParam == CAPTURE_DEVICE_PROBE) + hr = probe_devices(Enumerator, eCapture, &CaptureDevices); + + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + } + + if(--deviceCount == 0 && SUCCEEDED(cohr)) + CoUninitialize(); + + ReturnMsgResponse(req, hr); + continue; + + default: + ERR("Unexpected message: %u\n", msg.message); + continue; + } + } + TRACE("Message loop finished\n"); + + return 0; +} + + +typedef struct ALCmmdevPlayback { + DERIVE_FROM_TYPE(ALCbackend); + DERIVE_FROM_TYPE(ALCmmdevProxy); + + WCHAR *devid; + + IMMDevice *mmdev; + IAudioClient *client; + IAudioRenderClient *render; + HANDLE NotifyEvent; + + HANDLE MsgEvent; + + volatile UINT32 Padding; + + volatile int killNow; + althrd_t thread; +} ALCmmdevPlayback; + +static int ALCmmdevPlayback_mixerProc(void *arg); + +static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device); +static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self); +static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name); +static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self); +static void ALCmmdevPlayback_close(ALCmmdevPlayback *self); +static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self); +static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self); +static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self); +static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self); +static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self); +static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self); +static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self); +static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) +static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples) +static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self); +static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback) + +DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback); +DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback); + + +static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device) +{ + SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self); + SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self); + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self)); + + self->devid = NULL; + + self->mmdev = NULL; + self->client = NULL; + self->render = NULL; + self->NotifyEvent = NULL; + + self->MsgEvent = NULL; + + self->Padding = 0; + + self->killNow = 0; +} + +static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self) +{ + if(self->NotifyEvent != NULL) + CloseHandle(self->NotifyEvent); + self->NotifyEvent = NULL; + if(self->MsgEvent != NULL) + CloseHandle(self->MsgEvent); + self->MsgEvent = NULL; + + free(self->devid); + self->devid = NULL; + + ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self)); + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); +} + + +FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg) +{ + ALCmmdevPlayback *self = arg; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + UINT32 buffer_len, written; + ALuint update_size, len; + BYTE *buffer; + HRESULT hr; + + hr = CoInitialize(NULL); + if(FAILED(hr)) + { + ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); + return 1; + } + + SetRTPriority(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + update_size = device->UpdateSize; + buffer_len = update_size * device->NumUpdates; + while(!self->killNow) + { + hr = IAudioClient_GetCurrentPadding(self->client, &written); + if(FAILED(hr)) + { + ERR("Failed to get padding: 0x%08lx\n", hr); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); + break; + } + self->Padding = written; + + len = buffer_len - written; + if(len < update_size) + { + DWORD res; + res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE); + if(res != WAIT_OBJECT_0) + ERR("WaitForSingleObjectEx error: 0x%lx\n", res); + continue; + } + len -= len%update_size; + + hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer); + if(SUCCEEDED(hr)) + { + ALCdevice_Lock(device); + aluMixData(device, buffer, len); + self->Padding = written + len; + ALCdevice_Unlock(device); + hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0); + } + if(FAILED(hr)) + { + ERR("Failed to buffer data: 0x%08lx\n", hr); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); + break; + } + } + self->Padding = 0; + + CoUninitialize(); + return 0; +} + + +static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) +{ + memset(out, 0, sizeof(*out)); + if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + *out = *(const 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 ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName) +{ + HRESULT hr = S_OK; + + self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(self->NotifyEvent == NULL || self->MsgEvent == NULL) + { + ERR("Failed to create message events: %lu\n", GetLastError()); + hr = E_FAIL; + } + + if(SUCCEEDED(hr)) + { + if(deviceName) + { + const DevMap *iter, *end; + + if(VECTOR_SIZE(PlaybackDevices) == 0) + { + ThreadRequest req = { self->MsgEvent, 0 }; + if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE)) + (void)WaitForResponse(&req); + } + + hr = E_FAIL; + iter = VECTOR_ITER_BEGIN(PlaybackDevices); + end = VECTOR_ITER_END(PlaybackDevices); + for(;iter != end;iter++) + { + if(al_string_cmp_cstr(iter->name, deviceName) == 0) + { + self->devid = strdupW(iter->devid); + hr = S_OK; + break; + } + } + if(FAILED(hr)) + WARN("Failed to find device name matching \"%s\"\n", deviceName); + } + } + + if(SUCCEEDED(hr)) + { + ThreadRequest req = { self->MsgEvent, 0 }; + + hr = E_FAIL; + if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + hr = WaitForResponse(&req); + else + ERR("Failed to post thread message: %lu\n", GetLastError()); + } + + if(FAILED(hr)) + { + if(self->NotifyEvent != NULL) + CloseHandle(self->NotifyEvent); + self->NotifyEvent = NULL; + if(self->MsgEvent != NULL) + CloseHandle(self->MsgEvent); + self->MsgEvent = NULL; + + free(self->devid); + self->devid = NULL; + + ERR("Device init failed: 0x%08lx\n", hr); + return ALC_INVALID_VALUE; + } + + return ALC_NO_ERROR; +} + +static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + void *ptr; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); + if(SUCCEEDED(hr)) + { + IMMDeviceEnumerator *Enumerator = ptr; + if(!self->devid) + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &self->mmdev); + else + hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev); + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + } + if(SUCCEEDED(hr)) + hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr); + if(SUCCEEDED(hr)) + { + self->client = ptr; + get_device_name(self->mmdev, &device->DeviceName); + } + + if(FAILED(hr)) + { + if(self->mmdev) + IMMDevice_Release(self->mmdev); + self->mmdev = NULL; + } + + return hr; +} + + +static void ALCmmdevPlayback_close(ALCmmdevPlayback *self) +{ + ThreadRequest req = { self->MsgEvent, 0 }; + + if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + (void)WaitForResponse(&req); + + CloseHandle(self->MsgEvent); + self->MsgEvent = NULL; + + CloseHandle(self->NotifyEvent); + self->NotifyEvent = NULL; + + free(self->devid); + self->devid = NULL; +} + +static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self) +{ + if(self->client) + IAudioClient_Release(self->client); + self->client = NULL; + + if(self->mmdev) + IMMDevice_Release(self->mmdev); + self->mmdev = NULL; +} + + +static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self) +{ + ThreadRequest req = { self->MsgEvent, 0 }; + HRESULT hr = E_FAIL; + + if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + hr = WaitForResponse(&req); + + return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; +} + +static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + WAVEFORMATEXTENSIBLE OutputType; + WAVEFORMATEX *wfx = NULL; + REFERENCE_TIME min_per, buf_time; + UINT32 buffer_len, min_len; + void *ptr = NULL; + HRESULT hr; + + if(self->client) + IAudioClient_Release(self->client); + self->client = NULL; + + hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr); + if(FAILED(hr)) + { + ERR("Failed to reactivate audio client: 0x%08lx\n", hr); + return hr; + } + self->client = ptr; + + hr = IAudioClient_GetMixFormat(self->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; + + buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 + + device->Frequency-1) / device->Frequency; + + 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 DevFmtUInt: + device->FmtType = DevFmtInt; + /* fall-through */ + case DevFmtInt: + OutputType.Format.wBitsPerSample = 32; + OutputType.Samples.wValidBitsPerSample = 32; + 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(self->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx); + if(FAILED(hr)) + { + ERR("Failed to check format support: 0x%08lx\n", hr); + hr = IAudioClient_GetMixFormat(self->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; + + device->Frequency = OutputType.Format.nSamplesPerSec; + 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.Format.wBitsPerSample == 8) + device->FmtType = DevFmtUByte; + else if(OutputType.Format.wBitsPerSample == 16) + device->FmtType = DevFmtShort; + else if(OutputType.Format.wBitsPerSample == 32) + device->FmtType = DevFmtInt; + else + { + device->FmtType = DevFmtShort; + OutputType.Format.wBitsPerSample = 16; + } + } + else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + { + device->FmtType = DevFmtFloat; + OutputType.Format.wBitsPerSample = 32; + } + else + { + ERR("Unhandled format sub-type\n"); + device->FmtType = DevFmtShort; + OutputType.Format.wBitsPerSample = 16; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + } + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + } + + SetDefaultWFXChannelOrder(device); + + hr = IAudioClient_Initialize(self->client, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + buf_time, 0, &OutputType.Format, NULL); + if(FAILED(hr)) + { + ERR("Failed to initialize audio client: 0x%08lx\n", hr); + return hr; + } + + hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL); + if(SUCCEEDED(hr)) + { + min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000); + /* Find the nearest multiple of the period size to the update size */ + if(min_len < device->UpdateSize) + min_len *= (device->UpdateSize + min_len/2)/min_len; + hr = IAudioClient_GetBufferSize(self->client, &buffer_len); + } + if(FAILED(hr)) + { + ERR("Failed to get audio buffer info: 0x%08lx\n", hr); + return hr; + } + + device->UpdateSize = min_len; + device->NumUpdates = buffer_len / device->UpdateSize; + if(device->NumUpdates <= 1) + { + ERR("Audio client returned buffer_len < period*2; expect break up\n"); + device->NumUpdates = 2; + device->UpdateSize = buffer_len / device->NumUpdates; + } + + hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent); + if(FAILED(hr)) + { + ERR("Failed to set event handle: 0x%08lx\n", hr); + return hr; + } + + return hr; +} + + +static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self) +{ + ThreadRequest req = { self->MsgEvent, 0 }; + HRESULT hr = E_FAIL; + + if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + hr = WaitForResponse(&req); + + return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; +} + +static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self) +{ + HRESULT hr; + void *ptr; + + ResetEvent(self->NotifyEvent); + hr = IAudioClient_Start(self->client); + if(FAILED(hr)) + ERR("Failed to start audio client: 0x%08lx\n", hr); + + if(SUCCEEDED(hr)) + hr = IAudioClient_GetService(self->client, &IID_IAudioRenderClient, &ptr); + if(SUCCEEDED(hr)) + { + self->render = ptr; + self->killNow = 0; + if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success) + { + if(self->render) + IAudioRenderClient_Release(self->render); + self->render = NULL; + IAudioClient_Stop(self->client); + ERR("Failed to start thread\n"); + hr = E_FAIL; + } + } + + return hr; +} + + +static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self) +{ + ThreadRequest req = { self->MsgEvent, 0 }; + if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + (void)WaitForResponse(&req); +} + +static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self) +{ + int res; + + if(!self->render) + return; + + self->killNow = 1; + althrd_join(self->thread, &res); + + IAudioRenderClient_Release(self->render); + self->render = NULL; + IAudioClient_Stop(self->client); +} + + +static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return (ALint64)self->Padding * 1000000000 / device->Frequency; +} + + +static inline void AppendAllDevicesList2(const DevMap *entry) +{ AppendAllDevicesList(al_string_get_cstr(entry->name)); } +static inline void AppendCaptureDeviceList2(const DevMap *entry) +{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); } + +typedef struct ALCmmdevBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCmmdevBackendFactory; +#define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } } + +static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self); +static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self); +static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type); +static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type); + +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory); + + +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, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID); + if(ThreadHdl != NULL) + InitResult = WaitForResponse(&req); + CloseHandle(req.FinishedEvt); + } + } + return SUCCEEDED(InitResult); +} + +static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self)) +{ + VECTOR_INIT(PlaybackDevices); + VECTOR_INIT(CaptureDevices); + + if(!MMDevApiLoad()) + return ALC_FALSE; + return ALC_TRUE; +} + +static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self)) +{ + clear_devlist(&PlaybackDevices); + VECTOR_DEINIT(PlaybackDevices); + + clear_devlist(&CaptureDevices); + VECTOR_DEINIT(CaptureDevices); + + if(ThreadHdl) + { + TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID); + PostThreadMessage(ThreadID, WM_QUIT, 0, 0); + CloseHandle(ThreadHdl); + ThreadHdl = NULL; + } +} + +static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type) +{ + ThreadRequest req = { NULL, 0 }; + + req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL); + if(req.FinishedEvt == NULL) + ERR("Failed to create event: %lu\n", GetLastError()); + else + { + HRESULT hr = E_FAIL; + if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type)) + hr = WaitForResponse(&req); + if(SUCCEEDED(hr)) switch(type) + { + case ALL_DEVICE_PROBE: + VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2); + break; + + case CAPTURE_DEVICE_PROBE: + VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2); + break; + } + CloseHandle(req.FinishedEvt); + req.FinishedEvt = NULL; + } +} + +static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCmmdevPlayback *backend; + + backend = ALCmmdevPlayback_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCmmdevPlayback_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} + + +ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void) +{ + static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} diff --git a/libs/openal-soft/Alc/backends/null.c b/libs/openal-soft/Alc/backends/null.c new file mode 100644 index 00000000..3f09c5d6 --- /dev/null +++ b/libs/openal-soft/Alc/backends/null.c @@ -0,0 +1,228 @@ +/** + * 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 +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include "alMain.h" +#include "alu.h" +#include "threads.h" +#include "compat.h" + +#include "backends/base.h" + + +typedef struct ALCnullBackend { + DERIVE_FROM_TYPE(ALCbackend); + + volatile int killNow; + althrd_t thread; +} ALCnullBackend; + +static int ALCnullBackend_mixerProc(void *ptr); + +static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device); +static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct) +static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name); +static void ALCnullBackend_close(ALCnullBackend *self); +static ALCboolean ALCnullBackend_reset(ALCnullBackend *self); +static ALCboolean ALCnullBackend_start(ALCnullBackend *self); +static void ALCnullBackend_stop(ALCnullBackend *self); +static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples) +static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend) + +DEFINE_ALCBACKEND_VTABLE(ALCnullBackend); + + +static const ALCchar nullDevice[] = "No Output"; + + +static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCnullBackend, ALCbackend, self); +} + + +static int ALCnullBackend_mixerProc(void *ptr) +{ + ALCnullBackend *self = (ALCnullBackend*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + struct timespec now, start; + ALuint64 avail, done; + const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 / + device->Frequency / 2); + + SetRTPriority(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + done = 0; + if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC) + { + ERR("Failed to get starting time\n"); + return 1; + } + while(!self->killNow && device->Connected) + { + if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) + { + ERR("Failed to get current time\n"); + return 1; + } + + avail = (now.tv_sec - start.tv_sec) * device->Frequency; + avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000; + if(avail < done) + { + /* Oops, time skipped backwards. Reset the number of samples done + * with one update available since we (likely) just came back from + * sleeping. */ + done = avail - device->UpdateSize; + } + + if(avail-done < device->UpdateSize) + al_nssleep(0, restTime); + else while(avail-done >= device->UpdateSize) + { + aluMixData(device, NULL, device->UpdateSize); + done += device->UpdateSize; + } + } + + return 0; +} + + +static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name) +{ + ALCdevice *device; + + if(!name) + name = nullDevice; + else if(strcmp(name, nullDevice) != 0) + return ALC_INVALID_VALUE; + + device = STATIC_CAST(ALCbackend, self)->mDevice; + al_string_copy_cstr(&device->DeviceName, name); + + return ALC_NO_ERROR; +} + +static void ALCnullBackend_close(ALCnullBackend* UNUSED(self)) +{ +} + +static ALCboolean ALCnullBackend_reset(ALCnullBackend *self) +{ + SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice); + return ALC_TRUE; +} + +static ALCboolean ALCnullBackend_start(ALCnullBackend *self) +{ + self->killNow = 0; + if(althrd_create(&self->thread, ALCnullBackend_mixerProc, self) != althrd_success) + return ALC_FALSE; + return ALC_TRUE; +} + +static void ALCnullBackend_stop(ALCnullBackend *self) +{ + int res; + + if(self->killNow) + return; + + self->killNow = 1; + althrd_join(self->thread, &res); +} + + +typedef struct ALCnullBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCnullBackendFactory; +#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCnullBackendFactory, ALCbackendFactory) } } + +ALCbackendFactory *ALCnullBackendFactory_getFactory(void); + +static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self); +static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit) +static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type); +static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory); + + +ALCbackendFactory *ALCnullBackendFactory_getFactory(void) +{ + static ALCnullBackendFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} + + +static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory* UNUSED(self)) +{ + return ALC_TRUE; +} + +static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type) +{ + switch(type) + { + case ALL_DEVICE_PROBE: + AppendAllDevicesList(nullDevice); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} + +static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCnullBackend *backend; + + backend = ALCnullBackend_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCnullBackend_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} diff --git a/libs/openal-soft/Alc/backends/opensl.c b/libs/openal-soft/Alc/backends/opensl.c new file mode 100644 index 00000000..220e6e5c --- /dev/null +++ b/libs/openal-soft/Alc/backends/opensl.c @@ -0,0 +1,424 @@ +/* + * 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 "alu.h" + + +#include +#include + +/* Helper macros */ +#define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS +#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS + + +typedef struct { + /* engine interfaces */ + SLObjectItf engineObject; + SLEngineItf engine; + + /* output mix interfaces */ + SLObjectItf outputMix; + + /* buffer queue player interfaces */ + SLObjectItf bufferQueueObject; + + void *buffer; + ALuint bufferSize; + ALuint curBuffer; + + 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"; +#ifdef SL_RESULT_READONLY + case SL_RESULT_READONLY: return "ReadOnly"; +#endif +#ifdef SL_RESULT_ENGINEOPTION_UNSUPPORTED + case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported"; +#endif +#ifdef SL_RESULT_SOURCE_SINK_INCOMPATIBLE + case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible"; +#endif + } + 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; + ALvoid *buf; + SLresult result; + + buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize; + aluMixData(Device, buf, data->bufferSize/data->frameSize); + + result = VCALL(bq,Enqueue)(buf, data->bufferSize); + PRINTERR(result, "bq->Enqueue"); + + data->curBuffer = (data->curBuffer+1) % Device->NumUpdates; +} + + +static ALCenum 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_INVALID_VALUE; + + data = calloc(1, sizeof(*data)); + if(!data) + return ALC_OUT_OF_MEMORY; + + // create engine + result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL); + PRINTERR(result, "slCreateEngine"); + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(data->engineObject,Realize)(SL_BOOLEAN_FALSE); + PRINTERR(result, "engine->Realize"); + } + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(data->engineObject,GetInterface)(SL_IID_ENGINE, &data->engine); + PRINTERR(result, "engine->GetInterface"); + } + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(data->engine,CreateOutputMix)(&data->outputMix, 0, NULL, NULL); + PRINTERR(result, "engine->CreateOutputMix"); + } + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(data->outputMix,Realize)(SL_BOOLEAN_FALSE); + PRINTERR(result, "outputMix->Realize"); + } + + if(SL_RESULT_SUCCESS != result) + { + if(data->outputMix != NULL) + VCALL0(data->outputMix,Destroy)(); + data->outputMix = NULL; + + if(data->engineObject != NULL) + VCALL0(data->engineObject,Destroy)(); + data->engineObject = NULL; + data->engine = NULL; + + free(data); + return ALC_INVALID_VALUE; + } + + al_string_copy_cstr(&Device->DeviceName, deviceName); + Device->ExtraData = data; + + return ALC_NO_ERROR; +} + + +static void opensl_close_playback(ALCdevice *Device) +{ + osl_data *data = Device->ExtraData; + + if(data->bufferQueueObject != NULL) + VCALL0(data->bufferQueueObject,Destroy)(); + data->bufferQueueObject = NULL; + + VCALL0(data->outputMix,Destroy)(); + data->outputMix = NULL; + + VCALL0(data->engineObject,Destroy)(); + 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; + SLDataLocator_OutputMix loc_outmix; + SLDataFormat_PCM format_pcm; + SLDataSource audioSrc; + SLDataSink audioSnk; + SLInterfaceID id; + SLboolean req; + SLresult result; + + + 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 = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : + SL_BYTEORDER_BIGENDIAN; + + 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; + + + if(data->bufferQueueObject != NULL) + VCALL0(data->bufferQueueObject,Destroy)(); + data->bufferQueueObject = NULL; + + result = VCALL(data->engine,CreateAudioPlayer)(&data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req); + PRINTERR(result, "engine->CreateAudioPlayer"); + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(data->bufferQueueObject,Realize)(SL_BOOLEAN_FALSE); + PRINTERR(result, "bufferQueue->Realize"); + } + + if(SL_RESULT_SUCCESS != result) + { + if(data->bufferQueueObject != NULL) + VCALL0(data->bufferQueueObject,Destroy)(); + data->bufferQueueObject = NULL; + + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static ALCboolean opensl_start_playback(ALCdevice *Device) +{ + osl_data *data = Device->ExtraData; + SLAndroidSimpleBufferQueueItf bufferQueue; + SLPlayItf player; + SLresult result; + ALuint i; + + result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue); + PRINTERR(result, "bufferQueue->GetInterface"); + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(bufferQueue,RegisterCallback)(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(Device->NumUpdates, 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) + { + ALvoid *buf = (ALbyte*)data->buffer + i*data->bufferSize; + result = VCALL(bufferQueue,Enqueue)(buf, data->bufferSize); + PRINTERR(result, "bufferQueue->Enqueue"); + } + } + data->curBuffer = 0; + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player); + PRINTERR(result, "bufferQueue->GetInterface"); + } + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING); + PRINTERR(result, "player->SetPlayState"); + } + + if(SL_RESULT_SUCCESS != result) + { + if(data->bufferQueueObject != NULL) + VCALL0(data->bufferQueueObject,Destroy)(); + 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; + SLPlayItf player; + SLAndroidSimpleBufferQueueItf bufferQueue; + SLresult result; + + result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player); + PRINTERR(result, "bufferQueue->GetInterface"); + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED); + PRINTERR(result, "player->SetPlayState"); + } + + result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue); + PRINTERR(result, "bufferQueue->GetInterface"); + if(SL_RESULT_SUCCESS == result) + { + result = VCALL0(bufferQueue,Clear)(); + PRINTERR(result, "bufferQueue->Clear"); + } + + free(data->buffer); + data->buffer = NULL; + data->bufferSize = 0; +} + + +static const BackendFuncs opensl_funcs = { + opensl_open_playback, + opensl_close_playback, + opensl_reset_playback, + opensl_start_playback, + opensl_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + ALCdevice_GetLatencyDefault +}; + + +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 ALL_DEVICE_PROBE: + AppendAllDevicesList(opensl_device); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff --git a/libs/openal-soft/Alc/backends/oss.c b/libs/openal-soft/Alc/backends/oss.c new file mode 100644 index 00000000..93e026e9 --- /dev/null +++ b/libs/openal-soft/Alc/backends/oss.c @@ -0,0 +1,632 @@ +/** + * 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 "alu.h" +#include "threads.h" +#include "compat.h" + +#include "backends/base.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"; + +static const char *oss_driver = "/dev/dsp"; +static const char *oss_capture = "/dev/dsp"; + +static int log2i(ALCuint x) +{ + int y = 0; + while (x > 1) + { + x >>= 1; + y++; + } + return y; +} + + +typedef struct ALCplaybackOSS { + DERIVE_FROM_TYPE(ALCbackend); + + int fd; + + ALubyte *mix_data; + int data_size; + + volatile int killNow; + althrd_t thread; +} ALCplaybackOSS; + +static int ALCplaybackOSS_mixerProc(void *ptr); + +static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device); +static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct) +static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name); +static void ALCplaybackOSS_close(ALCplaybackOSS *self); +static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self); +static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self); +static void ALCplaybackOSS_stop(ALCplaybackOSS *self); +static DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) +static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples) +static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS) +DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS); + + +static int ALCplaybackOSS_mixerProc(void *ptr) +{ + ALCplaybackOSS *self = (ALCplaybackOSS*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + ALint frameSize; + ssize_t wrote; + + SetRTPriority(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + while(!self->killNow && device->Connected) + { + ALint len = self->data_size; + ALubyte *WritePtr = self->mix_data; + + aluMixData(device, WritePtr, len/frameSize); + while(len > 0 && !self->killNow) + { + wrote = write(self->fd, WritePtr, len); + if(wrote < 0) + { + if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) + { + ERR("write failed: %s\n", strerror(errno)); + ALCplaybackOSS_lock(self); + aluHandleDisconnect(device); + ALCplaybackOSS_unlock(self); + break; + } + + al_nssleep(0, 1000000); + continue; + } + + len -= wrote; + WritePtr += wrote; + } + } + + return 0; +} + + +static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCplaybackOSS, ALCbackend, self); +} + +static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + + if(!name) + name = oss_device; + else if(strcmp(name, oss_device) != 0) + return ALC_INVALID_VALUE; + + self->killNow = 0; + + self->fd = open(oss_driver, O_WRONLY); + if(self->fd == -1) + { + ERR("Could not open %s: %s\n", oss_driver, strerror(errno)); + return ALC_INVALID_VALUE; + } + + al_string_copy_cstr(&device->DeviceName, name); + + return ALC_NO_ERROR; +} + +static void ALCplaybackOSS_close(ALCplaybackOSS *self) +{ + close(self->fd); + self->fd = -1; +} + +static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + 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 DevFmtInt: + case DevFmtUInt: + 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(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &info)); + if(0) + { + err: + 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; + } + + device->Frequency = ossSpeed; + device->UpdateSize = info.fragsize / frameSize; + device->NumUpdates = info.fragments + 1; + + SetDefaultChannelOrder(device); + + return ALC_TRUE; +} + +static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + + self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + self->mix_data = calloc(1, self->data_size); + + self->killNow = 0; + if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success) + { + free(self->mix_data); + self->mix_data = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void ALCplaybackOSS_stop(ALCplaybackOSS *self) +{ + int res; + + if(self->killNow) + return; + + self->killNow = 1; + althrd_join(self->thread, &res); + + if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0) + ERR("Error resetting device: %s\n", strerror(errno)); + + free(self->mix_data); + self->mix_data = NULL; +} + + +typedef struct ALCcaptureOSS { + DERIVE_FROM_TYPE(ALCbackend); + + int fd; + + ALubyte *read_data; + int data_size; + + RingBuffer *ring; + int doCapture; + + volatile int killNow; + althrd_t thread; +} ALCcaptureOSS; + +static int ALCcaptureOSS_recordProc(void *ptr); + +static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device); +static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct) +static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name); +static void ALCcaptureOSS_close(ALCcaptureOSS *self); +static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset) +static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self); +static void ALCcaptureOSS_stop(ALCcaptureOSS *self); +static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples); +static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self); +static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS) +DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS); + + +static int ALCcaptureOSS_recordProc(void *ptr) +{ + ALCcaptureOSS *self = (ALCcaptureOSS*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + int frameSize; + int amt; + + SetRTPriority(); + althrd_setname(althrd_current(), "alsoft-record"); + + frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + while(!self->killNow) + { + amt = read(self->fd, self->read_data, self->data_size); + if(amt < 0) + { + ERR("read failed: %s\n", strerror(errno)); + ALCcaptureOSS_lock(self); + aluHandleDisconnect(device); + ALCcaptureOSS_unlock(self); + break; + } + if(amt == 0) + { + al_nssleep(0, 1000000); + continue; + } + if(self->doCapture) + WriteRingBuffer(self->ring, self->read_data, amt/frameSize); + } + + return 0; +} + + +static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCcaptureOSS, ALCbackend, self); +} + +static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + int numFragmentsLogSize; + int log2FragmentSize; + unsigned int periods; + audio_buf_info info; + ALuint frameSize; + int numChannels; + int ossFormat; + int ossSpeed; + char *err; + + if(!name) + name = oss_device; + else if(strcmp(name, oss_device) != 0) + return ALC_INVALID_VALUE; + + self->fd = open(oss_capture, O_RDONLY); + if(self->fd == -1) + { + ERR("Could not open %s: %s\n", oss_capture, strerror(errno)); + return ALC_INVALID_VALUE; + } + + 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 DevFmtInt: + case DevFmtUInt: + case DevFmtFloat: + ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType)); + return ALC_INVALID_VALUE; + } + + 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(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed)); + CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETISPACE, &info)); + if(0) + { + err: + ERR("%s failed: %s\n", err, strerror(errno)); + close(self->fd); + self->fd = -1; + return ALC_INVALID_VALUE; + } +#undef CHECKERR + + if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels) + { + ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels); + close(self->fd); + self->fd = -1; + return ALC_INVALID_VALUE; + } + + 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(self->fd); + self->fd = -1; + return ALC_INVALID_VALUE; + } + + self->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates); + if(!self->ring) + { + ERR("Ring buffer create failed\n"); + close(self->fd); + self->fd = -1; + return ALC_OUT_OF_MEMORY; + } + + self->data_size = info.fragsize; + self->read_data = calloc(1, self->data_size); + + self->killNow = 0; + if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success) + { + device->ExtraData = NULL; + close(self->fd); + self->fd = -1; + return ALC_OUT_OF_MEMORY; + } + + al_string_copy_cstr(&device->DeviceName, name); + + return ALC_NO_ERROR; +} + +static void ALCcaptureOSS_close(ALCcaptureOSS *self) +{ + int res; + + self->killNow = 1; + althrd_join(self->thread, &res); + + close(self->fd); + self->fd = -1; + + DestroyRingBuffer(self->ring); + self->ring = NULL; + + free(self->read_data); + self->read_data = NULL; +} + +static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self) +{ + self->doCapture = 1; + return ALC_TRUE; +} + +static void ALCcaptureOSS_stop(ALCcaptureOSS *self) +{ + self->doCapture = 0; +} + +static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples) +{ + ReadRingBuffer(self->ring, buffer, samples); + return ALC_NO_ERROR; +} + +static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self) +{ + return RingBufferSize(self->ring); +} + + +typedef struct ALCossBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCossBackendFactory; +#define ALCOSSBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCossBackendFactory, ALCbackendFactory) } } + +ALCbackendFactory *ALCossBackendFactory_getFactory(void); + +static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self); +static DECLARE_FORWARD(ALCossBackendFactory, ALCbackendFactory, void, deinit) +static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type); +static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory); + + +ALCbackendFactory *ALCossBackendFactory_getFactory(void) +{ + static ALCossBackendFactory factory = ALCOSSBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} + + +ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self)) +{ + ConfigValueStr("oss", "device", &oss_driver); + ConfigValueStr("oss", "capture", &oss_capture); + + return ALC_TRUE; +} + +ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback || type == ALCbackend_Capture) + return ALC_TRUE; + return ALC_FALSE; +} + +void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type) +{ + switch(type) + { + case ALL_DEVICE_PROBE: + { +#ifdef HAVE_STAT + struct stat buf; + if(stat(oss_driver, &buf) == 0) +#endif + AppendAllDevicesList(oss_device); + } + break; + + case CAPTURE_DEVICE_PROBE: + { +#ifdef HAVE_STAT + struct stat buf; + if(stat(oss_capture, &buf) == 0) +#endif + AppendCaptureDeviceList(oss_device); + } + break; + } +} + +ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCplaybackOSS *backend; + + backend = ALCplaybackOSS_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCplaybackOSS_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + if(type == ALCbackend_Capture) + { + ALCcaptureOSS *backend; + + backend = ALCcaptureOSS_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCcaptureOSS_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} diff --git a/libs/openal-soft/Alc/backends/portaudio.c b/libs/openal-soft/Alc/backends/portaudio.c new file mode 100644 index 00000000..5f11526d --- /dev/null +++ b/libs/openal-soft/Alc/backends/portaudio.c @@ -0,0 +1,469 @@ +/** + * 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 "alu.h" +#include "compat.h" + +#include + + +static const ALCchar pa_device[] = "PortAudio Default"; + + +#ifdef HAVE_DYNLOAD +static void *pa_handle; +#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_GetDefaultInputDevice); +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_GetDefaultInputDevice pPa_GetDefaultInputDevice +#define Pa_GetStreamInfo pPa_GetStreamInfo +#endif + +static ALCboolean pa_load(void) +{ + PaError err; + +#ifdef HAVE_DYNLOAD + if(!pa_handle) + { +#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_GetDefaultInputDevice); + LOAD_FUNC(Pa_GetStreamInfo); +#undef LOAD_FUNC + + 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; + } + } +#else + if((err=Pa_Initialize()) != paNoError) + { + ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err)); + return ALC_FALSE; + } +#endif + return ALC_TRUE; +} + + +typedef struct { + PaStream *stream; + PaStreamParameters params; + ALuint update_size; + + RingBuffer *ring; +} pa_data; + + +static int pa_callback(const void *UNUSED(inputBuffer), void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo), + const PaStreamCallbackFlags UNUSED(statusFlags), void *userData) +{ + ALCdevice *device = (ALCdevice*)userData; + + aluMixData(device, outputBuffer, framesPerBuffer); + return 0; +} + +static int pa_capture_cb(const void *inputBuffer, void *UNUSED(outputBuffer), + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo), + const PaStreamCallbackFlags UNUSED(statusFlags), void *userData) +{ + ALCdevice *device = (ALCdevice*)userData; + pa_data *data = (pa_data*)device->ExtraData; + + WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer); + return 0; +} + + +static ALCenum pa_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + pa_data *data; + PaError err; + + if(!deviceName) + deviceName = pa_device; + else if(strcmp(deviceName, pa_device) != 0) + return ALC_INVALID_VALUE; + + data = (pa_data*)calloc(1, sizeof(pa_data)); + data->update_size = device->UpdateSize; + + data->params.device = -1; + if(!ConfigValueInt("port", "device", &data->params.device) || + data->params.device < 0) + data->params.device = Pa_GetDefaultOutputDevice(); + data->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) / + (float)device->Frequency; + data->params.hostApiSpecificStreamInfo = NULL; + + data->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2); + + switch(device->FmtType) + { + case DevFmtByte: + data->params.sampleFormat = paInt8; + break; + case DevFmtUByte: + data->params.sampleFormat = paUInt8; + break; + case DevFmtUShort: + /* fall-through */ + case DevFmtShort: + data->params.sampleFormat = paInt16; + break; + case DevFmtUInt: + /* fall-through */ + case DevFmtInt: + data->params.sampleFormat = paInt32; + break; + case DevFmtFloat: + data->params.sampleFormat = paFloat32; + break; + } + +retry_open: + err = Pa_OpenStream(&data->stream, NULL, &data->params, device->Frequency, + device->UpdateSize, paNoFlag, pa_callback, device); + if(err != paNoError) + { + if(data->params.sampleFormat == paFloat32) + { + data->params.sampleFormat = paInt16; + goto retry_open; + } + ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err)); + free(data); + return ALC_INVALID_VALUE; + } + + device->ExtraData = data; + al_string_copy_cstr(&device->DeviceName, deviceName); + + return ALC_NO_ERROR; +} + +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; + + streamInfo = Pa_GetStreamInfo(data->stream); + device->Frequency = streamInfo->sampleRate; + device->UpdateSize = data->update_size; + + if(data->params.sampleFormat == paInt8) + device->FmtType = DevFmtByte; + else if(data->params.sampleFormat == paUInt8) + device->FmtType = DevFmtUByte; + else if(data->params.sampleFormat == paInt16) + device->FmtType = DevFmtShort; + else if(data->params.sampleFormat == paInt32) + device->FmtType = DevFmtInt; + else if(data->params.sampleFormat == paFloat32) + device->FmtType = DevFmtFloat; + else + { + ERR("Unexpected sample format: 0x%lx\n", data->params.sampleFormat); + return ALC_FALSE; + } + + if(data->params.channelCount == 2) + device->FmtChans = DevFmtStereo; + else if(data->params.channelCount == 1) + device->FmtChans = DevFmtMono; + else + { + ERR("Unexpected channel count: %u\n", data->params.channelCount); + return ALC_FALSE; + } + SetDefaultChannelOrder(device); + + return ALC_TRUE; +} + +static ALCboolean pa_start_playback(ALCdevice *device) +{ + pa_data *data = (pa_data*)device->ExtraData; + PaError err; + + 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 ALCenum pa_open_capture(ALCdevice *device, const ALCchar *deviceName) +{ + ALuint frame_size; + pa_data *data; + PaError err; + + if(!deviceName) + deviceName = pa_device; + else if(strcmp(deviceName, pa_device) != 0) + return ALC_INVALID_VALUE; + + data = (pa_data*)calloc(1, sizeof(pa_data)); + if(data == NULL) + return ALC_OUT_OF_MEMORY; + + frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates); + if(data->ring == NULL) + goto error; + + data->params.device = -1; + if(!ConfigValueInt("port", "capture", &data->params.device) || + data->params.device < 0) + data->params.device = Pa_GetDefaultInputDevice(); + data->params.suggestedLatency = 0.0f; + data->params.hostApiSpecificStreamInfo = NULL; + + switch(device->FmtType) + { + case DevFmtByte: + data->params.sampleFormat = paInt8; + break; + case DevFmtUByte: + data->params.sampleFormat = paUInt8; + break; + case DevFmtShort: + data->params.sampleFormat = paInt16; + break; + case DevFmtInt: + data->params.sampleFormat = paInt32; + break; + case DevFmtFloat: + data->params.sampleFormat = paFloat32; + break; + case DevFmtUInt: + case DevFmtUShort: + ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType)); + goto error; + } + data->params.channelCount = ChannelsFromDevFmt(device->FmtChans); + + err = Pa_OpenStream(&data->stream, &data->params, 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; + } + + al_string_copy_cstr(&device->DeviceName, deviceName); + + device->ExtraData = data; + return ALC_NO_ERROR; + +error: + DestroyRingBuffer(data->ring); + free(data); + return ALC_INVALID_VALUE; +} + +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)); + + DestroyRingBuffer(data->ring); + data->ring = NULL; + + 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 ALCenum pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) +{ + pa_data *data = device->ExtraData; + ReadRingBuffer(data->ring, buffer, samples); + return ALC_NO_ERROR; +} + +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_start_playback, + pa_stop_playback, + pa_open_capture, + pa_close_capture, + pa_start_capture, + pa_stop_capture, + pa_capture_samples, + pa_available_samples, + ALCdevice_GetLatencyDefault +}; + +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) +{ +#ifdef HAVE_DYNLOAD + if(pa_handle) + { + Pa_Terminate(); + CloseLib(pa_handle); + pa_handle = NULL; + } +#else + Pa_Terminate(); +#endif +} + +void alc_pa_probe(enum DevProbe type) +{ + switch(type) + { + case ALL_DEVICE_PROBE: + AppendAllDevicesList(pa_device); + break; + case CAPTURE_DEVICE_PROBE: + AppendCaptureDeviceList(pa_device); + break; + } +} diff --git a/libs/openal-soft/Alc/backends/pulseaudio.c b/libs/openal-soft/Alc/backends/pulseaudio.c new file mode 100644 index 00000000..8bd731ff --- /dev/null +++ b/libs/openal-soft/Alc/backends/pulseaudio.c @@ -0,0 +1,1785 @@ +/** + * 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 + +#include "alMain.h" +#include "alu.h" +#include "threads.h" +#include "compat.h" + +#include "backends/base.h" + +#include + +#if PA_API_VERSION == 12 + +#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 + +#ifdef HAVE_DYNLOAD +static void *pa_handle; +#define MAKE_FUNC(x) static __typeof(x) * p##x +MAKE_FUNC(pa_context_unref); +MAKE_FUNC(pa_sample_spec_valid); +MAKE_FUNC(pa_frame_size); +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_is_corked); +MAKE_FUNC(pa_stream_cork); +MAKE_FUNC(pa_stream_is_suspended); +MAKE_FUNC(pa_stream_get_device_name); +MAKE_FUNC(pa_stream_get_latency); +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_with_proplist); +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_by_name); +MAKE_FUNC(pa_context_get_source_info_list); +MAKE_FUNC(pa_operation_get_state); +MAKE_FUNC(pa_operation_unref); +MAKE_FUNC(pa_proplist_new); +MAKE_FUNC(pa_proplist_free); +MAKE_FUNC(pa_proplist_set); +#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_frame_size ppa_frame_size +#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_is_corked ppa_stream_is_corked +#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_stream_get_latency ppa_stream_get_latency +#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_with_proplist ppa_stream_new_with_proplist +#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_by_name ppa_context_get_source_info_by_name +#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 +#define pa_proplist_new ppa_proplist_new +#define pa_proplist_free ppa_proplist_free +#define pa_proplist_set ppa_proplist_set +#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 + +static ALCboolean pulse_load(void) +{ + ALCboolean ret = ALC_TRUE; +#ifdef HAVE_DYNLOAD + if(!pa_handle) + { +#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)) { \ + ret = ALC_FALSE; \ + } \ +} while(0) + LOAD_FUNC(pa_context_unref); + LOAD_FUNC(pa_sample_spec_valid); + LOAD_FUNC(pa_stream_drop); + LOAD_FUNC(pa_frame_size); + 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_is_corked); + LOAD_FUNC(pa_stream_cork); + LOAD_FUNC(pa_stream_is_suspended); + LOAD_FUNC(pa_stream_get_device_name); + LOAD_FUNC(pa_stream_get_latency); + 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_with_proplist); + 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_by_name); + LOAD_FUNC(pa_context_get_source_info_list); + LOAD_FUNC(pa_operation_get_state); + LOAD_FUNC(pa_operation_unref); + LOAD_FUNC(pa_proplist_new); + LOAD_FUNC(pa_proplist_free); + LOAD_FUNC(pa_proplist_set); +#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 + + if(ret == ALC_FALSE) + { + CloseLib(pa_handle); + pa_handle = NULL; + } + } +#endif /* HAVE_DYNLOAD */ + return ret; +} + + +/* Global flags and properties */ +static pa_context_flags_t pulse_ctx_flags; +static pa_proplist *prop_filter; + + +/* PulseAudio Event Callbacks */ +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 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_success_callback(pa_stream *UNUSED(stream), int UNUSED(success), void *pdata) +{ + pa_threaded_mainloop *loop = pdata; + pa_threaded_mainloop_signal(loop, 0); +} + +static void wait_for_operation(pa_operation *op, pa_threaded_mainloop *loop) +{ + if(op) + { + while(pa_operation_get_state(op) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(loop); + pa_operation_unref(op); + } +} + + +static 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_open(pa_threaded_mainloop **loop, pa_context **context, + void(*state_cb)(pa_context*,void*), void *ptr) +{ + if(!(*loop = pa_threaded_mainloop_new())) + { + ERR("pa_threaded_mainloop_new() failed!\n"); + return ALC_FALSE; + } + if(pa_threaded_mainloop_start(*loop) < 0) + { + ERR("pa_threaded_mainloop_start() failed\n"); + goto error; + } + + pa_threaded_mainloop_lock(*loop); + + *context = connect_context(*loop, AL_FALSE); + if(!*context) + { + pa_threaded_mainloop_unlock(*loop); + pa_threaded_mainloop_stop(*loop); + goto error; + } + pa_context_set_state_callback(*context, state_cb, ptr); + + pa_threaded_mainloop_unlock(*loop); + return ALC_TRUE; + +error: + pa_threaded_mainloop_free(*loop); + *loop = NULL; + + return ALC_FALSE; +} + +static void pulse_close(pa_threaded_mainloop *loop, pa_context *context, + pa_stream *stream) +{ + pa_threaded_mainloop_lock(loop); + + if(stream) + { + pa_stream_set_state_callback(stream, NULL, NULL); + pa_stream_set_moved_callback(stream, NULL, NULL); + pa_stream_set_write_callback(stream, NULL, NULL); +#if PA_CHECK_VERSION(0,9,15) + if(pa_stream_set_buffer_attr_callback) + pa_stream_set_buffer_attr_callback(stream, NULL, NULL); +#endif + pa_stream_disconnect(stream); + pa_stream_unref(stream); + } + + pa_context_disconnect(context); + pa_context_unref(context); + + pa_threaded_mainloop_unlock(loop); + + pa_threaded_mainloop_stop(loop); + pa_threaded_mainloop_free(loop); +} + + +typedef struct { + al_string name; + al_string device_name; +} DevMap; +TYPEDEF_VECTOR(DevMap, vector_DevMap) + +static vector_DevMap PlaybackDevices; +static vector_DevMap CaptureDevices; + +static void clear_devlist(vector_DevMap *list) +{ +#define DEINIT_STRS(i) (AL_STRING_DEINIT((i)->name),AL_STRING_DEINIT((i)->device_name)) + VECTOR_FOR_EACH(DevMap, *list, DEINIT_STRS); +#undef DEINIT_STRS + VECTOR_RESIZE(*list, 0); +} + + +typedef struct ALCpulsePlayback { + DERIVE_FROM_TYPE(ALCbackend); + + al_string device_name; + + pa_buffer_attr attr; + pa_sample_spec spec; + + pa_threaded_mainloop *loop; + + pa_stream *stream; + pa_context *context; + + volatile ALboolean killNow; + althrd_t thread; +} ALCpulsePlayback; + +static void ALCpulsePlayback_deviceCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata); +static void ALCpulsePlayback_probeDevices(void); + +static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata); +static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata); +static void ALCpulsePlayback_streamStateCallback(pa_stream *stream, void *pdata); +static void ALCpulsePlayback_streamWriteCallback(pa_stream *p, size_t nbytes, void *userdata); +static void ALCpulsePlayback_sinkInfoCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata); +static void ALCpulsePlayback_sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata); +static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata); +static pa_stream *ALCpulsePlayback_connectStream(const char *device_name, pa_threaded_mainloop *loop, + pa_context *context, pa_stream_flags_t flags, + pa_buffer_attr *attr, pa_sample_spec *spec, + pa_channel_map *chanmap); +static int ALCpulsePlayback_mixerProc(void *ptr); + +static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device); +static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self); +static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name); +static void ALCpulsePlayback_close(ALCpulsePlayback *self); +static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self); +static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self); +static void ALCpulsePlayback_stop(ALCpulsePlayback *self); +static DECLARE_FORWARD2(ALCpulsePlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) +static DECLARE_FORWARD(ALCpulsePlayback, ALCbackend, ALCuint, availableSamples) +static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self); +static void ALCpulsePlayback_lock(ALCpulsePlayback *self); +static void ALCpulsePlayback_unlock(ALCpulsePlayback *self); +DECLARE_DEFAULT_ALLOCATORS(ALCpulsePlayback) + +DEFINE_ALCBACKEND_VTABLE(ALCpulsePlayback); + + +static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCpulsePlayback, ALCbackend, self); + + AL_STRING_INIT(self->device_name); +} + +static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self) +{ + AL_STRING_DEINIT(self->device_name); + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); +} + + +static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata) +{ + pa_threaded_mainloop *loop = pdata; + const DevMap *iter; + DevMap entry; + + if(eol) + { + pa_threaded_mainloop_signal(loop, 0); + return; + } + +#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0) + VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_INFO_NAME); +#undef MATCH_INFO_NAME + if(iter != VECTOR_ITER_END(PlaybackDevices)) + return; + + TRACE("Got device \"%s\", \"%s\"\n", info->description, info->name); + + AL_STRING_INIT(entry.name); + AL_STRING_INIT(entry.device_name); + + al_string_copy_cstr(&entry.name, info->description); + al_string_copy_cstr(&entry.device_name, info->name); + + VECTOR_PUSH_BACK(PlaybackDevices, entry); +} + +static void ALCpulsePlayback_probeDevices(void) +{ + pa_threaded_mainloop *loop; + + clear_devlist(&PlaybackDevices); + + if((loop=pa_threaded_mainloop_new()) && + pa_threaded_mainloop_start(loop) >= 0) + { + pa_context *context; + + pa_threaded_mainloop_lock(loop); + context = connect_context(loop, AL_FALSE); + if(context) + { + pa_operation *o; + pa_stream_flags_t flags; + pa_sample_spec spec; + pa_stream *stream; + + flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | + PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE; + + spec.format = PA_SAMPLE_S16NE; + spec.rate = 44100; + spec.channels = 2; + + stream = ALCpulsePlayback_connectStream(NULL, loop, context, flags, + NULL, &spec, NULL); + if(stream) + { + o = pa_context_get_sink_info_by_name(context, pa_stream_get_device_name(stream), + ALCpulsePlayback_deviceCallback, loop); + wait_for_operation(o, loop); + + pa_stream_disconnect(stream); + pa_stream_unref(stream); + stream = NULL; + } + + o = pa_context_get_sink_info_list(context, ALCpulsePlayback_deviceCallback, loop); + wait_for_operation(o, loop); + + pa_context_disconnect(context); + pa_context_unref(context); + } + pa_threaded_mainloop_unlock(loop); + pa_threaded_mainloop_stop(loop); + } + if(loop) + pa_threaded_mainloop_free(loop); +} + + +static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata) +{ + ALCpulsePlayback *self = pdata; + + self->attr = *pa_stream_get_buffer_attr(stream); + TRACE("minreq=%d, tlength=%d, prebuf=%d\n", self->attr.minreq, self->attr.tlength, self->attr.prebuf); +} + +static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata) +{ + ALCpulsePlayback *self = pdata; + if(pa_context_get_state(context) == PA_CONTEXT_FAILED) + { + ERR("Received context failure!\n"); + aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice); + } + pa_threaded_mainloop_signal(self->loop, 0); +} + +static void ALCpulsePlayback_streamStateCallback(pa_stream *stream, void *pdata) +{ + ALCpulsePlayback *self = pdata; + if(pa_stream_get_state(stream) == PA_STREAM_FAILED) + { + ERR("Received stream failure!\n"); + aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice); + } + pa_threaded_mainloop_signal(self->loop, 0); +} + +static void ALCpulsePlayback_streamWriteCallback(pa_stream* UNUSED(p), size_t UNUSED(nbytes), void *pdata) +{ + ALCpulsePlayback *self = pdata; + pa_threaded_mainloop_signal(self->loop, 0); +} + +static void ALCpulsePlayback_sinkInfoCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata) +{ + ALCpulsePlayback *self = pdata; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + char chanmap_str[256] = ""; + const struct { + const char *str; + enum DevFmtChannels chans; + } chanmaps[] = { + { "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right", + DevFmtX71 }, + { "front-left,front-right,front-center,lfe,rear-center,side-left,side-right", + DevFmtX61 }, + { "front-left,front-right,front-center,lfe,rear-left,rear-right", + DevFmtX51 }, + { "front-left,front-right,front-center,lfe,side-left,side-right", + DevFmtX51Side }, + { "front-left,front-right,rear-left,rear-right", DevFmtQuad }, + { "front-left,front-right", DevFmtStereo }, + { "mono", DevFmtMono }, + { NULL, 0 } + }; + int i; + + if(eol) + { + pa_threaded_mainloop_signal(self->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 ALCpulsePlayback_sinkNameCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata) +{ + ALCpulsePlayback *self = pdata; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + + if(eol) + { + pa_threaded_mainloop_signal(self->loop, 0); + return; + } + + al_string_copy_cstr(&device->DeviceName, info->description); +} + + +static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata) +{ + ALCpulsePlayback *self = pdata; + + al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream)); + + TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name)); +} + + +static pa_stream *ALCpulsePlayback_connectStream(const char *device_name, + pa_threaded_mainloop *loop, pa_context *context, + pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, + pa_channel_map *chanmap) +{ + pa_stream_state_t state; + pa_stream *stream; + + stream = pa_stream_new_with_proplist(context, "Playback Stream", spec, chanmap, prop_filter); + if(!stream) + { + ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context))); + return NULL; + } + + pa_stream_set_state_callback(stream, stream_state_callback, loop); + + if(pa_stream_connect_playback(stream, device_name, attr, flags, NULL, NULL) < 0) + { + ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context))); + pa_stream_unref(stream); + return NULL; + } + + while((state=pa_stream_get_state(stream)) != PA_STREAM_READY) + { + if(!PA_STREAM_IS_GOOD(state)) + { + ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context))); + pa_stream_unref(stream); + return NULL; + } + + pa_threaded_mainloop_wait(loop); + } + pa_stream_set_state_callback(stream, NULL, NULL); + + return stream; +} + + +static int ALCpulsePlayback_mixerProc(void *ptr) +{ + ALCpulsePlayback *self = ptr; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + ALuint buffer_size; + ALint update_size; + size_t frame_size; + ssize_t len; + + SetRTPriority(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + pa_threaded_mainloop_lock(self->loop); + frame_size = pa_frame_size(&self->spec); + update_size = device->UpdateSize * frame_size; + + /* Sanitize buffer metrics, in case we actually have less than what we + * asked for. */ + buffer_size = minu(update_size*device->NumUpdates, self->attr.tlength); + update_size = minu(update_size, buffer_size/2); + do { + len = pa_stream_writable_size(self->stream) - self->attr.tlength + + buffer_size; + if(len < update_size) + { + if(pa_stream_is_corked(self->stream) == 1) + { + pa_operation *o; + o = pa_stream_cork(self->stream, 0, NULL, NULL); + if(o) pa_operation_unref(o); + } + pa_threaded_mainloop_wait(self->loop); + continue; + } + len -= len%update_size; + + 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(self->stream, &buf, &newlen) < 0) +#endif + { + buf = pa_xmalloc(newlen); + free_func = pa_xfree; + } + + aluMixData(device, buf, newlen/frame_size); + + pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); + len -= newlen; + } + } while(!self->killNow && device->Connected); + pa_threaded_mainloop_unlock(self->loop); + + return 0; +} + + +static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name) +{ + const char *pulse_name = NULL; + pa_stream_flags_t flags; + pa_sample_spec spec; + pa_operation *o; + + if(name) + { + const DevMap *iter; + + if(VECTOR_SIZE(PlaybackDevices) == 0) + ALCpulsePlayback_probeDevices(); + +#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0) + VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME); +#undef MATCH_NAME + if(iter == VECTOR_ITER_END(PlaybackDevices)) + return ALC_INVALID_VALUE; + pulse_name = al_string_get_cstr(iter->device_name); + } + + if(!pulse_open(&self->loop, &self->context, ALCpulsePlayback_contextStateCallback, self)) + return ALC_INVALID_VALUE; + + pa_threaded_mainloop_lock(self->loop); + + flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | + PA_STREAM_FIX_CHANNELS; + if(!GetConfigValueBool("pulse", "allow-moves", 0)) + flags |= PA_STREAM_DONT_MOVE; + + spec.format = PA_SAMPLE_S16NE; + spec.rate = 44100; + spec.channels = 2; + + TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); + self->stream = ALCpulsePlayback_connectStream(pulse_name, self->loop, self->context, + flags, NULL, &spec, NULL); + if(!self->stream) + { + pa_threaded_mainloop_unlock(self->loop); + pulse_close(self->loop, self->context, self->stream); + self->loop = NULL; + self->context = NULL; + return ALC_INVALID_VALUE; + } + pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self); + + al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream)); + o = pa_context_get_sink_info_by_name(self->context, + al_string_get_cstr(self->device_name), + ALCpulsePlayback_sinkNameCallback, self); + wait_for_operation(o, self->loop); + + pa_threaded_mainloop_unlock(self->loop); + + return ALC_NO_ERROR; +} + +static void ALCpulsePlayback_close(ALCpulsePlayback *self) +{ + pulse_close(self->loop, self->context, self->stream); + self->loop = NULL; + self->context = NULL; + self->stream = NULL; + + al_string_clear(&self->device_name); +} + +static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + pa_stream_flags_t flags = 0; + const char *mapname = NULL; + pa_channel_map chanmap; + ALuint len; + + pa_threaded_mainloop_lock(self->loop); + + if(self->stream) + { + pa_stream_set_moved_callback(self->stream, NULL, NULL); +#if PA_CHECK_VERSION(0,9,15) + if(pa_stream_set_buffer_attr_callback) + pa_stream_set_buffer_attr_callback(self->stream, NULL, NULL); +#endif + pa_stream_disconnect(self->stream); + pa_stream_unref(self->stream); + self->stream = NULL; + } + + if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) + { + pa_operation *o; + o = pa_context_get_sink_info_by_name(self->context, + al_string_get_cstr(self->device_name), + ALCpulsePlayback_sinkInfoCallback, self); + wait_for_operation(o, self->loop); + } + if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) + flags |= PA_STREAM_FIX_RATE; + + flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE; + flags |= PA_STREAM_ADJUST_LATENCY; + flags |= PA_STREAM_START_CORKED; + if(!GetConfigValueBool("pulse", "allow-moves", 0)) + flags |= PA_STREAM_DONT_MOVE; + + switch(device->FmtType) + { + case DevFmtByte: + device->FmtType = DevFmtUByte; + /* fall-through */ + case DevFmtUByte: + self->spec.format = PA_SAMPLE_U8; + break; + case DevFmtUShort: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + self->spec.format = PA_SAMPLE_S16NE; + break; + case DevFmtUInt: + device->FmtType = DevFmtInt; + /* fall-through */ + case DevFmtInt: + self->spec.format = PA_SAMPLE_S32NE; + break; + case DevFmtFloat: + self->spec.format = PA_SAMPLE_FLOAT32NE; + break; + } + self->spec.rate = device->Frequency; + self->spec.channels = ChannelsFromDevFmt(device->FmtChans); + + if(pa_sample_spec_valid(&self->spec) == 0) + { + ERR("Invalid sample format\n"); + pa_threaded_mainloop_unlock(self->loop); + return ALC_FALSE; + } + + switch(device->FmtChans) + { + case DevFmtMono: + mapname = "mono"; + break; + case DevFmtStereo: + mapname = "front-left,front-right"; + break; + case DevFmtQuad: + mapname = "front-left,front-right,rear-left,rear-right"; + break; + case DevFmtX51: + mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right"; + break; + case DevFmtX51Side: + mapname = "front-left,front-right,front-center,lfe,side-left,side-right"; + break; + case DevFmtX61: + mapname = "front-left,front-right,front-center,lfe,rear-center,side-left,side-right"; + break; + case DevFmtX71: + mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right"; + break; + } + if(!pa_channel_map_parse(&chanmap, mapname)) + { + ERR("Failed to build channel map for %s\n", DevFmtChannelsString(device->FmtChans)); + pa_threaded_mainloop_unlock(self->loop); + return ALC_FALSE; + } + SetDefaultWFXChannelOrder(device); + + self->attr.fragsize = -1; + self->attr.prebuf = 0; + self->attr.minreq = device->UpdateSize * pa_frame_size(&self->spec); + self->attr.tlength = self->attr.minreq * maxu(device->NumUpdates, 2); + self->attr.maxlength = -1; + + self->stream = ALCpulsePlayback_connectStream(al_string_get_cstr(self->device_name), + self->loop, self->context, flags, + &self->attr, &self->spec, &chanmap); + if(!self->stream) + { + pa_threaded_mainloop_unlock(self->loop); + return ALC_FALSE; + } + pa_stream_set_state_callback(self->stream, ALCpulsePlayback_streamStateCallback, self); + pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self); + pa_stream_set_write_callback(self->stream, ALCpulsePlayback_streamWriteCallback, self); + + self->spec = *(pa_stream_get_sample_spec(self->stream)); + if(device->Frequency != self->spec.rate) + { + pa_operation *o; + + /* Server updated our playback rate, so modify the buffer attribs + * accordingly. */ + device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency * + self->spec.rate + 0.5); + self->attr.minreq = device->UpdateSize * pa_frame_size(&self->spec); + self->attr.tlength = self->attr.minreq * clampu(device->NumUpdates, 2, 16); + self->attr.maxlength = -1; + self->attr.prebuf = 0; + + o = pa_stream_set_buffer_attr(self->stream, &self->attr, + stream_success_callback, self->loop); + wait_for_operation(o, self->loop); + + device->Frequency = self->spec.rate; + } + +#if PA_CHECK_VERSION(0,9,15) + if(pa_stream_set_buffer_attr_callback) + pa_stream_set_buffer_attr_callback(self->stream, ALCpulsePlayback_bufferAttrCallback, self); +#endif + ALCpulsePlayback_bufferAttrCallback(self->stream, self); + + len = self->attr.minreq / pa_frame_size(&self->spec); + if((CPUCapFlags&CPU_CAP_SSE)) + len = (len+3)&~3; + device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5); + device->NumUpdates = clampu(device->NumUpdates, 2, 16); + device->UpdateSize = len; + + pa_threaded_mainloop_unlock(self->loop); + return ALC_TRUE; +} + +static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self) +{ + self->killNow = AL_FALSE; + if(althrd_create(&self->thread, ALCpulsePlayback_mixerProc, self) != althrd_success) + return ALC_FALSE; + return ALC_TRUE; +} + +static void ALCpulsePlayback_stop(ALCpulsePlayback *self) +{ + pa_operation *o; + int res; + + if(!self->stream) + return; + + self->killNow = AL_TRUE; + althrd_join(self->thread, &res); + + pa_threaded_mainloop_lock(self->loop); + + o = pa_stream_cork(self->stream, 1, stream_success_callback, self->loop); + wait_for_operation(o, self->loop); + + pa_threaded_mainloop_unlock(self->loop); +} + + +static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self) +{ + pa_usec_t latency = 0; + int neg, err; + + if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0) + { + /* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon + * after starting the stream and no timing info has been received from + * the server yet. Should we wait, possibly stalling the app, or give a + * dummy value? Either way, it shouldn't be 0. */ + if(err != -PA_ERR_NODATA) + ERR("Failed to get stream latency: 0x%x\n", err); + return 0; + } + + if(neg) latency = 0; + return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000; +} + + +static void ALCpulsePlayback_lock(ALCpulsePlayback *self) +{ + pa_threaded_mainloop_lock(self->loop); +} + +static void ALCpulsePlayback_unlock(ALCpulsePlayback *self) +{ + pa_threaded_mainloop_unlock(self->loop); +} + + +typedef struct ALCpulseCapture { + DERIVE_FROM_TYPE(ALCbackend); + + al_string device_name; + + const void *cap_store; + size_t cap_len; + size_t cap_remain; + + ALCuint last_readable; + + pa_buffer_attr attr; + pa_sample_spec spec; + + pa_threaded_mainloop *loop; + + pa_stream *stream; + pa_context *context; +} ALCpulseCapture; + +static void ALCpulseCapture_deviceCallback(pa_context *context, const pa_source_info *info, int eol, void *pdata); +static void ALCpulseCapture_probeDevices(void); + +static void ALCpulseCapture_contextStateCallback(pa_context *context, void *pdata); +static void ALCpulseCapture_streamStateCallback(pa_stream *stream, void *pdata); +static void ALCpulseCapture_sourceNameCallback(pa_context *context, const pa_source_info *info, int eol, void *pdata); +static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata); +static pa_stream *ALCpulseCapture_connectStream(const char *device_name, + pa_threaded_mainloop *loop, pa_context *context, + pa_stream_flags_t flags, pa_buffer_attr *attr, + pa_sample_spec *spec, pa_channel_map *chanmap); + +static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device); +static void ALCpulseCapture_Destruct(ALCpulseCapture *self); +static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name); +static void ALCpulseCapture_close(ALCpulseCapture *self); +static DECLARE_FORWARD(ALCpulseCapture, ALCbackend, ALCboolean, reset) +static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self); +static void ALCpulseCapture_stop(ALCpulseCapture *self); +static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples); +static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self); +static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self); +static void ALCpulseCapture_lock(ALCpulseCapture *self); +static void ALCpulseCapture_unlock(ALCpulseCapture *self); +DECLARE_DEFAULT_ALLOCATORS(ALCpulseCapture) + +DEFINE_ALCBACKEND_VTABLE(ALCpulseCapture); + + +static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCpulseCapture, ALCbackend, self); + + AL_STRING_INIT(self->device_name); +} + +static void ALCpulseCapture_Destruct(ALCpulseCapture *self) +{ + AL_STRING_DEINIT(self->device_name); + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); +} + + +static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa_source_info *info, int eol, void *pdata) +{ + pa_threaded_mainloop *loop = pdata; + const DevMap *iter; + DevMap entry; + + if(eol) + { + pa_threaded_mainloop_signal(loop, 0); + return; + } + +#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0) + VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_INFO_NAME); +#undef MATCH_INFO_NAME + if(iter != VECTOR_ITER_END(CaptureDevices)) + return; + + TRACE("Got device \"%s\", \"%s\"\n", info->description, info->name); + + AL_STRING_INIT(entry.name); + AL_STRING_INIT(entry.device_name); + + al_string_copy_cstr(&entry.name, info->description); + al_string_copy_cstr(&entry.device_name, info->name); + + VECTOR_PUSH_BACK(CaptureDevices, entry); +} + +static void ALCpulseCapture_probeDevices(void) +{ + pa_threaded_mainloop *loop; + + clear_devlist(&CaptureDevices); + + if((loop=pa_threaded_mainloop_new()) && + pa_threaded_mainloop_start(loop) >= 0) + { + pa_context *context; + + pa_threaded_mainloop_lock(loop); + context = connect_context(loop, AL_FALSE); + if(context) + { + pa_operation *o; + pa_stream_flags_t flags; + pa_sample_spec spec; + pa_stream *stream; + + flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | + PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE; + + spec.format = PA_SAMPLE_S16NE; + spec.rate = 44100; + spec.channels = 1; + + stream = ALCpulseCapture_connectStream(NULL, loop, context, flags, + NULL, &spec, NULL); + if(stream) + { + o = pa_context_get_source_info_by_name(context, pa_stream_get_device_name(stream), + ALCpulseCapture_deviceCallback, loop); + wait_for_operation(o, loop); + + pa_stream_disconnect(stream); + pa_stream_unref(stream); + stream = NULL; + } + + o = pa_context_get_source_info_list(context, ALCpulseCapture_deviceCallback, loop); + wait_for_operation(o, loop); + + pa_context_disconnect(context); + pa_context_unref(context); + } + pa_threaded_mainloop_unlock(loop); + pa_threaded_mainloop_stop(loop); + } + if(loop) + pa_threaded_mainloop_free(loop); +} + + +static void ALCpulseCapture_contextStateCallback(pa_context *context, void *pdata) +{ + ALCpulseCapture *self = pdata; + if(pa_context_get_state(context) == PA_CONTEXT_FAILED) + { + ERR("Received context failure!\n"); + aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice); + } + pa_threaded_mainloop_signal(self->loop, 0); +} + +static void ALCpulseCapture_streamStateCallback(pa_stream *stream, void *pdata) +{ + ALCpulseCapture *self = pdata; + if(pa_stream_get_state(stream) == PA_STREAM_FAILED) + { + ERR("Received stream failure!\n"); + aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice); + } + pa_threaded_mainloop_signal(self->loop, 0); +} + + +static void ALCpulseCapture_sourceNameCallback(pa_context *UNUSED(context), const pa_source_info *info, int eol, void *pdata) +{ + ALCpulseCapture *self = pdata; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + + if(eol) + { + pa_threaded_mainloop_signal(self->loop, 0); + return; + } + + al_string_copy_cstr(&device->DeviceName, info->description); +} + + +static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata) +{ + ALCpulseCapture *self = pdata; + + al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream)); + + TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name)); +} + + +static pa_stream *ALCpulseCapture_connectStream(const char *device_name, + pa_threaded_mainloop *loop, pa_context *context, + pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, + pa_channel_map *chanmap) +{ + pa_stream_state_t state; + pa_stream *stream; + + stream = pa_stream_new_with_proplist(context, "Capture Stream", spec, chanmap, prop_filter); + if(!stream) + { + ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context))); + return NULL; + } + + pa_stream_set_state_callback(stream, stream_state_callback, loop); + + if(pa_stream_connect_record(stream, device_name, attr, flags) < 0) + { + ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context))); + pa_stream_unref(stream); + return NULL; + } + + while((state=pa_stream_get_state(stream)) != PA_STREAM_READY) + { + if(!PA_STREAM_IS_GOOD(state)) + { + ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context))); + pa_stream_unref(stream); + return NULL; + } + + pa_threaded_mainloop_wait(loop); + } + pa_stream_set_state_callback(stream, NULL, NULL); + + return stream; +} + + +static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + const char *pulse_name = NULL; + pa_stream_flags_t flags = 0; + pa_channel_map chanmap; + pa_operation *o; + ALuint samples; + + if(name) + { + const DevMap *iter; + + if(VECTOR_SIZE(CaptureDevices) == 0) + ALCpulseCapture_probeDevices(); + +#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0) + VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME); +#undef MATCH_NAME + if(iter == VECTOR_ITER_END(CaptureDevices)) + return ALC_INVALID_VALUE; + pulse_name = al_string_get_cstr(iter->device_name); + } + + if(!pulse_open(&self->loop, &self->context, ALCpulseCapture_contextStateCallback, self)) + return ALC_INVALID_VALUE; + + pa_threaded_mainloop_lock(self->loop); + + self->spec.rate = device->Frequency; + self->spec.channels = ChannelsFromDevFmt(device->FmtChans); + + switch(device->FmtType) + { + case DevFmtUByte: + self->spec.format = PA_SAMPLE_U8; + break; + case DevFmtShort: + self->spec.format = PA_SAMPLE_S16NE; + break; + case DevFmtInt: + self->spec.format = PA_SAMPLE_S32NE; + break; + case DevFmtFloat: + self->spec.format = PA_SAMPLE_FLOAT32NE; + break; + case DevFmtByte: + case DevFmtUShort: + case DevFmtUInt: + ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType)); + pa_threaded_mainloop_unlock(self->loop); + goto fail; + } + + if(pa_sample_spec_valid(&self->spec) == 0) + { + ERR("Invalid sample format\n"); + pa_threaded_mainloop_unlock(self->loop); + goto fail; + } + + if(!pa_channel_map_init_auto(&chanmap, self->spec.channels, PA_CHANNEL_MAP_WAVEEX)) + { + ERR("Couldn't build map for channel count (%d)!\n", self->spec.channels); + pa_threaded_mainloop_unlock(self->loop); + goto fail; + } + + samples = device->UpdateSize * device->NumUpdates; + samples = maxu(samples, 100 * device->Frequency / 1000); + + self->attr.minreq = -1; + self->attr.prebuf = -1; + self->attr.maxlength = samples * pa_frame_size(&self->spec); + self->attr.tlength = -1; + self->attr.fragsize = minu(samples, 50*device->Frequency/1000) * + pa_frame_size(&self->spec); + + flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY; + if(!GetConfigValueBool("pulse", "allow-moves", 0)) + flags |= PA_STREAM_DONT_MOVE; + + TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); + self->stream = ALCpulseCapture_connectStream(pulse_name, self->loop, self->context, + flags, &self->attr, &self->spec, + &chanmap); + if(!self->stream) + { + pa_threaded_mainloop_unlock(self->loop); + goto fail; + } + pa_stream_set_moved_callback(self->stream, ALCpulseCapture_streamMovedCallback, self); + pa_stream_set_state_callback(self->stream, ALCpulseCapture_streamStateCallback, self); + + al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream)); + o = pa_context_get_source_info_by_name(self->context, + al_string_get_cstr(self->device_name), + ALCpulseCapture_sourceNameCallback, self); + wait_for_operation(o, self->loop); + + pa_threaded_mainloop_unlock(self->loop); + return ALC_NO_ERROR; + +fail: + pulse_close(self->loop, self->context, self->stream); + self->loop = NULL; + self->context = NULL; + self->stream = NULL; + + return ALC_INVALID_VALUE; +} + +static void ALCpulseCapture_close(ALCpulseCapture *self) +{ + pulse_close(self->loop, self->context, self->stream); + self->loop = NULL; + self->context = NULL; + self->stream = NULL; + + al_string_clear(&self->device_name); +} + +static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self) +{ + pa_operation *o; + o = pa_stream_cork(self->stream, 0, stream_success_callback, self->loop); + wait_for_operation(o, self->loop); + + return ALC_TRUE; +} + +static void ALCpulseCapture_stop(ALCpulseCapture *self) +{ + pa_operation *o; + o = pa_stream_cork(self->stream, 1, stream_success_callback, self->loop); + wait_for_operation(o, self->loop); +} + +static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples) +{ + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + ALCuint todo = samples * pa_frame_size(&self->spec); + + /* Capture is done in fragment-sized chunks, so we loop until we get all + * that's available */ + self->last_readable -= todo; + while(todo > 0) + { + size_t rem = todo; + + if(self->cap_len == 0) + { + pa_stream_state_t state; + + state = pa_stream_get_state(self->stream); + if(!PA_STREAM_IS_GOOD(state)) + { + aluHandleDisconnect(device); + break; + } + if(pa_stream_peek(self->stream, &self->cap_store, &self->cap_len) < 0) + { + ERR("pa_stream_peek() failed: %s\n", + pa_strerror(pa_context_errno(self->context))); + aluHandleDisconnect(device); + break; + } + self->cap_remain = self->cap_len; + } + if(rem > self->cap_remain) + rem = self->cap_remain; + + memcpy(buffer, self->cap_store, rem); + + buffer = (ALbyte*)buffer + rem; + todo -= rem; + + self->cap_store = (ALbyte*)self->cap_store + rem; + self->cap_remain -= rem; + if(self->cap_remain == 0) + { + pa_stream_drop(self->stream); + self->cap_len = 0; + } + } + if(todo > 0) + memset(buffer, ((device->FmtType==DevFmtUByte) ? 0x80 : 0), todo); + + return ALC_NO_ERROR; +} + +static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + size_t readable = self->cap_remain; + + if(device->Connected) + { + ssize_t got = pa_stream_readable_size(self->stream); + if(got < 0) + { + ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got)); + aluHandleDisconnect(device); + } + else if((size_t)got > self->cap_len) + readable += got - self->cap_len; + } + + if(self->last_readable < readable) + self->last_readable = readable; + return self->last_readable / pa_frame_size(&self->spec); +} + + +static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self) +{ + pa_usec_t latency = 0; + int neg; + + if(pa_stream_get_latency(self->stream, &latency, &neg) != 0) + { + ERR("Failed to get stream latency!\n"); + return 0; + } + + if(neg) latency = 0; + return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000; +} + + +static void ALCpulseCapture_lock(ALCpulseCapture *self) +{ + pa_threaded_mainloop_lock(self->loop); +} + +static void ALCpulseCapture_unlock(ALCpulseCapture *self) +{ + pa_threaded_mainloop_unlock(self->loop); +} + + +static inline void AppendAllDevicesList2(const DevMap *entry) +{ AppendAllDevicesList(al_string_get_cstr(entry->name)); } +static inline void AppendCaptureDeviceList2(const DevMap *entry) +{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); } + +typedef struct ALCpulseBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCpulseBackendFactory; +#define ALCPULSEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCpulseBackendFactory, ALCbackendFactory) } } + +static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory *self); +static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory *self); +static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory *self, ALCbackend_Type type); +static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory *self, ALCdevice *device, ALCbackend_Type type); + +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory); + + +static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory* UNUSED(self)) +{ + ALCboolean ret = ALC_FALSE; + + VECTOR_INIT(PlaybackDevices); + VECTOR_INIT(CaptureDevices); + + if(pulse_load()) + { + pa_threaded_mainloop *loop; + + pulse_ctx_flags = 0; + if(!GetConfigValueBool("pulse", "spawn-server", 1)) + pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN; + + 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; + + /* Some libraries (Phonon, Qt) set some pulseaudio properties + * through environment variables, which causes all streams in + * the process to inherit them. This attempts to filter those + * properties out by setting them to 0-length data. */ + prop_filter = pa_proplist_new(); + pa_proplist_set(prop_filter, PA_PROP_MEDIA_ROLE, NULL, 0); + pa_proplist_set(prop_filter, "phonon.streamid", NULL, 0); + + 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); + } + + return ret; +} + +static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory* UNUSED(self)) +{ + clear_devlist(&PlaybackDevices); + VECTOR_DEINIT(PlaybackDevices); + + clear_devlist(&CaptureDevices); + VECTOR_DEINIT(CaptureDevices); + + if(prop_filter) + pa_proplist_free(prop_filter); + prop_filter = NULL; + + /* PulseAudio doesn't like being CloseLib'd sometimes */ +} + +static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback || type == ALCbackend_Capture) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type) +{ + switch(type) + { + case ALL_DEVICE_PROBE: + ALCpulsePlayback_probeDevices(); + VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2); + break; + + case CAPTURE_DEVICE_PROBE: + ALCpulseCapture_probeDevices(); + VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2); + break; + } +} + +static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCpulsePlayback *backend; + + backend = ALCpulsePlayback_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCpulsePlayback_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + if(type == ALCbackend_Capture) + { + ALCpulseCapture *backend; + + backend = ALCpulseCapture_New(sizeof(*backend)); + if(!backend) return NULL; + memset(backend, 0, sizeof(*backend)); + + ALCpulseCapture_Construct(backend, device); + + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} + + +#else /* PA_API_VERSION == 12 */ + +#warning "Unsupported API version, backend will be unavailable!" + +typedef struct ALCpulseBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCpulseBackendFactory; +#define ALCPULSEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCpulseBackendFactory, ALCbackendFactory) } } + +static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory* UNUSED(self)) +{ + return ALC_FALSE; +} + +static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory* UNUSED(self)) +{ +} + +static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UNUSED(self), ALCbackend_Type UNUSED(type)) +{ + return ALC_FALSE; +} + +static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type)) +{ +} + +static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory* UNUSED(self), ALCdevice* UNUSED(device), ALCbackend_Type UNUSED(type)) +{ + return NULL; +} + +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory); + +#endif /* PA_API_VERSION == 12 */ + +ALCbackendFactory *ALCpulseBackendFactory_getFactory(void) +{ + static ALCpulseBackendFactory factory = ALCPULSEBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} diff --git a/libs/openal-soft/Alc/backends/qsa.c b/libs/openal-soft/Alc/backends/qsa.c new file mode 100644 index 00000000..9258e4e5 --- /dev/null +++ b/libs/openal-soft/Alc/backends/qsa.c @@ -0,0 +1,1161 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2011-2013 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "alMain.h" +#include "alu.h" +#include "threads.h" + + +typedef struct { + snd_pcm_t* pcmHandle; + int audio_fd; + + snd_pcm_channel_setup_t csetup; + snd_pcm_channel_params_t cparams; + + ALvoid* buffer; + ALsizei size; + + volatile int killNow; + althrd_t thread; +} qsa_data; + +typedef struct { + ALCchar* name; + int card; + int dev; +} DevMap; + +static const ALCchar qsaDevice[] = "QSA Default"; +static DevMap* allDevNameMap; +static ALuint numDevNames; +static DevMap* allCaptureDevNameMap; +static ALuint numCaptureDevNames; + +static const struct { + int32_t format; +} formatlist[] = { + {SND_PCM_SFMT_FLOAT_LE}, + {SND_PCM_SFMT_S32_LE}, + {SND_PCM_SFMT_U32_LE}, + {SND_PCM_SFMT_S16_LE}, + {SND_PCM_SFMT_U16_LE}, + {SND_PCM_SFMT_S8}, + {SND_PCM_SFMT_U8}, + {0}, +}; + +static const struct { + int32_t rate; +} ratelist[] = { + {192000}, + {176400}, + {96000}, + {88200}, + {48000}, + {44100}, + {32000}, + {24000}, + {22050}, + {16000}, + {12000}, + {11025}, + {8000}, + {0}, +}; + +static const struct { + int32_t channels; +} channellist[] = { + {8}, + {7}, + {6}, + {4}, + {2}, + {1}, + {0}, +}; + +static DevMap *deviceList(int type, ALuint *count) +{ + snd_ctl_t* handle; + snd_pcm_info_t pcminfo; + int max_cards, card, err, dev, num_devices, idx; + DevMap* dev_list; + char name[1024]; + struct snd_ctl_hw_info info; + void* temp; + + idx=0; + num_devices=0; + max_cards=snd_cards(); + + if (max_cards<=0) + { + return 0; + } + + dev_list=malloc(sizeof(DevMap)*1); + dev_list[0].name=strdup(qsaDevice); + num_devices=1; + + for (card=0; cardExtraData; + char* write_ptr; + int avail; + snd_pcm_channel_status_t status; + struct sched_param param; + fd_set wfds; + int selectret; + struct timeval timeout; + + SetRTPriority(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + /* Increase default 10 priority to 11 to avoid jerky sound */ + SchedGet(0, 0, ¶m); + param.sched_priority=param.sched_curpriority+1; + SchedSet(0, 0, SCHED_NOCHANGE, ¶m); + + ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + while (!data->killNow) + { + ALint len=data->size; + write_ptr=data->buffer; + + avail=len/frame_size; + aluMixData(device, write_ptr, avail); + + while (len>0 && !data->killNow) + { + FD_ZERO(&wfds); + FD_SET(data->audio_fd, &wfds); + timeout.tv_sec=2; + timeout.tv_usec=0; + + /* Select also works like time slice to OS */ + selectret=select(data->audio_fd+1, NULL, &wfds, NULL, &timeout); + switch (selectret) + { + case -1: + aluHandleDisconnect(device); + return 1; + case 0: + break; + default: + if (FD_ISSET(data->audio_fd, &wfds)) + { + break; + } + break; + } + + int wrote=snd_pcm_plugin_write(data->pcmHandle, write_ptr, len); + + if (wrote<=0) + { + if ((errno==EAGAIN) || (errno==EWOULDBLOCK)) + { + continue; + } + + memset(&status, 0, sizeof (status)); + status.channel=SND_PCM_CHANNEL_PLAYBACK; + + snd_pcm_plugin_status(data->pcmHandle, &status); + + /* we need to reinitialize the sound channel if we've underrun the buffer */ + if ((status.status==SND_PCM_STATUS_UNDERRUN) || + (status.status==SND_PCM_STATUS_READY)) + { + if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0) + { + aluHandleDisconnect(device); + break; + } + } + } + else + { + write_ptr+=wrote; + len-=wrote; + } + } + } + + return 0; +} + +/************/ +/* Playback */ +/************/ + +static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName) +{ + qsa_data* data; + char driver[64]; + int status; + int card, dev; + + strncpy(driver, GetConfigValue("qsa", "device", qsaDevice), sizeof(driver)-1); + driver[sizeof(driver)-1]=0; + + data=(qsa_data*)calloc(1, sizeof(qsa_data)); + if (data==NULL) + { + return ALC_OUT_OF_MEMORY; + } + + if (!deviceName) + { + deviceName=driver; + } + + if (strcmp(deviceName, qsaDevice)==0) + { + if (!deviceName) + { + deviceName=qsaDevice; + } + + status=snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK); + } + else + { + size_t idx; + + if (!allDevNameMap) + { + allDevNameMap=deviceList(SND_PCM_CHANNEL_PLAYBACK, &numDevNames); + } + + for (idx=0; idx0) + { + break; + } + } + } + if (idx==numDevNames) + { + free(data); + return ALC_INVALID_DEVICE; + } + + status=snd_pcm_open(&data->pcmHandle, allDevNameMap[idx].card, allDevNameMap[idx].dev, SND_PCM_OPEN_PLAYBACK); + } + + if (status<0) + { + free(data); + return ALC_INVALID_DEVICE; + } + + data->audio_fd=snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK); + if (data->audio_fd<0) + { + free(data); + return ALC_INVALID_DEVICE; + } + + al_string_copy_cstr(&device->DeviceName, deviceName); + device->ExtraData = data; + + return ALC_NO_ERROR; +} + +static void qsa_close_playback(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + + if (data->buffer!=NULL) + { + free(data->buffer); + data->buffer=NULL; + } + + snd_pcm_close(data->pcmHandle); + free(data); + + device->ExtraData=NULL; +} + +static ALCboolean qsa_reset_playback(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + int32_t format=-1; + + switch(device->FmtType) + { + case DevFmtByte: + format=SND_PCM_SFMT_S8; + break; + case DevFmtUByte: + format=SND_PCM_SFMT_U8; + break; + case DevFmtShort: + format=SND_PCM_SFMT_S16_LE; + break; + case DevFmtUShort: + format=SND_PCM_SFMT_U16_LE; + break; + case DevFmtInt: + format=SND_PCM_SFMT_S32_LE; + break; + case DevFmtUInt: + format=SND_PCM_SFMT_U32_LE; + break; + case DevFmtFloat: + format=SND_PCM_SFMT_FLOAT_LE; + break; + } + + /* we actually don't want to block on writes */ + snd_pcm_nonblock_mode(data->pcmHandle, 1); + /* Disable mmap to control data transfer to the audio device */ + snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP); + snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_BUFFER_PARTIAL_BLOCKS); + + // configure a sound channel + memset(&data->cparams, 0, sizeof(data->cparams)); + data->cparams.channel=SND_PCM_CHANNEL_PLAYBACK; + data->cparams.mode=SND_PCM_MODE_BLOCK; + data->cparams.start_mode=SND_PCM_START_FULL; + data->cparams.stop_mode=SND_PCM_STOP_STOP; + + data->cparams.buf.block.frag_size=device->UpdateSize* + ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType); + data->cparams.buf.block.frags_max=device->NumUpdates; + data->cparams.buf.block.frags_min=device->NumUpdates; + + data->cparams.format.interleave=1; + data->cparams.format.rate=device->Frequency; + data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans); + data->cparams.format.format=format; + + if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0) + { + int original_rate=data->cparams.format.rate; + int original_voices=data->cparams.format.voices; + int original_format=data->cparams.format.format; + int it; + int jt; + + for (it=0; it<1; it++) + { + /* Check for second pass */ + if (it==1) + { + original_rate=ratelist[0].rate; + original_voices=channellist[0].channels; + original_format=formatlist[0].format; + } + + do { + /* At first downgrade sample format */ + jt=0; + do { + if (formatlist[jt].format==data->cparams.format.format) + { + data->cparams.format.format=formatlist[jt+1].format; + break; + } + if (formatlist[jt].format==0) + { + data->cparams.format.format=0; + break; + } + jt++; + } while(1); + + if (data->cparams.format.format==0) + { + data->cparams.format.format=original_format; + + /* At secod downgrade sample rate */ + jt=0; + do { + if (ratelist[jt].rate==data->cparams.format.rate) + { + data->cparams.format.rate=ratelist[jt+1].rate; + break; + } + if (ratelist[jt].rate==0) + { + data->cparams.format.rate=0; + break; + } + jt++; + } while(1); + + if (data->cparams.format.rate==0) + { + data->cparams.format.rate=original_rate; + data->cparams.format.format=original_format; + + /* At third downgrade channels number */ + jt=0; + do { + if(channellist[jt].channels==data->cparams.format.voices) + { + data->cparams.format.voices=channellist[jt+1].channels; + break; + } + if (channellist[jt].channels==0) + { + data->cparams.format.voices=0; + break; + } + jt++; + } while(1); + } + + if (data->cparams.format.voices==0) + { + break; + } + } + + data->cparams.buf.block.frag_size=device->UpdateSize* + data->cparams.format.voices* + snd_pcm_format_width(data->cparams.format.format)/8; + data->cparams.buf.block.frags_max=device->NumUpdates; + data->cparams.buf.block.frags_min=device->NumUpdates; + if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0) + { + continue; + } + else + { + break; + } + } while(1); + + if (data->cparams.format.voices!=0) + { + break; + } + } + + if (data->cparams.format.voices==0) + { + return ALC_FALSE; + } + } + + if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0) + { + return ALC_FALSE; + } + + memset(&data->csetup, 0, sizeof(data->csetup)); + data->csetup.channel=SND_PCM_CHANNEL_PLAYBACK; + if (snd_pcm_plugin_setup(data->pcmHandle, &data->csetup)<0) + { + return ALC_FALSE; + } + + /* now fill back to the our AL device */ + device->Frequency=data->cparams.format.rate; + + switch (data->cparams.format.voices) + { + case 1: + device->FmtChans=DevFmtMono; + break; + case 2: + device->FmtChans=DevFmtStereo; + break; + case 4: + device->FmtChans=DevFmtQuad; + break; + case 6: + device->FmtChans=DevFmtX51; + break; + case 7: + device->FmtChans=DevFmtX61; + break; + case 8: + device->FmtChans=DevFmtX71; + break; + default: + device->FmtChans=DevFmtMono; + break; + } + + switch (data->cparams.format.format) + { + case SND_PCM_SFMT_S8: + device->FmtType=DevFmtByte; + break; + case SND_PCM_SFMT_U8: + device->FmtType=DevFmtUByte; + break; + case SND_PCM_SFMT_S16_LE: + device->FmtType=DevFmtShort; + break; + case SND_PCM_SFMT_U16_LE: + device->FmtType=DevFmtUShort; + break; + case SND_PCM_SFMT_S32_LE: + device->FmtType=DevFmtInt; + break; + case SND_PCM_SFMT_U32_LE: + device->FmtType=DevFmtUInt; + break; + case SND_PCM_SFMT_FLOAT_LE: + device->FmtType=DevFmtFloat; + break; + default: + device->FmtType=DevFmtShort; + break; + } + + SetDefaultChannelOrder(device); + + device->UpdateSize=data->csetup.buf.block.frag_size/ + (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType)); + device->NumUpdates=data->csetup.buf.block.frags; + + data->size=data->csetup.buf.block.frag_size; + data->buffer=malloc(data->size); + if (!data->buffer) + { + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static ALCboolean qsa_start_playback(ALCdevice* device) +{ + qsa_data *data = (qsa_data*)device->ExtraData; + + data->killNow = 0; + if(althrd_create(&data->thread, qsa_proc_playback, device) != althrd_success) + return ALC_FALSE; + + return ALC_TRUE; +} + +static void qsa_stop_playback(ALCdevice* device) +{ + qsa_data *data = (qsa_data*)device->ExtraData; + int res; + + if(data->killNow) + return; + + data->killNow = 1; + althrd_join(data->thread, &res); +} + +/***********/ +/* Capture */ +/***********/ + +static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName) +{ + qsa_data* data; + int format=-1; + char driver[64]; + int card, dev; + int status; + + strncpy(driver, GetConfigValue("qsa", "capture", qsaDevice), sizeof(driver)-1); + driver[sizeof(driver)-1]=0; + + data=(qsa_data*)calloc(1, sizeof(qsa_data)); + if (data==NULL) + { + return ALC_OUT_OF_MEMORY; + } + + if (!deviceName) + { + deviceName=driver; + } + + if (strcmp(deviceName, qsaDevice)==0) + { + if (!deviceName) + { + deviceName=qsaDevice; + } + + status=snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE); + } + else + { + size_t idx; + + if (!allCaptureDevNameMap) + { + allCaptureDevNameMap=deviceList(SND_PCM_CHANNEL_CAPTURE, &numDevNames); + } + + for (idx=0; idx0) + { + break; + } + } + } + if (idx==numDevNames) + { + free(data); + return ALC_INVALID_DEVICE; + } + + status=snd_pcm_open(&data->pcmHandle, allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev, SND_PCM_OPEN_CAPTURE); + } + + if (status<0) + { + free(data); + return ALC_INVALID_DEVICE; + } + + data->audio_fd=snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE); + if (data->audio_fd<0) + { + free(data); + return ALC_INVALID_DEVICE; + } + + al_string_copy_cstr(&device->DeviceName, deviceName); + device->ExtraData = data; + + switch (device->FmtType) + { + case DevFmtByte: + format=SND_PCM_SFMT_S8; + break; + case DevFmtUByte: + format=SND_PCM_SFMT_U8; + break; + case DevFmtShort: + format=SND_PCM_SFMT_S16_LE; + break; + case DevFmtUShort: + format=SND_PCM_SFMT_U16_LE; + break; + case DevFmtInt: + format=SND_PCM_SFMT_S32_LE; + break; + case DevFmtUInt: + format=SND_PCM_SFMT_U32_LE; + break; + case DevFmtFloat: + format=SND_PCM_SFMT_FLOAT_LE; + break; + } + + /* we actually don't want to block on reads */ + snd_pcm_nonblock_mode(data->pcmHandle, 1); + /* Disable mmap to control data transfer to the audio device */ + snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP); + + /* configure a sound channel */ + memset(&data->cparams, 0, sizeof(data->cparams)); + data->cparams.mode=SND_PCM_MODE_BLOCK; + data->cparams.channel=SND_PCM_CHANNEL_CAPTURE; + data->cparams.start_mode=SND_PCM_START_GO; + data->cparams.stop_mode=SND_PCM_STOP_STOP; + + data->cparams.buf.block.frag_size=device->UpdateSize* + ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType); + data->cparams.buf.block.frags_max=device->NumUpdates; + data->cparams.buf.block.frags_min=device->NumUpdates; + + data->cparams.format.interleave=1; + data->cparams.format.rate=device->Frequency; + data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans); + data->cparams.format.format=format; + + if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0) + { + int original_rate=data->cparams.format.rate; + int original_voices=data->cparams.format.voices; + int original_format=data->cparams.format.format; + int it; + int jt; + + for (it=0; it<1; it++) + { + /* Check for second pass */ + if (it==1) + { + original_rate=ratelist[0].rate; + original_voices=channellist[0].channels; + original_format=formatlist[0].format; + } + + do { + /* At first downgrade sample format */ + jt=0; + do { + if (formatlist[jt].format==data->cparams.format.format) + { + data->cparams.format.format=formatlist[jt+1].format; + break; + } + if (formatlist[jt].format==0) + { + data->cparams.format.format=0; + break; + } + jt++; + } while(1); + + if (data->cparams.format.format==0) + { + data->cparams.format.format=original_format; + + /* At secod downgrade sample rate */ + jt=0; + do { + if (ratelist[jt].rate==data->cparams.format.rate) + { + data->cparams.format.rate=ratelist[jt+1].rate; + break; + } + if (ratelist[jt].rate==0) + { + data->cparams.format.rate=0; + break; + } + jt++; + } while(1); + + if (data->cparams.format.rate==0) + { + data->cparams.format.rate=original_rate; + data->cparams.format.format=original_format; + + /* At third downgrade channels number */ + jt=0; + do { + if(channellist[jt].channels==data->cparams.format.voices) + { + data->cparams.format.voices=channellist[jt+1].channels; + break; + } + if (channellist[jt].channels==0) + { + data->cparams.format.voices=0; + break; + } + jt++; + } while(1); + } + + if (data->cparams.format.voices==0) + { + break; + } + } + + data->cparams.buf.block.frag_size=device->UpdateSize* + data->cparams.format.voices* + snd_pcm_format_width(data->cparams.format.format)/8; + data->cparams.buf.block.frags_max=device->NumUpdates; + data->cparams.buf.block.frags_min=device->NumUpdates; + if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0) + { + continue; + } + else + { + break; + } + } while(1); + + if (data->cparams.format.voices!=0) + { + break; + } + } + + if (data->cparams.format.voices==0) + { + return ALC_INVALID_VALUE; + } + } + + /* now fill back to the our AL device */ + device->Frequency=data->cparams.format.rate; + + switch (data->cparams.format.voices) + { + case 1: + device->FmtChans=DevFmtMono; + break; + case 2: + device->FmtChans=DevFmtStereo; + break; + case 4: + device->FmtChans=DevFmtQuad; + break; + case 6: + device->FmtChans=DevFmtX51; + break; + case 7: + device->FmtChans=DevFmtX61; + break; + case 8: + device->FmtChans=DevFmtX71; + break; + default: + device->FmtChans=DevFmtMono; + break; + } + + switch (data->cparams.format.format) + { + case SND_PCM_SFMT_S8: + device->FmtType=DevFmtByte; + break; + case SND_PCM_SFMT_U8: + device->FmtType=DevFmtUByte; + break; + case SND_PCM_SFMT_S16_LE: + device->FmtType=DevFmtShort; + break; + case SND_PCM_SFMT_U16_LE: + device->FmtType=DevFmtUShort; + break; + case SND_PCM_SFMT_S32_LE: + device->FmtType=DevFmtInt; + break; + case SND_PCM_SFMT_U32_LE: + device->FmtType=DevFmtUInt; + break; + case SND_PCM_SFMT_FLOAT_LE: + device->FmtType=DevFmtFloat; + break; + default: + device->FmtType=DevFmtShort; + break; + } + + return ALC_NO_ERROR; +} + +static void qsa_close_capture(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + + if (data->pcmHandle!=NULL) + { + snd_pcm_close(data->pcmHandle); + } + free(data); + device->ExtraData=NULL; +} + +static void qsa_start_capture(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + int rstatus; + + if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) + { + ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); + return; + } + + memset(&data->csetup, 0, sizeof(data->csetup)); + data->csetup.channel=SND_PCM_CHANNEL_CAPTURE; + if ((rstatus=snd_pcm_plugin_setup(data->pcmHandle, &data->csetup))<0) + { + ERR("capture setup failed: %s\n", snd_strerror(rstatus)); + return; + } + + snd_pcm_capture_go(data->pcmHandle); + + device->UpdateSize=data->csetup.buf.block.frag_size/ + (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType)); + device->NumUpdates=data->csetup.buf.block.frags; +} + +static void qsa_stop_capture(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + + snd_pcm_capture_flush(data->pcmHandle); +} + +static ALCuint qsa_available_samples(ALCdevice* device) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + snd_pcm_channel_status_t status; + ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + ALint free_size; + int rstatus; + + memset(&status, 0, sizeof (status)); + status.channel=SND_PCM_CHANNEL_CAPTURE; + snd_pcm_plugin_status(data->pcmHandle, &status); + if ((status.status==SND_PCM_STATUS_OVERRUN) || + (status.status==SND_PCM_STATUS_READY)) + { + if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) + { + ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); + aluHandleDisconnect(device); + return 0; + } + + snd_pcm_capture_go(data->pcmHandle); + return 0; + } + + free_size=data->csetup.buf.block.frag_size*data->csetup.buf.block.frags; + free_size-=status.free; + + return free_size/frame_size; +} + +static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) +{ + qsa_data* data=(qsa_data*)device->ExtraData; + char* read_ptr; + snd_pcm_channel_status_t status; + fd_set rfds; + int selectret; + struct timeval timeout; + int bytes_read; + ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + ALint len=samples*frame_size; + int rstatus; + + read_ptr=buffer; + + while (len>0) + { + FD_ZERO(&rfds); + FD_SET(data->audio_fd, &rfds); + timeout.tv_sec=2; + timeout.tv_usec=0; + + /* Select also works like time slice to OS */ + bytes_read=0; + selectret=select(data->audio_fd+1, &rfds, NULL, NULL, &timeout); + switch (selectret) + { + case -1: + aluHandleDisconnect(device); + return ALC_INVALID_DEVICE; + case 0: + break; + default: + if (FD_ISSET(data->audio_fd, &rfds)) + { + bytes_read=snd_pcm_plugin_read(data->pcmHandle, read_ptr, len); + break; + } + break; + } + + if (bytes_read<=0) + { + if ((errno==EAGAIN) || (errno==EWOULDBLOCK)) + { + continue; + } + + memset(&status, 0, sizeof (status)); + status.channel=SND_PCM_CHANNEL_CAPTURE; + snd_pcm_plugin_status(data->pcmHandle, &status); + + /* we need to reinitialize the sound channel if we've overrun the buffer */ + if ((status.status==SND_PCM_STATUS_OVERRUN) || + (status.status==SND_PCM_STATUS_READY)) + { + if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) + { + ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); + aluHandleDisconnect(device); + return ALC_INVALID_DEVICE; + } + snd_pcm_capture_go(data->pcmHandle); + } + } + else + { + read_ptr+=bytes_read; + len-=bytes_read; + } + } + + return ALC_NO_ERROR; +} + +static ALint64 qsa_get_latency(ALCdevice* device) +{ + ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + return (ALint64)(device->UpdateSize*device->NumUpdates/frame_size)* + 1000000000/device->Frequency; +} + +BackendFuncs qsa_funcs= +{ + qsa_open_playback, + qsa_close_playback, + qsa_reset_playback, + qsa_start_playback, + qsa_stop_playback, + qsa_open_capture, + qsa_close_capture, + qsa_start_capture, + qsa_stop_capture, + qsa_capture_samples, + qsa_available_samples, + qsa_get_latency, +}; + +ALCboolean alc_qsa_init(BackendFuncs* func_list) +{ + *func_list=qsa_funcs; + + return ALC_TRUE; +} + +void alc_qsa_deinit(void) +{ + ALuint i; + + for (i=0; i +#include +#include + +#include "alMain.h" +#include "alu.h" +#include "threads.h" + +#include + + +static const ALCchar sndio_device[] = "SndIO Default"; + + +static ALCboolean sndio_load(void) +{ + return ALC_TRUE; +} + + +typedef struct { + struct sio_hdl *sndHandle; + + ALvoid *mix_data; + ALsizei data_size; + + volatile int killNow; + althrd_t thread; +} sndio_data; + + +static int sndio_proc(void *ptr) +{ + ALCdevice *device = ptr; + sndio_data *data = device->ExtraData; + ALsizei frameSize; + size_t wrote; + + SetRTPriority(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + 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"); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); + break; + } + + len -= wrote; + WritePtr += wrote; + } + } + + return 0; +} + + + +static ALCenum 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_INVALID_VALUE; + + 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_INVALID_VALUE; + } + + al_string_copy_cstr(&device->DeviceName, deviceName); + device->ExtraData = data; + + return ALC_NO_ERROR; +} + +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: + case DevFmtShort: + par.bits = 16; + par.sig = 1; + break; + case DevFmtUShort: + par.bits = 16; + par.sig = 0; + break; + case DevFmtInt: + par.bits = 32; + par.sig = 1; + break; + case DevFmtUInt: + par.bits = 32; + 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.bits != par.bps*8) + { + ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8); + return ALC_FALSE; + } + + device->Frequency = par.rate; + device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo); + + 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 if(par.bits == 32 && par.sig == 1) + device->FmtType = DevFmtInt; + else if(par.bits == 32 && par.sig == 0) + device->FmtType = DevFmtUInt; + 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); + + return ALC_TRUE; +} + +static ALCboolean sndio_start_playback(ALCdevice *device) +{ + sndio_data *data = device->ExtraData; + + if(!sio_start(data->sndHandle)) + { + ERR("Error starting playback\n"); + return ALC_FALSE; + } + + data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + data->mix_data = calloc(1, data->data_size); + + data->killNow = 0; + if(althrd_create(&data->thread, sndio_proc, device) != althrd_success) + { + 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; + int res; + + if(data->killNow) + return; + + data->killNow = 1; + althrd_join(data->thread, &res); + + 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_start_playback, + sndio_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + ALCdevice_GetLatencyDefault +}; + +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) +{ +} + +void alc_sndio_probe(enum DevProbe type) +{ + switch(type) + { + case ALL_DEVICE_PROBE: + AppendAllDevicesList(sndio_device); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff --git a/libs/openal-soft/Alc/backends/solaris.c b/libs/openal-soft/Alc/backends/solaris.c new file mode 100644 index 00000000..20d861d2 --- /dev/null +++ b/libs/openal-soft/Alc/backends/solaris.c @@ -0,0 +1,288 @@ +/** + * 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 "alu.h" +#include "threads.h" +#include "compat.h" + +#include + + +static const ALCchar solaris_device[] = "Solaris Default"; + +static const char *solaris_driver = "/dev/audio"; + +typedef struct { + int fd; + + ALubyte *mix_data; + int data_size; + + volatile int killNow; + althrd_t thread; +} solaris_data; + + +static int SolarisProc(void *ptr) +{ + ALCdevice *Device = (ALCdevice*)ptr; + solaris_data *data = (solaris_data*)Device->ExtraData; + ALint frameSize; + int wrote; + + SetRTPriority(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); + + while(!data->killNow && Device->Connected) + { + ALint len = data->data_size; + ALubyte *WritePtr = data->mix_data; + + aluMixData(Device, 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)); + ALCdevice_Lock(Device); + aluHandleDisconnect(Device); + ALCdevice_Unlock(Device); + break; + } + + al_nssleep(0, 1000000); + continue; + } + + len -= wrote; + WritePtr += wrote; + } + } + + return 0; +} + + +static ALCenum solaris_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + solaris_data *data; + + if(!deviceName) + deviceName = solaris_device; + else if(strcmp(deviceName, solaris_device) != 0) + return ALC_INVALID_VALUE; + + data = (solaris_data*)calloc(1, sizeof(solaris_data)); + data->killNow = 0; + + data->fd = open(solaris_driver, O_WRONLY); + if(data->fd == -1) + { + free(data); + ERR("Could not open %s: %s\n", solaris_driver, strerror(errno)); + return ALC_INVALID_VALUE; + } + + al_string_copy_cstr(&device->DeviceName, deviceName); + device->ExtraData = data; + return ALC_NO_ERROR; +} + +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 DevFmtInt: + case DevFmtUInt: + 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_LINEAR8 && device->FmtType == DevFmtUByte) || + (info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtByte) || + (info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtShort) || + (info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtInt))) + { + ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(device->FmtType), + info.play.precision, info.play.encoding); + return ALC_FALSE; + } + + device->Frequency = info.play.sample_rate; + device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1; + + SetDefaultChannelOrder(device); + + return ALC_TRUE; +} + +static ALCboolean solaris_start_playback(ALCdevice *device) +{ + solaris_data *data = (solaris_data*)device->ExtraData; + + data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + data->mix_data = calloc(1, data->data_size); + + data->killNow = 0; + if(althrd_create(&data->thread, SolarisProc, device) != althrd_success) + { + 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; + int res; + + if(data->killNow) + return; + + data->killNow = 1; + althrd_join(data->thread, &res); + + 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_start_playback, + solaris_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + ALCdevice_GetLatencyDefault +}; + +ALCboolean alc_solaris_init(BackendFuncs *func_list) +{ + ConfigValueStr("solaris", "device", &solaris_driver); + + *func_list = solaris_funcs; + return ALC_TRUE; +} + +void alc_solaris_deinit(void) +{ +} + +void alc_solaris_probe(enum DevProbe type) +{ + switch(type) + { + case ALL_DEVICE_PROBE: + { +#ifdef HAVE_STAT + struct stat buf; + if(stat(solaris_driver, &buf) == 0) +#endif + AppendAllDevicesList(solaris_device); + } + break; + + case CAPTURE_DEVICE_PROBE: + break; + } +} diff --git a/libs/openal-soft/Alc/backends/wave.c b/libs/openal-soft/Alc/backends/wave.c new file mode 100644 index 00000000..421ca5d7 --- /dev/null +++ b/libs/openal-soft/Alc/backends/wave.c @@ -0,0 +1,377 @@ +/** + * 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 +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include "alMain.h" +#include "alu.h" +#include "threads.h" +#include "compat.h" + + +typedef struct { + FILE *f; + long DataStart; + + ALvoid *buffer; + ALuint size; + + volatile int killNow; + althrd_t 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 int WaveProc(void *ptr) +{ + ALCdevice *device = (ALCdevice*)ptr; + wave_data *data = (wave_data*)device->ExtraData; + struct timespec now, start; + ALint64 avail, done; + ALuint frameSize; + size_t fs; + const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 / + device->Frequency / 2); + + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + done = 0; + if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC) + { + ERR("Failed to get starting time\n"); + return 1; + } + while(!data->killNow && device->Connected) + { + if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) + { + ERR("Failed to get current time\n"); + return 1; + } + + avail = (now.tv_sec - start.tv_sec) * device->Frequency; + avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000; + if(avail < done) + { + /* Oops, time skipped backwards. Reset the number of samples done + * with one update available since we (likely) just came back from + * sleeping. */ + done = avail - device->UpdateSize; + } + + if(avail-done < device->UpdateSize) + al_nssleep(0, restTime); + else while(avail-done >= device->UpdateSize) + { + aluMixData(device, data->buffer, device->UpdateSize); + done += device->UpdateSize; + + if(!IS_LITTLE_ENDIAN) + { + ALuint bytesize = BytesFromDevFmt(device->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, device->UpdateSize, + data->f); + (void)fs; + } + if(ferror(data->f)) + { + ERR("Error writing to file\n"); + ALCdevice_Lock(device); + aluHandleDisconnect(device); + ALCdevice_Unlock(device); + break; + } + } + } + + return 0; +} + +static ALCenum wave_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + wave_data *data; + const char *fname; + + fname = GetConfigValue("wave", "file", ""); + if(!fname[0]) + return ALC_INVALID_VALUE; + + if(!deviceName) + deviceName = waveDevice; + else if(strcmp(deviceName, waveDevice) != 0) + return ALC_INVALID_VALUE; + + data = (wave_data*)calloc(1, sizeof(wave_data)); + + data->f = al_fopen(fname, "wb"); + if(!data->f) + { + free(data); + ERR("Could not open file '%s': %s\n", fname, strerror(errno)); + return ALC_INVALID_VALUE; + } + + al_string_copy_cstr(&device->DeviceName, deviceName); + device->ExtraData = data; + return ALC_NO_ERROR; +} + +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 DevFmtUInt: + device->FmtType = DevFmtInt; + break; + case DevFmtUByte: + case DevFmtShort: + case DevFmtInt: + 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); + (void)val; + + 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); + + SetDefaultWFXChannelOrder(device); + + return ALC_TRUE; +} + +static ALCboolean wave_start_playback(ALCdevice *device) +{ + wave_data *data = (wave_data*)device->ExtraData; + + data->size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + data->buffer = malloc(data->size); + if(!data->buffer) + { + ERR("Buffer malloc failed\n"); + return ALC_FALSE; + } + + data->killNow = 0; + if(althrd_create(&data->thread, WaveProc, device) != althrd_success) + { + 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; + int res; + + if(data->killNow) + return; + + data->killNow = 1; + althrd_join(data->thread, &res); + + 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_start_playback, + wave_stop_playback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + ALCdevice_GetLatencyDefault +}; + +ALCboolean alc_wave_init(BackendFuncs *func_list) +{ + *func_list = wave_funcs; + return ALC_TRUE; +} + +void alc_wave_deinit(void) +{ +} + +void alc_wave_probe(enum DevProbe type) +{ + if(!ConfigValueExists("wave", "file")) + return; + + switch(type) + { + case ALL_DEVICE_PROBE: + AppendAllDevicesList(waveDevice); + break; + case CAPTURE_DEVICE_PROBE: + break; + } +} diff --git a/libs/openal-soft/Alc/backends/winmm.c b/libs/openal-soft/Alc/backends/winmm.c new file mode 100644 index 00000000..cad66470 --- /dev/null +++ b/libs/openal-soft/Alc/backends/winmm.c @@ -0,0 +1,716 @@ +/** + * 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 "alu.h" +#include "threads.h" + +#ifndef WAVE_FORMAT_IEEE_FLOAT +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 +#endif + + +typedef struct { + // MMSYSTEM Device + volatile ALboolean killNow; + althrd_t thread; + + RefCount WaveBuffersCommitted; + WAVEHDR WaveBuffer[4]; + + union { + HWAVEIN In; + HWAVEOUT Out; + } WaveHandle; + + WAVEFORMATEX Format; + + RingBuffer *Ring; +} WinMMData; + + +TYPEDEF_VECTOR(al_string, vector_al_string) +static vector_al_string PlaybackDevices; +static vector_al_string CaptureDevices; + +static void clear_devlist(vector_al_string *list) +{ + VECTOR_FOR_EACH(al_string, *list, al_string_deinit); + VECTOR_RESIZE(*list, 0); +} + + +static void ProbePlaybackDevices(void) +{ + al_string *iter, *end; + ALuint numdevs; + ALuint i; + + clear_devlist(&PlaybackDevices); + + numdevs = waveOutGetNumDevs(); + VECTOR_RESERVE(PlaybackDevices, numdevs); + for(i = 0;i < numdevs;i++) + { + WAVEOUTCAPSW WaveCaps; + al_string dname; + + AL_STRING_INIT(dname); + if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) + { + ALuint count = 0; + do { + al_string_copy_wcstr(&dname, WaveCaps.szPname); + if(count != 0) + { + char str[64]; + snprintf(str, sizeof(str), " #%d", count+1); + al_string_append_cstr(&dname, str); + } + count++; + + iter = VECTOR_ITER_BEGIN(PlaybackDevices); + end = VECTOR_ITER_END(PlaybackDevices); + for(;iter != end;iter++) + { + if(al_string_cmp(*iter, dname) == 0) + break; + } + } while(iter != end); + + TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i); + } + VECTOR_PUSH_BACK(PlaybackDevices, dname); + } +} + +static void ProbeCaptureDevices(void) +{ + al_string *iter, *end; + ALuint numdevs; + ALuint i; + + clear_devlist(&CaptureDevices); + + numdevs = waveInGetNumDevs(); + VECTOR_RESERVE(CaptureDevices, numdevs); + for(i = 0;i < numdevs;i++) + { + WAVEINCAPSW WaveCaps; + al_string dname; + + AL_STRING_INIT(dname); + if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) + { + ALuint count = 0; + do { + al_string_copy_wcstr(&dname, WaveCaps.szPname); + if(count != 0) + { + char str[64]; + snprintf(str, sizeof(str), " #%d", count+1); + al_string_append_cstr(&dname, str); + } + count++; + + iter = VECTOR_ITER_BEGIN(CaptureDevices); + end = VECTOR_ITER_END(CaptureDevices); + for(;iter != end;iter++) + { + if(al_string_cmp(*iter, dname) == 0) + break; + } + } while(iter != end); + + TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i); + } + VECTOR_PUSH_BACK(CaptureDevices, dname); + } +} + + +/* + 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 UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2)) +{ + ALCdevice *Device = (ALCdevice*)instance; + WinMMData *data = Device->ExtraData; + + if(msg != WOM_DONE) + return; + + DecrementRef(&data->WaveBuffersCommitted); + PostThreadMessage(data->thread, msg, 0, param1); +} + +FORCE_ALIGN static int PlaybackThreadProc(void *arg) +{ + ALCdevice *Device = (ALCdevice*)arg; + WinMMData *data = Device->ExtraData; + WAVEHDR *WaveHdr; + MSG msg; + + SetRTPriority(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + while(GetMessage(&msg, NULL, 0, 0)) + { + if(msg.message != WOM_DONE) + continue; + + if(data->killNow) + { + if(ReadRef(&data->WaveBuffersCommitted) == 0) + break; + continue; + } + + WaveHdr = ((WAVEHDR*)msg.lParam); + aluMixData(Device, WaveHdr->lpData, WaveHdr->dwBufferLength / + data->Format.nBlockAlign); + + // Send buffer back to play more data + waveOutWrite(data->WaveHandle.Out, WaveHdr, sizeof(WAVEHDR)); + IncrementRef(&data->WaveBuffersCommitted); + } + + 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 UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2)) +{ + ALCdevice *Device = (ALCdevice*)instance; + WinMMData *data = Device->ExtraData; + + if(msg != WIM_DATA) + return; + + DecrementRef(&data->WaveBuffersCommitted); + PostThreadMessage(data->thread, msg, 0, param1); +} + +static int CaptureThreadProc(void *arg) +{ + ALCdevice *Device = (ALCdevice*)arg; + WinMMData *data = Device->ExtraData; + WAVEHDR *WaveHdr; + MSG msg; + + althrd_setname(althrd_current(), "alsoft-record"); + + while(GetMessage(&msg, NULL, 0, 0)) + { + if(msg.message != WIM_DATA) + continue; + /* Don't wait for other buffers to finish before quitting. We're + * closing so we don't need them. */ + if(data->killNow) + break; + + WaveHdr = ((WAVEHDR*)msg.lParam); + WriteRingBuffer(data->Ring, (ALubyte*)WaveHdr->lpData, + WaveHdr->dwBytesRecorded/data->Format.nBlockAlign); + + // Send buffer back to capture more data + waveInAddBuffer(data->WaveHandle.In, WaveHdr, sizeof(WAVEHDR)); + IncrementRef(&data->WaveBuffersCommitted); + } + + return 0; +} + + +static ALCenum WinMMOpenPlayback(ALCdevice *Device, const ALCchar *deviceName) +{ + WinMMData *data = NULL; + const al_string *iter, *end; + UINT DeviceID; + MMRESULT res; + + if(VECTOR_SIZE(PlaybackDevices) == 0) + ProbePlaybackDevices(); + + // Find the Device ID matching the deviceName if valid + iter = VECTOR_ITER_BEGIN(PlaybackDevices); + end = VECTOR_ITER_END(PlaybackDevices); + for(;iter != end;iter++) + { + if(!al_string_empty(*iter) && + (!deviceName || al_string_cmp_cstr(*iter, deviceName) == 0)) + { + DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices)); + break; + } + } + if(iter == end) + return ALC_INVALID_VALUE; + + data = calloc(1, sizeof(*data)); + if(!data) + return ALC_OUT_OF_MEMORY; + Device->ExtraData = data; + +retry_open: + memset(&data->Format, 0, sizeof(WAVEFORMATEX)); + if(Device->FmtType == DevFmtFloat) + { + data->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + data->Format.wBitsPerSample = 32; + } + else + { + data->Format.wFormatTag = WAVE_FORMAT_PCM; + if(Device->FmtType == DevFmtUByte || Device->FmtType == DevFmtByte) + data->Format.wBitsPerSample = 8; + else + data->Format.wBitsPerSample = 16; + } + data->Format.nChannels = ((Device->FmtChans == DevFmtMono) ? 1 : 2); + data->Format.nBlockAlign = data->Format.wBitsPerSample * + data->Format.nChannels / 8; + data->Format.nSamplesPerSec = Device->Frequency; + data->Format.nAvgBytesPerSec = data->Format.nSamplesPerSec * + data->Format.nBlockAlign; + data->Format.cbSize = 0; + + if((res=waveOutOpen(&data->WaveHandle.Out, DeviceID, &data->Format, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) + { + if(Device->FmtType == DevFmtFloat) + { + Device->FmtType = DevFmtShort; + goto retry_open; + } + ERR("waveOutOpen failed: %u\n", res); + goto failure; + } + + al_string_copy(&Device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID)); + return ALC_NO_ERROR; + +failure: + if(data->WaveHandle.Out) + waveOutClose(data->WaveHandle.Out); + + free(data); + Device->ExtraData = NULL; + return ALC_INVALID_VALUE; +} + +static void WinMMClosePlayback(ALCdevice *device) +{ + WinMMData *data = (WinMMData*)device->ExtraData; + + // Close the Wave device + waveOutClose(data->WaveHandle.Out); + data->WaveHandle.Out = 0; + + free(data); + device->ExtraData = NULL; +} + +static ALCboolean WinMMResetPlayback(ALCdevice *device) +{ + WinMMData *data = (WinMMData*)device->ExtraData; + + device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize * + data->Format.nSamplesPerSec / + device->Frequency); + device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4; + device->NumUpdates = 4; + device->Frequency = data->Format.nSamplesPerSec; + + if(data->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) + { + if(data->Format.wBitsPerSample == 32) + device->FmtType = DevFmtFloat; + else + { + ERR("Unhandled IEEE float sample depth: %d\n", data->Format.wBitsPerSample); + return ALC_FALSE; + } + } + else if(data->Format.wFormatTag == WAVE_FORMAT_PCM) + { + if(data->Format.wBitsPerSample == 16) + device->FmtType = DevFmtShort; + else if(data->Format.wBitsPerSample == 8) + device->FmtType = DevFmtUByte; + else + { + ERR("Unhandled PCM sample depth: %d\n", data->Format.wBitsPerSample); + return ALC_FALSE; + } + } + else + { + ERR("Unhandled format tag: 0x%04x\n", data->Format.wFormatTag); + return ALC_FALSE; + } + + if(data->Format.nChannels == 2) + device->FmtChans = DevFmtStereo; + else if(data->Format.nChannels == 1) + device->FmtChans = DevFmtMono; + else + { + ERR("Unhandled channel count: %d\n", data->Format.nChannels); + return ALC_FALSE; + } + SetDefaultWFXChannelOrder(device); + + return ALC_TRUE; +} + +static ALCboolean WinMMStartPlayback(ALCdevice *device) +{ + WinMMData *data = (WinMMData*)device->ExtraData; + ALbyte *BufferData; + ALint BufferSize; + ALuint i; + + data->killNow = AL_FALSE; + if(althrd_create(&data->thread, PlaybackThreadProc, device) != althrd_success) + return ALC_FALSE; + + InitRef(&data->WaveBuffersCommitted, 0); + + // Create 4 Buffers + BufferSize = device->UpdateSize*device->NumUpdates / 4; + BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + + BufferData = calloc(4, BufferSize); + for(i = 0;i < 4;i++) + { + memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR)); + data->WaveBuffer[i].dwBufferLength = BufferSize; + data->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData : + (data->WaveBuffer[i-1].lpData + + data->WaveBuffer[i-1].dwBufferLength)); + waveOutPrepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR)); + waveOutWrite(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR)); + IncrementRef(&data->WaveBuffersCommitted); + } + + return ALC_TRUE; +} + +static void WinMMStopPlayback(ALCdevice *device) +{ + WinMMData *data = (WinMMData*)device->ExtraData; + void *buffer = NULL; + int i; + + if(data->killNow) + return; + + // Set flag to stop processing headers + data->killNow = AL_TRUE; + althrd_join(data->thread, &i); + + // Release the wave buffers + for(i = 0;i < 4;i++) + { + waveOutUnprepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR)); + if(i == 0) buffer = data->WaveBuffer[i].lpData; + data->WaveBuffer[i].lpData = NULL; + } + free(buffer); +} + + +static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName) +{ + const al_string *iter, *end; + ALbyte *BufferData = NULL; + DWORD CapturedDataSize; + WinMMData *data = NULL; + ALint BufferSize; + UINT DeviceID; + MMRESULT res; + ALuint i; + + if(VECTOR_SIZE(CaptureDevices) == 0) + ProbeCaptureDevices(); + + // Find the Device ID matching the deviceName if valid + iter = VECTOR_ITER_BEGIN(CaptureDevices); + end = VECTOR_ITER_END(CaptureDevices); + for(;iter != end;iter++) + { + if(!al_string_empty(*iter) && + (!deviceName || al_string_cmp_cstr(*iter, deviceName) == 0)) + { + DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices)); + break; + } + } + if(iter == end) + return ALC_INVALID_VALUE; + + switch(Device->FmtChans) + { + case DevFmtMono: + case DevFmtStereo: + break; + + case DevFmtQuad: + case DevFmtX51: + case DevFmtX51Side: + case DevFmtX61: + case DevFmtX71: + return ALC_INVALID_ENUM; + } + + switch(Device->FmtType) + { + case DevFmtUByte: + case DevFmtShort: + case DevFmtInt: + case DevFmtFloat: + break; + + case DevFmtByte: + case DevFmtUShort: + case DevFmtUInt: + return ALC_INVALID_ENUM; + } + + data = calloc(1, sizeof(*data)); + if(!data) + return ALC_OUT_OF_MEMORY; + Device->ExtraData = data; + + memset(&data->Format, 0, sizeof(WAVEFORMATEX)); + data->Format.wFormatTag = ((Device->FmtType == DevFmtFloat) ? + WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM); + data->Format.nChannels = ChannelsFromDevFmt(Device->FmtChans); + data->Format.wBitsPerSample = BytesFromDevFmt(Device->FmtType) * 8; + data->Format.nBlockAlign = data->Format.wBitsPerSample * + data->Format.nChannels / 8; + data->Format.nSamplesPerSec = Device->Frequency; + data->Format.nAvgBytesPerSec = data->Format.nSamplesPerSec * + data->Format.nBlockAlign; + data->Format.cbSize = 0; + + if((res=waveInOpen(&data->WaveHandle.In, DeviceID, &data->Format, (DWORD_PTR)&WaveInProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) + { + ERR("waveInOpen failed: %u\n", res); + goto failure; + } + + // Allocate circular memory buffer for the captured audio + CapturedDataSize = Device->UpdateSize*Device->NumUpdates; + + // Make sure circular buffer is at least 100ms in size + if(CapturedDataSize < (data->Format.nSamplesPerSec / 10)) + CapturedDataSize = data->Format.nSamplesPerSec / 10; + + data->Ring = CreateRingBuffer(data->Format.nBlockAlign, CapturedDataSize); + if(!data->Ring) + goto failure; + + InitRef(&data->WaveBuffersCommitted, 0); + + // Create 4 Buffers of 50ms each + BufferSize = data->Format.nAvgBytesPerSec / 20; + BufferSize -= (BufferSize % data->Format.nBlockAlign); + + BufferData = calloc(4, BufferSize); + if(!BufferData) + goto failure; + + for(i = 0;i < 4;i++) + { + memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR)); + data->WaveBuffer[i].dwBufferLength = BufferSize; + data->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData : + (data->WaveBuffer[i-1].lpData + + data->WaveBuffer[i-1].dwBufferLength)); + data->WaveBuffer[i].dwFlags = 0; + data->WaveBuffer[i].dwLoops = 0; + waveInPrepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR)); + waveInAddBuffer(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR)); + IncrementRef(&data->WaveBuffersCommitted); + } + + if(althrd_create(&data->thread, CaptureThreadProc, Device) != althrd_success) + goto failure; + + al_string_copy(&Device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID)); + return ALC_NO_ERROR; + +failure: + if(BufferData) + { + for(i = 0;i < 4;i++) + waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR)); + free(BufferData); + } + + if(data->Ring) + DestroyRingBuffer(data->Ring); + + if(data->WaveHandle.In) + waveInClose(data->WaveHandle.In); + + free(data); + Device->ExtraData = NULL; + return ALC_INVALID_VALUE; +} + +static void WinMMCloseCapture(ALCdevice *Device) +{ + WinMMData *data = (WinMMData*)Device->ExtraData; + void *buffer = NULL; + int i; + + /* Tell the processing thread to quit and wait for it to do so. */ + data->killNow = AL_TRUE; + PostThreadMessage(data->thread, WM_QUIT, 0, 0); + + althrd_join(data->thread, &i); + + /* Make sure capture is stopped and all pending buffers are flushed. */ + waveInReset(data->WaveHandle.In); + + // Release the wave buffers + for(i = 0;i < 4;i++) + { + waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR)); + if(i == 0) buffer = data->WaveBuffer[i].lpData; + data->WaveBuffer[i].lpData = NULL; + } + free(buffer); + + DestroyRingBuffer(data->Ring); + data->Ring = NULL; + + // Close the Wave device + waveInClose(data->WaveHandle.In); + data->WaveHandle.In = 0; + + free(data); + Device->ExtraData = NULL; +} + +static void WinMMStartCapture(ALCdevice *Device) +{ + WinMMData *data = (WinMMData*)Device->ExtraData; + waveInStart(data->WaveHandle.In); +} + +static void WinMMStopCapture(ALCdevice *Device) +{ + WinMMData *data = (WinMMData*)Device->ExtraData; + waveInStop(data->WaveHandle.In); +} + +static ALCenum WinMMCaptureSamples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples) +{ + WinMMData *data = (WinMMData*)Device->ExtraData; + ReadRingBuffer(data->Ring, Buffer, Samples); + return ALC_NO_ERROR; +} + +static ALCuint WinMMAvailableSamples(ALCdevice *Device) +{ + WinMMData *data = (WinMMData*)Device->ExtraData; + return RingBufferSize(data->Ring); +} + + +static inline void AppendAllDevicesList2(const al_string *name) +{ + if(!al_string_empty(*name)) + AppendAllDevicesList(al_string_get_cstr(*name)); +} +static inline void AppendCaptureDeviceList2(const al_string *name) +{ + if(!al_string_empty(*name)) + AppendCaptureDeviceList(al_string_get_cstr(*name)); +} + +static const BackendFuncs WinMMFuncs = { + WinMMOpenPlayback, + WinMMClosePlayback, + WinMMResetPlayback, + WinMMStartPlayback, + WinMMStopPlayback, + WinMMOpenCapture, + WinMMCloseCapture, + WinMMStartCapture, + WinMMStopCapture, + WinMMCaptureSamples, + WinMMAvailableSamples, + ALCdevice_GetLatencyDefault +}; + +ALCboolean alcWinMMInit(BackendFuncs *FuncList) +{ + VECTOR_INIT(PlaybackDevices); + VECTOR_INIT(CaptureDevices); + + *FuncList = WinMMFuncs; + return ALC_TRUE; +} + +void alcWinMMDeinit() +{ + clear_devlist(&PlaybackDevices); + VECTOR_DEINIT(PlaybackDevices); + + clear_devlist(&CaptureDevices); + VECTOR_DEINIT(CaptureDevices); +} + +void alcWinMMProbe(enum DevProbe type) +{ + switch(type) + { + case ALL_DEVICE_PROBE: + ProbePlaybackDevices(); + VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2); + break; + + case CAPTURE_DEVICE_PROBE: + ProbeCaptureDevices(); + VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2); + break; + } +} diff --git a/libs/openal-soft/Alc/bs2b.c b/libs/openal-soft/Alc/bs2b.c new file mode 100644 index 00000000..d936f75a --- /dev/null +++ b/libs/openal-soft/Alc/bs2b.c @@ -0,0 +1,143 @@ +/*- + * 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 + +#include "bs2b.h" +#include "alu.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/* Set up all data. */ +static void init(struct bs2b *bs2b) +{ + float Fc_lo, Fc_hi; + float G_lo, G_hi; + float x, g; + + bs2b->srate = clampi(bs2b->srate, 2000, 192000); + + switch(bs2b->level) + { + case BS2B_LOW_CLEVEL: /* Low crossfeed level */ + Fc_lo = 360.0f; + Fc_hi = 501.0f; + G_lo = 0.398107170553497f; + G_hi = 0.205671765275719f; + break; + + case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */ + Fc_lo = 500.0f; + Fc_hi = 711.0f; + G_lo = 0.459726988530872f; + G_hi = 0.228208484414988f; + break; + + case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */ + Fc_lo = 700.0f; + Fc_hi = 1021.0f; + G_lo = 0.530884444230988f; + G_hi = 0.250105790667544f; + break; + + case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */ + Fc_lo = 360.0f; + Fc_hi = 494.0f; + G_lo = 0.316227766016838f; + G_hi = 0.168236228897329f; + break; + + case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */ + Fc_lo = 500.0f; + Fc_hi = 689.0f; + G_lo = 0.354813389233575f; + G_hi = 0.187169483835901f; + break; + + default: /* High easy crossfeed level */ + bs2b->level = BS2B_HIGH_ECLEVEL; + + Fc_lo = 700.0f; + Fc_hi = 975.0f; + G_lo = 0.398107170553497f; + G_hi = 0.205671765275719f; + break; + } /* switch */ + + g = 1.0f / (1.0f - G_hi + G_lo); + + /* $fc = $Fc / $s; + * $d = 1 / 2 / pi / $fc; + * $x = exp(-1 / $d); + */ + x = expf(-2.0f * F_PI * Fc_lo / bs2b->srate); + bs2b->b1_lo = x; + bs2b->a0_lo = G_lo * (1.0f - x) * g; + + x = expf(-2.0f * F_PI * Fc_hi / bs2b->srate); + bs2b->b1_hi = x; + bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g; + bs2b->a1_hi = -x * g; +} /* 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) +{ + memset(&bs2b->last_sample, 0, sizeof(bs2b->last_sample)); +} /* bs2b_clear */ + +extern inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict samples); diff --git a/libs/openal-soft/Alc/compat.h b/libs/openal-soft/Alc/compat.h new file mode 100644 index 00000000..f54ef9ce --- /dev/null +++ b/libs/openal-soft/Alc/compat.h @@ -0,0 +1,32 @@ +#ifndef AL_COMPAT_H +#define AL_COMPAT_H + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include + +WCHAR *strdupW(const WCHAR *str); + +/* Opens a file with standard I/O. The filename is expected to be UTF-8. */ +FILE *al_fopen(const char *fname, const char *mode); + +#define HAVE_DYNLOAD 1 + +#else + +#define al_fopen fopen + +#if defined(HAVE_DLFCN_H) && !defined(IN_IDE_PARSER) +#define HAVE_DYNLOAD 1 +#endif + +#endif + +#ifdef HAVE_DYNLOAD +void *LoadLib(const char *name); +void CloseLib(void *handle); +void *GetSymbol(void *handle, const char *name); +#endif + +#endif /* AL_COMPAT_H */ diff --git a/libs/openal-soft/Alc/effects/autowah.c b/libs/openal-soft/Alc/effects/autowah.c new file mode 100644 index 00000000..c8317c8b --- /dev/null +++ b/libs/openal-soft/Alc/effects/autowah.c @@ -0,0 +1,272 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2013 by Anis A. Hireche, Nasca Octavian Paul + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include + +#include "config.h" +#include "alu.h" +#include "alFilter.h" +#include "alError.h" +#include "alMain.h" +#include "alAuxEffectSlot.h" + + +/* Auto-wah is simply a low-pass filter with a cutoff frequency that shifts up + * or down depending on the input signal, and a resonant peak at the cutoff. + * + * Currently, we assume a cutoff frequency range of 500hz (no amplitude) to + * 3khz (peak gain). Peak gain is assumed to be in normalized scale. + */ + +typedef struct ALautowahState { + DERIVE_FROM_TYPE(ALeffectState); + + /* Effect gains for each channel */ + ALfloat Gain[MaxChannels]; + + /* Effect parameters */ + ALfloat AttackRate; + ALfloat ReleaseRate; + ALfloat Resonance; + ALfloat PeakGain; + ALfloat GainCtrl; + ALfloat Frequency; + + /* Samples processing */ + ALfilterState LowPass; +} ALautowahState; + +static ALvoid ALautowahState_Destruct(ALautowahState *UNUSED(state)) +{ +} + +static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device) +{ + state->Frequency = (ALfloat)device->Frequency; + return AL_TRUE; +} + +static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, const ALeffectslot *slot) +{ + ALfloat attackTime, releaseTime; + ALfloat gain; + + attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency; + releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency; + + state->AttackRate = powf(1.0f/GAIN_SILENCE_THRESHOLD, 1.0f/attackTime); + state->ReleaseRate = powf(GAIN_SILENCE_THRESHOLD/1.0f, 1.0f/releaseTime); + state->PeakGain = slot->EffectProps.Autowah.PeakGain; + state->Resonance = slot->EffectProps.Autowah.Resonance; + + gain = sqrtf(1.0f / device->NumChan) * slot->Gain; + SetGains(device, gain, state->Gain); +} + +static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE]) +{ + ALuint it, kt; + ALuint base; + + for(base = 0;base < SamplesToDo;) + { + ALfloat temps[64]; + ALuint td = minu(SamplesToDo-base, 64); + ALfloat gain = state->GainCtrl; + + for(it = 0;it < td;it++) + { + ALfloat smp = SamplesIn[it+base]; + ALfloat alpha, w0; + ALfloat amplitude; + ALfloat cutoff; + + /* Similar to compressor, we get the current amplitude of the + * incoming signal, and attack or release to reach it. */ + amplitude = fabsf(smp); + if(amplitude > gain) + gain = minf(gain*state->AttackRate, amplitude); + else if(amplitude < gain) + gain = maxf(gain*state->ReleaseRate, amplitude); + gain = maxf(gain, GAIN_SILENCE_THRESHOLD); + + /* FIXME: What range does the filter cover? */ + cutoff = lerp(20.0f, 20000.0f, minf(gain/state->PeakGain, 1.0f)); + + /* The code below is like calling ALfilterState_setParams with + * ALfilterType_LowPass. However, instead of passing a bandwidth, + * we use the resonance property for Q. This also inlines the call. + */ + w0 = F_2PI * cutoff / state->Frequency; + + /* FIXME: Resonance controls the resonant peak, or Q. How? Not sure + * that Q = resonance*0.1. */ + alpha = sinf(w0) / (2.0f * state->Resonance*0.1f); + state->LowPass.b[0] = (1.0f - cosf(w0)) / 2.0f; + state->LowPass.b[1] = 1.0f - cosf(w0); + state->LowPass.b[2] = (1.0f - cosf(w0)) / 2.0f; + state->LowPass.a[0] = 1.0f + alpha; + state->LowPass.a[1] = -2.0f * cosf(w0); + state->LowPass.a[2] = 1.0f - alpha; + + state->LowPass.b[2] /= state->LowPass.a[0]; + state->LowPass.b[1] /= state->LowPass.a[0]; + state->LowPass.b[0] /= state->LowPass.a[0]; + state->LowPass.a[2] /= state->LowPass.a[0]; + state->LowPass.a[1] /= state->LowPass.a[0]; + state->LowPass.a[0] /= state->LowPass.a[0]; + + temps[it] = ALfilterState_processSingle(&state->LowPass, smp); + } + state->GainCtrl = gain; + + for(kt = 0;kt < MaxChannels;kt++) + { + ALfloat gain = state->Gain[kt]; + if(!(gain > GAIN_SILENCE_THRESHOLD)) + continue; + + for(it = 0;it < td;it++) + SamplesOut[kt][base+it] += gain * temps[it]; + } + + base += td; + } +} + +DECLARE_DEFAULT_ALLOCATORS(ALautowahState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState); + + +typedef struct ALautowahStateFactory { + DERIVE_FROM_TYPE(ALeffectStateFactory); +} ALautowahStateFactory; + +static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory)) +{ + ALautowahState *state; + + state = ALautowahState_New(sizeof(*state)); + if(!state) return NULL; + SET_VTABLE2(ALautowahState, ALeffectState, state); + + state->AttackRate = 1.0f; + state->ReleaseRate = 1.0f; + state->Resonance = 2.0f; + state->PeakGain = 1.0f; + state->GainCtrl = 1.0f; + + ALfilterState_clear(&state->LowPass); + + return STATIC_CAST(ALeffectState, state); +} + +DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory); + +ALeffectStateFactory *ALautowahStateFactory_getFactory(void) +{ + static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } }; + + return STATIC_CAST(ALeffectStateFactory, &AutowahFactory); +} + + +void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALautowah_setParami(effect, context, param, vals[0]); +} +void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_AUTOWAH_ATTACK_TIME: + if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Autowah.AttackTime = val; + break; + + case AL_AUTOWAH_RELEASE_TIME: + if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Autowah.ReleaseTime = val; + break; + + case AL_AUTOWAH_RESONANCE: + if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Autowah.Resonance = val; + break; + + case AL_AUTOWAH_PEAK_GAIN: + if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Autowah.PeakGain = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + ALautowah_setParamf(effect, context, param, vals[0]); +} + +void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +void ALautowah_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALautowah_getParami(effect, context, param, vals); +} +void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_AUTOWAH_ATTACK_TIME: + *val = props->Autowah.AttackTime; + break; + + case AL_AUTOWAH_RELEASE_TIME: + *val = props->Autowah.ReleaseTime; + break; + + case AL_AUTOWAH_RESONANCE: + *val = props->Autowah.Resonance; + break; + + case AL_AUTOWAH_PEAK_GAIN: + *val = props->Autowah.PeakGain; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + ALautowah_getParamf(effect, context, param, vals); +} + +DEFINE_ALEFFECT_VTABLE(ALautowah); diff --git a/libs/openal-soft/Alc/effects/chorus.c b/libs/openal-soft/Alc/effects/chorus.c new file mode 100644 index 00000000..a6bb199e --- /dev/null +++ b/libs/openal-soft/Alc/effects/chorus.c @@ -0,0 +1,397 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2013 by Mike Gorchak + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include +#include + +#include "alMain.h" +#include "alFilter.h" +#include "alAuxEffectSlot.h" +#include "alError.h" +#include "alu.h" + + +enum ChorusWaveForm { + CWF_Triangle = AL_CHORUS_WAVEFORM_TRIANGLE, + CWF_Sinusoid = AL_CHORUS_WAVEFORM_SINUSOID +}; + +typedef struct ALchorusState { + DERIVE_FROM_TYPE(ALeffectState); + + ALfloat *SampleBuffer[2]; + ALuint BufferLength; + ALuint offset; + ALuint lfo_range; + ALfloat lfo_scale; + ALint lfo_disp; + + /* Gains for left and right sides */ + ALfloat Gain[2][MaxChannels]; + + /* effect parameters */ + enum ChorusWaveForm waveform; + ALint delay; + ALfloat depth; + ALfloat feedback; +} ALchorusState; + +static ALvoid ALchorusState_Destruct(ALchorusState *state) +{ + free(state->SampleBuffer[0]); + state->SampleBuffer[0] = NULL; + state->SampleBuffer[1] = NULL; +} + +static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device) +{ + ALuint maxlen; + ALuint it; + + maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1; + maxlen = NextPowerOf2(maxlen); + + if(maxlen != state->BufferLength) + { + void *temp; + + temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2); + if(!temp) return AL_FALSE; + state->SampleBuffer[0] = temp; + state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen; + + state->BufferLength = maxlen; + } + + for(it = 0;it < state->BufferLength;it++) + { + state->SampleBuffer[0][it] = 0.0f; + state->SampleBuffer[1][it] = 0.0f; + } + + return AL_TRUE; +} + +static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot) +{ + ALfloat frequency = (ALfloat)Device->Frequency; + ALfloat rate; + ALint phase; + + switch(Slot->EffectProps.Chorus.Waveform) + { + case AL_CHORUS_WAVEFORM_TRIANGLE: + state->waveform = CWF_Triangle; + break; + case AL_CHORUS_WAVEFORM_SINUSOID: + state->waveform = CWF_Sinusoid; + break; + } + state->depth = Slot->EffectProps.Chorus.Depth; + state->feedback = Slot->EffectProps.Chorus.Feedback; + state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency); + + /* Gains for left and right sides */ + ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]); + ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]); + + phase = Slot->EffectProps.Chorus.Phase; + rate = Slot->EffectProps.Chorus.Rate; + if(!(rate > 0.0f)) + { + state->lfo_scale = 0.0f; + state->lfo_range = 1; + state->lfo_disp = 0; + } + else + { + /* Calculate LFO coefficient */ + state->lfo_range = fastf2u(frequency/rate + 0.5f); + switch(state->waveform) + { + case CWF_Triangle: + state->lfo_scale = 4.0f / state->lfo_range; + break; + case CWF_Sinusoid: + state->lfo_scale = F_2PI / state->lfo_range; + break; + } + + /* Calculate lfo phase displacement */ + state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f)); + } +} + +static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state) +{ + ALfloat lfo_value; + + lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range)); + lfo_value *= state->depth * state->delay; + *delay_left = fastf2i(lfo_value) + state->delay; + + offset += state->lfo_disp; + lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range)); + lfo_value *= state->depth * state->delay; + *delay_right = fastf2i(lfo_value) + state->delay; +} + +static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state) +{ + ALfloat lfo_value; + + lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range)); + lfo_value *= state->depth * state->delay; + *delay_left = fastf2i(lfo_value) + state->delay; + + offset += state->lfo_disp; + lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range)); + lfo_value *= state->depth * state->delay; + *delay_right = fastf2i(lfo_value) + state->delay; +} + +#define DECL_TEMPLATE(Func) \ +static void Process##Func(ALchorusState *state, const ALuint SamplesToDo, \ + const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \ +{ \ + const ALuint bufmask = state->BufferLength-1; \ + ALfloat *restrict leftbuf = state->SampleBuffer[0]; \ + ALfloat *restrict rightbuf = state->SampleBuffer[1]; \ + ALuint offset = state->offset; \ + const ALfloat feedback = state->feedback; \ + ALuint it; \ + \ + for(it = 0;it < SamplesToDo;it++) \ + { \ + ALint delay_left, delay_right; \ + Func(&delay_left, &delay_right, offset, state); \ + \ + out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \ + leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \ + \ + out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \ + rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \ + \ + offset++; \ + } \ + state->offset = offset; \ +} + +DECL_TEMPLATE(Triangle) +DECL_TEMPLATE(Sinusoid) + +#undef DECL_TEMPLATE + +static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +{ + ALuint it, kt; + ALuint base; + + for(base = 0;base < SamplesToDo;) + { + ALfloat temps[64][2]; + ALuint td = minu(SamplesToDo-base, 64); + + switch(state->waveform) + { + case CWF_Triangle: + ProcessTriangle(state, td, SamplesIn+base, temps); + break; + case CWF_Sinusoid: + ProcessSinusoid(state, td, SamplesIn+base, temps); + break; + } + + for(kt = 0;kt < MaxChannels;kt++) + { + ALfloat gain = state->Gain[0][kt]; + if(gain > GAIN_SILENCE_THRESHOLD) + { + for(it = 0;it < td;it++) + SamplesOut[kt][it+base] += temps[it][0] * gain; + } + + gain = state->Gain[1][kt]; + if(gain > GAIN_SILENCE_THRESHOLD) + { + for(it = 0;it < td;it++) + SamplesOut[kt][it+base] += temps[it][1] * gain; + } + } + + base += td; + } +} + +DECLARE_DEFAULT_ALLOCATORS(ALchorusState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState); + + +typedef struct ALchorusStateFactory { + DERIVE_FROM_TYPE(ALeffectStateFactory); +} ALchorusStateFactory; + +static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(factory)) +{ + ALchorusState *state; + + state = ALchorusState_New(sizeof(*state)); + if(!state) return NULL; + SET_VTABLE2(ALchorusState, ALeffectState, state); + + state->BufferLength = 0; + state->SampleBuffer[0] = NULL; + state->SampleBuffer[1] = NULL; + state->offset = 0; + state->lfo_range = 1; + state->waveform = CWF_Triangle; + + return STATIC_CAST(ALeffectState, state); +} + +DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALchorusStateFactory); + + +ALeffectStateFactory *ALchorusStateFactory_getFactory(void) +{ + static ALchorusStateFactory ChorusFactory = { { GET_VTABLE2(ALchorusStateFactory, ALeffectStateFactory) } }; + + return STATIC_CAST(ALeffectStateFactory, &ChorusFactory); +} + + +void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_CHORUS_WAVEFORM: + if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Chorus.Waveform = val; + break; + + case AL_CHORUS_PHASE: + if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Chorus.Phase = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALchorus_setParami(effect, context, param, vals[0]); +} +void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_CHORUS_RATE: + if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Chorus.Rate = val; + break; + + case AL_CHORUS_DEPTH: + if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Chorus.Depth = val; + break; + + case AL_CHORUS_FEEDBACK: + if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Chorus.Feedback = val; + break; + + case AL_CHORUS_DELAY: + if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Chorus.Delay = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + ALchorus_setParamf(effect, context, param, vals[0]); +} + +void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_CHORUS_WAVEFORM: + *val = props->Chorus.Waveform; + break; + + case AL_CHORUS_PHASE: + *val = props->Chorus.Phase; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALchorus_getParami(effect, context, param, vals); +} +void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_CHORUS_RATE: + *val = props->Chorus.Rate; + break; + + case AL_CHORUS_DEPTH: + *val = props->Chorus.Depth; + break; + + case AL_CHORUS_FEEDBACK: + *val = props->Chorus.Feedback; + break; + + case AL_CHORUS_DELAY: + *val = props->Chorus.Delay; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + ALchorus_getParamf(effect, context, param, vals); +} + +DEFINE_ALEFFECT_VTABLE(ALchorus); diff --git a/libs/openal-soft/Alc/effects/compressor.c b/libs/openal-soft/Alc/effects/compressor.c new file mode 100644 index 00000000..9728cabe --- /dev/null +++ b/libs/openal-soft/Alc/effects/compressor.c @@ -0,0 +1,220 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2013 by Anis A. Hireche + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include + +#include "config.h" +#include "alError.h" +#include "alMain.h" +#include "alAuxEffectSlot.h" +#include "alu.h" + + +typedef struct ALcompressorState { + DERIVE_FROM_TYPE(ALeffectState); + + /* Effect gains for each channel */ + ALfloat Gain[MaxChannels]; + + /* Effect parameters */ + ALboolean Enabled; + ALfloat AttackRate; + ALfloat ReleaseRate; + ALfloat GainCtrl; +} ALcompressorState; + +static ALvoid ALcompressorState_Destruct(ALcompressorState *UNUSED(state)) +{ +} + +static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device) +{ + const ALfloat attackTime = device->Frequency * 0.2f; /* 200ms Attack */ + const ALfloat releaseTime = device->Frequency * 0.4f; /* 400ms Release */ + + state->AttackRate = 1.0f / attackTime; + state->ReleaseRate = 1.0f / releaseTime; + + return AL_TRUE; +} + +static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *Device, const ALeffectslot *Slot) +{ + ALfloat gain; + + state->Enabled = Slot->EffectProps.Compressor.OnOff; + + gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain; + SetGains(Device, gain, state->Gain); +} + +static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE]) +{ + ALuint it, kt; + ALuint base; + + for(base = 0;base < SamplesToDo;) + { + ALfloat temps[64]; + ALuint td = minu(SamplesToDo-base, 64); + + if(state->Enabled) + { + ALfloat output, smp, amplitude; + ALfloat gain = state->GainCtrl; + + for(it = 0;it < td;it++) + { + smp = SamplesIn[it+base]; + + amplitude = fabsf(smp); + if(amplitude > gain) + gain = minf(gain+state->AttackRate, amplitude); + else if(amplitude < gain) + gain = maxf(gain-state->ReleaseRate, amplitude); + output = 1.0f / clampf(gain, 0.5f, 2.0f); + + temps[it] = smp * output; + } + + state->GainCtrl = gain; + } + else + { + ALfloat output, smp, amplitude; + ALfloat gain = state->GainCtrl; + + for(it = 0;it < td;it++) + { + smp = SamplesIn[it+base]; + + amplitude = 1.0f; + if(amplitude > gain) + gain = minf(gain+state->AttackRate, amplitude); + else if(amplitude < gain) + gain = maxf(gain-state->ReleaseRate, amplitude); + output = 1.0f / clampf(gain, 0.5f, 2.0f); + + temps[it] = smp * output; + } + + state->GainCtrl = gain; + } + + + for(kt = 0;kt < MaxChannels;kt++) + { + ALfloat gain = state->Gain[kt]; + if(!(gain > GAIN_SILENCE_THRESHOLD)) + continue; + + for(it = 0;it < td;it++) + SamplesOut[kt][base+it] += gain * temps[it]; + } + + base += td; + } +} + +DECLARE_DEFAULT_ALLOCATORS(ALcompressorState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState); + + +typedef struct ALcompressorStateFactory { + DERIVE_FROM_TYPE(ALeffectStateFactory); +} ALcompressorStateFactory; + +static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *UNUSED(factory)) +{ + ALcompressorState *state; + + state = ALcompressorState_New(sizeof(*state)); + if(!state) return NULL; + SET_VTABLE2(ALcompressorState, ALeffectState, state); + + state->Enabled = AL_TRUE; + state->AttackRate = 0.0f; + state->ReleaseRate = 0.0f; + state->GainCtrl = 1.0f; + + return STATIC_CAST(ALeffectState, state); +} + +DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALcompressorStateFactory); + +ALeffectStateFactory *ALcompressorStateFactory_getFactory(void) +{ + static ALcompressorStateFactory CompressorFactory = { { GET_VTABLE2(ALcompressorStateFactory, ALeffectStateFactory) } }; + + return STATIC_CAST(ALeffectStateFactory, &CompressorFactory); +} + + +void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_COMPRESSOR_ONOFF: + if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Compressor.OnOff = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALcompressor_setParami(effect, context, param, vals[0]); +} +void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +void ALcompressor_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + ALcompressor_setParamf(effect, context, param, vals[0]); +} + +void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_COMPRESSOR_ONOFF: + *val = props->Compressor.OnOff; + break; + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALcompressor_getParami(effect, context, param, vals); +} +void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +void ALcompressor_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + ALcompressor_getParamf(effect, context, param, vals); +} + +DEFINE_ALEFFECT_VTABLE(ALcompressor); diff --git a/libs/openal-soft/Alc/effects/dedicated.c b/libs/openal-soft/Alc/effects/dedicated.c new file mode 100644 index 00000000..89af1c84 --- /dev/null +++ b/libs/openal-soft/Alc/effects/dedicated.c @@ -0,0 +1,164 @@ +/** + * 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 { + DERIVE_FROM_TYPE(ALeffectState); + + ALfloat gains[MaxChannels]; +} ALdedicatedState; + + +static ALvoid ALdedicatedState_Destruct(ALdedicatedState *UNUSED(state)) +{ +} + +static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device)) +{ + return AL_TRUE; +} + +static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot) +{ + ALfloat Gain; + ALsizei s; + + Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain; + if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE) + ComputeAngleGains(device, atan2f(0.0f, 1.0f), 0.0f, Gain, state->gains); + else if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) + { + for(s = 0;s < MaxChannels;s++) + state->gains[s] = 0.0f; + state->gains[LFE] = Gain; + } +} + +static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +{ + const ALfloat *gains = state->gains; + ALuint i, c; + + for(c = 0;c < MaxChannels;c++) + { + if(!(gains[c] > GAIN_SILENCE_THRESHOLD)) + continue; + + for(i = 0;i < SamplesToDo;i++) + SamplesOut[c][i] = SamplesIn[i] * gains[c]; + } +} + +DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState); + + +typedef struct ALdedicatedStateFactory { + DERIVE_FROM_TYPE(ALeffectStateFactory); +} ALdedicatedStateFactory; + +ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory)) +{ + ALdedicatedState *state; + ALsizei s; + + state = ALdedicatedState_New(sizeof(*state)); + if(!state) return NULL; + SET_VTABLE2(ALdedicatedState, ALeffectState, state); + + for(s = 0;s < MaxChannels;s++) + state->gains[s] = 0.0f; + + return STATIC_CAST(ALeffectState, state); +} + +DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdedicatedStateFactory); + + +ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void) +{ + static ALdedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(ALdedicatedStateFactory, ALeffectStateFactory) } }; + + return STATIC_CAST(ALeffectStateFactory, &DedicatedFactory); +} + + +void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +void ALdedicated_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALdedicated_setParami(effect, context, param, vals[0]); +} +void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_DEDICATED_GAIN: + if(!(val >= 0.0f && isfinite(val))) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Dedicated.Gain = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + ALdedicated_setParamf(effect, context, param, vals[0]); +} + +void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +void ALdedicated_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALdedicated_getParami(effect, context, param, vals); +} +void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_DEDICATED_GAIN: + *val = props->Dedicated.Gain; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + ALdedicated_getParamf(effect, context, param, vals); +} + +DEFINE_ALEFFECT_VTABLE(ALdedicated); diff --git a/libs/openal-soft/Alc/effects/distortion.c b/libs/openal-soft/Alc/effects/distortion.c new file mode 100644 index 00000000..b535655b --- /dev/null +++ b/libs/openal-soft/Alc/effects/distortion.c @@ -0,0 +1,297 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2013 by Mike Gorchak + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include +#include + +#include "alMain.h" +#include "alFilter.h" +#include "alAuxEffectSlot.h" +#include "alError.h" +#include "alu.h" + + +typedef struct ALdistortionState { + DERIVE_FROM_TYPE(ALeffectState); + + /* Effect gains for each channel */ + ALfloat Gain[MaxChannels]; + + /* Effect parameters */ + ALfilterState lowpass; + ALfilterState bandpass; + ALfloat attenuation; + ALfloat edge_coeff; +} ALdistortionState; + +static ALvoid ALdistortionState_Destruct(ALdistortionState *UNUSED(state)) +{ +} + +static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device)) +{ + return AL_TRUE; +} + +static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Device, const ALeffectslot *Slot) +{ + ALfloat frequency = (ALfloat)Device->Frequency; + ALfloat bandwidth; + ALfloat cutoff; + ALfloat edge; + ALfloat gain; + + /* Store distorted signal attenuation settings */ + state->attenuation = Slot->EffectProps.Distortion.Gain; + + /* Store waveshaper edge settings */ + edge = sinf(Slot->EffectProps.Distortion.Edge * (F_PI_2)); + edge = minf(edge, 0.99f); + state->edge_coeff = 2.0f * edge / (1.0f-edge); + + /* Lowpass filter */ + cutoff = Slot->EffectProps.Distortion.LowpassCutoff; + /* Bandwidth value is constant in octaves */ + bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f); + ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f, + cutoff / (frequency*4.0f), bandwidth); + + /* Bandpass filter */ + cutoff = Slot->EffectProps.Distortion.EQCenter; + /* Convert bandwidth in Hz to octaves */ + bandwidth = Slot->EffectProps.Distortion.EQBandwidth / (cutoff * 0.67f); + ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f, + cutoff / (frequency*4.0f), bandwidth); + + gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain; + SetGains(Device, gain, state->Gain); +} + +static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +{ + const ALfloat fc = state->edge_coeff; + float oversample_buffer[64][4]; + ALuint base; + ALuint it; + ALuint ot; + ALuint kt; + + for(base = 0;base < SamplesToDo;) + { + ALfloat temps[64]; + ALuint td = minu(SamplesToDo-base, 64); + + /* Perform 4x oversampling to avoid aliasing. */ + /* Oversampling greatly improves distortion */ + /* quality and allows to implement lowpass and */ + /* bandpass filters using high frequencies, at */ + /* which classic IIR filters became unstable. */ + + /* Fill oversample buffer using zero stuffing */ + for(it = 0;it < td;it++) + { + oversample_buffer[it][0] = SamplesIn[it+base]; + oversample_buffer[it][1] = 0.0f; + oversample_buffer[it][2] = 0.0f; + oversample_buffer[it][3] = 0.0f; + } + + /* First step, do lowpass filtering of original signal, */ + /* additionally perform buffer interpolation and lowpass */ + /* cutoff for oversampling (which is fortunately first */ + /* step of distortion). So combine three operations into */ + /* the one. */ + for(it = 0;it < td;it++) + { + for(ot = 0;ot < 4;ot++) + { + ALfloat smp; + smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]); + + /* Restore signal power by multiplying sample by amount of oversampling */ + oversample_buffer[it][ot] = smp * 4.0f; + } + } + + for(it = 0;it < td;it++) + { + /* Second step, do distortion using waveshaper function */ + /* to emulate signal processing during tube overdriving. */ + /* Three steps of waveshaping are intended to modify */ + /* waveform without boost/clipping/attenuation process. */ + for(ot = 0;ot < 4;ot++) + { + ALfloat smp = oversample_buffer[it][ot]; + + smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)); + smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f; + smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)); + + /* Third step, do bandpass filtering of distorted signal */ + smp = ALfilterState_processSingle(&state->bandpass, smp); + oversample_buffer[it][ot] = smp; + } + + /* Fourth step, final, do attenuation and perform decimation, */ + /* store only one sample out of 4. */ + temps[it] = oversample_buffer[it][0] * state->attenuation; + } + + for(kt = 0;kt < MaxChannels;kt++) + { + ALfloat gain = state->Gain[kt]; + if(!(gain > GAIN_SILENCE_THRESHOLD)) + continue; + + for(it = 0;it < td;it++) + SamplesOut[kt][base+it] += gain * temps[it]; + } + + base += td; + } +} + +DECLARE_DEFAULT_ALLOCATORS(ALdistortionState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState); + + +typedef struct ALdistortionStateFactory { + DERIVE_FROM_TYPE(ALeffectStateFactory); +} ALdistortionStateFactory; + +static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *UNUSED(factory)) +{ + ALdistortionState *state; + + state = ALdistortionState_New(sizeof(*state)); + if(!state) return NULL; + SET_VTABLE2(ALdistortionState, ALeffectState, state); + + ALfilterState_clear(&state->lowpass); + ALfilterState_clear(&state->bandpass); + + return STATIC_CAST(ALeffectState, state); +} + +DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory); + + +ALeffectStateFactory *ALdistortionStateFactory_getFactory(void) +{ + static ALdistortionStateFactory DistortionFactory = { { GET_VTABLE2(ALdistortionStateFactory, ALeffectStateFactory) } }; + + return STATIC_CAST(ALeffectStateFactory, &DistortionFactory); +} + + +void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +void ALdistortion_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALdistortion_setParami(effect, context, param, vals[0]); +} +void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_DISTORTION_EDGE: + if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Distortion.Edge = val; + break; + + case AL_DISTORTION_GAIN: + if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Distortion.Gain = val; + break; + + case AL_DISTORTION_LOWPASS_CUTOFF: + if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Distortion.LowpassCutoff = val; + break; + + case AL_DISTORTION_EQCENTER: + if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Distortion.EQCenter = val; + break; + + case AL_DISTORTION_EQBANDWIDTH: + if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Distortion.EQBandwidth = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + ALdistortion_setParamf(effect, context, param, vals[0]); +} + +void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +void ALdistortion_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALdistortion_getParami(effect, context, param, vals); +} +void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_DISTORTION_EDGE: + *val = props->Distortion.Edge; + break; + + case AL_DISTORTION_GAIN: + *val = props->Distortion.Gain; + break; + + case AL_DISTORTION_LOWPASS_CUTOFF: + *val = props->Distortion.LowpassCutoff; + break; + + case AL_DISTORTION_EQCENTER: + *val = props->Distortion.EQCenter; + break; + + case AL_DISTORTION_EQBANDWIDTH: + *val = props->Distortion.EQBandwidth; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + ALdistortion_getParamf(effect, context, param, vals); +} + +DEFINE_ALEFFECT_VTABLE(ALdistortion); diff --git a/libs/openal-soft/Alc/effects/echo.c b/libs/openal-soft/Alc/effects/echo.c new file mode 100644 index 00000000..049ae9c7 --- /dev/null +++ b/libs/openal-soft/Alc/effects/echo.c @@ -0,0 +1,293 @@ +/** + * 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 { + DERIVE_FROM_TYPE(ALeffectState); + + ALfloat *SampleBuffer; + ALuint BufferLength; + + // The echo is two tap. The delay is the number of samples from before the + // current offset + struct { + ALuint delay; + } Tap[2]; + ALuint Offset; + /* The panning gains for the two taps */ + ALfloat Gain[2][MaxChannels]; + + ALfloat FeedGain; + + ALfilterState Filter; +} ALechoState; + +static ALvoid ALechoState_Destruct(ALechoState *state) +{ + free(state->SampleBuffer); + state->SampleBuffer = NULL; +} + +static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device) +{ + ALuint maxlen, i; + + // Use the next power of 2 for the buffer length, so the tap offsets can be + // wrapped using a mask instead of a modulo + maxlen = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1; + maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1; + maxlen = NextPowerOf2(maxlen); + + if(maxlen != state->BufferLength) + { + void *temp; + + temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat)); + if(!temp) return AL_FALSE; + state->SampleBuffer = temp; + state->BufferLength = maxlen; + } + for(i = 0;i < state->BufferLength;i++) + state->SampleBuffer[i] = 0.0f; + + return AL_TRUE; +} + +static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot) +{ + ALuint frequency = Device->Frequency; + ALfloat lrpan, gain; + ALfloat dirGain; + + state->Tap[0].delay = fastf2u(Slot->EffectProps.Echo.Delay * frequency) + 1; + state->Tap[1].delay = fastf2u(Slot->EffectProps.Echo.LRDelay * frequency); + state->Tap[1].delay += state->Tap[0].delay; + + lrpan = Slot->EffectProps.Echo.Spread; + + state->FeedGain = Slot->EffectProps.Echo.Feedback; + + ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf, + 1.0f - Slot->EffectProps.Echo.Damping, + LOWPASSFREQREF/frequency, 0.0f); + + gain = Slot->Gain; + dirGain = fabsf(lrpan); + + /* First tap panning */ + ComputeAngleGains(Device, atan2f(-lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[0]); + + /* Second tap panning */ + ComputeAngleGains(Device, atan2f(+lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[1]); +} + +static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +{ + const ALuint mask = state->BufferLength-1; + const ALuint tap1 = state->Tap[0].delay; + const ALuint tap2 = state->Tap[1].delay; + ALuint offset = state->Offset; + ALfloat smp; + ALuint base; + ALuint i, k; + + for(base = 0;base < SamplesToDo;) + { + ALfloat temps[64][2]; + ALuint td = minu(SamplesToDo-base, 64); + + for(i = 0;i < td;i++) + { + /* First tap */ + temps[i][0] = state->SampleBuffer[(offset-tap1) & mask]; + /* Second tap */ + temps[i][1] = state->SampleBuffer[(offset-tap2) & mask]; + + // Apply damping and feedback gain to the second tap, and mix in the + // new sample + smp = ALfilterState_processSingle(&state->Filter, temps[i][1]+SamplesIn[i+base]); + state->SampleBuffer[offset&mask] = smp * state->FeedGain; + offset++; + } + + for(k = 0;k < MaxChannels;k++) + { + ALfloat gain = state->Gain[0][k]; + if(gain > GAIN_SILENCE_THRESHOLD) + { + for(i = 0;i < td;i++) + SamplesOut[k][i+base] += temps[i][0] * gain; + } + + gain = state->Gain[1][k]; + if(gain > GAIN_SILENCE_THRESHOLD) + { + for(i = 0;i < td;i++) + SamplesOut[k][i+base] += temps[i][1] * gain; + } + } + + base += td; + } + + state->Offset = offset; +} + +DECLARE_DEFAULT_ALLOCATORS(ALechoState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALechoState); + + +typedef struct ALechoStateFactory { + DERIVE_FROM_TYPE(ALeffectStateFactory); +} ALechoStateFactory; + +ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory)) +{ + ALechoState *state; + + state = ALechoState_New(sizeof(*state)); + if(!state) return NULL; + SET_VTABLE2(ALechoState, ALeffectState, state); + + state->BufferLength = 0; + state->SampleBuffer = NULL; + + state->Tap[0].delay = 0; + state->Tap[1].delay = 0; + state->Offset = 0; + + ALfilterState_clear(&state->Filter); + + return STATIC_CAST(ALeffectState, state); +} + +DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory); + +ALeffectStateFactory *ALechoStateFactory_getFactory(void) +{ + static ALechoStateFactory EchoFactory = { { GET_VTABLE2(ALechoStateFactory, ALeffectStateFactory) } }; + + return STATIC_CAST(ALeffectStateFactory, &EchoFactory); +} + + +void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +void ALecho_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALecho_setParami(effect, context, param, vals[0]); +} +void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_ECHO_DELAY: + if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Echo.Delay = val; + break; + + case AL_ECHO_LRDELAY: + if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Echo.LRDelay = val; + break; + + case AL_ECHO_DAMPING: + if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Echo.Damping = val; + break; + + case AL_ECHO_FEEDBACK: + if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Echo.Feedback = val; + break; + + case AL_ECHO_SPREAD: + if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Echo.Spread = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + ALecho_setParamf(effect, context, param, vals[0]); +} + +void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +void ALecho_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALecho_getParami(effect, context, param, vals); +} +void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_ECHO_DELAY: + *val = props->Echo.Delay; + break; + + case AL_ECHO_LRDELAY: + *val = props->Echo.LRDelay; + break; + + case AL_ECHO_DAMPING: + *val = props->Echo.Damping; + break; + + case AL_ECHO_FEEDBACK: + *val = props->Echo.Feedback; + break; + + case AL_ECHO_SPREAD: + *val = props->Echo.Spread; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + ALecho_getParamf(effect, context, param, vals); +} + +DEFINE_ALEFFECT_VTABLE(ALecho); diff --git a/libs/openal-soft/Alc/effects/equalizer.c b/libs/openal-soft/Alc/effects/equalizer.c new file mode 100644 index 00000000..cfe7c46c --- /dev/null +++ b/libs/openal-soft/Alc/effects/equalizer.c @@ -0,0 +1,334 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2013 by Mike Gorchak + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include +#include + +#include "alMain.h" +#include "alFilter.h" +#include "alAuxEffectSlot.h" +#include "alError.h" +#include "alu.h" + + +/* The document "Effects Extension Guide.pdf" says that low and high * + * frequencies are cutoff frequencies. This is not fully correct, they * + * are corner frequencies for low and high shelf filters. If they were * + * just cutoff frequencies, there would be no need in cutoff frequency * + * gains, which are present. Documentation for "Creative Proteus X2" * + * software describes 4-band equalizer functionality in a much better * + * way. This equalizer seems to be a predecessor of OpenAL 4-band * + * equalizer. With low and high shelf filters we are able to cutoff * + * frequencies below and/or above corner frequencies using attenuation * + * gains (below 1.0) and amplify all low and/or high frequencies using * + * gains above 1.0. * + * * + * Low-shelf Low Mid Band High Mid Band High-shelf * + * corner center center corner * + * frequency frequency frequency frequency * + * 50Hz..800Hz 200Hz..3000Hz 1000Hz..8000Hz 4000Hz..16000Hz * + * * + * | | | | * + * | | | | * + * B -----+ /--+--\ /--+--\ +----- * + * O |\ | | | | | | /| * + * O | \ - | - - | - / | * + * S + | \ | | | | | | / | * + * T | | | | | | | | | | * + * ---------+---------------+------------------+---------------+-------- * + * C | | | | | | | | | | * + * U - | / | | | | | | \ | * + * T | / - | - - | - \ | * + * O |/ | | | | | | \| * + * F -----+ \--+--/ \--+--/ +----- * + * F | | | | * + * | | | | * + * * + * Gains vary from 0.126 up to 7.943, which means from -18dB attenuation * + * up to +18dB amplification. Band width varies from 0.01 up to 1.0 in * + * octaves for two mid bands. * + * * + * Implementation is based on the "Cookbook formulae for audio EQ biquad * + * filter coefficients" by Robert Bristow-Johnson * + * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */ + +typedef struct ALequalizerState { + DERIVE_FROM_TYPE(ALeffectState); + + /* Effect gains for each channel */ + ALfloat Gain[MaxChannels]; + + /* Effect parameters */ + ALfilterState filter[4]; +} ALequalizerState; + +static ALvoid ALequalizerState_Destruct(ALequalizerState *UNUSED(state)) +{ +} + +static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device)) +{ + return AL_TRUE; +} + +static ALvoid ALequalizerState_update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot) +{ + ALfloat frequency = (ALfloat)device->Frequency; + ALfloat gain = sqrtf(1.0f / device->NumChan) * slot->Gain; + + SetGains(device, gain, state->Gain); + + /* Calculate coefficients for the each type of filter */ + ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf, + sqrtf(slot->EffectProps.Equalizer.LowGain), + slot->EffectProps.Equalizer.LowCutoff/frequency, + 0.0f); + + ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking, + sqrtf(slot->EffectProps.Equalizer.Mid1Gain), + slot->EffectProps.Equalizer.Mid1Center/frequency, + slot->EffectProps.Equalizer.Mid1Width); + + ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking, + sqrtf(slot->EffectProps.Equalizer.Mid2Gain), + slot->EffectProps.Equalizer.Mid2Center/frequency, + slot->EffectProps.Equalizer.Mid2Width); + + ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf, + sqrtf(slot->EffectProps.Equalizer.HighGain), + slot->EffectProps.Equalizer.HighCutoff/frequency, + 0.0f); +} + +static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +{ + ALuint base; + ALuint it; + ALuint kt; + ALuint ft; + + for(base = 0;base < SamplesToDo;) + { + ALfloat temps[64]; + ALuint td = minu(SamplesToDo-base, 64); + + for(it = 0;it < td;it++) + { + ALfloat smp = SamplesIn[base+it]; + + for(ft = 0;ft < 4;ft++) + smp = ALfilterState_processSingle(&state->filter[ft], smp); + + temps[it] = smp; + } + + for(kt = 0;kt < MaxChannels;kt++) + { + ALfloat gain = state->Gain[kt]; + if(!(gain > GAIN_SILENCE_THRESHOLD)) + continue; + + for(it = 0;it < td;it++) + SamplesOut[kt][base+it] += gain * temps[it]; + } + + base += td; + } +} + +DECLARE_DEFAULT_ALLOCATORS(ALequalizerState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState); + + +typedef struct ALequalizerStateFactory { + DERIVE_FROM_TYPE(ALeffectStateFactory); +} ALequalizerStateFactory; + +ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory)) +{ + ALequalizerState *state; + int it; + + state = ALequalizerState_New(sizeof(*state)); + if(!state) return NULL; + SET_VTABLE2(ALequalizerState, ALeffectState, state); + + /* Initialize sample history only on filter creation to avoid */ + /* sound clicks if filter settings were changed in runtime. */ + for(it = 0; it < 4; it++) + ALfilterState_clear(&state->filter[it]); + + return STATIC_CAST(ALeffectState, state); +} + +DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory); + +ALeffectStateFactory *ALequalizerStateFactory_getFactory(void) +{ + static ALequalizerStateFactory EqualizerFactory = { { GET_VTABLE2(ALequalizerStateFactory, ALeffectStateFactory) } }; + + return STATIC_CAST(ALeffectStateFactory, &EqualizerFactory); +} + + +void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +void ALequalizer_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALequalizer_setParami(effect, context, param, vals[0]); +} +void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_EQUALIZER_LOW_GAIN: + if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Equalizer.LowGain = val; + break; + + case AL_EQUALIZER_LOW_CUTOFF: + if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Equalizer.LowCutoff = val; + break; + + case AL_EQUALIZER_MID1_GAIN: + if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Equalizer.Mid1Gain = val; + break; + + case AL_EQUALIZER_MID1_CENTER: + if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Equalizer.Mid1Center = val; + break; + + case AL_EQUALIZER_MID1_WIDTH: + if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Equalizer.Mid1Width = val; + break; + + case AL_EQUALIZER_MID2_GAIN: + if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Equalizer.Mid2Gain = val; + break; + + case AL_EQUALIZER_MID2_CENTER: + if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Equalizer.Mid2Center = val; + break; + + case AL_EQUALIZER_MID2_WIDTH: + if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Equalizer.Mid2Width = val; + break; + + case AL_EQUALIZER_HIGH_GAIN: + if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Equalizer.HighGain = val; + break; + + case AL_EQUALIZER_HIGH_CUTOFF: + if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Equalizer.HighCutoff = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + ALequalizer_setParamf(effect, context, param, vals[0]); +} + +void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +void ALequalizer_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALequalizer_getParami(effect, context, param, vals); +} +void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_EQUALIZER_LOW_GAIN: + *val = props->Equalizer.LowGain; + break; + + case AL_EQUALIZER_LOW_CUTOFF: + *val = props->Equalizer.LowCutoff; + break; + + case AL_EQUALIZER_MID1_GAIN: + *val = props->Equalizer.Mid1Gain; + break; + + case AL_EQUALIZER_MID1_CENTER: + *val = props->Equalizer.Mid1Center; + break; + + case AL_EQUALIZER_MID1_WIDTH: + *val = props->Equalizer.Mid1Width; + break; + + case AL_EQUALIZER_MID2_GAIN: + *val = props->Equalizer.Mid2Gain; + break; + + case AL_EQUALIZER_MID2_CENTER: + *val = props->Equalizer.Mid2Center; + break; + + case AL_EQUALIZER_MID2_WIDTH: + *val = props->Equalizer.Mid2Width; + break; + + case AL_EQUALIZER_HIGH_GAIN: + *val = props->Equalizer.HighGain; + break; + + case AL_EQUALIZER_HIGH_CUTOFF: + *val = props->Equalizer.HighCutoff; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + ALequalizer_getParamf(effect, context, param, vals); +} + +DEFINE_ALEFFECT_VTABLE(ALequalizer); diff --git a/libs/openal-soft/Alc/effects/flanger.c b/libs/openal-soft/Alc/effects/flanger.c new file mode 100644 index 00000000..117cd1f2 --- /dev/null +++ b/libs/openal-soft/Alc/effects/flanger.c @@ -0,0 +1,396 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2013 by Mike Gorchak + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include +#include + +#include "alMain.h" +#include "alFilter.h" +#include "alAuxEffectSlot.h" +#include "alError.h" +#include "alu.h" + + +enum FlangerWaveForm { + FWF_Triangle = AL_FLANGER_WAVEFORM_TRIANGLE, + FWF_Sinusoid = AL_FLANGER_WAVEFORM_SINUSOID +}; + +typedef struct ALflangerState { + DERIVE_FROM_TYPE(ALeffectState); + + ALfloat *SampleBuffer[2]; + ALuint BufferLength; + ALuint offset; + ALuint lfo_range; + ALfloat lfo_scale; + ALint lfo_disp; + + /* Gains for left and right sides */ + ALfloat Gain[2][MaxChannels]; + + /* effect parameters */ + enum FlangerWaveForm waveform; + ALint delay; + ALfloat depth; + ALfloat feedback; +} ALflangerState; + +static ALvoid ALflangerState_Destruct(ALflangerState *state) +{ + free(state->SampleBuffer[0]); + state->SampleBuffer[0] = NULL; + state->SampleBuffer[1] = NULL; +} + +static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device) +{ + ALuint maxlen; + ALuint it; + + maxlen = fastf2u(AL_FLANGER_MAX_DELAY * 3.0f * Device->Frequency) + 1; + maxlen = NextPowerOf2(maxlen); + + if(maxlen != state->BufferLength) + { + void *temp; + + temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2); + if(!temp) return AL_FALSE; + state->SampleBuffer[0] = temp; + state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen; + + state->BufferLength = maxlen; + } + + for(it = 0;it < state->BufferLength;it++) + { + state->SampleBuffer[0][it] = 0.0f; + state->SampleBuffer[1][it] = 0.0f; + } + + return AL_TRUE; +} + +static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, const ALeffectslot *Slot) +{ + ALfloat frequency = (ALfloat)Device->Frequency; + ALfloat rate; + ALint phase; + + switch(Slot->EffectProps.Flanger.Waveform) + { + case AL_FLANGER_WAVEFORM_TRIANGLE: + state->waveform = FWF_Triangle; + break; + case AL_FLANGER_WAVEFORM_SINUSOID: + state->waveform = FWF_Sinusoid; + break; + } + state->depth = Slot->EffectProps.Flanger.Depth; + state->feedback = Slot->EffectProps.Flanger.Feedback; + state->delay = fastf2i(Slot->EffectProps.Flanger.Delay * frequency); + + /* Gains for left and right sides */ + ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]); + ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]); + + phase = Slot->EffectProps.Flanger.Phase; + rate = Slot->EffectProps.Flanger.Rate; + if(!(rate > 0.0f)) + { + state->lfo_scale = 0.0f; + state->lfo_range = 1; + state->lfo_disp = 0; + } + else + { + /* Calculate LFO coefficient */ + state->lfo_range = fastf2u(frequency/rate + 0.5f); + switch(state->waveform) + { + case FWF_Triangle: + state->lfo_scale = 4.0f / state->lfo_range; + break; + case FWF_Sinusoid: + state->lfo_scale = F_2PI / state->lfo_range; + break; + } + + /* Calculate lfo phase displacement */ + state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f)); + } +} + +static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state) +{ + ALfloat lfo_value; + + lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range)); + lfo_value *= state->depth * state->delay; + *delay_left = fastf2i(lfo_value) + state->delay; + + offset += state->lfo_disp; + lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range)); + lfo_value *= state->depth * state->delay; + *delay_right = fastf2i(lfo_value) + state->delay; +} + +static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state) +{ + ALfloat lfo_value; + + lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range)); + lfo_value *= state->depth * state->delay; + *delay_left = fastf2i(lfo_value) + state->delay; + + offset += state->lfo_disp; + lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range)); + lfo_value *= state->depth * state->delay; + *delay_right = fastf2i(lfo_value) + state->delay; +} + +#define DECL_TEMPLATE(Func) \ +static void Process##Func(ALflangerState *state, const ALuint SamplesToDo, \ + const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \ +{ \ + const ALuint bufmask = state->BufferLength-1; \ + ALfloat *restrict leftbuf = state->SampleBuffer[0]; \ + ALfloat *restrict rightbuf = state->SampleBuffer[1]; \ + ALuint offset = state->offset; \ + const ALfloat feedback = state->feedback; \ + ALuint it; \ + \ + for(it = 0;it < SamplesToDo;it++) \ + { \ + ALint delay_left, delay_right; \ + Func(&delay_left, &delay_right, offset, state); \ + \ + out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \ + leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \ + \ + out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \ + rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \ + \ + offset++; \ + } \ + state->offset = offset; \ +} + +DECL_TEMPLATE(Triangle) +DECL_TEMPLATE(Sinusoid) + +#undef DECL_TEMPLATE + +static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +{ + ALuint it, kt; + ALuint base; + + for(base = 0;base < SamplesToDo;) + { + ALfloat temps[64][2]; + ALuint td = minu(SamplesToDo-base, 64); + + switch(state->waveform) + { + case FWF_Triangle: + ProcessTriangle(state, td, SamplesIn+base, temps); + break; + case FWF_Sinusoid: + ProcessSinusoid(state, td, SamplesIn+base, temps); + break; + } + + for(kt = 0;kt < MaxChannels;kt++) + { + ALfloat gain = state->Gain[0][kt]; + if(gain > GAIN_SILENCE_THRESHOLD) + { + for(it = 0;it < td;it++) + SamplesOut[kt][it+base] += temps[it][0] * gain; + } + + gain = state->Gain[1][kt]; + if(gain > GAIN_SILENCE_THRESHOLD) + { + for(it = 0;it < td;it++) + SamplesOut[kt][it+base] += temps[it][1] * gain; + } + } + + base += td; + } +} + +DECLARE_DEFAULT_ALLOCATORS(ALflangerState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState); + + +typedef struct ALflangerStateFactory { + DERIVE_FROM_TYPE(ALeffectStateFactory); +} ALflangerStateFactory; + +ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factory)) +{ + ALflangerState *state; + + state = ALflangerState_New(sizeof(*state)); + if(!state) return NULL; + SET_VTABLE2(ALflangerState, ALeffectState, state); + + state->BufferLength = 0; + state->SampleBuffer[0] = NULL; + state->SampleBuffer[1] = NULL; + state->offset = 0; + state->lfo_range = 1; + state->waveform = FWF_Triangle; + + return STATIC_CAST(ALeffectState, state); +} + +DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALflangerStateFactory); + +ALeffectStateFactory *ALflangerStateFactory_getFactory(void) +{ + static ALflangerStateFactory FlangerFactory = { { GET_VTABLE2(ALflangerStateFactory, ALeffectStateFactory) } }; + + return STATIC_CAST(ALeffectStateFactory, &FlangerFactory); +} + + +void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_FLANGER_WAVEFORM: + if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Flanger.Waveform = val; + break; + + case AL_FLANGER_PHASE: + if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Flanger.Phase = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALflanger_setParami(effect, context, param, vals[0]); +} +void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_FLANGER_RATE: + if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Flanger.Rate = val; + break; + + case AL_FLANGER_DEPTH: + if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Flanger.Depth = val; + break; + + case AL_FLANGER_FEEDBACK: + if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Flanger.Feedback = val; + break; + + case AL_FLANGER_DELAY: + if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Flanger.Delay = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + ALflanger_setParamf(effect, context, param, vals[0]); +} + +void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_FLANGER_WAVEFORM: + *val = props->Flanger.Waveform; + break; + + case AL_FLANGER_PHASE: + *val = props->Flanger.Phase; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALflanger_getParami(effect, context, param, vals); +} +void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_FLANGER_RATE: + *val = props->Flanger.Rate; + break; + + case AL_FLANGER_DEPTH: + *val = props->Flanger.Depth; + break; + + case AL_FLANGER_FEEDBACK: + *val = props->Flanger.Feedback; + break; + + case AL_FLANGER_DELAY: + *val = props->Flanger.Delay; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + ALflanger_getParamf(effect, context, param, vals); +} + +DEFINE_ALEFFECT_VTABLE(ALflanger); diff --git a/libs/openal-soft/Alc/effects/modulator.c b/libs/openal-soft/Alc/effects/modulator.c new file mode 100644 index 00000000..a5475708 --- /dev/null +++ b/libs/openal-soft/Alc/effects/modulator.c @@ -0,0 +1,303 @@ +/** + * 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 { + DERIVE_FROM_TYPE(ALeffectState); + + enum { + SINUSOID, + SAWTOOTH, + SQUARE + } Waveform; + + ALuint index; + ALuint step; + + ALfloat Gain[MaxChannels]; + + ALfilterState Filter; +} ALmodulatorState; + +#define WAVEFORM_FRACBITS 24 +#define WAVEFORM_FRACONE (1<> (WAVEFORM_FRACBITS - 1)) & 1); +} + +#define DECL_TEMPLATE(func) \ +static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \ + const ALfloat *restrict SamplesIn, \ + ALfloat (*restrict SamplesOut)[BUFFERSIZE]) \ +{ \ + const ALuint step = state->step; \ + ALuint index = state->index; \ + ALuint base; \ + \ + for(base = 0;base < SamplesToDo;) \ + { \ + ALfloat temps[64]; \ + ALuint td = minu(SamplesToDo-base, 64); \ + ALuint i, k; \ + \ + for(i = 0;i < td;i++) \ + { \ + ALfloat samp; \ + samp = SamplesIn[base+i]; \ + samp = ALfilterState_processSingle(&state->Filter, samp); \ + \ + index += step; \ + index &= WAVEFORM_FRACMASK; \ + temps[i] = samp * func(index); \ + } \ + \ + for(k = 0;k < MaxChannels;k++) \ + { \ + ALfloat gain = state->Gain[k]; \ + if(!(gain > GAIN_SILENCE_THRESHOLD)) \ + continue; \ + \ + for(i = 0;i < td;i++) \ + SamplesOut[k][base+i] += gain * temps[i]; \ + } \ + \ + base += td; \ + } \ + state->index = index; \ +} + +DECL_TEMPLATE(Sin) +DECL_TEMPLATE(Saw) +DECL_TEMPLATE(Square) + +#undef DECL_TEMPLATE + + +static ALvoid ALmodulatorState_Destruct(ALmodulatorState *UNUSED(state)) +{ +} + +static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device)) +{ + return AL_TRUE; +} + +static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device, const ALeffectslot *Slot) +{ + ALfloat gain, cw, a; + + if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID) + state->Waveform = SINUSOID; + else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH) + state->Waveform = SAWTOOTH; + else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE) + state->Waveform = SQUARE; + + state->step = fastf2u(Slot->EffectProps.Modulator.Frequency*WAVEFORM_FRACONE / + Device->Frequency); + if(state->step == 0) state->step = 1; + + /* Custom filter coeffs, which match the old version instead of a low-shelf. */ + cw = cosf(F_2PI * Slot->EffectProps.Modulator.HighPassCutoff / Device->Frequency); + a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f); + + state->Filter.b[0] = a; + state->Filter.b[1] = -a; + state->Filter.b[2] = 0.0f; + state->Filter.a[0] = 1.0f; + state->Filter.a[1] = -a; + state->Filter.a[2] = 0.0f; + + gain = sqrtf(1.0f/Device->NumChan) * Slot->Gain; + SetGains(Device, gain, state->Gain); +} + +static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +{ + switch(state->Waveform) + { + case SINUSOID: + ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut); + break; + + case SAWTOOTH: + ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut); + break; + + case SQUARE: + ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut); + break; + } +} + +DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState); + + +typedef struct ALmodulatorStateFactory { + DERIVE_FROM_TYPE(ALeffectStateFactory); +} ALmodulatorStateFactory; + +static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UNUSED(factory)) +{ + ALmodulatorState *state; + + state = ALmodulatorState_New(sizeof(*state)); + if(!state) return NULL; + SET_VTABLE2(ALmodulatorState, ALeffectState, state); + + state->index = 0; + state->step = 1; + + ALfilterState_clear(&state->Filter); + + return STATIC_CAST(ALeffectState, state); +} + +DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALmodulatorStateFactory); + +ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void) +{ + static ALmodulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ALmodulatorStateFactory, ALeffectStateFactory) } }; + + return STATIC_CAST(ALeffectStateFactory, &ModulatorFactory); +} + + +void ALmodulator_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: + if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Modulator.Frequency = val; + break; + + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: + if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Modulator.HighPassCutoff = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALmodulator_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + ALmodulator_setParamf(effect, context, param, vals[0]); +} +void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: + ALmodulator_setParamf(effect, context, param, (ALfloat)val); + break; + + case AL_RING_MODULATOR_WAVEFORM: + if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Modulator.Waveform = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALmodulator_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALmodulator_setParami(effect, context, param, vals[0]); +} + +void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: + *val = (ALint)props->Modulator.Frequency; + break; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: + *val = (ALint)props->Modulator.HighPassCutoff; + break; + case AL_RING_MODULATOR_WAVEFORM: + *val = props->Modulator.Waveform; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALmodulator_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALmodulator_getParami(effect, context, param, vals); +} +void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: + *val = props->Modulator.Frequency; + break; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: + *val = props->Modulator.HighPassCutoff; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALmodulator_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + ALmodulator_getParamf(effect, context, param, vals); +} + +DEFINE_ALEFFECT_VTABLE(ALmodulator); diff --git a/libs/openal-soft/Alc/effects/null.c b/libs/openal-soft/Alc/effects/null.c new file mode 100644 index 00000000..6dacd021 --- /dev/null +++ b/libs/openal-soft/Alc/effects/null.c @@ -0,0 +1,165 @@ +#include "config.h" + +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "alMain.h" +#include "alAuxEffectSlot.h" +#include "alError.h" + + +typedef struct ALnullState { + DERIVE_FROM_TYPE(ALeffectState); +} ALnullState; + + +/* This destructs (not free!) the effect state. It's called only when the + * effect slot is no longer used. + */ +static ALvoid ALnullState_Destruct(ALnullState* UNUSED(state)) +{ +} + +/* This updates the device-dependant effect state. This is called on + * initialization and any time the device parameters (eg. playback frequency, + * format) have been changed. + */ +static ALboolean ALnullState_deviceUpdate(ALnullState* UNUSED(state), ALCdevice* UNUSED(device)) +{ + return AL_TRUE; +} + +/* This updates the effect state. This is called any time the effect is + * (re)loaded into a slot. + */ +static ALvoid ALnullState_update(ALnullState* UNUSED(state), ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot)) +{ +} + +/* This processes the effect state, for the given number of samples from the + * input to the output buffer. The result should be added to the output buffer, + * not replace it. + */ +static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloat *restrict UNUSED(samplesIn), ALfloat (*restrict samplesOut)[BUFFERSIZE]) +{ + /* NOTE: Couldn't use the UNUSED macro on samplesOut due to the way GCC's + * __attribute__ declaration interacts with the parenthesis. */ + (void)samplesOut; +} + +/* This allocates memory to store the object, before it gets constructed. + * DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method. + */ +static void *ALnullState_New(size_t size) +{ + return malloc(size); +} + +/* This frees the memory used by the object, after it has been destructed. + * DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method. + */ +static void ALnullState_Delete(void *ptr) +{ + free(ptr); +} + +/* Define the forwards and the ALeffectState vtable for this type. */ +DEFINE_ALEFFECTSTATE_VTABLE(ALnullState); + + +typedef struct ALnullStateFactory { + DERIVE_FROM_TYPE(ALeffectStateFactory); +} ALnullStateFactory; + +/* Creates ALeffectState objects of the appropriate type. */ +ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory)) +{ + ALnullState *state; + + state = ALnullState_New(sizeof(*state)); + if(!state) return NULL; + /* Set vtables for inherited types. */ + SET_VTABLE2(ALnullState, ALeffectState, state); + + return STATIC_CAST(ALeffectState, state); +} + +/* Define the ALeffectStateFactory vtable for this type. */ +DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory); + +ALeffectStateFactory *ALnullStateFactory_getFactory(void) +{ + static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } }; + + return STATIC_CAST(ALeffectStateFactory, &NullFactory); +} + + +void ALnull_setParami(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val)) +{ + switch(param) + { + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALnull_setParamiv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals)) +{ + switch(param) + { + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALnull_setParamf(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val)) +{ + switch(param) + { + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALnull_setParamfv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals)) +{ + switch(param) + { + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} + +void ALnull_getParami(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val)) +{ + switch(param) + { + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALnull_getParamiv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals)) +{ + switch(param) + { + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALnull_getParamf(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val)) +{ + switch(param) + { + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALnull_getParamfv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals)) +{ + switch(param) + { + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} + +DEFINE_ALEFFECT_VTABLE(ALnull); diff --git a/libs/openal-soft/Alc/effects/reverb.c b/libs/openal-soft/Alc/effects/reverb.c new file mode 100644 index 00000000..245aed41 --- /dev/null +++ b/libs/openal-soft/Alc/effects/reverb.c @@ -0,0 +1,1779 @@ +/** + * 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 "alMain.h" +#include "alu.h" +#include "alAuxEffectSlot.h" +#include "alEffect.h" +#include "alFilter.h" +#include "alError.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 ALreverbState { + DERIVE_FROM_TYPE(ALeffectState); + + ALboolean IsEax; + + // All delay lines are allocated as a single buffer to reduce memory + // fragmentation and management code. + ALfloat *SampleBuffer; + ALuint TotalSamples; + + // Master effect filters + ALfilterState LpFilter; + ALfilterState HpFilter; // EAX only + + 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; + + /* Temporary storage used when processing, before deinterlacing. */ + ALfloat ReverbSamples[BUFFERSIZE][4]; + ALfloat EarlySamples[BUFFERSIZE][4]; +} ALreverbState; + +/* 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; + + +// 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(ALreverbState *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 - cosf(F_2PI * State->Mod.Index / State->Mod.Range); + + // The depth determines the range over which to read the input samples + // from, so it must be filtered to reduce the distortion caused by even + // 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 = fastf2u(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(ALreverbState *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(ALreverbState *State, ALfloat in, ALfloat *restrict 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(ALreverbState *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(ALreverbState *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(ALreverbState *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(ALreverbState *State, const ALfloat *restrict in, ALfloat *restrict 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(ALreverbState *State, ALfloat in, ALfloat *restrict 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(ALreverbState *State, ALfloat in, ALfloat *restrict out) +{ + ALfloat feed, late[4], taps[4]; + + // Filter the incoming sample. + in = ALfilterState_processSingle(&State->LpFilter, 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, out); + + // 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); + + // Mix early reflections and late reverb. + out[0] += late[0]; + out[1] += late[1]; + out[2] += late[2]; + out[3] += late[3]; + + // 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(ALreverbState *State, ALfloat in, ALfloat *restrict early, ALfloat *restrict late) +{ + ALfloat feed, taps[4]; + + // Low-pass filter the incoming sample. + in = ALfilterState_processSingle(&State->LpFilter, in); + in = ALfilterState_processSingle(&State->HpFilter, 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++; +} + +static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +{ + ALfloat (*restrict out)[4] = State->ReverbSamples; + ALuint index, c; + + /* Process reverb for these samples. */ + for(index = 0;index < SamplesToDo;index++) + VerbPass(State, SamplesIn[index], out[index]); + + for(c = 0;c < MaxChannels;c++) + { + ALfloat gain = State->Gain[c]; + if(!(gain > GAIN_SILENCE_THRESHOLD)) + continue; + + for(index = 0;index < SamplesToDo;index++) + SamplesOut[c][index] += gain * out[index][c&3]; + } +} + +static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +{ + ALfloat (*restrict early)[4] = State->EarlySamples; + ALfloat (*restrict late)[4] = State->ReverbSamples; + ALuint index, c; + + /* Process reverb for these samples. */ + for(index = 0;index < SamplesToDo;index++) + EAXVerbPass(State, SamplesIn[index], early[index], late[index]); + + for(c = 0;c < MaxChannels;c++) + { + ALfloat earlyGain, lateGain; + + earlyGain = State->Early.PanGain[c]; + if(earlyGain > GAIN_SILENCE_THRESHOLD) + { + for(index = 0;index < SamplesToDo;index++) + SamplesOut[c][index] += earlyGain*early[index][c&3]; + } + lateGain = State->Late.PanGain[c]; + if(lateGain > GAIN_SILENCE_THRESHOLD) + { + for(index = 0;index < SamplesToDo;index++) + SamplesOut[c][index] += lateGain*late[index][c&3]; + } + } +} + +static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE]) +{ + if(State->IsEax) + ALreverbState_processEax(State, SamplesToDo, SamplesIn, SamplesOut); + else + ALreverbState_processStandard(State, SamplesToDo, SamplesIn, SamplesOut); +} + +// Given the allocated sample buffer, this function updates each delay line +// offset. +static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLine *Delay) +{ + Delay->Line = &sampleBuffer[(ALintptrEXT)Delay->Line]; +} + +// 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(fastf2u(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; +} + +/* Calculates the delay line metrics and allocates the shared sample buffer + * for all lines given the sample rate (frequency). If an allocation failure + * occurs, it returns AL_FALSE. + */ +static ALboolean AllocLines(ALuint frequency, ALreverbState *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; + + /* 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. + length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + + AL_EAXREVERB_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]); + } + + // 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) + { + TRACE("New reverb buffer length: %u samples (%f sec)\n", totalSamples, totalSamples/(float)frequency); + 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]); + } + 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; +} + +static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device) +{ + ALuint frequency = Device->Frequency, index; + + // Allocate the delay lines. + if(!AllocLines(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 = powf(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] = fastf2u(EARLY_LINE_LENGTH[index] * + frequency); + State->Late.ApOffset[index] = fastf2u(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 = fastf2u(ECHO_ALLPASS_LENGTH * frequency); + + 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 powf(0.001f/*-60 dB*/, length/decayTime); +} + +// Calculate a decay length from a coefficient and the time until the decay +// reaches -60 dB. +static inline ALfloat CalcDecayLength(ALfloat coeff, ALfloat decayTime) +{ + return log10f(coeff) * decayTime / log10f(0.001f)/*-60 dB*/; +} + +// 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 sqrtf(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 = sqrtf(3.0f); + t = diffusion * atanf(n); + + // Calculate the first mixing matrix coefficient. + *x = cosf(t); + // Calculate the second mixing matrix coefficient. + *y = sinf(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; + if(g < 0.9999f) /* 1-epsilon */ + { + /* Be careful with gains < 0.001, as that causes the coefficient + * head towards 1, which will flatten the signal. */ + g = maxf(g, 0.001f); + coeff = (1 - g*cw - sqrtf(2*g*(1-cw) - g*g*(1 - cw*cw))) / + (1 - g); + } + + // Very low decay times will produce minimal output, so apply an + // upper bound to the coefficient. + 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, ALreverbState *State) +{ + ALuint range; + + /* 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). + */ + range = maxu(fastf2u(modTime*frequency), 1); + State->Mod.Index = (ALuint)(State->Mod.Index * (ALuint64)range / + State->Mod.Range); + State->Mod.Range = range; + + /* 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, ALreverbState *State) +{ + // Calculate the initial delay taps. + State->DelayTap[0] = fastf2u(earlyDelay * frequency); + State->DelayTap[1] = fastf2u((earlyDelay + lateDelay) * frequency); +} + +// Update the early reflections gain and line coefficients. +static ALvoid UpdateEarlyLines(ALfloat reverbGain, ALfloat earlyGain, ALfloat lateDelay, ALreverbState *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, ALreverbState *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 * powf(DECO_MULTIPLIER, (ALfloat)index)) * + LATE_LINE_LENGTH[0] * (1.0f + (density * LATE_LINE_MULTIPLIER)); + State->DecoTap[index] = fastf2u(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, ALreverbState *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 * powf(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] = fastf2u(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, ALreverbState *State) +{ + // Update the offset and coefficient for the echo delay line. + State->Echo.Offset = fastf2u(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 * powf(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, ALreverbState *State) +{ + ALfloat earlyPan[3] = { ReflectionsPan[0], ReflectionsPan[1], + ReflectionsPan[2] }; + ALfloat latePan[3] = { LateReverbPan[0], LateReverbPan[1], + LateReverbPan[2] }; + ALfloat ambientGain; + ALfloat dirGain; + ALfloat length; + + Gain *= ReverbBoost; + + /* Attenuate reverb according to its coverage (dirGain=0 will give + * Gain*ambientGain, and dirGain=1 will give Gain). */ + ambientGain = minf(sqrtf(2.0f/Device->NumChan), 1.0f); + + length = earlyPan[0]*earlyPan[0] + earlyPan[1]*earlyPan[1] + earlyPan[2]*earlyPan[2]; + if(length > 1.0f) + { + length = 1.0f / sqrtf(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 / sqrtf(length); + latePan[0] *= length; + latePan[1] *= length; + latePan[2] *= length; + } + + dirGain = sqrtf(earlyPan[0]*earlyPan[0] + earlyPan[2]*earlyPan[2]); + ComputeAngleGains(Device, atan2f(earlyPan[0], earlyPan[2]), (1.0f-dirGain)*F_PI, + lerp(ambientGain, 1.0f, dirGain) * Gain, State->Early.PanGain); + + dirGain = sqrtf(latePan[0]*latePan[0] + latePan[2]*latePan[2]); + ComputeAngleGains(Device, atan2f(latePan[0], latePan[2]), (1.0f-dirGain)*F_PI, + lerp(ambientGain, 1.0f, dirGain) * Gain, State->Late.PanGain); +} + +static ALvoid ALreverbState_update(ALreverbState *State, ALCdevice *Device, const ALeffectslot *Slot) +{ + ALuint frequency = Device->Frequency; + ALfloat lfscale, hfscale, hfRatio; + ALfloat cw, x, y; + + if(Slot->EffectType == AL_EFFECT_EAXREVERB && !EmulateEAXReverb) + State->IsEax = AL_TRUE; + else if(Slot->EffectType == AL_EFFECT_REVERB || EmulateEAXReverb) + State->IsEax = AL_FALSE; + + // Calculate the master low-pass filter (from the master effect HF gain). + if(State->IsEax) + { + hfscale = Slot->EffectProps.Reverb.HFReference / frequency; + ALfilterState_setParams(&State->LpFilter, ALfilterType_HighShelf, + Slot->EffectProps.Reverb.GainHF, + hfscale, 0.0f); + lfscale = Slot->EffectProps.Reverb.LFReference / frequency; + ALfilterState_setParams(&State->HpFilter, ALfilterType_LowShelf, + Slot->EffectProps.Reverb.GainLF, + lfscale, 0.0f); + } + else + { + hfscale = LOWPASSFREQREF / frequency; + ALfilterState_setParams(&State->LpFilter, ALfilterType_HighShelf, + Slot->EffectProps.Reverb.GainHF, + hfscale, 0.0f); + } + + if(State->IsEax) + { + // Update the modulator line. + UpdateModulator(Slot->EffectProps.Reverb.ModulationTime, + Slot->EffectProps.Reverb.ModulationDepth, + frequency, State); + } + + // Update the initial effect delay. + UpdateDelayLine(Slot->EffectProps.Reverb.ReflectionsDelay, + Slot->EffectProps.Reverb.LateReverbDelay, + frequency, State); + + // Update the early lines. + UpdateEarlyLines(Slot->EffectProps.Reverb.Gain, + Slot->EffectProps.Reverb.ReflectionsGain, + Slot->EffectProps.Reverb.LateReverbDelay, State); + + // Update the decorrelator. + UpdateDecorrelator(Slot->EffectProps.Reverb.Density, frequency, State); + + // Get the mixing matrix coefficients (x and y). + CalcMatrixCoeffs(Slot->EffectProps.Reverb.Diffusion, &x, &y); + // Then divide x into y to simplify the matrix calculation. + State->Late.MixCoeff = y / x; + + // If the HF limit parameter is flagged, calculate an appropriate limit + // based on the air absorption parameter. + hfRatio = Slot->EffectProps.Reverb.DecayHFRatio; + if(Slot->EffectProps.Reverb.DecayHFLimit && + Slot->EffectProps.Reverb.AirAbsorptionGainHF < 1.0f) + hfRatio = CalcLimitedHfRatio(hfRatio, + Slot->EffectProps.Reverb.AirAbsorptionGainHF, + Slot->EffectProps.Reverb.DecayTime); + + cw = cosf(F_2PI * hfscale); + // Update the late lines. + UpdateLateLines(Slot->EffectProps.Reverb.Gain, Slot->EffectProps.Reverb.LateReverbGain, + x, Slot->EffectProps.Reverb.Density, Slot->EffectProps.Reverb.DecayTime, + Slot->EffectProps.Reverb.Diffusion, hfRatio, cw, frequency, State); + + if(State->IsEax) + { + // Update the echo line. + UpdateEchoLine(Slot->EffectProps.Reverb.Gain, Slot->EffectProps.Reverb.LateReverbGain, + Slot->EffectProps.Reverb.EchoTime, Slot->EffectProps.Reverb.DecayTime, + Slot->EffectProps.Reverb.Diffusion, Slot->EffectProps.Reverb.EchoDepth, + hfRatio, cw, frequency, State); + + // Update early and late 3D panning. + Update3DPanning(Device, Slot->EffectProps.Reverb.ReflectionsPan, + Slot->EffectProps.Reverb.LateReverbPan, Slot->Gain, State); + } + else + { + /* Update channel gains */ + ALfloat gain = sqrtf(2.0f/Device->NumChan) * ReverbBoost * Slot->Gain; + SetGains(Device, gain, State->Gain); + } +} + + +static ALvoid ALreverbState_Destruct(ALreverbState *State) +{ + free(State->SampleBuffer); + State->SampleBuffer = NULL; +} + +DECLARE_DEFAULT_ALLOCATORS(ALreverbState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState); + + +typedef struct ALreverbStateFactory { + DERIVE_FROM_TYPE(ALeffectStateFactory); +} ALreverbStateFactory; + +static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(factory)) +{ + ALreverbState *state; + ALuint index; + + state = ALreverbState_New(sizeof(*state)); + if(!state) return NULL; + SET_VTABLE2(ALreverbState, ALeffectState, state); + + state->TotalSamples = 0; + state->SampleBuffer = NULL; + + ALfilterState_clear(&state->LpFilter); + ALfilterState_clear(&state->HpFilter); + + 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 STATIC_CAST(ALeffectState, state); +} + +DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALreverbStateFactory); + +ALeffectStateFactory *ALreverbStateFactory_getFactory(void) +{ + static ALreverbStateFactory ReverbFactory = { { GET_VTABLE2(ALreverbStateFactory, ALeffectStateFactory) } }; + + return STATIC_CAST(ALeffectStateFactory, &ReverbFactory); +} + + +void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_EAXREVERB_DECAY_HFLIMIT: + if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.DecayHFLimit = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALeaxreverb_setParami(effect, context, param, vals[0]); +} +void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_EAXREVERB_DENSITY: + if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.Density = val; + break; + + case AL_EAXREVERB_DIFFUSION: + if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.Diffusion = val; + break; + + case AL_EAXREVERB_GAIN: + if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.Gain = val; + break; + + case AL_EAXREVERB_GAINHF: + if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.GainHF = val; + break; + + case AL_EAXREVERB_GAINLF: + if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.GainLF = val; + break; + + case AL_EAXREVERB_DECAY_TIME: + if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.DecayTime = val; + break; + + case AL_EAXREVERB_DECAY_HFRATIO: + if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.DecayHFRatio = val; + break; + + case AL_EAXREVERB_DECAY_LFRATIO: + if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.DecayLFRatio = val; + break; + + case AL_EAXREVERB_REFLECTIONS_GAIN: + if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.ReflectionsGain = val; + break; + + case AL_EAXREVERB_REFLECTIONS_DELAY: + if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.ReflectionsDelay = val; + break; + + case AL_EAXREVERB_LATE_REVERB_GAIN: + if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.LateReverbGain = val; + break; + + case AL_EAXREVERB_LATE_REVERB_DELAY: + if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.LateReverbDelay = val; + break; + + case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: + if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.AirAbsorptionGainHF = val; + break; + + case AL_EAXREVERB_ECHO_TIME: + if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.EchoTime = val; + break; + + case AL_EAXREVERB_ECHO_DEPTH: + if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.EchoDepth = val; + break; + + case AL_EAXREVERB_MODULATION_TIME: + if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.ModulationTime = val; + break; + + case AL_EAXREVERB_MODULATION_DEPTH: + if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.ModulationDepth = val; + break; + + case AL_EAXREVERB_HFREFERENCE: + if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.HFReference = val; + break; + + case AL_EAXREVERB_LFREFERENCE: + if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.LFReference = val; + break; + + case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: + if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.RoomRolloffFactor = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_EAXREVERB_REFLECTIONS_PAN: + if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2]))) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + LockContext(context); + props->Reverb.ReflectionsPan[0] = vals[0]; + props->Reverb.ReflectionsPan[1] = vals[1]; + props->Reverb.ReflectionsPan[2] = vals[2]; + UnlockContext(context); + break; + case AL_EAXREVERB_LATE_REVERB_PAN: + if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2]))) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + LockContext(context); + props->Reverb.LateReverbPan[0] = vals[0]; + props->Reverb.LateReverbPan[1] = vals[1]; + props->Reverb.LateReverbPan[2] = vals[2]; + UnlockContext(context); + break; + + default: + ALeaxreverb_setParamf(effect, context, param, vals[0]); + break; + } +} + +void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_EAXREVERB_DECAY_HFLIMIT: + *val = props->Reverb.DecayHFLimit; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALeaxreverb_getParami(effect, context, param, vals); +} +void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_EAXREVERB_DENSITY: + *val = props->Reverb.Density; + break; + + case AL_EAXREVERB_DIFFUSION: + *val = props->Reverb.Diffusion; + break; + + case AL_EAXREVERB_GAIN: + *val = props->Reverb.Gain; + break; + + case AL_EAXREVERB_GAINHF: + *val = props->Reverb.GainHF; + break; + + case AL_EAXREVERB_GAINLF: + *val = props->Reverb.GainLF; + break; + + case AL_EAXREVERB_DECAY_TIME: + *val = props->Reverb.DecayTime; + break; + + case AL_EAXREVERB_DECAY_HFRATIO: + *val = props->Reverb.DecayHFRatio; + break; + + case AL_EAXREVERB_DECAY_LFRATIO: + *val = props->Reverb.DecayLFRatio; + break; + + case AL_EAXREVERB_REFLECTIONS_GAIN: + *val = props->Reverb.ReflectionsGain; + break; + + case AL_EAXREVERB_REFLECTIONS_DELAY: + *val = props->Reverb.ReflectionsDelay; + break; + + case AL_EAXREVERB_LATE_REVERB_GAIN: + *val = props->Reverb.LateReverbGain; + break; + + case AL_EAXREVERB_LATE_REVERB_DELAY: + *val = props->Reverb.LateReverbDelay; + break; + + case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: + *val = props->Reverb.AirAbsorptionGainHF; + break; + + case AL_EAXREVERB_ECHO_TIME: + *val = props->Reverb.EchoTime; + break; + + case AL_EAXREVERB_ECHO_DEPTH: + *val = props->Reverb.EchoDepth; + break; + + case AL_EAXREVERB_MODULATION_TIME: + *val = props->Reverb.ModulationTime; + break; + + case AL_EAXREVERB_MODULATION_DEPTH: + *val = props->Reverb.ModulationDepth; + break; + + case AL_EAXREVERB_HFREFERENCE: + *val = props->Reverb.HFReference; + break; + + case AL_EAXREVERB_LFREFERENCE: + *val = props->Reverb.LFReference; + break; + + case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: + *val = props->Reverb.RoomRolloffFactor; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_EAXREVERB_REFLECTIONS_PAN: + LockContext(context); + vals[0] = props->Reverb.ReflectionsPan[0]; + vals[1] = props->Reverb.ReflectionsPan[1]; + vals[2] = props->Reverb.ReflectionsPan[2]; + UnlockContext(context); + break; + case AL_EAXREVERB_LATE_REVERB_PAN: + LockContext(context); + vals[0] = props->Reverb.LateReverbPan[0]; + vals[1] = props->Reverb.LateReverbPan[1]; + vals[2] = props->Reverb.LateReverbPan[2]; + UnlockContext(context); + break; + + default: + ALeaxreverb_getParamf(effect, context, param, vals); + break; + } +} + +DEFINE_ALEFFECT_VTABLE(ALeaxreverb); + +void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_REVERB_DECAY_HFLIMIT: + if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.DecayHFLimit = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALreverb_setParami(effect, context, param, vals[0]); +} +void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_REVERB_DENSITY: + if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.Density = val; + break; + + case AL_REVERB_DIFFUSION: + if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.Diffusion = val; + break; + + case AL_REVERB_GAIN: + if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.Gain = val; + break; + + case AL_REVERB_GAINHF: + if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.GainHF = val; + break; + + case AL_REVERB_DECAY_TIME: + if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.DecayTime = val; + break; + + case AL_REVERB_DECAY_HFRATIO: + if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.DecayHFRatio = val; + break; + + case AL_REVERB_REFLECTIONS_GAIN: + if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.ReflectionsGain = val; + break; + + case AL_REVERB_REFLECTIONS_DELAY: + if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.ReflectionsDelay = val; + break; + + case AL_REVERB_LATE_REVERB_GAIN: + if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.LateReverbGain = val; + break; + + case AL_REVERB_LATE_REVERB_DELAY: + if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.LateReverbDelay = val; + break; + + case AL_REVERB_AIR_ABSORPTION_GAINHF: + if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.AirAbsorptionGainHF = val; + break; + + case AL_REVERB_ROOM_ROLLOFF_FACTOR: + if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + props->Reverb.RoomRolloffFactor = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + ALreverb_setParamf(effect, context, param, vals[0]); +} + +void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_REVERB_DECAY_HFLIMIT: + *val = props->Reverb.DecayHFLimit; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALreverb_getParami(effect, context, param, vals); +} +void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_REVERB_DENSITY: + *val = props->Reverb.Density; + break; + + case AL_REVERB_DIFFUSION: + *val = props->Reverb.Diffusion; + break; + + case AL_REVERB_GAIN: + *val = props->Reverb.Gain; + break; + + case AL_REVERB_GAINHF: + *val = props->Reverb.GainHF; + break; + + case AL_REVERB_DECAY_TIME: + *val = props->Reverb.DecayTime; + break; + + case AL_REVERB_DECAY_HFRATIO: + *val = props->Reverb.DecayHFRatio; + break; + + case AL_REVERB_REFLECTIONS_GAIN: + *val = props->Reverb.ReflectionsGain; + break; + + case AL_REVERB_REFLECTIONS_DELAY: + *val = props->Reverb.ReflectionsDelay; + break; + + case AL_REVERB_LATE_REVERB_GAIN: + *val = props->Reverb.LateReverbGain; + break; + + case AL_REVERB_LATE_REVERB_DELAY: + *val = props->Reverb.LateReverbDelay; + break; + + case AL_REVERB_AIR_ABSORPTION_GAINHF: + *val = props->Reverb.AirAbsorptionGainHF; + break; + + case AL_REVERB_ROOM_ROLLOFF_FACTOR: + *val = props->Reverb.RoomRolloffFactor; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ + ALreverb_getParamf(effect, context, param, vals); +} + +DEFINE_ALEFFECT_VTABLE(ALreverb); diff --git a/libs/openal-soft/Alc/evtqueue.h b/libs/openal-soft/Alc/evtqueue.h new file mode 100644 index 00000000..95702d79 --- /dev/null +++ b/libs/openal-soft/Alc/evtqueue.h @@ -0,0 +1,31 @@ +#ifndef AL_EVTQUEUE_H +#define AL_EVTQUEUE_H + +#include "AL/al.h" + +#include "alMain.h" + +typedef struct MidiEvent { + ALuint64 time; + ALuint event; + union { + ALuint val[2]; + struct { + ALvoid *data; + ALsizei size; + } sysex; + } param; +} MidiEvent; + +typedef struct EvtQueue { + MidiEvent *events; + ALsizei pos; + ALsizei size; + ALsizei maxsize; +} EvtQueue; + +void InitEvtQueue(EvtQueue *queue); +void ResetEvtQueue(EvtQueue *queue); +ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt); + +#endif /* AL_EVTQUEUE_H */ diff --git a/libs/openal-soft/Alc/helpers.c b/libs/openal-soft/Alc/helpers.c new file mode 100644 index 00000000..fc879d95 --- /dev/null +++ b/libs/openal-soft/Alc/helpers.c @@ -0,0 +1,814 @@ +/** + * 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 + */ + +#ifdef _WIN32 +#ifdef __MINGW32__ +#define _WIN32_IE 0x501 +#else +#define _WIN32_IE 0x400 +#endif +#endif + +#include "config.h" + +#include +#include +#include +#include +#ifdef HAVE_MALLOC_H +#include +#endif + +#ifndef AL_NO_UID_DEFS +#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); + +#ifdef HAVE_MMDEVAPI +#include +DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14); +#endif +#endif +#endif /* AL_NO_UID_DEFS */ + +#ifdef HAVE_DLFCN_H +#include +#endif +#ifdef HAVE_INTRIN_H +#include +#endif +#ifdef HAVE_CPUID_H +#include +#endif +#ifdef HAVE_SYS_SYSCONF_H +#include +#endif +#ifdef HAVE_FLOAT_H +#include +#endif +#ifdef HAVE_IEEEFP_H +#include +#endif + +#ifdef _WIN32_IE +#include +#endif + +#include "alMain.h" +#include "alu.h" +#include "atomic.h" +#include "uintmap.h" +#include "vector.h" +#include "alstring.h" +#include "compat.h" +#include "threads.h" + + +extern inline ALuint NextPowerOf2(ALuint value); +extern inline ALint fastf2i(ALfloat f); +extern inline ALuint fastf2u(ALfloat f); + + +ALuint CPUCapFlags = 0; + + +void FillCPUCaps(ALuint capfilter) +{ + ALuint caps = 0; + +/* FIXME: We really should get this for all available CPUs in case different + * CPUs have different caps (is that possible on one machine?). */ +#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_X64)) + union { + unsigned int regs[4]; + char str[sizeof(unsigned int[4])]; + } cpuinf[3]; + + if(!__get_cpuid(0, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3])) + ERR("Failed to get CPUID\n"); + else + { + unsigned int maxfunc = cpuinf[0].regs[0]; + unsigned int maxextfunc = 0; + + if(__get_cpuid(0x80000000, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3])) + maxextfunc = cpuinf[0].regs[0]; + TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc); + + TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8); + if(maxextfunc >= 0x80000004 && + __get_cpuid(0x80000002, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]) && + __get_cpuid(0x80000003, &cpuinf[1].regs[0], &cpuinf[1].regs[1], &cpuinf[1].regs[2], &cpuinf[1].regs[3]) && + __get_cpuid(0x80000004, &cpuinf[2].regs[0], &cpuinf[2].regs[1], &cpuinf[2].regs[2], &cpuinf[2].regs[3])) + TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str); + + if(maxfunc >= 1 && + __get_cpuid(1, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3])) + { + if((cpuinf[0].regs[3]&(1<<25))) + { + caps |= CPU_CAP_SSE; + if((cpuinf[0].regs[3]&(1<<26))) + { + caps |= CPU_CAP_SSE2; + if((cpuinf[0].regs[2]&(1<<19))) + caps |= CPU_CAP_SSE4_1; + } + } + } + } +#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_X64)) + union { + int regs[4]; + char str[sizeof(int[4])]; + } cpuinf[3]; + + (__cpuid)(cpuinf[0].regs, 0); + if(cpuinf[0].regs[0] == 0) + ERR("Failed to get CPUID\n"); + else + { + unsigned int maxfunc = cpuinf[0].regs[0]; + unsigned int maxextfunc; + + (__cpuid)(cpuinf[0].regs, 0x80000000); + maxextfunc = cpuinf[0].regs[0]; + + TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc); + + TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8); + if(maxextfunc >= 0x80000004) + { + (__cpuid)(cpuinf[0].regs, 0x80000002); + (__cpuid)(cpuinf[1].regs, 0x80000003); + (__cpuid)(cpuinf[2].regs, 0x80000004); + TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str); + } + + if(maxfunc >= 1) + { + (__cpuid)(cpuinf[0].regs, 1); + if((cpuinf[0].regs[3]&(1<<25))) + { + caps |= CPU_CAP_SSE; + if((cpuinf[0].regs[3]&(1<<26))) + { + caps |= CPU_CAP_SSE2; + if((cpuinf[0].regs[2]&(1<<19))) + caps |= CPU_CAP_SSE4_1; + } + } + } + } +#else + /* Assume support for whatever's supported if we can't check for it */ +#if defined(HAVE_SSE4_1) +#warning "Assuming SSE 4.1 run-time support!" + capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE4_1; +#elif defined(HAVE_SSE2) +#warning "Assuming SSE 2 run-time support!" + capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2; +#elif defined(HAVE_SSE) +#warning "Assuming SSE run-time support!" + capfilter |= CPU_CAP_SSE; +#endif +#endif +#ifdef HAVE_NEON + /* Assume Neon support if compiled with it */ + caps |= CPU_CAP_NEON; +#endif + + TRACE("Extensions:%s%s%s%s%s\n", + ((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""), + ((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""), + ((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""), + ((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +Neon" : " -Neon") : ""), + ((!capfilter) ? " -none-" : "") + ); + CPUCapFlags = caps & capfilter; +} + + +void *al_malloc(size_t alignment, size_t size) +{ +#if defined(HAVE_ALIGNED_ALLOC) + size = (size+(alignment-1))&~(alignment-1); + return aligned_alloc(alignment, size); +#elif defined(HAVE_POSIX_MEMALIGN) + void *ret; + if(posix_memalign(&ret, alignment, size) == 0) + return ret; + return NULL; +#elif defined(HAVE__ALIGNED_MALLOC) + return _aligned_malloc(size, alignment); +#else + char *ret = malloc(size+alignment); + if(ret != NULL) + { + *(ret++) = 0x00; + while(((ALintptrEXT)ret&(alignment-1)) != 0) + *(ret++) = 0x55; + } + return ret; +#endif +} + +void *al_calloc(size_t alignment, size_t size) +{ + void *ret = al_malloc(alignment, size); + if(ret) memset(ret, 0, size); + return ret; +} + +void al_free(void *ptr) +{ +#if defined(HAVE_ALIGNED_ALLOC) || defined(HAVE_POSIX_MEMALIGN) + free(ptr); +#elif defined(HAVE__ALIGNED_MALLOC) + _aligned_free(ptr); +#else + if(ptr != NULL) + { + char *finder = ptr; + do { + --finder; + } while(*finder == 0x55); + free(finder); + } +#endif +} + + +void SetMixerFPUMode(FPUCtl *ctl) +{ +#ifdef HAVE_FENV_H + fegetenv(STATIC_CAST(fenv_t, ctl)); +#if defined(__GNUC__) && defined(HAVE_SSE) + if((CPUCapFlags&CPU_CAP_SSE)) + __asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state)); +#endif + +#ifdef FE_TOWARDZERO + fesetround(FE_TOWARDZERO); +#endif +#if defined(__GNUC__) && defined(HAVE_SSE) + if((CPUCapFlags&CPU_CAP_SSE)) + { + int sseState = ctl->sse_state; + sseState |= 0x6000; /* set round-to-zero */ + sseState |= 0x8000; /* set flush-to-zero */ + if((CPUCapFlags&CPU_CAP_SSE2)) + sseState |= 0x0040; /* set denormals-are-zero */ + __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState)); + } +#endif + +#elif defined(HAVE___CONTROL87_2) + + int mode; + __control87_2(0, 0, &ctl->state, NULL); + __control87_2(_RC_CHOP, _MCW_RC, &mode, NULL); +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + { + __control87_2(0, 0, NULL, &ctl->sse_state); + __control87_2(_RC_CHOP|_DN_FLUSH, _MCW_RC|_MCW_DN, NULL, &mode); + } +#endif + +#elif defined(HAVE__CONTROLFP) + + ctl->state = _controlfp(0, 0); + (void)_controlfp(_RC_CHOP, _MCW_RC); +#endif +} + +void RestoreFPUMode(const FPUCtl *ctl) +{ +#ifdef HAVE_FENV_H + fesetenv(STATIC_CAST(fenv_t, ctl)); +#if defined(__GNUC__) && defined(HAVE_SSE) + if((CPUCapFlags&CPU_CAP_SSE)) + __asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state)); +#endif + +#elif defined(HAVE___CONTROL87_2) + + int mode; + __control87_2(ctl->state, _MCW_RC, &mode, NULL); +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + __control87_2(ctl->sse_state, _MCW_RC|_MCW_DN, NULL, &mode); +#endif + +#elif defined(HAVE__CONTROLFP) + + _controlfp(ctl->state, _MCW_RC); +#endif +} + + +#ifdef _WIN32 + +static WCHAR *FromUTF8(const char *str) +{ + WCHAR *out = NULL; + int len; + + if((len=MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)) > 0) + { + out = calloc(sizeof(WCHAR), len); + MultiByteToWideChar(CP_UTF8, 0, str, -1, out, len); + } + return out; +} + + +void *LoadLib(const char *name) +{ + HANDLE hdl = NULL; + WCHAR *wname; + + wname = FromUTF8(name); + if(!wname) + ERR("Failed to convert UTF-8 filename: \"%s\"\n", name); + else + { + hdl = LoadLibraryW(wname); + free(wname); + } + return hdl; +} +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; +} + +WCHAR *strdupW(const WCHAR *str) +{ + const WCHAR *n; + WCHAR *ret; + size_t len; + + n = str; + while(*n) n++; + len = n - str; + + ret = calloc(sizeof(WCHAR), len+1); + if(ret != NULL) + memcpy(ret, str, sizeof(WCHAR)*len); + return ret; +} + +FILE *al_fopen(const char *fname, const char *mode) +{ + WCHAR *wname=NULL, *wmode=NULL; + FILE *file = NULL; + + wname = FromUTF8(fname); + wmode = FromUTF8(mode); + if(!wname) + ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname); + else if(!wmode) + ERR("Failed to convert UTF-8 mode: \"%s\"\n", mode); + else + file = _wfopen(wname, wmode); + + free(wname); + free(wmode); + + return file; +} + +#else + +#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) + { + WARN("Failed to load %s: %s\n", name, err); + sym = NULL; + } + return sym; +} + +#endif +#endif + + +void al_print(const char *type, const char *func, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(LogFile, "AL lib: %s %s: ", type, func); + vfprintf(LogFile, fmt, ap); + va_end(ap); + + fflush(LogFile); +} + +#ifdef _WIN32 +static inline int is_slash(int c) +{ return (c == '\\' || c == '/'); } + +FILE *OpenDataFile(const char *fname, const char *subdir) +{ + static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA }; + WCHAR *wname=NULL, *wsubdir=NULL; + FILE *f; + int i; + + /* If the path is absolute, open it directly. */ + if(fname[0] != '\0' && fname[1] == ':' && is_slash(fname[2])) + { + if((f=al_fopen(fname, "rb")) != NULL) + { + TRACE("Opened %s\n", fname); + return f; + } + WARN("Could not open %s\n", fname); + return NULL; + } + + /* If it's relative, try the current directory first before the data directories. */ + if((f=al_fopen(fname, "rb")) != NULL) + { + TRACE("Opened %s\n", fname); + return f; + } + WARN("Could not open %s\n", fname); + + wname = FromUTF8(fname); + wsubdir = FromUTF8(subdir); + if(!wname) + ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname); + else if(!wsubdir) + ERR("Failed to convert UTF-8 subdir: \"%s\"\n", subdir); + else for(i = 0;i < 2;i++) + { + WCHAR buffer[PATH_MAX]; + size_t len; + + if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) == FALSE) + continue; + + len = lstrlenW(buffer); + if(len > 0 && is_slash(buffer[len-1])) + buffer[--len] = '\0'; + _snwprintf(buffer+len, PATH_MAX-len, L"/%ls/%ls", wsubdir, wname); + len = lstrlenW(buffer); + while(len > 0) + { + --len; + if(buffer[len] == '/') + buffer[len] = '\\'; + } + + if((f=_wfopen(buffer, L"rb")) != NULL) + { + TRACE("Opened %ls\n", buffer); + return f; + } + WARN("Could not open %ls\n", buffer); + } + free(wname); + free(wsubdir); + + return NULL; +} +#else +FILE *OpenDataFile(const char *fname, const char *subdir) +{ + char buffer[PATH_MAX] = ""; + const char *str, *next; + FILE *f; + + if(fname[0] == '/') + { + if((f=al_fopen(fname, "rb")) != NULL) + { + TRACE("Opened %s\n", fname); + return f; + } + WARN("Could not open %s\n", fname); + return NULL; + } + + if((f=al_fopen(fname, "rb")) != NULL) + { + TRACE("Opened %s\n", fname); + return f; + } + WARN("Could not open %s\n", fname); + + if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0') + snprintf(buffer, sizeof(buffer), "%s/%s/%s", str, subdir, fname); + else if((str=getenv("HOME")) != NULL && str[0] != '\0') + snprintf(buffer, sizeof(buffer), "%s/.local/share/%s/%s", str, subdir, fname); + if(buffer[0]) + { + if((f=al_fopen(buffer, "rb")) != NULL) + { + TRACE("Opened %s\n", buffer); + return f; + } + WARN("Could not open %s\n", buffer); + } + + if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0') + str = "/usr/local/share/:/usr/share/"; + + next = str; + while((str=next) != NULL && str[0] != '\0') + { + size_t len; + next = strchr(str, ':'); + + if(!next) + len = strlen(str); + else + { + len = next - str; + next++; + } + + if(len > sizeof(buffer)-1) + len = sizeof(buffer)-1; + strncpy(buffer, str, len); + buffer[len] = '\0'; + snprintf(buffer+len, sizeof(buffer)-len, "/%s/%s", subdir, fname); + + if((f=al_fopen(buffer, "rb")) != NULL) + { + TRACE("Opened %s\n", buffer); + return f; + } + WARN("Could not open %s\n", buffer); + } + + return NULL; +} +#endif + + +void SetRTPriority(void) +{ + ALboolean failed = AL_FALSE; + +#ifdef _WIN32 + if(RTPrioLevel > 0) + failed = !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); +#elif defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__) + if(RTPrioLevel > 0) + { + struct sched_param param; + /* 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 + /* Real-time priority not available */ + failed = (RTPrioLevel>0); +#endif + if(failed) + ERR("Failed to set priority level for thread\n"); +} + + +ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count, ALboolean exact) +{ + vector_ *vecptr = (vector_*)ptr; + if(obj_count < 0) + return AL_FALSE; + if((*vecptr ? (*vecptr)->Capacity : 0) < obj_count) + { + ALsizei old_size = (*vecptr ? (*vecptr)->Size : 0); + void *temp; + + /* Use the next power-of-2 size if we don't need to allocate the exact + * amount. This is preferred when regularly increasing the vector since + * it means fewer reallocations. Though it means it also wastes some + * memory. */ + if(exact == AL_FALSE) + { + obj_count = NextPowerOf2((ALuint)obj_count); + if(obj_count < 0) return AL_FALSE; + } + + /* Need to be explicit with the caller type's base size, because it + * could have extra padding before the start of the array (that is, + * sizeof(*vector_) may not equal base_size). */ + temp = realloc(*vecptr, base_size + obj_size*obj_count); + if(temp == NULL) return AL_FALSE; + + *vecptr = temp; + (*vecptr)->Capacity = obj_count; + (*vecptr)->Size = old_size; + } + return AL_TRUE; +} + +ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count) +{ + vector_ *vecptr = (vector_*)ptr; + if(obj_count < 0) + return AL_FALSE; + if(*vecptr || obj_count > 0) + { + if(!vector_reserve((char*)vecptr, base_size, obj_size, obj_count, AL_TRUE)) + return AL_FALSE; + (*vecptr)->Size = obj_count; + } + return AL_TRUE; +} + +ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend) +{ + vector_ *vecptr = (vector_*)ptr; + if(datstart != datend) + { + ptrdiff_t ins_elem = (*vecptr ? ((char*)ins_pos - ((char*)(*vecptr) + base_size)) : + ((char*)ins_pos - (char*)NULL)) / + obj_size; + ptrdiff_t numins = ((const char*)datend - (const char*)datstart) / obj_size; + + assert(numins > 0); + if(INT_MAX-VECTOR_SIZE(*vecptr) <= numins || + !vector_reserve((char*)vecptr, base_size, obj_size, VECTOR_SIZE(*vecptr)+numins, AL_TRUE)) + return AL_FALSE; + + /* NOTE: ins_pos may have been invalidated if *vecptr moved. Use ins_elem instead. */ + if(ins_elem < (*vecptr)->Size) + { + memmove((char*)(*vecptr) + base_size + ((ins_elem+numins)*obj_size), + (char*)(*vecptr) + base_size + ((ins_elem )*obj_size), + ((*vecptr)->Size-ins_elem)*obj_size); + } + memcpy((char*)(*vecptr) + base_size + (ins_elem*obj_size), + datstart, numins*obj_size); + (*vecptr)->Size += (ALsizei)numins; + } + return AL_TRUE; +} + + +extern inline void al_string_deinit(al_string *str); +extern inline ALsizei al_string_length(const_al_string str); +extern inline ALboolean al_string_empty(const_al_string str); +extern inline const al_string_char_type *al_string_get_cstr(const_al_string str); + +void al_string_clear(al_string *str) +{ + /* Reserve one more character than the total size of the string. This is to + * ensure we have space to add a null terminator in the string data so it + * can be used as a C-style string. */ + VECTOR_RESERVE(*str, 1); + VECTOR_RESIZE(*str, 0); + *VECTOR_ITER_END(*str) = 0; +} + +static inline int al_string_compare(const al_string_char_type *str1, ALsizei str1len, + const al_string_char_type *str2, ALsizei str2len) +{ + ALsizei complen = mini(str1len, str2len); + int ret = memcmp(str1, str2, complen); + if(ret == 0) + { + if(str1len > str2len) return 1; + if(str1len < str2len) return -1; + } + return ret; +} +int al_string_cmp(const_al_string str1, const_al_string str2) +{ + return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1), + &VECTOR_FRONT(str2), al_string_length(str2)); +} +int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2) +{ + return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1), + str2, (ALsizei)strlen(str2)); +} + +void al_string_copy(al_string *str, const_al_string from) +{ + ALsizei len = VECTOR_SIZE(from); + VECTOR_RESERVE(*str, len+1); + VECTOR_RESIZE(*str, 0); + VECTOR_INSERT(*str, VECTOR_ITER_END(*str), VECTOR_ITER_BEGIN(from), VECTOR_ITER_BEGIN(from)+len); + *VECTOR_ITER_END(*str) = 0; +} + +void al_string_copy_cstr(al_string *str, const al_string_char_type *from) +{ + size_t len = strlen(from); + VECTOR_RESERVE(*str, len+1); + VECTOR_RESIZE(*str, 0); + VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len); + *VECTOR_ITER_END(*str) = 0; +} + +void al_string_append_char(al_string *str, const al_string_char_type c) +{ + VECTOR_RESERVE(*str, al_string_length(*str)+2); + VECTOR_PUSH_BACK(*str, c); + *VECTOR_ITER_END(*str) = 0; +} + +void al_string_append_cstr(al_string *str, const al_string_char_type *from) +{ + size_t len = strlen(from); + if(len != 0) + { + VECTOR_RESERVE(*str, al_string_length(*str)+len+1); + VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len); + *VECTOR_ITER_END(*str) = 0; + } +} + +void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to) +{ + if(to != from) + { + VECTOR_RESERVE(*str, al_string_length(*str)+(to-from)+1); + VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, to); + *VECTOR_ITER_END(*str) = 0; + } +} + +#ifdef _WIN32 +void al_string_copy_wcstr(al_string *str, const wchar_t *from) +{ + int len; + if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0) + { + VECTOR_RESERVE(*str, len); + VECTOR_RESIZE(*str, len-1); + WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str), len, NULL, NULL); + *VECTOR_ITER_END(*str) = 0; + } +} +#endif diff --git a/libs/openal-soft/Alc/hrtf.c b/libs/openal-soft/Alc/hrtf.c new file mode 100644 index 00000000..707655a0 --- /dev/null +++ b/libs/openal-soft/Alc/hrtf.c @@ -0,0 +1,820 @@ +/** + * 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 + +#include "AL/al.h" +#include "AL/alc.h" +#include "alMain.h" +#include "alSource.h" +#include "alu.h" +#include "hrtf.h" + + +/* Current data set limits defined by the makehrtf utility. */ +#define MIN_IR_SIZE (8) +#define MAX_IR_SIZE (128) +#define MOD_IR_SIZE (8) + +#define MIN_EV_COUNT (5) +#define MAX_EV_COUNT (128) + +#define MIN_AZ_COUNT (1) +#define MAX_AZ_COUNT (128) + +struct Hrtf { + ALuint sampleRate; + ALuint irSize; + ALubyte evCount; + + const ALubyte *azCount; + const ALushort *evOffset; + const ALshort *coeffs; + const ALubyte *delays; + + struct Hrtf *next; +}; + +static const ALchar magicMarker00[8] = "MinPHR00"; +static const ALchar magicMarker01[8] = "MinPHR01"; + +/* First value for pass-through coefficients (remaining are 0), used for omni- + * directional sounds. */ +static const ALfloat PassthruCoeff = 32767.0f * 0.707106781187f/*sqrt(0.5)*/; + +static struct Hrtf *LoadedHrtfs = NULL; + +/* Calculate the elevation indices given the polar elevation in radians. + * This will return two indices between 0 and (evcount - 1) and an + * interpolation factor between 0.0 and 1.0. + */ +static void CalcEvIndices(ALuint evcount, ALfloat ev, ALuint *evidx, ALfloat *evmu) +{ + ev = (F_PI_2 + ev) * (evcount-1) / F_PI; + evidx[0] = fastf2u(ev); + evidx[1] = minu(evidx[0] + 1, evcount-1); + *evmu = ev - evidx[0]; +} + +/* Calculate the azimuth indices given the polar azimuth in radians. This + * will return two indices between 0 and (azcount - 1) and an interpolation + * factor between 0.0 and 1.0. + */ +static void CalcAzIndices(ALuint azcount, ALfloat az, ALuint *azidx, ALfloat *azmu) +{ + az = (F_2PI + az) * azcount / (F_2PI); + azidx[0] = fastf2u(az) % azcount; + azidx[1] = (azidx[0] + 1) % azcount; + *azmu = az - floorf(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 that 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, change; + + // Calculate the normalized dB gain change. + newGain = maxf(newGain, 0.0001f); + oldGain = maxf(oldGain, 0.0001f); + gainChange = fabsf(log10f(newGain / oldGain) / log10f(0.0001f)); + + // Calculate the angle change only 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]) + { + ALfloat dotp = olddir[0]*newdir[0] + olddir[1]*newdir[1] + olddir[2]*newdir[2]; + angleChange = acosf(clampf(dotp, -1.0f, 1.0f)) / F_PI; + } + } + + // Use the largest of the two changes for the delta factor, and apply a + // significance shaping function to it. + change = maxf(angleChange * 25.0f, gainChange) * 2.0f; + return minf(change, 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 data set. The coefficients + * are also normalized and attenuated by the specified gain. + */ +void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays) +{ + ALuint evidx[2], lidx[4], ridx[4]; + ALfloat mu[3], blend[4]; + ALuint i; + + /* Claculate elevation indices and interpolation factor. */ + CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]); + + for(i = 0;i < 2;i++) + { + ALuint azcount = Hrtf->azCount[evidx[i]]; + ALuint evoffset = Hrtf->evOffset[evidx[i]]; + ALuint azidx[2]; + + /* Calculate azimuth indices and interpolation factor for this elevation. */ + CalcAzIndices(azcount, azimuth, azidx, &mu[i]); + + /* Calculate a set of linear HRIR indices for left and right channels. */ + lidx[i*2 + 0] = evoffset + azidx[0]; + lidx[i*2 + 1] = evoffset + azidx[1]; + ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount); + ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount); + } + + /* Calculate 4 blending weights for 2D bilinear interpolation. */ + blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]); + blend[1] = ( mu[0]) * (1.0f-mu[2]); + blend[2] = (1.0f-mu[1]) * ( mu[2]); + blend[3] = ( mu[1]) * ( mu[2]); + + /* Calculate the HRIR delays using linear interpolation. */ + delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] + + Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) * + dirfact + 0.5f) << HRTFDELAY_BITS; + delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] + + Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) * + dirfact + 0.5f) << HRTFDELAY_BITS; + + /* Calculate the sample offsets for the HRIR indices. */ + lidx[0] *= Hrtf->irSize; + lidx[1] *= Hrtf->irSize; + lidx[2] *= Hrtf->irSize; + lidx[3] *= Hrtf->irSize; + ridx[0] *= Hrtf->irSize; + ridx[1] *= Hrtf->irSize; + ridx[2] *= Hrtf->irSize; + ridx[3] *= Hrtf->irSize; + + /* 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) + { + ALfloat c; + + gain *= 1.0f/32767.0f; + + i = 0; + c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] + + Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]); + coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain; + c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] + + Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]); + coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain; + + for(i = 1;i < Hrtf->irSize;i++) + { + c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] + + Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]); + coeffs[i][0] = lerp(0.0f, c, dirfact) * gain; + c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] + + Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]); + coeffs[i][1] = lerp(0.0f, c, dirfact) * gain; + } + } + else + { + for(i = 0;i < Hrtf->irSize;i++) + { + coeffs[i][0] = 0.0f; + coeffs[i][1] = 0.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 data set. 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(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep) +{ + ALuint evidx[2], lidx[4], ridx[4]; + ALfloat mu[3], blend[4]; + ALfloat left, right; + ALfloat step; + ALuint i; + + /* Claculate elevation indices and interpolation factor. */ + CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]); + + for(i = 0;i < 2;i++) + { + ALuint azcount = Hrtf->azCount[evidx[i]]; + ALuint evoffset = Hrtf->evOffset[evidx[i]]; + ALuint azidx[2]; + + /* Calculate azimuth indices and interpolation factor for this elevation. */ + CalcAzIndices(azcount, azimuth, azidx, &mu[i]); + + /* Calculate a set of linear HRIR indices for left and right channels. */ + lidx[i*2 + 0] = evoffset + azidx[0]; + lidx[i*2 + 1] = evoffset + azidx[1]; + ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount); + ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount); + } + + // Calculate the stepping parameters. + delta = maxf(floorf(delta*(Hrtf->sampleRate*0.015f) + 0.5f), 1.0f); + step = 1.0f / delta; + + /* Calculate 4 blending weights for 2D bilinear interpolation. */ + blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]); + blend[1] = ( mu[0]) * (1.0f-mu[2]); + blend[2] = (1.0f-mu[1]) * ( mu[2]); + blend[3] = ( mu[1]) * ( mu[2]); + + /* Calculate the HRIR delays using linear interpolation. Then calculate + * the delay stepping values using the target and previous running + * delays. + */ + left = (ALfloat)(delays[0] - (delayStep[0] * counter)); + right = (ALfloat)(delays[1] - (delayStep[1] * counter)); + + delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] + + Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) * + dirfact + 0.5f) << HRTFDELAY_BITS; + delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] + + Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) * + dirfact + 0.5f) << HRTFDELAY_BITS; + + delayStep[0] = fastf2i(step * (delays[0] - left)); + delayStep[1] = fastf2i(step * (delays[1] - right)); + + /* Calculate the sample offsets for the HRIR indices. */ + lidx[0] *= Hrtf->irSize; + lidx[1] *= Hrtf->irSize; + lidx[2] *= Hrtf->irSize; + lidx[3] *= Hrtf->irSize; + ridx[0] *= Hrtf->irSize; + ridx[1] *= Hrtf->irSize; + ridx[2] *= Hrtf->irSize; + ridx[3] *= Hrtf->irSize; + + /* 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) + { + ALfloat c; + + gain *= 1.0f/32767.0f; + + i = 0; + left = coeffs[i][0] - (coeffStep[i][0] * counter); + right = coeffs[i][1] - (coeffStep[i][1] * counter); + + c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] + + Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]); + coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain; + c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] + + Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]); + coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain; + + coeffStep[i][0] = step * (coeffs[i][0] - left); + coeffStep[i][1] = step * (coeffs[i][1] - right); + + for(i = 1;i < Hrtf->irSize;i++) + { + left = coeffs[i][0] - (coeffStep[i][0] * counter); + right = coeffs[i][1] - (coeffStep[i][1] * counter); + + c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] + + Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]); + coeffs[i][0] = lerp(0.0f, c, dirfact) * gain; + c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] + + Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]); + coeffs[i][1] = lerp(0.0f, c, dirfact) * gain; + + coeffStep[i][0] = step * (coeffs[i][0] - left); + coeffStep[i][1] = step * (coeffs[i][1] - right); + } + } + else + { + for(i = 0;i < Hrtf->irSize;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; + } + } + + /* 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 fastf2u(delta); +} + + +static struct Hrtf *LoadHrtf00(FILE *f, ALuint deviceRate) +{ + const ALubyte maxDelay = SRC_HISTORY_LENGTH-1; + struct Hrtf *Hrtf = NULL; + ALboolean failed = AL_FALSE; + ALuint rate = 0, irCount = 0; + ALushort irSize = 0; + ALubyte evCount = 0; + ALubyte *azCount = NULL; + ALushort *evOffset = NULL; + ALshort *coeffs = NULL; + ALubyte *delays = NULL; + ALuint i, j; + + rate = fgetc(f); + rate |= fgetc(f)<<8; + rate |= fgetc(f)<<16; + rate |= fgetc(f)<<24; + + irCount = fgetc(f); + irCount |= fgetc(f)<<8; + + irSize = fgetc(f); + irSize |= fgetc(f)<<8; + + evCount = fgetc(f); + + if(rate != deviceRate) + { + ERR("HRIR rate does not match device rate: rate=%d (%d)\n", + rate, deviceRate); + failed = AL_TRUE; + } + if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE)) + { + ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n", + irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE); + failed = AL_TRUE; + } + if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT) + { + ERR("Unsupported elevation count: evCount=%d (%d to %d)\n", + evCount, MIN_EV_COUNT, MAX_EV_COUNT); + failed = AL_TRUE; + } + + if(failed) + return NULL; + + azCount = malloc(sizeof(azCount[0])*evCount); + evOffset = malloc(sizeof(evOffset[0])*evCount); + if(azCount == NULL || evOffset == NULL) + { + ERR("Out of memory.\n"); + failed = AL_TRUE; + } + + if(!failed) + { + evOffset[0] = fgetc(f); + evOffset[0] |= fgetc(f)<<8; + for(i = 1;i < evCount;i++) + { + evOffset[i] = fgetc(f); + evOffset[i] |= fgetc(f)<<8; + if(evOffset[i] <= evOffset[i-1]) + { + ERR("Invalid evOffset: evOffset[%d]=%d (last=%d)\n", + i, evOffset[i], evOffset[i-1]); + failed = AL_TRUE; + } + + azCount[i-1] = evOffset[i] - evOffset[i-1]; + if(azCount[i-1] < MIN_AZ_COUNT || azCount[i-1] > MAX_AZ_COUNT) + { + ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n", + i-1, azCount[i-1], MIN_AZ_COUNT, MAX_AZ_COUNT); + failed = AL_TRUE; + } + } + if(irCount <= evOffset[i-1]) + { + ERR("Invalid evOffset: evOffset[%d]=%d (irCount=%d)\n", + i-1, evOffset[i-1], irCount); + failed = AL_TRUE; + } + + azCount[i-1] = irCount - evOffset[i-1]; + if(azCount[i-1] < MIN_AZ_COUNT || azCount[i-1] > MAX_AZ_COUNT) + { + ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n", + i-1, azCount[i-1], MIN_AZ_COUNT, MAX_AZ_COUNT); + failed = AL_TRUE; + } + } + + if(!failed) + { + coeffs = malloc(sizeof(coeffs[0])*irSize*irCount); + delays = malloc(sizeof(delays[0])*irCount); + if(coeffs == NULL || delays == NULL) + { + ERR("Out of memory.\n"); + failed = AL_TRUE; + } + } + + if(!failed) + { + for(i = 0;i < irCount*irSize;i+=irSize) + { + for(j = 0;j < irSize;j++) + { + ALshort coeff; + coeff = fgetc(f); + coeff |= fgetc(f)<<8; + coeffs[i+j] = coeff; + } + } + for(i = 0;i < irCount;i++) + { + delays[i] = fgetc(f); + if(delays[i] > maxDelay) + { + ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay); + failed = AL_TRUE; + } + } + + if(feof(f)) + { + ERR("Premature end of data\n"); + failed = AL_TRUE; + } + } + + if(!failed) + { + Hrtf = malloc(sizeof(struct Hrtf)); + if(Hrtf == NULL) + { + ERR("Out of memory.\n"); + failed = AL_TRUE; + } + } + + if(!failed) + { + Hrtf->sampleRate = rate; + Hrtf->irSize = irSize; + Hrtf->evCount = evCount; + Hrtf->azCount = azCount; + Hrtf->evOffset = evOffset; + Hrtf->coeffs = coeffs; + Hrtf->delays = delays; + Hrtf->next = NULL; + return Hrtf; + } + + free(azCount); + free(evOffset); + free(coeffs); + free(delays); + return NULL; +} + + +static struct Hrtf *LoadHrtf01(FILE *f, ALuint deviceRate) +{ + const ALubyte maxDelay = SRC_HISTORY_LENGTH-1; + struct Hrtf *Hrtf = NULL; + ALboolean failed = AL_FALSE; + ALuint rate = 0, irCount = 0; + ALubyte irSize = 0, evCount = 0; + ALubyte *azCount = NULL; + ALushort *evOffset = NULL; + ALshort *coeffs = NULL; + ALubyte *delays = NULL; + ALuint i, j; + + rate = fgetc(f); + rate |= fgetc(f)<<8; + rate |= fgetc(f)<<16; + rate |= fgetc(f)<<24; + + irSize = fgetc(f); + + evCount = fgetc(f); + + if(rate != deviceRate) + { + ERR("HRIR rate does not match device rate: rate=%d (%d)\n", + rate, deviceRate); + failed = AL_TRUE; + } + if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE)) + { + ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n", + irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE); + failed = AL_TRUE; + } + if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT) + { + ERR("Unsupported elevation count: evCount=%d (%d to %d)\n", + evCount, MIN_EV_COUNT, MAX_EV_COUNT); + failed = AL_TRUE; + } + + if(failed) + return NULL; + + azCount = malloc(sizeof(azCount[0])*evCount); + evOffset = malloc(sizeof(evOffset[0])*evCount); + if(azCount == NULL || evOffset == NULL) + { + ERR("Out of memory.\n"); + failed = AL_TRUE; + } + + if(!failed) + { + for(i = 0;i < evCount;i++) + { + azCount[i] = fgetc(f); + if(azCount[i] < MIN_AZ_COUNT || azCount[i] > MAX_AZ_COUNT) + { + ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n", + i, azCount[i], MIN_AZ_COUNT, MAX_AZ_COUNT); + failed = AL_TRUE; + } + } + } + + if(!failed) + { + evOffset[0] = 0; + irCount = azCount[0]; + for(i = 1;i < evCount;i++) + { + evOffset[i] = evOffset[i-1] + azCount[i-1]; + irCount += azCount[i]; + } + + coeffs = malloc(sizeof(coeffs[0])*irSize*irCount); + delays = malloc(sizeof(delays[0])*irCount); + if(coeffs == NULL || delays == NULL) + { + ERR("Out of memory.\n"); + failed = AL_TRUE; + } + } + + if(!failed) + { + for(i = 0;i < irCount*irSize;i+=irSize) + { + for(j = 0;j < irSize;j++) + { + ALshort coeff; + coeff = fgetc(f); + coeff |= fgetc(f)<<8; + coeffs[i+j] = coeff; + } + } + for(i = 0;i < irCount;i++) + { + delays[i] = fgetc(f); + if(delays[i] > maxDelay) + { + ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay); + failed = AL_TRUE; + } + } + + if(feof(f)) + { + ERR("Premature end of data\n"); + failed = AL_TRUE; + } + } + + if(!failed) + { + Hrtf = malloc(sizeof(struct Hrtf)); + if(Hrtf == NULL) + { + ERR("Out of memory.\n"); + failed = AL_TRUE; + } + } + + if(!failed) + { + Hrtf->sampleRate = rate; + Hrtf->irSize = irSize; + Hrtf->evCount = evCount; + Hrtf->azCount = azCount; + Hrtf->evOffset = evOffset; + Hrtf->coeffs = coeffs; + Hrtf->delays = delays; + Hrtf->next = NULL; + return Hrtf; + } + + free(azCount); + free(evOffset); + free(coeffs); + free(delays); + return NULL; +} + + +static struct Hrtf *LoadHrtf(ALuint deviceRate) +{ + const char *fnamelist = "default-%r.mhr"; + + ConfigValueStr(NULL, "hrtf_tables", &fnamelist); + while(*fnamelist != '\0') + { + struct Hrtf *Hrtf = NULL; + char fname[PATH_MAX]; + const char *next; + ALchar magic[8]; + ALuint i; + FILE *f; + + i = 0; + while(isspace(*fnamelist) || *fnamelist == ',') + fnamelist++; + next = fnamelist; + while(*(fnamelist=next) != '\0' && *fnamelist != ',') + { + next = strpbrk(fnamelist, "%,"); + while(fnamelist != next && *fnamelist && i < sizeof(fname)) + fname[i++] = *(fnamelist++); + + if(!next || *next == ',') + break; + + /* *next == '%' */ + next++; + if(*next == 'r') + { + int wrote = snprintf(&fname[i], sizeof(fname)-i, "%u", deviceRate); + i += minu(wrote, sizeof(fname)-i); + next++; + } + else if(*next == '%') + { + if(i < sizeof(fname)) + fname[i++] = '%'; + next++; + } + else + ERR("Invalid marker '%%%c'\n", *next); + } + i = minu(i, sizeof(fname)-1); + fname[i] = '\0'; + while(i > 0 && isspace(fname[i-1])) + i--; + fname[i] = '\0'; + + if(fname[0] == '\0') + continue; + + TRACE("Loading %s...\n", fname); + f = OpenDataFile(fname, "openal/hrtf"); + if(f == NULL) + { + ERR("Could not open %s\n", fname); + continue; + } + + if(fread(magic, 1, sizeof(magic), f) != sizeof(magic)) + ERR("Failed to read header from %s\n", fname); + else + { + if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0) + { + TRACE("Detected data set format v0\n"); + Hrtf = LoadHrtf00(f, deviceRate); + } + else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0) + { + TRACE("Detected data set format v1\n"); + Hrtf = LoadHrtf01(f, deviceRate); + } + else + ERR("Invalid header in %s: \"%.8s\"\n", fname, magic); + } + + fclose(f); + f = NULL; + + if(Hrtf) + { + Hrtf->next = LoadedHrtfs; + LoadedHrtfs = Hrtf; + TRACE("Loaded HRTF support for format: %s %uhz\n", + DevFmtChannelsString(DevFmtStereo), Hrtf->sampleRate); + return Hrtf; + } + + ERR("Failed to load %s\n", fname); + } + + return NULL; +} + +const struct Hrtf *GetHrtf(enum DevFmtChannels chans, ALCuint srate) +{ + if(chans == DevFmtStereo) + { + struct Hrtf *Hrtf = LoadedHrtfs; + while(Hrtf != NULL) + { + if(srate == Hrtf->sampleRate) + return Hrtf; + Hrtf = Hrtf->next; + } + + Hrtf = LoadHrtf(srate); + if(Hrtf != NULL) + return Hrtf; + } + ERR("Incompatible format: %s %uhz\n", DevFmtChannelsString(chans), srate); + return NULL; +} + +ALCboolean FindHrtfFormat(enum DevFmtChannels *chans, ALCuint *srate) +{ + const struct Hrtf *hrtf = LoadedHrtfs; + while(hrtf != NULL) + { + if(*srate == hrtf->sampleRate) + break; + hrtf = hrtf->next; + } + + if(hrtf == NULL) + { + hrtf = LoadHrtf(*srate); + if(hrtf == NULL) return ALC_FALSE; + } + + *chans = DevFmtStereo; + *srate = hrtf->sampleRate; + return ALC_TRUE; +} + +void FreeHrtfs(void) +{ + struct Hrtf *Hrtf = NULL; + + while((Hrtf=LoadedHrtfs) != NULL) + { + LoadedHrtfs = Hrtf->next; + free((void*)Hrtf->azCount); + free((void*)Hrtf->evOffset); + free((void*)Hrtf->coeffs); + free((void*)Hrtf->delays); + free(Hrtf); + } +} + +ALuint GetHrtfIrSize (const struct Hrtf *Hrtf) +{ + return Hrtf->irSize; +} diff --git a/libs/openal-soft/Alc/hrtf.h b/libs/openal-soft/Alc/hrtf.h new file mode 100644 index 00000000..938bf552 --- /dev/null +++ b/libs/openal-soft/Alc/hrtf.h @@ -0,0 +1,28 @@ +#ifndef ALC_HRTF_H +#define ALC_HRTF_H + +#include "AL/al.h" +#include "AL/alc.h" + +enum DevFmtChannels; + +struct Hrtf; + +#define HRIR_BITS (7) +#define HRIR_LENGTH (1< +#include +#include + +#include "midi/base.h" + +#include "alMidi.h" +#include "alMain.h" +#include "alError.h" +#include "alThunk.h" +#include "evtqueue.h" +#include "rwlock.h" +#include "alu.h" + + +extern inline ALboolean IsValidCtrlInput(int cc); + +extern inline size_t Reader_read(Reader *self, void *buf, size_t len); + + +/* MIDI events */ +#define SYSEX_EVENT (0xF0) + + +void InitEvtQueue(EvtQueue *queue) +{ + queue->events = NULL; + queue->maxsize = 0; + queue->size = 0; + queue->pos = 0; +} + +void ResetEvtQueue(EvtQueue *queue) +{ + ALsizei i; + for(i = 0;i < queue->size;i++) + { + if(queue->events[i].event == SYSEX_EVENT) + { + free(queue->events[i].param.sysex.data); + queue->events[i].param.sysex.data = NULL; + } + } + + free(queue->events); + queue->events = NULL; + queue->maxsize = 0; + queue->size = 0; + queue->pos = 0; +} + +ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt) +{ + ALsizei pos; + + if(queue->maxsize == queue->size) + { + if(queue->pos > 0) + { + /* Queue has some stale entries, remove them to make space for more + * events. */ + for(pos = 0;pos < queue->pos;pos++) + { + if(queue->events[pos].event == SYSEX_EVENT) + { + free(queue->events[pos].param.sysex.data); + queue->events[pos].param.sysex.data = NULL; + } + } + memmove(&queue->events[0], &queue->events[queue->pos], + (queue->size-queue->pos)*sizeof(queue->events[0])); + queue->size -= queue->pos; + queue->pos = 0; + } + else + { + /* Queue is full, double the allocated space. */ + void *temp = NULL; + ALsizei newsize; + + newsize = (queue->maxsize ? (queue->maxsize<<1) : 16); + if(newsize > queue->maxsize) + temp = realloc(queue->events, newsize * sizeof(queue->events[0])); + if(!temp) + return AL_OUT_OF_MEMORY; + + queue->events = temp; + queue->maxsize = newsize; + } + } + + pos = queue->pos; + if(queue->size > 0) + { + ALsizei high = queue->size - 1; + while(pos < high) + { + ALsizei mid = pos + (high-pos)/2; + if(queue->events[mid].time < evt->time) + pos = mid + 1; + else + high = mid; + } + while(pos < queue->size && queue->events[pos].time <= evt->time) + pos++; + + if(pos < queue->size) + memmove(&queue->events[pos+1], &queue->events[pos], + (queue->size-pos)*sizeof(queue->events[0])); + } + + queue->events[pos] = *evt; + queue->size++; + + return AL_NO_ERROR; +} + + +void MidiSynth_Construct(MidiSynth *self, ALCdevice *device) +{ + InitEvtQueue(&self->EventQueue); + + RWLockInit(&self->Lock); + + self->Soundfonts = NULL; + self->NumSoundfonts = 0; + + self->Gain = 1.0f; + self->State = AL_INITIAL; + + self->ClockBase = 0; + self->SamplesDone = 0; + self->SampleRate = device->Frequency; +} + +void MidiSynth_Destruct(MidiSynth *self) +{ + ALsizei i; + + for(i = 0;i < self->NumSoundfonts;i++) + DecrementRef(&self->Soundfonts[i]->ref); + free(self->Soundfonts); + self->Soundfonts = NULL; + self->NumSoundfonts = 0; + + ResetEvtQueue(&self->EventQueue); +} + + +ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids) +{ + ALCdevice *device = context->Device; + ALsoundfont **sfonts; + ALsizei i; + + if(self->State != AL_INITIAL && self->State != AL_STOPPED) + return AL_INVALID_OPERATION; + + sfonts = calloc(1, count * sizeof(sfonts[0])); + if(!sfonts) return AL_OUT_OF_MEMORY; + + for(i = 0;i < count;i++) + { + if(ids[i] == 0) + sfonts[i] = ALsoundfont_getDefSoundfont(context); + else if(!(sfonts[i]=LookupSfont(device, ids[i]))) + { + free(sfonts); + return AL_INVALID_VALUE; + } + } + + for(i = 0;i < count;i++) + IncrementRef(&sfonts[i]->ref); + sfonts = ExchangePtr((XchgPtr*)&self->Soundfonts, sfonts); + count = ExchangeInt(&self->NumSoundfonts, count); + + for(i = 0;i < count;i++) + DecrementRef(&sfonts[i]->ref); + free(sfonts); + + return AL_NO_ERROR; +} + +extern inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain); +extern inline ALfloat MidiSynth_getGain(const MidiSynth *self); +extern inline void MidiSynth_setState(MidiSynth *self, ALenum state); +extern inline ALenum MidiSynth_getState(const MidiSynth *self); + +void MidiSynth_stop(MidiSynth *self) +{ + ResetEvtQueue(&self->EventQueue); + + self->ClockBase = 0; + self->SamplesDone = 0; +} + +extern inline void MidiSynth_reset(MidiSynth *self); +extern inline ALuint64 MidiSynth_getTime(const MidiSynth *self); +extern inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self); + +void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate) +{ + if(self->SampleRate != srate) + { + self->ClockBase += self->SamplesDone * MIDI_CLOCK_RES / self->SampleRate; + self->SamplesDone = 0; + self->SampleRate = srate; + } +} + +extern inline void MidiSynth_update(MidiSynth *self, ALCdevice *device); + +ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2) +{ + MidiEvent entry; + entry.time = time; + entry.event = event; + entry.param.val[0] = param1; + entry.param.val[1] = param2; + return InsertEvtQueue(&self->EventQueue, &entry); +} + +ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size) +{ + MidiEvent entry; + ALenum err; + + entry.time = time; + entry.event = SYSEX_EVENT; + entry.param.sysex.size = size; + entry.param.sysex.data = malloc(size); + if(!entry.param.sysex.data) + return AL_OUT_OF_MEMORY; + memcpy(entry.param.sysex.data, data, size); + + err = InsertEvtQueue(&self->EventQueue, &entry); + if(err != AL_NO_ERROR) + free(entry.param.sysex.data); + return err; +} diff --git a/libs/openal-soft/Alc/midi/base.h b/libs/openal-soft/Alc/midi/base.h new file mode 100644 index 00000000..157f2399 --- /dev/null +++ b/libs/openal-soft/Alc/midi/base.h @@ -0,0 +1,133 @@ +#ifndef AL_MIDI_BASE_H +#define AL_MIDI_BASE_H + +#include "alMain.h" +#include "atomic.h" +#include "evtqueue.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ALsoundfont; + +typedef size_t (*ReaderCb)(void *ptr, size_t size, void *stream); +typedef struct Reader { + ReaderCb cb; + void *ptr; + int error; +} Reader; +inline size_t Reader_read(Reader *self, void *buf, size_t len) +{ + size_t got = (!self->error) ? self->cb(buf, len, self->ptr) : 0; + if(got < len) self->error = 1; + return got; +} +#define READERR(x_) ((x_)->error) + +ALboolean loadSf2(Reader *stream, struct ALsoundfont *sfont, ALCcontext *context); + + +#define MIDI_CLOCK_RES U64(1000000000) + + +struct MidiSynthVtable; + +typedef struct MidiSynth { + EvtQueue EventQueue; + + ALuint64 ClockBase; + ALuint SamplesDone; + ALuint SampleRate; + + /* NOTE: This rwlock is for the state and soundfont. The EventQueue and + * related must instead use the device lock as they're used in the mixer + * thread. + */ + RWLock Lock; + + struct ALsoundfont **Soundfonts; + ALsizei NumSoundfonts; + + volatile ALfloat Gain; + volatile ALenum State; + + const struct MidiSynthVtable *vtbl; +} MidiSynth; + +void MidiSynth_Construct(MidiSynth *self, ALCdevice *device); +void MidiSynth_Destruct(MidiSynth *self); +ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids); +inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain) { self->Gain = gain; } +inline ALfloat MidiSynth_getGain(const MidiSynth *self) { return self->Gain; } +inline void MidiSynth_setState(MidiSynth *self, ALenum state) { ExchangeInt(&self->State, state); } +inline ALenum MidiSynth_getState(const MidiSynth *self) { return self->State; } +void MidiSynth_stop(MidiSynth *self); +inline void MidiSynth_reset(MidiSynth *self) { MidiSynth_stop(self); } +inline ALuint64 MidiSynth_getTime(const MidiSynth *self) +{ return self->ClockBase + (self->SamplesDone*MIDI_CLOCK_RES/self->SampleRate); } +inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self) +{ + if(self->EventQueue.pos == self->EventQueue.size) + return UINT64_MAX; + return self->EventQueue.events[self->EventQueue.pos].time; +} +void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate); +inline void MidiSynth_update(MidiSynth *self, ALCdevice *device) +{ MidiSynth_setSampleRate(self, device->Frequency); } +ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2); +ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size); + + +struct MidiSynthVtable { + void (*const Destruct)(MidiSynth *self); + + ALenum (*const selectSoundfonts)(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids); + + void (*const setGain)(MidiSynth *self, ALfloat gain); + + void (*const stop)(MidiSynth *self); + void (*const reset)(MidiSynth *self); + + void (*const update)(MidiSynth *self, ALCdevice *device); + void (*const process)(MidiSynth *self, ALuint samples, ALfloat (*restrict DryBuffer)[BUFFERSIZE]); + + void (*const Delete)(void *ptr); +}; + +#define DEFINE_MIDISYNTH_VTABLE(T) \ +DECLARE_THUNK(T, MidiSynth, void, Destruct) \ +DECLARE_THUNK3(T, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) \ +DECLARE_THUNK1(T, MidiSynth, void, setGain, ALfloat) \ +DECLARE_THUNK(T, MidiSynth, void, stop) \ +DECLARE_THUNK(T, MidiSynth, void, reset) \ +DECLARE_THUNK1(T, MidiSynth, void, update, ALCdevice*) \ +DECLARE_THUNK2(T, MidiSynth, void, process, ALuint, ALfloatBUFFERSIZE*restrict) \ +static void T##_MidiSynth_Delete(void *ptr) \ +{ T##_Delete(STATIC_UPCAST(T, MidiSynth, (MidiSynth*)ptr)); } \ + \ +static const struct MidiSynthVtable T##_MidiSynth_vtable = { \ + T##_MidiSynth_Destruct, \ + \ + T##_MidiSynth_selectSoundfonts, \ + T##_MidiSynth_setGain, \ + T##_MidiSynth_stop, \ + T##_MidiSynth_reset, \ + T##_MidiSynth_update, \ + T##_MidiSynth_process, \ + \ + T##_MidiSynth_Delete, \ +} + + +MidiSynth *SSynth_create(ALCdevice *device); +MidiSynth *FSynth_create(ALCdevice *device); +MidiSynth *DSynth_create(ALCdevice *device); + +MidiSynth *SynthCreate(ALCdevice *device); + +#ifdef __cplusplus +} +#endif + +#endif /* AL_MIDI_BASE_H */ diff --git a/libs/openal-soft/Alc/midi/dummy.c b/libs/openal-soft/Alc/midi/dummy.c new file mode 100644 index 00000000..d50b8fef --- /dev/null +++ b/libs/openal-soft/Alc/midi/dummy.c @@ -0,0 +1,76 @@ + +#include "config.h" + +#include +#include +#include +#include + +#include "alMain.h" +#include "alError.h" +#include "evtqueue.h" +#include "rwlock.h" +#include "alu.h" + +#include "midi/base.h" + +typedef struct DSynth { + DERIVE_FROM_TYPE(MidiSynth); +} DSynth; + +static void DSynth_Construct(DSynth *self, ALCdevice *device); +static DECLARE_FORWARD(DSynth, MidiSynth, void, Destruct) +static DECLARE_FORWARD3(DSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) +static DECLARE_FORWARD1(DSynth, MidiSynth, void, setGain, ALfloat) +static DECLARE_FORWARD(DSynth, MidiSynth, void, stop) +static DECLARE_FORWARD(DSynth, MidiSynth, void, reset) +static DECLARE_FORWARD1(DSynth, MidiSynth, void, update, ALCdevice*) +static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]); +DECLARE_DEFAULT_ALLOCATORS(DSynth) +DEFINE_MIDISYNTH_VTABLE(DSynth); + + +static void DSynth_Construct(DSynth *self, ALCdevice *device) +{ + MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device); + SET_VTABLE2(DSynth, MidiSynth, self); +} + + +static void DSynth_processQueue(DSynth *self, ALuint64 time) +{ + EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue; + + while(queue->pos < queue->size && queue->events[queue->pos].time <= time) + queue->pos++; +} + +static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloatBUFFERSIZE*restrict UNUSED(DryBuffer)) +{ + MidiSynth *synth = STATIC_CAST(MidiSynth, self); + ALuint64 curtime; + + if(synth->State != AL_PLAYING) + return; + + synth->SamplesDone += SamplesToDo; + synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES; + synth->SamplesDone %= synth->SampleRate; + + curtime = MidiSynth_getTime(synth); + DSynth_processQueue(self, maxi64(curtime-1, 0)); +} + + +MidiSynth *DSynth_create(ALCdevice *device) +{ + DSynth *synth = DSynth_New(sizeof(*synth)); + if(!synth) + { + ERR("Failed to allocate DSynth\n"); + return NULL; + } + memset(synth, 0, sizeof(*synth)); + DSynth_Construct(synth, device); + return STATIC_CAST(MidiSynth, synth); +} diff --git a/libs/openal-soft/Alc/midi/fluidsynth.c b/libs/openal-soft/Alc/midi/fluidsynth.c new file mode 100644 index 00000000..82159127 --- /dev/null +++ b/libs/openal-soft/Alc/midi/fluidsynth.c @@ -0,0 +1,930 @@ + +#include "config.h" + +#include +#include +#include + +#include "midi/base.h" + +#include "alMain.h" +#include "alError.h" +#include "alMidi.h" +#include "alu.h" +#include "compat.h" +#include "evtqueue.h" +#include "rwlock.h" + +#ifdef HAVE_FLUIDSYNTH + +#include + + +#ifdef HAVE_DYNLOAD +#define FLUID_FUNCS(MAGIC) \ + MAGIC(new_fluid_synth); \ + MAGIC(delete_fluid_synth); \ + MAGIC(new_fluid_settings); \ + MAGIC(delete_fluid_settings); \ + MAGIC(fluid_settings_setint); \ + MAGIC(fluid_settings_setnum); \ + MAGIC(fluid_synth_noteon); \ + MAGIC(fluid_synth_noteoff); \ + MAGIC(fluid_synth_program_change); \ + MAGIC(fluid_synth_pitch_bend); \ + MAGIC(fluid_synth_channel_pressure); \ + MAGIC(fluid_synth_cc); \ + MAGIC(fluid_synth_sysex); \ + MAGIC(fluid_synth_bank_select); \ + MAGIC(fluid_synth_set_channel_type); \ + MAGIC(fluid_synth_all_sounds_off); \ + MAGIC(fluid_synth_system_reset); \ + MAGIC(fluid_synth_set_gain); \ + MAGIC(fluid_synth_set_sample_rate); \ + MAGIC(fluid_synth_write_float); \ + MAGIC(fluid_synth_add_sfloader); \ + MAGIC(fluid_synth_sfload); \ + MAGIC(fluid_synth_sfunload); \ + MAGIC(fluid_synth_alloc_voice); \ + MAGIC(fluid_synth_start_voice); \ + MAGIC(fluid_voice_gen_set); \ + MAGIC(fluid_voice_add_mod); \ + MAGIC(fluid_mod_set_source1); \ + MAGIC(fluid_mod_set_source2); \ + MAGIC(fluid_mod_set_amount); \ + MAGIC(fluid_mod_set_dest); + +void *fsynth_handle = NULL; +#define DECL_FUNC(x) __typeof(x) *p##x +FLUID_FUNCS(DECL_FUNC) +#undef DECL_FUNC + +#define new_fluid_synth pnew_fluid_synth +#define delete_fluid_synth pdelete_fluid_synth +#define new_fluid_settings pnew_fluid_settings +#define delete_fluid_settings pdelete_fluid_settings +#define fluid_settings_setint pfluid_settings_setint +#define fluid_settings_setnum pfluid_settings_setnum +#define fluid_synth_noteon pfluid_synth_noteon +#define fluid_synth_noteoff pfluid_synth_noteoff +#define fluid_synth_program_change pfluid_synth_program_change +#define fluid_synth_pitch_bend pfluid_synth_pitch_bend +#define fluid_synth_channel_pressure pfluid_synth_channel_pressure +#define fluid_synth_cc pfluid_synth_cc +#define fluid_synth_sysex pfluid_synth_sysex +#define fluid_synth_bank_select pfluid_synth_bank_select +#define fluid_synth_set_channel_type pfluid_synth_set_channel_type +#define fluid_synth_all_sounds_off pfluid_synth_all_sounds_off +#define fluid_synth_system_reset pfluid_synth_system_reset +#define fluid_synth_set_gain pfluid_synth_set_gain +#define fluid_synth_set_sample_rate pfluid_synth_set_sample_rate +#define fluid_synth_write_float pfluid_synth_write_float +#define fluid_synth_add_sfloader pfluid_synth_add_sfloader +#define fluid_synth_sfload pfluid_synth_sfload +#define fluid_synth_sfunload pfluid_synth_sfunload +#define fluid_synth_alloc_voice pfluid_synth_alloc_voice +#define fluid_synth_start_voice pfluid_synth_start_voice +#define fluid_voice_gen_set pfluid_voice_gen_set +#define fluid_voice_add_mod pfluid_voice_add_mod +#define fluid_mod_set_source1 pfluid_mod_set_source1 +#define fluid_mod_set_source2 pfluid_mod_set_source2 +#define fluid_mod_set_amount pfluid_mod_set_amount +#define fluid_mod_set_dest pfluid_mod_set_dest + +static ALboolean LoadFSynth(void) +{ + ALboolean ret = AL_TRUE; + if(!fsynth_handle) + { + fsynth_handle = LoadLib("libfluidsynth.so.1"); + if(!fsynth_handle) return AL_FALSE; + +#define LOAD_FUNC(x) do { \ + p##x = GetSymbol(fsynth_handle, #x); \ + if(!p##x) ret = AL_FALSE; \ +} while(0) + FLUID_FUNCS(LOAD_FUNC) +#undef LOAD_FUNC + + if(ret == AL_FALSE) + { + CloseLib(fsynth_handle); + fsynth_handle = NULL; + } + } + return ret; +} +#else +static inline ALboolean LoadFSynth(void) { return AL_TRUE; } +#endif + + +/* MIDI events */ +#define SYSEX_EVENT (0xF0) + +/* MIDI controllers */ +#define CTRL_BANKSELECT_MSB (0) +#define CTRL_BANKSELECT_LSB (32) +#define CTRL_ALLNOTESOFF (123) + + +static int getModInput(ALenum input) +{ + switch(input) + { + case AL_ONE_SOFT: return FLUID_MOD_NONE; + case AL_NOTEON_VELOCITY_SOFT: return FLUID_MOD_VELOCITY; + case AL_NOTEON_KEY_SOFT: return FLUID_MOD_KEY; + case AL_KEYPRESSURE_SOFT: return FLUID_MOD_KEYPRESSURE; + case AL_CHANNELPRESSURE_SOFT: return FLUID_MOD_CHANNELPRESSURE; + case AL_PITCHBEND_SOFT: return FLUID_MOD_PITCHWHEEL; + case AL_PITCHBEND_SENSITIVITY_SOFT: return FLUID_MOD_PITCHWHEELSENS; + } + return input&0x7F; +} + +static int getModFlags(ALenum input, ALenum type, ALenum form) +{ + int ret = 0; + + switch(type) + { + case AL_UNORM_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE; break; + case AL_UNORM_REV_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE; break; + case AL_SNORM_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE; break; + case AL_SNORM_REV_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE; break; + } + switch(form) + { + case AL_LINEAR_SOFT: ret |= FLUID_MOD_LINEAR; break; + case AL_CONCAVE_SOFT: ret |= FLUID_MOD_CONCAVE; break; + case AL_CONVEX_SOFT: ret |= FLUID_MOD_CONVEX; break; + case AL_SWITCH_SOFT: ret |= FLUID_MOD_SWITCH; break; + } + /* Source input values less than 128 correspond to a MIDI continuous + * controller. Otherwise, it's a general controller. */ + if(input < 128) ret |= FLUID_MOD_CC; + else ret |= FLUID_MOD_GC; + + return ret; +} + +static enum fluid_gen_type getModDest(ALenum gen) +{ + switch(gen) + { + case AL_MOD_LFO_TO_PITCH_SOFT: return GEN_MODLFOTOPITCH; + case AL_VIBRATO_LFO_TO_PITCH_SOFT: return GEN_VIBLFOTOPITCH; + case AL_MOD_ENV_TO_PITCH_SOFT: return GEN_MODENVTOPITCH; + case AL_FILTER_CUTOFF_SOFT: return GEN_FILTERFC; + case AL_FILTER_RESONANCE_SOFT: return GEN_FILTERQ; + case AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT: return GEN_MODLFOTOFILTERFC; + case AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT: return GEN_MODENVTOFILTERFC; + case AL_MOD_LFO_TO_VOLUME_SOFT: return GEN_MODLFOTOVOL; + case AL_CHORUS_SEND_SOFT: return GEN_CHORUSSEND; + case AL_REVERB_SEND_SOFT: return GEN_REVERBSEND; + case AL_PAN_SOFT: return GEN_PAN; + case AL_MOD_LFO_DELAY_SOFT: return GEN_MODLFODELAY; + case AL_MOD_LFO_FREQUENCY_SOFT: return GEN_MODLFOFREQ; + case AL_VIBRATO_LFO_DELAY_SOFT: return GEN_VIBLFODELAY; + case AL_VIBRATO_LFO_FREQUENCY_SOFT: return GEN_VIBLFOFREQ; + case AL_MOD_ENV_DELAYTIME_SOFT: return GEN_MODENVDELAY; + case AL_MOD_ENV_ATTACKTIME_SOFT: return GEN_MODENVATTACK; + case AL_MOD_ENV_HOLDTIME_SOFT: return GEN_MODENVHOLD; + case AL_MOD_ENV_DECAYTIME_SOFT: return GEN_MODENVDECAY; + case AL_MOD_ENV_SUSTAINVOLUME_SOFT: return GEN_MODENVSUSTAIN; + case AL_MOD_ENV_RELEASETIME_SOFT: return GEN_MODENVRELEASE; + case AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOMODENVHOLD; + case AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOMODENVDECAY; + case AL_VOLUME_ENV_DELAYTIME_SOFT: return GEN_VOLENVDELAY; + case AL_VOLUME_ENV_ATTACKTIME_SOFT: return GEN_VOLENVATTACK; + case AL_VOLUME_ENV_HOLDTIME_SOFT: return GEN_VOLENVHOLD; + case AL_VOLUME_ENV_DECAYTIME_SOFT: return GEN_VOLENVDECAY; + case AL_VOLUME_ENV_SUSTAINVOLUME_SOFT: return GEN_VOLENVSUSTAIN; + case AL_VOLUME_ENV_RELEASETIME_SOFT: return GEN_VOLENVRELEASE; + case AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOVOLENVHOLD; + case AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOVOLENVDECAY; + case AL_ATTENUATION_SOFT: return GEN_ATTENUATION; + case AL_TUNING_COARSE_SOFT: return GEN_COARSETUNE; + case AL_TUNING_FINE_SOFT: return GEN_FINETUNE; + case AL_TUNING_SCALE_SOFT: return GEN_SCALETUNE; + } + ERR("Unhandled generator: 0x%04x\n", gen); + return 0; +} + +static int getSf2LoopMode(ALenum mode) +{ + switch(mode) + { + case AL_NONE: return 0; + case AL_LOOP_CONTINUOUS_SOFT: return 1; + case AL_LOOP_UNTIL_RELEASE_SOFT: return 3; + } + return 0; +} + +static int getSampleType(ALenum type) +{ + switch(type) + { + case AL_MONO_SOFT: return FLUID_SAMPLETYPE_MONO; + case AL_RIGHT_SOFT: return FLUID_SAMPLETYPE_RIGHT; + case AL_LEFT_SOFT: return FLUID_SAMPLETYPE_LEFT; + } + return FLUID_SAMPLETYPE_MONO; +} + +typedef struct FSample { + DERIVE_FROM_TYPE(fluid_sample_t); + + ALfontsound *Sound; + + fluid_mod_t *Mods; + ALsizei NumMods; +} FSample; + +static void FSample_Construct(FSample *self, ALfontsound *sound) +{ + fluid_sample_t *sample = STATIC_CAST(fluid_sample_t, self); + memset(sample->name, 0, sizeof(sample->name)); + sample->start = sound->Start; + sample->end = sound->End; + sample->loopstart = sound->LoopStart; + sample->loopend = sound->LoopEnd; + sample->samplerate = sound->SampleRate; + sample->origpitch = sound->PitchKey; + sample->pitchadj = sound->PitchCorrection; + sample->sampletype = getSampleType(sound->SampleType); + sample->valid = !!sound->Buffer; + sample->data = sound->Buffer ? sound->Buffer->data : NULL; + + sample->amplitude_that_reaches_noise_floor_is_valid = 0; + sample->amplitude_that_reaches_noise_floor = 0.0; + + sample->refcount = 0; + + sample->notify = NULL; + + sample->userdata = self; + + self->Sound = sound; + + self->NumMods = 0; + self->Mods = calloc(sound->ModulatorMap.size*4, sizeof(fluid_mod_t[4])); + if(self->Mods) + { + ALsizei i, j, k; + + for(i = j = 0;i < sound->ModulatorMap.size;i++) + { + ALsfmodulator *mod = sound->ModulatorMap.array[i].value; + for(k = 0;k < 4;k++,mod++) + { + if(mod->Dest == AL_NONE) + continue; + fluid_mod_set_source1(&self->Mods[j], getModInput(mod->Source[0].Input), + getModFlags(mod->Source[0].Input, mod->Source[0].Type, + mod->Source[0].Form)); + fluid_mod_set_source2(&self->Mods[j], getModInput(mod->Source[1].Input), + getModFlags(mod->Source[1].Input, mod->Source[1].Type, + mod->Source[1].Form)); + fluid_mod_set_amount(&self->Mods[j], mod->Amount); + fluid_mod_set_dest(&self->Mods[j], getModDest(mod->Dest)); + self->Mods[j++].next = NULL; + } + } + self->NumMods = j; + } +} + +static void FSample_Destruct(FSample *self) +{ + free(self->Mods); + self->Mods = NULL; + self->NumMods = 0; +} + + +typedef struct FPreset { + DERIVE_FROM_TYPE(fluid_preset_t); + + char Name[16]; + + int Preset; + int Bank; + + FSample *Samples; + ALsizei NumSamples; +} FPreset; + +static char* FPreset_getName(fluid_preset_t *preset); +static int FPreset_getPreset(fluid_preset_t *preset); +static int FPreset_getBank(fluid_preset_t *preset); +static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int velocity); + +static void FPreset_Construct(FPreset *self, ALsfpreset *preset, fluid_sfont_t *parent) +{ + STATIC_CAST(fluid_preset_t, self)->data = self; + STATIC_CAST(fluid_preset_t, self)->sfont = parent; + STATIC_CAST(fluid_preset_t, self)->free = NULL; + STATIC_CAST(fluid_preset_t, self)->get_name = FPreset_getName; + STATIC_CAST(fluid_preset_t, self)->get_banknum = FPreset_getBank; + STATIC_CAST(fluid_preset_t, self)->get_num = FPreset_getPreset; + STATIC_CAST(fluid_preset_t, self)->noteon = FPreset_noteOn; + STATIC_CAST(fluid_preset_t, self)->notify = NULL; + + memset(self->Name, 0, sizeof(self->Name)); + self->Preset = preset->Preset; + self->Bank = preset->Bank; + + self->NumSamples = 0; + self->Samples = calloc(1, preset->NumSounds * sizeof(self->Samples[0])); + if(self->Samples) + { + ALsizei i; + self->NumSamples = preset->NumSounds; + for(i = 0;i < self->NumSamples;i++) + FSample_Construct(&self->Samples[i], preset->Sounds[i]); + } +} + +static void FPreset_Destruct(FPreset *self) +{ + ALsizei i; + + for(i = 0;i < self->NumSamples;i++) + FSample_Destruct(&self->Samples[i]); + free(self->Samples); + self->Samples = NULL; + self->NumSamples = 0; +} + +static ALboolean FPreset_canDelete(FPreset *self) +{ + ALsizei i; + for(i = 0;i < self->NumSamples;i++) + { + if(fluid_sample_refcount(STATIC_CAST(fluid_sample_t, &self->Samples[i])) != 0) + return AL_FALSE; + } + return AL_TRUE; +} + +static char* FPreset_getName(fluid_preset_t *preset) +{ + return ((FPreset*)preset->data)->Name; +} + +static int FPreset_getPreset(fluid_preset_t *preset) +{ + return ((FPreset*)preset->data)->Preset; +} + +static int FPreset_getBank(fluid_preset_t *preset) +{ + return ((FPreset*)preset->data)->Bank; +} + +static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int vel) +{ + FPreset *self = ((FPreset*)preset->data); + ALsizei i; + + for(i = 0;i < self->NumSamples;i++) + { + FSample *sample = &self->Samples[i]; + ALfontsound *sound = sample->Sound; + fluid_voice_t *voice; + ALsizei m; + + if(!(key >= sound->MinKey && key <= sound->MaxKey && vel >= sound->MinVelocity && vel <= sound->MaxVelocity)) + continue; + + voice = fluid_synth_alloc_voice(synth, STATIC_CAST(fluid_sample_t, sample), channel, key, vel); + if(voice == NULL) return FLUID_FAILED; + + fluid_voice_gen_set(voice, GEN_MODLFOTOPITCH, sound->ModLfoToPitch); + fluid_voice_gen_set(voice, GEN_VIBLFOTOPITCH, sound->VibratoLfoToPitch); + fluid_voice_gen_set(voice, GEN_MODENVTOPITCH, sound->ModEnvToPitch); + fluid_voice_gen_set(voice, GEN_FILTERFC, sound->FilterCutoff); + fluid_voice_gen_set(voice, GEN_FILTERQ, sound->FilterQ); + fluid_voice_gen_set(voice, GEN_MODLFOTOFILTERFC, sound->ModLfoToFilterCutoff); + fluid_voice_gen_set(voice, GEN_MODENVTOFILTERFC, sound->ModEnvToFilterCutoff); + fluid_voice_gen_set(voice, GEN_MODLFOTOVOL, sound->ModLfoToVolume); + fluid_voice_gen_set(voice, GEN_CHORUSSEND, sound->ChorusSend); + fluid_voice_gen_set(voice, GEN_REVERBSEND, sound->ReverbSend); + fluid_voice_gen_set(voice, GEN_PAN, sound->Pan); + fluid_voice_gen_set(voice, GEN_MODLFODELAY, sound->ModLfo.Delay); + fluid_voice_gen_set(voice, GEN_MODLFOFREQ, sound->ModLfo.Frequency); + fluid_voice_gen_set(voice, GEN_VIBLFODELAY, sound->VibratoLfo.Delay); + fluid_voice_gen_set(voice, GEN_VIBLFOFREQ, sound->VibratoLfo.Frequency); + fluid_voice_gen_set(voice, GEN_MODENVDELAY, sound->ModEnv.DelayTime); + fluid_voice_gen_set(voice, GEN_MODENVATTACK, sound->ModEnv.AttackTime); + fluid_voice_gen_set(voice, GEN_MODENVHOLD, sound->ModEnv.HoldTime); + fluid_voice_gen_set(voice, GEN_MODENVDECAY, sound->ModEnv.DecayTime); + fluid_voice_gen_set(voice, GEN_MODENVSUSTAIN, sound->ModEnv.SustainAttn); + fluid_voice_gen_set(voice, GEN_MODENVRELEASE, sound->ModEnv.ReleaseTime); + fluid_voice_gen_set(voice, GEN_KEYTOMODENVHOLD, sound->ModEnv.KeyToHoldTime); + fluid_voice_gen_set(voice, GEN_KEYTOMODENVDECAY, sound->ModEnv.KeyToDecayTime); + fluid_voice_gen_set(voice, GEN_VOLENVDELAY, sound->VolEnv.DelayTime); + fluid_voice_gen_set(voice, GEN_VOLENVATTACK, sound->VolEnv.AttackTime); + fluid_voice_gen_set(voice, GEN_VOLENVHOLD, sound->VolEnv.HoldTime); + fluid_voice_gen_set(voice, GEN_VOLENVDECAY, sound->VolEnv.DecayTime); + fluid_voice_gen_set(voice, GEN_VOLENVSUSTAIN, sound->VolEnv.SustainAttn); + fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, sound->VolEnv.ReleaseTime); + fluid_voice_gen_set(voice, GEN_KEYTOVOLENVHOLD, sound->VolEnv.KeyToHoldTime); + fluid_voice_gen_set(voice, GEN_KEYTOVOLENVDECAY, sound->VolEnv.KeyToDecayTime); + fluid_voice_gen_set(voice, GEN_ATTENUATION, sound->Attenuation); + fluid_voice_gen_set(voice, GEN_COARSETUNE, sound->CoarseTuning); + fluid_voice_gen_set(voice, GEN_FINETUNE, sound->FineTuning); + fluid_voice_gen_set(voice, GEN_SAMPLEMODE, getSf2LoopMode(sound->LoopMode)); + fluid_voice_gen_set(voice, GEN_SCALETUNE, sound->TuningScale); + fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, sound->ExclusiveClass); + for(m = 0;m < sample->NumMods;m++) + fluid_voice_add_mod(voice, &sample->Mods[m], FLUID_VOICE_OVERWRITE); + + fluid_synth_start_voice(synth, voice); + } + + return FLUID_OK; +} + + +typedef struct FSfont { + DERIVE_FROM_TYPE(fluid_sfont_t); + + char Name[16]; + + FPreset *Presets; + ALsizei NumPresets; + + ALsizei CurrentPos; +} FSfont; + +static int FSfont_free(fluid_sfont_t *sfont); +static char* FSfont_getName(fluid_sfont_t *sfont); +static fluid_preset_t* FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum); +static void FSfont_iterStart(fluid_sfont_t *sfont); +static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset); + +static void FSfont_Construct(FSfont *self, ALsoundfont *sfont) +{ + STATIC_CAST(fluid_sfont_t, self)->data = self; + STATIC_CAST(fluid_sfont_t, self)->id = FLUID_FAILED; + STATIC_CAST(fluid_sfont_t, self)->free = FSfont_free; + STATIC_CAST(fluid_sfont_t, self)->get_name = FSfont_getName; + STATIC_CAST(fluid_sfont_t, self)->get_preset = FSfont_getPreset; + STATIC_CAST(fluid_sfont_t, self)->iteration_start = FSfont_iterStart; + STATIC_CAST(fluid_sfont_t, self)->iteration_next = FSfont_iterNext; + + memset(self->Name, 0, sizeof(self->Name)); + self->CurrentPos = 0; + self->NumPresets = 0; + self->Presets = calloc(1, sfont->NumPresets * sizeof(self->Presets[0])); + if(self->Presets) + { + ALsizei i; + self->NumPresets = sfont->NumPresets; + for(i = 0;i < self->NumPresets;i++) + FPreset_Construct(&self->Presets[i], sfont->Presets[i], STATIC_CAST(fluid_sfont_t, self)); + } +} + +static void FSfont_Destruct(FSfont *self) +{ + ALsizei i; + + for(i = 0;i < self->NumPresets;i++) + FPreset_Destruct(&self->Presets[i]); + free(self->Presets); + self->Presets = NULL; + self->NumPresets = 0; + self->CurrentPos = 0; +} + +static int FSfont_free(fluid_sfont_t *sfont) +{ + FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont); + ALsizei i; + + for(i = 0;i < self->NumPresets;i++) + { + if(!FPreset_canDelete(&self->Presets[i])) + return 1; + } + + FSfont_Destruct(self); + free(self); + return 0; +} + +static char* FSfont_getName(fluid_sfont_t *sfont) +{ + return STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->Name; +} + +static fluid_preset_t *FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum) +{ + FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont); + ALsizei i; + + for(i = 0;i < self->NumPresets;i++) + { + FPreset *preset = &self->Presets[i]; + if(preset->Bank == (int)bank && preset->Preset == (int)prenum) + return STATIC_CAST(fluid_preset_t, preset); + } + + return NULL; +} + +static void FSfont_iterStart(fluid_sfont_t *sfont) +{ + STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->CurrentPos = 0; +} + +static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset) +{ + FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont); + if(self->CurrentPos >= self->NumPresets) + return 0; + *preset = *STATIC_CAST(fluid_preset_t, &self->Presets[self->CurrentPos++]); + preset->free = NULL; + return 1; +} + + +typedef struct FSynth { + DERIVE_FROM_TYPE(MidiSynth); + DERIVE_FROM_TYPE(fluid_sfloader_t); + + fluid_settings_t *Settings; + fluid_synth_t *Synth; + int *FontIDs; + ALsizei NumFontIDs; + + ALboolean ForceGM2BankSelect; + ALfloat GainScale; +} FSynth; + +static void FSynth_Construct(FSynth *self, ALCdevice *device); +static void FSynth_Destruct(FSynth *self); +static ALboolean FSynth_init(FSynth *self, ALCdevice *device); +static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids); +static void FSynth_setGain(FSynth *self, ALfloat gain); +static void FSynth_stop(FSynth *self); +static void FSynth_reset(FSynth *self); +static void FSynth_update(FSynth *self, ALCdevice *device); +static void FSynth_processQueue(FSynth *self, ALuint64 time); +static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]); +DECLARE_DEFAULT_ALLOCATORS(FSynth) +DEFINE_MIDISYNTH_VTABLE(FSynth); + +static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename); + + +static void FSynth_Construct(FSynth *self, ALCdevice *device) +{ + MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device); + SET_VTABLE2(FSynth, MidiSynth, self); + + STATIC_CAST(fluid_sfloader_t, self)->data = self; + STATIC_CAST(fluid_sfloader_t, self)->free = NULL; + STATIC_CAST(fluid_sfloader_t, self)->load = FSynth_loadSfont; + + self->Settings = NULL; + self->Synth = NULL; + self->FontIDs = NULL; + self->NumFontIDs = 0; + self->ForceGM2BankSelect = AL_FALSE; + self->GainScale = 0.2f; +} + +static void FSynth_Destruct(FSynth *self) +{ + ALsizei i; + + for(i = 0;i < self->NumFontIDs;i++) + fluid_synth_sfunload(self->Synth, self->FontIDs[i], 0); + free(self->FontIDs); + self->FontIDs = NULL; + self->NumFontIDs = 0; + + if(self->Synth != NULL) + delete_fluid_synth(self->Synth); + self->Synth = NULL; + + if(self->Settings != NULL) + delete_fluid_settings(self->Settings); + self->Settings = NULL; + + MidiSynth_Destruct(STATIC_CAST(MidiSynth, self)); +} + +static ALboolean FSynth_init(FSynth *self, ALCdevice *device) +{ + ALfloat vol; + + if(ConfigValueFloat("midi", "volume", &vol)) + { + if(!(vol <= 0.0f)) + { + ERR("MIDI volume %f clamped to 0\n", vol); + vol = 0.0f; + } + self->GainScale = powf(10.0f, vol / 20.0f); + } + + self->Settings = new_fluid_settings(); + if(!self->Settings) + { + ERR("Failed to create FluidSettings\n"); + return AL_FALSE; + } + + fluid_settings_setint(self->Settings, "synth.polyphony", 256); + fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale); + fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency); + + self->Synth = new_fluid_synth(self->Settings); + if(!self->Synth) + { + ERR("Failed to create FluidSynth\n"); + return AL_FALSE; + } + + fluid_synth_add_sfloader(self->Synth, STATIC_CAST(fluid_sfloader_t, self)); + + return AL_TRUE; +} + + +static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename) +{ + FSynth *self = STATIC_UPCAST(FSynth, fluid_sfloader_t, loader); + FSfont *sfont; + int idx; + + if(!filename || sscanf(filename, "_al_internal %d", &idx) != 1) + return NULL; + if(idx < 0 || idx >= STATIC_CAST(MidiSynth, self)->NumSoundfonts) + { + ERR("Received invalid soundfont index %d (max: %d)\n", idx, STATIC_CAST(MidiSynth, self)->NumSoundfonts); + return NULL; + } + + sfont = calloc(1, sizeof(sfont[0])); + if(!sfont) return NULL; + + FSfont_Construct(sfont, STATIC_CAST(MidiSynth, self)->Soundfonts[idx]); + return STATIC_CAST(fluid_sfont_t, sfont); +} + +static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids) +{ + int *fontid; + ALenum ret; + ALsizei i; + + ret = MidiSynth_selectSoundfonts(STATIC_CAST(MidiSynth, self), context, count, ids); + if(ret != AL_NO_ERROR) return ret; + + ALCdevice_Lock(context->Device); + for(i = 0;i < 16;i++) + fluid_synth_all_sounds_off(self->Synth, i); + ALCdevice_Unlock(context->Device); + + fontid = malloc(count * sizeof(fontid[0])); + if(fontid) + { + for(i = 0;i < STATIC_CAST(MidiSynth, self)->NumSoundfonts;i++) + { + char name[16]; + snprintf(name, sizeof(name), "_al_internal %d", i); + + fontid[i] = fluid_synth_sfload(self->Synth, name, 0); + if(fontid[i] == FLUID_FAILED) + ERR("Failed to load selected soundfont %d\n", i); + } + + fontid = ExchangePtr((XchgPtr*)&self->FontIDs, fontid); + count = ExchangeInt(&self->NumFontIDs, count); + } + else + { + ERR("Failed to allocate space for %d font IDs!\n", count); + fontid = ExchangePtr((XchgPtr*)&self->FontIDs, NULL); + count = ExchangeInt(&self->NumFontIDs, 0); + } + + for(i = 0;i < count;i++) + fluid_synth_sfunload(self->Synth, fontid[i], 0); + free(fontid); + + return ret; +} + + +static void FSynth_setGain(FSynth *self, ALfloat gain) +{ + fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale * gain); + fluid_synth_set_gain(self->Synth, self->GainScale * gain); + MidiSynth_setGain(STATIC_CAST(MidiSynth, self), gain); +} + + +static void FSynth_stop(FSynth *self) +{ + MidiSynth *synth = STATIC_CAST(MidiSynth, self); + ALuint64 curtime; + ALsizei chan; + + /* Make sure all pending events are processed. */ + curtime = MidiSynth_getTime(synth); + FSynth_processQueue(self, curtime); + + /* All notes off */ + for(chan = 0;chan < 16;chan++) + fluid_synth_cc(self->Synth, chan, CTRL_ALLNOTESOFF, 0); + + MidiSynth_stop(STATIC_CAST(MidiSynth, self)); +} + +static void FSynth_reset(FSynth *self) +{ + /* Reset to power-up status. */ + fluid_synth_system_reset(self->Synth); + + MidiSynth_reset(STATIC_CAST(MidiSynth, self)); +} + + +static void FSynth_update(FSynth *self, ALCdevice *device) +{ + fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency); + fluid_synth_set_sample_rate(self->Synth, device->Frequency); + MidiSynth_update(STATIC_CAST(MidiSynth, self), device); +} + + +static void FSynth_processQueue(FSynth *self, ALuint64 time) +{ + EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue; + + while(queue->pos < queue->size && queue->events[queue->pos].time <= time) + { + const MidiEvent *evt = &queue->events[queue->pos]; + + if(evt->event == SYSEX_EVENT) + { + static const ALbyte gm2_on[] = { 0x7E, 0x7F, 0x09, 0x03 }; + static const ALbyte gm2_off[] = { 0x7E, 0x7F, 0x09, 0x02 }; + int handled = 0; + + fluid_synth_sysex(self->Synth, evt->param.sysex.data, evt->param.sysex.size, NULL, NULL, &handled, 0); + if(!handled && evt->param.sysex.size >= (ALsizei)sizeof(gm2_on)) + { + if(memcmp(evt->param.sysex.data, gm2_on, sizeof(gm2_on)) == 0) + self->ForceGM2BankSelect = AL_TRUE; + else if(memcmp(evt->param.sysex.data, gm2_off, sizeof(gm2_off)) == 0) + self->ForceGM2BankSelect = AL_FALSE; + } + } + else switch((evt->event&0xF0)) + { + case AL_NOTEOFF_SOFT: + fluid_synth_noteoff(self->Synth, (evt->event&0x0F), evt->param.val[0]); + break; + case AL_NOTEON_SOFT: + fluid_synth_noteon(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]); + break; + case AL_KEYPRESSURE_SOFT: + break; + + case AL_CONTROLLERCHANGE_SOFT: + if(self->ForceGM2BankSelect) + { + int chan = (evt->event&0x0F); + if(evt->param.val[0] == CTRL_BANKSELECT_MSB) + { + if(evt->param.val[1] == 120 && (chan == 9 || chan == 10)) + fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_DRUM); + else if(evt->param.val[1] == 121) + fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_MELODIC); + break; + } + if(evt->param.val[0] == CTRL_BANKSELECT_LSB) + { + fluid_synth_bank_select(self->Synth, chan, evt->param.val[1]); + break; + } + } + fluid_synth_cc(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]); + break; + case AL_PROGRAMCHANGE_SOFT: + fluid_synth_program_change(self->Synth, (evt->event&0x0F), evt->param.val[0]); + break; + + case AL_CHANNELPRESSURE_SOFT: + fluid_synth_channel_pressure(self->Synth, (evt->event&0x0F), evt->param.val[0]); + break; + + case AL_PITCHBEND_SOFT: + fluid_synth_pitch_bend(self->Synth, (evt->event&0x0F), (evt->param.val[0]&0x7F) | + ((evt->param.val[1]&0x7F)<<7)); + break; + } + + queue->pos++; + } +} + +static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]) +{ + MidiSynth *synth = STATIC_CAST(MidiSynth, self); + ALenum state = synth->State; + ALuint64 curtime; + ALuint total = 0; + + if(state == AL_INITIAL) + return; + if(state != AL_PLAYING) + { + fluid_synth_write_float(self->Synth, SamplesToDo, DryBuffer[FrontLeft], 0, 1, + DryBuffer[FrontRight], 0, 1); + return; + } + + curtime = MidiSynth_getTime(synth); + while(total < SamplesToDo) + { + ALuint64 time, diff; + ALint tonext; + + time = MidiSynth_getNextEvtTime(synth); + diff = maxu64(time, curtime) - curtime; + if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX) + { + /* If there's no pending event, or if it's more than 1 second + * away, do as many samples as we can. */ + tonext = INT_MAX; + } + else + { + /* Figure out how many samples until the next event. */ + tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES); + tonext -= total; + } + + if(tonext > 0) + { + ALuint todo = minu(tonext, SamplesToDo-total); + fluid_synth_write_float(self->Synth, todo, DryBuffer[FrontLeft], total, 1, + DryBuffer[FrontRight], total, 1); + total += todo; + tonext -= todo; + } + if(total < SamplesToDo && tonext <= 0) + FSynth_processQueue(self, time); + } + + synth->SamplesDone += SamplesToDo; + synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES; + synth->SamplesDone %= synth->SampleRate; +} + + +MidiSynth *FSynth_create(ALCdevice *device) +{ + FSynth *synth; + + if(!LoadFSynth()) + return NULL; + + synth = FSynth_New(sizeof(*synth)); + if(!synth) + { + ERR("Failed to allocate FSynth\n"); + return NULL; + } + memset(synth, 0, sizeof(*synth)); + FSynth_Construct(synth, device); + + if(FSynth_init(synth, device) == AL_FALSE) + { + DELETE_OBJ(STATIC_CAST(MidiSynth, synth)); + return NULL; + } + + return STATIC_CAST(MidiSynth, synth); +} + +#else + +MidiSynth *FSynth_create(ALCdevice* UNUSED(device)) +{ + return NULL; +} + +#endif diff --git a/libs/openal-soft/Alc/midi/sf2load.c b/libs/openal-soft/Alc/midi/sf2load.c new file mode 100644 index 00000000..233f7983 --- /dev/null +++ b/libs/openal-soft/Alc/midi/sf2load.c @@ -0,0 +1,1377 @@ + +#include "config.h" + +#include +#include + +#include "alMain.h" +#include "alMidi.h" +#include "alError.h" +#include "alu.h" + +#include "midi/base.h" + + +static ALuint read_le32(Reader *stream) +{ + ALubyte buf[4]; + if(Reader_read(stream, buf, 4) != 4) + return 0; + return (buf[3]<<24) | (buf[2]<<16) | (buf[1]<<8) | buf[0]; +} +static ALushort read_le16(Reader *stream) +{ + ALubyte buf[2]; + if(Reader_read(stream, buf, 2) != 2) + return 0; + return (buf[1]<<8) | buf[0]; +} +static ALubyte read_8(Reader *stream) +{ + ALubyte buf[1]; + if(Reader_read(stream, buf, 1) != 1) + return 0; + return buf[0]; +} +static void skip(Reader *stream, ALuint amt) +{ + while(amt > 0 && !READERR(stream)) + { + char buf[4096]; + amt -= Reader_read(stream, buf, minu(sizeof(buf), amt)); + } +} + +typedef struct Generator { + ALushort mGenerator; + ALushort mAmount; +} Generator; +static void Generator_read(Generator *self, Reader *stream) +{ + self->mGenerator = read_le16(stream); + self->mAmount = read_le16(stream); +} + +static const ALint DefaultGenValue[60] = { + 0, /* 0 - startAddrOffset */ + 0, /* 1 - endAddrOffset */ + 0, /* 2 - startloopAddrOffset */ + 0, /* 3 - endloopAddrOffset */ + 0, /* 4 - startAddrCoarseOffset */ + 0, /* 5 - modLfoToPitch */ + 0, /* 6 - vibLfoToPitch */ + 0, /* 7 - modEnvToPitch */ + 13500, /* 8 - initialFilterFc */ + 0, /* 9 - initialFilterQ */ + 0, /* 10 - modLfoToFilterFc */ + 0, /* 11 - modEnvToFilterFc */ + 0, /* 12 - endAddrCoarseOffset */ + 0, /* 13 - modLfoToVolume */ + 0, /* 14 - */ + 0, /* 15 - chorusEffectsSend */ + 0, /* 16 - reverbEffectsSend */ + 0, /* 17 - pan */ + 0, /* 18 - */ + 0, /* 19 - */ + 0, /* 20 - */ + -12000, /* 21 - delayModLFO */ + 0, /* 22 - freqModLFO */ + -12000, /* 23 - delayVibLFO */ + 0, /* 24 - freqVibLFO */ + -12000, /* 25 - delayModEnv */ + -12000, /* 26 - attackModEnv */ + -12000, /* 27 - holdModEnv */ + -12000, /* 28 - decayModEnv */ + 0, /* 29 - sustainModEnv */ + -12000, /* 30 - releaseModEnv */ + 0, /* 31 - keynumToModEnvHold */ + 0, /* 32 - keynumToModEnvDecay */ + -12000, /* 33 - delayVolEnv */ + -12000, /* 34 - attackVolEnv */ + -12000, /* 35 - holdVolEnv */ + -12000, /* 36 - decayVolEnv */ + 0, /* 37 - sustainVolEnv */ + -12000, /* 38 - releaseVolEnv */ + 0, /* 39 - keynumToVolEnvHold */ + 0, /* 40 - keynumToVolEnvDecay */ + 0, /* 41 - */ + 0, /* 42 - */ + 0, /* 43 - keyRange */ + 0, /* 44 - velRange */ + 0, /* 45 - startloopAddrCoarseOffset */ + 0, /* 46 - keynum */ + 0, /* 47 - velocity */ + 0, /* 48 - initialAttenuation */ + 0, /* 49 - */ + 0, /* 50 - endloopAddrCoarseOffset */ + 0, /* 51 - corseTune */ + 0, /* 52 - fineTune */ + 0, /* 53 - */ + 0, /* 54 - sampleModes */ + 0, /* 55 - */ + 100, /* 56 - scaleTuning */ + 0, /* 57 - exclusiveClass */ + 0, /* 58 - overridingRootKey */ + 0, /* 59 - */ +}; + +typedef struct Modulator { + ALushort mSrcOp; + ALushort mDstOp; + ALshort mAmount; + ALushort mAmtSrcOp; + ALushort mTransOp; +} Modulator; +static void Modulator_read(Modulator *self, Reader *stream) +{ + self->mSrcOp = read_le16(stream); + self->mDstOp = read_le16(stream); + self->mAmount = read_le16(stream); + self->mAmtSrcOp = read_le16(stream); + self->mTransOp = read_le16(stream); +} + +typedef struct Zone { + ALushort mGenIdx; + ALushort mModIdx; +} Zone; +static void Zone_read(Zone *self, Reader *stream) +{ + self->mGenIdx = read_le16(stream); + self->mModIdx = read_le16(stream); +} + +typedef struct PresetHeader { + ALchar mName[20]; + ALushort mPreset; /* MIDI program number */ + ALushort mBank; + ALushort mZoneIdx; + ALuint mLibrary; + ALuint mGenre; + ALuint mMorphology; +} PresetHeader; +static void PresetHeader_read(PresetHeader *self, Reader *stream) +{ + Reader_read(stream, self->mName, sizeof(self->mName)); + self->mPreset = read_le16(stream); + self->mBank = read_le16(stream); + self->mZoneIdx = read_le16(stream); + self->mLibrary = read_le32(stream); + self->mGenre = read_le32(stream); + self->mMorphology = read_le32(stream); +} + +typedef struct InstrumentHeader { + ALchar mName[20]; + ALushort mZoneIdx; +} InstrumentHeader; +static void InstrumentHeader_read(InstrumentHeader *self, Reader *stream) +{ + Reader_read(stream, self->mName, sizeof(self->mName)); + self->mZoneIdx = read_le16(stream); +} + +typedef struct SampleHeader { + ALchar mName[20]; + ALuint mStart; + ALuint mEnd; + ALuint mStartloop; + ALuint mEndloop; + ALuint mSampleRate; + ALubyte mOriginalKey; + ALbyte mCorrection; + ALushort mSampleLink; + ALushort mSampleType; +} SampleHeader; +static void SampleHeader_read(SampleHeader *self, Reader *stream) +{ + Reader_read(stream, self->mName, sizeof(self->mName)); + self->mStart = read_le32(stream); + self->mEnd = read_le32(stream); + self->mStartloop = read_le32(stream); + self->mEndloop = read_le32(stream); + self->mSampleRate = read_le32(stream); + self->mOriginalKey = read_8(stream); + self->mCorrection = read_8(stream); + self->mSampleLink = read_le16(stream); + self->mSampleType = read_le16(stream); +} + + +typedef struct Soundfont { + ALuint ifil; + ALchar *irom; + + PresetHeader *phdr; + ALsizei phdr_size; + + Zone *pbag; + ALsizei pbag_size; + Modulator *pmod; + ALsizei pmod_size; + Generator *pgen; + ALsizei pgen_size; + + InstrumentHeader *inst; + ALsizei inst_size; + + Zone *ibag; + ALsizei ibag_size; + Modulator *imod; + ALsizei imod_size; + Generator *igen; + ALsizei igen_size; + + SampleHeader *shdr; + ALsizei shdr_size; +} Soundfont; + +static void Soundfont_Construct(Soundfont *self) +{ + self->ifil = 0; + self->irom = NULL; + + self->phdr = NULL; + self->phdr_size = 0; + + self->pbag = NULL; + self->pbag_size = 0; + self->pmod = NULL; + self->pmod_size = 0; + self->pgen = NULL; + self->pgen_size = 0; + + self->inst = NULL; + self->inst_size = 0; + + self->ibag = NULL; + self->ibag_size = 0; + self->imod = NULL; + self->imod_size = 0; + self->igen = NULL; + self->igen_size = 0; + + self->shdr = NULL; + self->shdr_size = 0; +} + +static void Soundfont_Destruct(Soundfont *self) +{ + free(self->irom); + self->irom = NULL; + + free(self->phdr); + self->phdr = NULL; + self->phdr_size = 0; + + free(self->pbag); + self->pbag = NULL; + self->pbag_size = 0; + free(self->pmod); + self->pmod = NULL; + self->pmod_size = 0; + free(self->pgen); + self->pgen = NULL; + self->pgen_size = 0; + + free(self->inst); + self->inst = NULL; + self->inst_size = 0; + + free(self->ibag); + self->ibag = NULL; + self->ibag_size = 0; + free(self->imod); + self->imod = NULL; + self->imod_size = 0; + free(self->igen); + self->igen = NULL; + self->igen_size = 0; + + free(self->shdr); + self->shdr = NULL; + self->shdr_size = 0; +} + + +#define FOURCC(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) +#define FOURCCFMT "%c%c%c%c" +#define FOURCCARGS(x) (char)((x)&0xff), (char)(((x)>>8)&0xff), (char)(((x)>>16)&0xff), (char)(((x)>>24)&0xff) +typedef struct RiffHdr { + ALuint mCode; + ALuint mSize; +} RiffHdr; +static void RiffHdr_read(RiffHdr *self, Reader *stream) +{ + self->mCode = read_le32(stream); + self->mSize = read_le32(stream); +} + + +typedef struct GenModList { + VECTOR(Generator) gens; + VECTOR(Modulator) mods; +} GenModList; + +static void GenModList_Construct(GenModList *self) +{ + VECTOR_INIT(self->gens); + VECTOR_INIT(self->mods); +} + +static void GenModList_Destruct(GenModList *self) +{ + VECTOR_DEINIT(self->mods); + VECTOR_DEINIT(self->gens); +} + +static GenModList GenModList_clone(const GenModList *self) +{ + GenModList ret; + + GenModList_Construct(&ret); + + VECTOR_INSERT(ret.gens, VECTOR_ITER_END(ret.gens), + VECTOR_ITER_BEGIN(self->gens), VECTOR_ITER_END(self->gens) + ); + VECTOR_INSERT(ret.mods, VECTOR_ITER_END(ret.mods), + VECTOR_ITER_BEGIN(self->mods), VECTOR_ITER_END(self->mods) + ); + + return ret; +} + +static void GenModList_insertGen(GenModList *self, const Generator *gen, ALboolean ispreset) +{ + Generator *i = VECTOR_ITER_BEGIN(self->gens); + Generator *end = VECTOR_ITER_END(self->gens); + for(;i != end;i++) + { + if(i->mGenerator == gen->mGenerator) + { + i->mAmount = gen->mAmount; + return; + } + } + + if(ispreset && + (gen->mGenerator == 0 || gen->mGenerator == 1 || gen->mGenerator == 2 || + gen->mGenerator == 3 || gen->mGenerator == 4 || gen->mGenerator == 12 || + gen->mGenerator == 45 || gen->mGenerator == 46 || gen->mGenerator == 47 || + gen->mGenerator == 50 || gen->mGenerator == 54 || gen->mGenerator == 57 || + gen->mGenerator == 58)) + return; + + if(VECTOR_PUSH_BACK(self->gens, *gen) == AL_FALSE) + { + ERR("Failed to insert generator (from %d elements)\n", VECTOR_SIZE(self->gens)); + return; + } +} +static void GenModList_accumGen(GenModList *self, const Generator *gen) +{ + Generator *i = VECTOR_ITER_BEGIN(self->gens); + Generator *end = VECTOR_ITER_END(self->gens); + for(;i != end;i++) + { + if(i->mGenerator == gen->mGenerator) + { + if(gen->mGenerator == 43 || gen->mGenerator == 44) + { + /* Range generators accumulate by taking the intersection of + * the two ranges. + */ + ALushort low = maxu(i->mAmount&0x00ff, gen->mAmount&0x00ff); + ALushort high = minu(i->mAmount&0xff00, gen->mAmount&0xff00); + i->mAmount = low | high; + } + else + i->mAmount += gen->mAmount; + return; + } + } + + if(VECTOR_PUSH_BACK(self->gens, *gen) == AL_FALSE) + { + ERR("Failed to insert generator (from %d elements)\n", VECTOR_SIZE(self->gens)); + return; + } + if(gen->mGenerator < 60) + VECTOR_BACK(self->gens).mAmount += DefaultGenValue[gen->mGenerator]; +} + +static void GenModList_insertMod(GenModList *self, const Modulator *mod) +{ + Modulator *i = VECTOR_ITER_BEGIN(self->mods); + Modulator *end = VECTOR_ITER_END(self->mods); + for(;i != end;i++) + { + if(i->mDstOp == mod->mDstOp && i->mSrcOp == mod->mSrcOp && + i->mAmtSrcOp == mod->mAmtSrcOp && i->mTransOp == mod->mTransOp) + { + i->mAmount = mod->mAmount; + return; + } + } + + if(VECTOR_PUSH_BACK(self->mods, *mod) == AL_FALSE) + { + ERR("Failed to insert modulator (from %d elements)\n", VECTOR_SIZE(self->mods)); + return; + } +} +static void GenModList_accumMod(GenModList *self, const Modulator *mod) +{ + Modulator *i = VECTOR_ITER_BEGIN(self->mods); + Modulator *end = VECTOR_ITER_END(self->mods); + for(;i != end;i++) + { + if(i->mDstOp == mod->mDstOp && i->mSrcOp == mod->mSrcOp && + i->mAmtSrcOp == mod->mAmtSrcOp && i->mTransOp == mod->mTransOp) + { + i->mAmount += mod->mAmount; + return; + } + } + + if(VECTOR_PUSH_BACK(self->mods, *mod) == AL_FALSE) + { + ERR("Failed to insert modulator (from %d elements)\n", VECTOR_SIZE(self->mods)); + return; + } + + if(mod->mSrcOp == 0x0502 && mod->mDstOp == 48 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) + VECTOR_BACK(self->mods).mAmount += 960; + else if(mod->mSrcOp == 0x0102 && mod->mDstOp == 8 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) + VECTOR_BACK(self->mods).mAmount += -2400; + else if(mod->mSrcOp == 0x000D && mod->mDstOp == 6 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) + VECTOR_BACK(self->mods).mAmount += 50; + else if(mod->mSrcOp == 0x0081 && mod->mDstOp == 6 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) + VECTOR_BACK(self->mods).mAmount += 50; + else if(mod->mSrcOp == 0x0582 && mod->mDstOp == 48 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) + VECTOR_BACK(self->mods).mAmount += 960; + else if(mod->mSrcOp == 0x028A && mod->mDstOp == 17 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) + VECTOR_BACK(self->mods).mAmount += 1000; + else if(mod->mSrcOp == 0x058B && mod->mDstOp == 48 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) + VECTOR_BACK(self->mods).mAmount += 960; + else if(mod->mSrcOp == 0x00DB && mod->mDstOp == 16 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) + VECTOR_BACK(self->mods).mAmount += 200; + else if(mod->mSrcOp == 0x00DD && mod->mDstOp == 15 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0) + VECTOR_BACK(self->mods).mAmount += 200; + /*else if(mod->mSrcOp == 0x020E && mod->mDstOp == ?initialpitch? && mod->mAmtSrcOp == 0x0010 && mod->mTransOp == 0) + VECTOR_BACK(self->mods).mAmount += 12700;*/ +} + + +#define ERROR_GOTO(lbl_, ...) do { \ + ERR(__VA_ARGS__); \ + goto lbl_; \ +} while(0) + +static ALboolean ensureFontSanity(const Soundfont *sfont) +{ + ALsizei i; + + for(i = 0;i < sfont->phdr_size;i++) + { + if(sfont->phdr[i].mZoneIdx >= sfont->pbag_size) + { + WARN("Preset %d has invalid zone index %d (max: %d)\n", i, + sfont->phdr[i].mZoneIdx, sfont->pbag_size); + return AL_FALSE; + } + if(i+1 < sfont->phdr_size && sfont->phdr[i+1].mZoneIdx < sfont->phdr[i].mZoneIdx) + { + WARN("Preset %d has invalid zone index (%d does not follow %d)\n", i+1, + sfont->phdr[i+1].mZoneIdx, sfont->phdr[i].mZoneIdx); + return AL_FALSE; + } + } + + for(i = 0;i < sfont->pbag_size;i++) + { + if(sfont->pbag[i].mGenIdx >= sfont->pgen_size) + { + WARN("Preset zone %d has invalid generator index %d (max: %d)\n", i, + sfont->pbag[i].mGenIdx, sfont->pgen_size); + return AL_FALSE; + } + if(i+1 < sfont->pbag_size && sfont->pbag[i+1].mGenIdx < sfont->pbag[i].mGenIdx) + { + WARN("Preset zone %d has invalid generator index (%d does not follow %d)\n", i+1, + sfont->pbag[i+1].mGenIdx, sfont->pbag[i].mGenIdx); + return AL_FALSE; + } + if(sfont->pbag[i].mModIdx >= sfont->pmod_size) + { + WARN("Preset zone %d has invalid modulator index %d (max: %d)\n", i, + sfont->pbag[i].mModIdx, sfont->pmod_size); + return AL_FALSE; + } + if(i+1 < sfont->pbag_size && sfont->pbag[i+1].mModIdx < sfont->pbag[i].mModIdx) + { + WARN("Preset zone %d has invalid modulator index (%d does not follow %d)\n", i+1, + sfont->pbag[i+1].mModIdx, sfont->pbag[i].mModIdx); + return AL_FALSE; + } + } + + + for(i = 0;i < sfont->inst_size;i++) + { + if(sfont->inst[i].mZoneIdx >= sfont->ibag_size) + { + WARN("Instrument %d has invalid zone index %d (max: %d)\n", i, + sfont->inst[i].mZoneIdx, sfont->ibag_size); + return AL_FALSE; + } + if(i+1 < sfont->inst_size && sfont->inst[i+1].mZoneIdx < sfont->inst[i].mZoneIdx) + { + WARN("Instrument %d has invalid zone index (%d does not follow %d)\n", i+1, + sfont->inst[i+1].mZoneIdx, sfont->inst[i].mZoneIdx); + return AL_FALSE; + } + } + + for(i = 0;i < sfont->ibag_size;i++) + { + if(sfont->ibag[i].mGenIdx >= sfont->igen_size) + { + WARN("Instrument zone %d has invalid generator index %d (max: %d)\n", i, + sfont->ibag[i].mGenIdx, sfont->igen_size); + return AL_FALSE; + } + if(i+1 < sfont->ibag_size && sfont->ibag[i+1].mGenIdx < sfont->ibag[i].mGenIdx) + { + WARN("Instrument zone %d has invalid generator index (%d does not follow %d)\n", i+1, + sfont->ibag[i+1].mGenIdx, sfont->ibag[i].mGenIdx); + return AL_FALSE; + } + if(sfont->ibag[i].mModIdx >= sfont->imod_size) + { + WARN("Instrument zone %d has invalid modulator index %d (max: %d)\n", i, + sfont->ibag[i].mModIdx, sfont->imod_size); + return AL_FALSE; + } + if(i+1 < sfont->ibag_size && sfont->ibag[i+1].mModIdx < sfont->ibag[i].mModIdx) + { + WARN("Instrument zone %d has invalid modulator index (%d does not follow %d)\n", i+1, + sfont->ibag[i+1].mModIdx, sfont->ibag[i].mModIdx); + return AL_FALSE; + } + } + + + for(i = 0;i < sfont->shdr_size-1;i++) + { + if((sfont->shdr[i].mSampleType&0x8000) && sfont->irom == NULL) + { + WARN("Sample header %d has ROM sample type without an irom sub-chunk\n", i); + return AL_FALSE; + } + } + + + return AL_TRUE; +} + +static ALboolean checkZone(const GenModList *zone, const PresetHeader *preset, const InstrumentHeader *inst, const SampleHeader *samp) +{ + Generator *gen = VECTOR_ITER_BEGIN(zone->gens); + Generator *gen_end = VECTOR_ITER_END(zone->gens); + for(;gen != gen_end;gen++) + { + if(gen->mGenerator == 43 || gen->mGenerator == 44) + { + int high = gen->mAmount>>8; + int low = gen->mAmount&0xff; + + if(!(low >= 0 && high <= 127 && high >= low)) + { + TRACE("Preset \"%s\", inst \"%s\", sample \"%s\": invalid %s range %d...%d\n", + preset->mName, inst->mName, samp->mName, + (gen->mGenerator == 43) ? "key" : + (gen->mGenerator == 44) ? "velocity" : "(unknown)", + low, high); + return AL_FALSE; + } + } + } + + return AL_TRUE; +} + +static ALenum getModSrcInput(int input) +{ + if(input == 0) return AL_ONE_SOFT; + if(input == 2) return AL_NOTEON_VELOCITY_SOFT; + if(input == 3) return AL_NOTEON_KEY_SOFT; + if(input == 10) return AL_KEYPRESSURE_SOFT; + if(input == 13) return AL_CHANNELPRESSURE_SOFT; + if(input == 14) return AL_PITCHBEND_SOFT; + if(input == 16) return AL_PITCHBEND_SENSITIVITY_SOFT; + if((input&0x80)) + { + if(IsValidCtrlInput(input^0x80)) + return input^0x80; + } + ERR("Unhandled modulator source input: 0x%02x\n", input); + return AL_INVALID; +} + +static ALenum getModSrcType(int type) +{ + if(type == 0x0000) return AL_UNORM_SOFT; + if(type == 0x0100) return AL_UNORM_REV_SOFT; + if(type == 0x0200) return AL_SNORM_SOFT; + if(type == 0x0300) return AL_SNORM_REV_SOFT; + ERR("Unhandled modulator source type: 0x%04x\n", type); + return AL_INVALID; +} + +static ALenum getModSrcForm(int form) +{ + if(form == 0x0000) return AL_LINEAR_SOFT; + if(form == 0x0400) return AL_CONCAVE_SOFT; + if(form == 0x0800) return AL_CONVEX_SOFT; + if(form == 0x0C00) return AL_SWITCH_SOFT; + ERR("Unhandled modulator source form: 0x%04x\n", form); + return AL_INVALID; +} + +static ALenum getModTransOp(int op) +{ + if(op == 0) return AL_LINEAR_SOFT; + if(op == 2) return AL_ABSOLUTE_SOFT; + ERR("Unhandled modulator transform op: 0x%04x\n", op); + return AL_INVALID; +} + +static ALenum getLoopMode(int mode) +{ + if(mode == 0) return AL_NONE; + if(mode == 1) return AL_LOOP_CONTINUOUS_SOFT; + if(mode == 3) return AL_LOOP_UNTIL_RELEASE_SOFT; + ERR("Unhandled loop mode: %d\n", mode); + return AL_NONE; +} + +static ALenum getSampleType(int type) +{ + if(type == 1) return AL_MONO_SOFT; + if(type == 2) return AL_RIGHT_SOFT; + if(type == 4) return AL_LEFT_SOFT; + if(type == 8) + { + WARN("Sample type \"linked\" ignored; pretending mono\n"); + return AL_MONO_SOFT; + } + ERR("Unhandled sample type: 0x%04x\n", type); + return AL_MONO_SOFT; +} + +static void fillZone(ALfontsound *sound, ALCcontext *context, const GenModList *zone) +{ + static const ALenum Gen2Param[60] = { + 0, /* 0 - startAddrOffset */ + 0, /* 1 - endAddrOffset */ + 0, /* 2 - startloopAddrOffset */ + 0, /* 3 - endloopAddrOffset */ + 0, /* 4 - startAddrCoarseOffset */ + AL_MOD_LFO_TO_PITCH_SOFT, /* 5 - modLfoToPitch */ + AL_VIBRATO_LFO_TO_PITCH_SOFT, /* 6 - vibLfoToPitch */ + AL_MOD_ENV_TO_PITCH_SOFT, /* 7 - modEnvToPitch */ + AL_FILTER_CUTOFF_SOFT, /* 8 - initialFilterFc */ + AL_FILTER_RESONANCE_SOFT, /* 9 - initialFilterQ */ + AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT, /* 10 - modLfoToFilterFc */ + AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT, /* 11 - modEnvToFilterFc */ + 0, /* 12 - endAddrCoarseOffset */ + AL_MOD_LFO_TO_VOLUME_SOFT, /* 13 - modLfoToVolume */ + 0, /* 14 - */ + AL_CHORUS_SEND_SOFT, /* 15 - chorusEffectsSend */ + AL_REVERB_SEND_SOFT, /* 16 - reverbEffectsSend */ + AL_PAN_SOFT, /* 17 - pan */ + 0, /* 18 - */ + 0, /* 19 - */ + 0, /* 20 - */ + AL_MOD_LFO_DELAY_SOFT, /* 21 - delayModLFO */ + AL_MOD_LFO_FREQUENCY_SOFT, /* 22 - freqModLFO */ + AL_VIBRATO_LFO_DELAY_SOFT, /* 23 - delayVibLFO */ + AL_VIBRATO_LFO_FREQUENCY_SOFT, /* 24 - freqVibLFO */ + AL_MOD_ENV_DELAYTIME_SOFT, /* 25 - delayModEnv */ + AL_MOD_ENV_ATTACKTIME_SOFT, /* 26 - attackModEnv */ + AL_MOD_ENV_HOLDTIME_SOFT, /* 27 - holdModEnv */ + AL_MOD_ENV_DECAYTIME_SOFT, /* 28 - decayModEnv */ + AL_MOD_ENV_SUSTAINVOLUME_SOFT, /* 29 - sustainModEnv */ + AL_MOD_ENV_RELEASETIME_SOFT, /* 30 - releaseModEnv */ + AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT, /* 31 - keynumToModEnvHold */ + AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT, /* 32 - keynumToModEnvDecay */ + AL_VOLUME_ENV_DELAYTIME_SOFT, /* 33 - delayVolEnv */ + AL_VOLUME_ENV_ATTACKTIME_SOFT, /* 34 - attackVolEnv */ + AL_VOLUME_ENV_HOLDTIME_SOFT, /* 35 - holdVolEnv */ + AL_VOLUME_ENV_DECAYTIME_SOFT, /* 36 - decayVolEnv */ + AL_VOLUME_ENV_SUSTAINVOLUME_SOFT, /* 37 - sustainVolEnv */ + AL_VOLUME_ENV_RELEASETIME_SOFT, /* 38 - releaseVolEnv */ + AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT, /* 39 - keynumToVolEnvHold */ + AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT, /* 40 - keynumToVolEnvDecay */ + 0, /* 41 - */ + 0, /* 42 - */ + AL_KEY_RANGE_SOFT, /* 43 - keyRange */ + AL_VELOCITY_RANGE_SOFT, /* 44 - velRange */ + 0, /* 45 - startloopAddrCoarseOffset */ + 0, /* 46 - keynum */ + 0, /* 47 - velocity */ + AL_ATTENUATION_SOFT, /* 48 - initialAttenuation */ + 0, /* 49 - */ + 0, /* 50 - endloopAddrCoarseOffset */ + AL_TUNING_COARSE_SOFT, /* 51 - corseTune */ + AL_TUNING_FINE_SOFT, /* 52 - fineTune */ + 0, /* 53 - */ + AL_LOOP_MODE_SOFT, /* 54 - sampleModes */ + 0, /* 55 - */ + AL_TUNING_SCALE_SOFT, /* 56 - scaleTuning */ + AL_EXCLUSIVE_CLASS_SOFT, /* 57 - exclusiveClass */ + AL_BASE_KEY_SOFT, /* 58 - overridingRootKey */ + 0, /* 59 - */ + }; + const Generator *gen, *gen_end; + const Modulator *mod, *mod_end; + + mod = VECTOR_ITER_BEGIN(zone->mods); + mod_end = VECTOR_ITER_END(zone->mods); + for(;mod != mod_end;mod++) + { + ALenum src0in = getModSrcInput(mod->mSrcOp&0xFF); + ALenum src0type = getModSrcType(mod->mSrcOp&0x0300); + ALenum src0form = getModSrcForm(mod->mSrcOp&0xFC00); + ALenum src1in = getModSrcInput(mod->mAmtSrcOp&0xFF); + ALenum src1type = getModSrcType(mod->mAmtSrcOp&0x0300); + ALenum src1form = getModSrcForm(mod->mAmtSrcOp&0xFC00); + ALenum trans = getModTransOp(mod->mTransOp); + ALenum dst = (mod->mDstOp < 60) ? Gen2Param[mod->mDstOp] : 0; + if(!dst || dst == AL_KEY_RANGE_SOFT || dst == AL_VELOCITY_RANGE_SOFT || + dst == AL_LOOP_MODE_SOFT || dst == AL_EXCLUSIVE_CLASS_SOFT || + dst == AL_BASE_KEY_SOFT) + ERR("Unhandled modulator destination: %d\n", mod->mDstOp); + else if(src0in != AL_INVALID && src0form != AL_INVALID && src0type != AL_INVALID && + src1in != AL_INVALID && src1form != AL_INVALID && src0type != AL_INVALID && + trans != AL_INVALID) + { + ALsizei idx = (ALsizei)(mod - VECTOR_ITER_BEGIN(zone->mods)); + ALfontsound_setModStagei(sound, context, idx, AL_SOURCE0_INPUT_SOFT, src0in); + ALfontsound_setModStagei(sound, context, idx, AL_SOURCE0_TYPE_SOFT, src0type); + ALfontsound_setModStagei(sound, context, idx, AL_SOURCE0_FORM_SOFT, src0form); + ALfontsound_setModStagei(sound, context, idx, AL_SOURCE1_INPUT_SOFT, src1in); + ALfontsound_setModStagei(sound, context, idx, AL_SOURCE1_TYPE_SOFT, src1type); + ALfontsound_setModStagei(sound, context, idx, AL_SOURCE1_FORM_SOFT, src1form); + ALfontsound_setModStagei(sound, context, idx, AL_AMOUNT_SOFT, mod->mAmount); + ALfontsound_setModStagei(sound, context, idx, AL_TRANSFORM_OP_SOFT, trans); + ALfontsound_setModStagei(sound, context, idx, AL_DESTINATION_SOFT, dst); + } + } + + gen = VECTOR_ITER_BEGIN(zone->gens); + gen_end = VECTOR_ITER_END(zone->gens); + for(;gen != gen_end;gen++) + { + ALint value = (ALshort)gen->mAmount; + if(gen->mGenerator == 0) + sound->Start += value; + else if(gen->mGenerator == 1) + sound->End += value; + else if(gen->mGenerator == 2) + sound->LoopStart += value; + else if(gen->mGenerator == 3) + sound->LoopEnd += value; + else if(gen->mGenerator == 4) + sound->Start += value<<15; + else if(gen->mGenerator == 12) + sound->End += value<<15; + else if(gen->mGenerator == 45) + sound->LoopStart += value<<15; + else if(gen->mGenerator == 50) + sound->LoopEnd += value<<15; + else if(gen->mGenerator == 43) + { + sound->MinKey = mini((value&0xff), 127); + sound->MaxKey = mini(((value>>8)&0xff), 127); + } + else if(gen->mGenerator == 44) + { + sound->MinVelocity = mini((value&0xff), 127); + sound->MaxVelocity = mini(((value>>8)&0xff), 127); + } + else + { + ALenum param = 0; + if(gen->mGenerator < 60) + param = Gen2Param[gen->mGenerator]; + if(param) + { + if(param == AL_BASE_KEY_SOFT) + { + if(!(value >= 0 && value <= 127)) + { + if(value != -1) + WARN("Invalid overridingRootKey generator value %d\n", value); + continue; + } + } + if(param == AL_FILTER_RESONANCE_SOFT || param == AL_ATTENUATION_SOFT) + value = maxi(0, value); + else if(param == AL_CHORUS_SEND_SOFT || param == AL_REVERB_SEND_SOFT) + value = clampi(value, 0, 1000); + else if(param == AL_LOOP_MODE_SOFT) + value = getLoopMode(value); + ALfontsound_setPropi(sound, context, param, value); + } + else + { + static ALuint warned[65536/32]; + if(!(warned[gen->mGenerator/32]&(1<<(gen->mGenerator&31)))) + { + warned[gen->mGenerator/32] |= 1<<(gen->mGenerator&31); + ERR("Unhandled generator %d\n", gen->mGenerator); + } + } + } + } +} + +static void processInstrument(ALfontsound ***sounds, ALsizei *sounds_size, ALCcontext *context, ALbuffer *buffer, InstrumentHeader *inst, const PresetHeader *preset, const Soundfont *sfont, const GenModList *pzone) +{ + const Generator *gen, *gen_end; + const Modulator *mod, *mod_end; + const Zone *zone, *zone_end; + GenModList gzone; + ALvoid *temp; + + if((inst+1)->mZoneIdx == inst->mZoneIdx) + ERR("Instrument with no zones!"); + + GenModList_Construct(&gzone); + zone = sfont->ibag + inst->mZoneIdx; + zone_end = sfont->ibag + (inst+1)->mZoneIdx; + if(zone_end-zone > 1) + { + gen = sfont->igen + zone->mGenIdx; + gen_end = sfont->igen + (zone+1)->mGenIdx; + + // If no generators, or last generator is not a sample, this is a global zone + for(;gen != gen_end;gen++) + { + if(gen->mGenerator == 53) + break; + } + + if(gen == gen_end) + { + gen = sfont->igen + zone->mGenIdx; + gen_end = sfont->igen + (zone+1)->mGenIdx; + for(;gen != gen_end;gen++) + GenModList_insertGen(&gzone, gen, AL_FALSE); + + mod = sfont->imod + zone->mModIdx; + mod_end = sfont->imod + (zone+1)->mModIdx; + for(;mod != mod_end;mod++) + GenModList_insertMod(&gzone, mod); + + zone++; + } + } + + temp = realloc(*sounds, (zone_end-zone + *sounds_size)*sizeof((*sounds)[0])); + if(!temp) + { + ERR("Failed reallocating fontsound storage to %d elements (from %d)\n", + (ALsizei)(zone_end-zone) + *sounds_size, *sounds_size); + return; + } + *sounds = temp; + for(;zone != zone_end;zone++) + { + GenModList lzone = GenModList_clone(&gzone); + mod = sfont->imod + zone->mModIdx; + mod_end = sfont->imod + (zone+1)->mModIdx; + for(;mod != mod_end;mod++) + GenModList_insertMod(&lzone, mod); + + gen = sfont->igen + zone->mGenIdx; + gen_end = sfont->igen + (zone+1)->mGenIdx; + for(;gen != gen_end;gen++) + { + if(gen->mGenerator == 53) + { + const SampleHeader *samp; + ALfontsound *sound; + + if(gen->mAmount >= sfont->shdr_size-1) + { + ERR("Generator %ld has invalid sample ID (%d of %d)\n", + (long)(gen-sfont->igen), gen->mAmount, sfont->shdr_size-1); + break; + } + samp = &sfont->shdr[gen->mAmount]; + + gen = VECTOR_ITER_BEGIN(pzone->gens); + gen_end = VECTOR_ITER_END(pzone->gens); + for(;gen != gen_end;gen++) + GenModList_accumGen(&lzone, gen); + + mod = VECTOR_ITER_BEGIN(pzone->mods); + mod_end = VECTOR_ITER_END(pzone->mods); + for(;mod != mod_end;mod++) + GenModList_accumMod(&lzone, mod); + + if(!checkZone(&lzone, preset, inst, samp)) + break; + /* Ignore ROM samples for now. */ + if((samp->mSampleType&0x8000)) + break; + + sound = NewFontsound(context); + (*sounds)[(*sounds_size)++] = sound; + ALfontsound_setPropi(sound, context, AL_BUFFER, buffer->id); + ALfontsound_setPropi(sound, context, AL_SAMPLE_START_SOFT, samp->mStart); + ALfontsound_setPropi(sound, context, AL_SAMPLE_END_SOFT, samp->mEnd); + ALfontsound_setPropi(sound, context, AL_SAMPLE_LOOP_START_SOFT, samp->mStartloop); + ALfontsound_setPropi(sound, context, AL_SAMPLE_LOOP_END_SOFT, samp->mEndloop); + ALfontsound_setPropi(sound, context, AL_SAMPLE_RATE_SOFT, samp->mSampleRate); + ALfontsound_setPropi(sound, context, AL_BASE_KEY_SOFT, (samp->mOriginalKey <= 127) ? samp->mOriginalKey : 60); + ALfontsound_setPropi(sound, context, AL_KEY_CORRECTION_SOFT, samp->mCorrection); + ALfontsound_setPropi(sound, context, AL_SAMPLE_TYPE_SOFT, getSampleType(samp->mSampleType&0x7fff)); + fillZone(sound, context, &lzone); + + break; + } + GenModList_insertGen(&lzone, gen, AL_FALSE); + } + + GenModList_Destruct(&lzone); + } + + GenModList_Destruct(&gzone); +} + +static size_t printStringChunk(Reader *stream, const RiffHdr *chnk, const char *title) +{ + size_t len = 0; + if(chnk->mSize == 0 || (chnk->mSize&1)) + ERR("Invalid "FOURCCFMT" size: %d\n", FOURCCARGS(chnk->mCode), chnk->mSize); + else + { + char *str = calloc(1, chnk->mSize+1); + len = Reader_read(stream, str, chnk->mSize); + + TRACE("%s: %s\n", title, str); + free(str); + } + return len; +} + +ALboolean loadSf2(Reader *stream, ALsoundfont *soundfont, ALCcontext *context) +{ + ALsfpreset **presets = NULL; + ALsizei presets_size = 0; + ALbuffer *buffer = NULL; + ALuint ltype; + Soundfont sfont; + RiffHdr riff; + RiffHdr list; + ALsizei i; + + Soundfont_Construct(&sfont); + + RiffHdr_read(&riff, stream); + if(riff.mCode != FOURCC('R','I','F','F')) + ERROR_GOTO(error, "Invalid Format, expected RIFF got '"FOURCCFMT"'\n", FOURCCARGS(riff.mCode)); + if((ltype=read_le32(stream)) != FOURCC('s','f','b','k')) + ERROR_GOTO(error, "Invalid Format, expected sfbk got '"FOURCCFMT"'\n", FOURCCARGS(ltype)); + + if(READERR(stream) != 0) + ERROR_GOTO(error, "Error reading file header\n"); + + RiffHdr_read(&list, stream); + if(list.mCode != FOURCC('L','I','S','T')) + ERROR_GOTO(error, "Invalid Format, expected LIST (INFO) got '"FOURCCFMT"'\n", FOURCCARGS(list.mCode)); + if((ltype=read_le32(stream)) != FOURCC('I','N','F','O')) + ERROR_GOTO(error, "Invalid Format, expected INFO got '"FOURCCFMT"'\n", FOURCCARGS(ltype)); + list.mSize -= 4; + while(list.mSize > 0 && !READERR(stream)) + { + RiffHdr chnk; + + if(list.mSize < 8) + { + WARN("Unexpected end of INFO list (%u extra bytes)\n", list.mSize); + skip(stream, list.mSize); + list.mSize = 0; + break; + } + + RiffHdr_read(&chnk, stream); + list.mSize -= 8; + if(list.mSize < chnk.mSize) + { + WARN("INFO sub-chunk '"FOURCCFMT"' has %u bytes, but only %u bytes remain\n", + FOURCCARGS(chnk.mCode), chnk.mSize, list.mSize); + skip(stream, list.mSize); + list.mSize = 0; + break; + } + list.mSize -= chnk.mSize; + + if(chnk.mCode == FOURCC('i','f','i','l')) + { + if(chnk.mSize != 4) + ERR("Invalid ifil chunk size: %d\n", chnk.mSize); + else + { + ALushort major = read_le16(stream); + ALushort minor = read_le16(stream); + chnk.mSize -= 4; + + if(major != 2) + ERROR_GOTO(error, "Unsupported SF2 format version: %d.%02d\n", major, minor); + TRACE("SF2 format version: %d.%02d\n", major, minor); + + sfont.ifil = (major<<16) | minor; + } + } + else if(chnk.mCode == FOURCC('i','r','o','m')) + { + if(chnk.mSize == 0 || (chnk.mSize&1)) + ERR("Invalid irom size: %d\n", chnk.mSize); + else + { + free(sfont.irom); + sfont.irom = calloc(1, chnk.mSize+1); + chnk.mSize -= Reader_read(stream, sfont.irom, chnk.mSize); + + TRACE("SF2 ROM ID: %s\n", sfont.irom); + } + } + else + { + static const struct { + ALuint code; + char title[16]; + } listinfos[] = { + { FOURCC('i','s','n','g'), "Engine ID" }, + { FOURCC('I','N','A','M'), "Name" }, + { FOURCC('I','C','R','D'), "Creation Date" }, + { FOURCC('I','E','N','G'), "Creator" }, + { FOURCC('I','P','R','D'), "Product ID" }, + { FOURCC('I','C','O','P'), "Copyright" }, + { FOURCC('I','C','M','T'), "Comment" }, + { FOURCC('I','S','F','T'), "Created With" }, + { 0, "" }, + }; + + for(i = 0;listinfos[i].code;i++) + { + if(listinfos[i].code == chnk.mCode) + { + chnk.mSize -= printStringChunk(stream, &chnk, listinfos[i].title); + break; + } + } + if(!listinfos[i].code) + TRACE("Skipping INFO sub-chunk '"FOURCCFMT"' (%u bytes)\n", FOURCCARGS(chnk.mCode), chnk.mSize); + } + skip(stream, chnk.mSize); + } + + if(READERR(stream) != 0) + ERROR_GOTO(error, "Error reading INFO chunk\n"); + if(sfont.ifil == 0) + ERROR_GOTO(error, "Missing ifil sub-chunk\n"); + + RiffHdr_read(&list, stream); + if(list.mCode != FOURCC('L','I','S','T')) + ERROR_GOTO(error, "Invalid Format, expected LIST (sdta) got '"FOURCCFMT"'\n", FOURCCARGS(list.mCode)); + if((ltype=read_le32(stream)) != FOURCC('s','d','t','a')) + ERROR_GOTO(error, "Invalid Format, expected sdta got '"FOURCCFMT"'\n", FOURCCARGS(ltype)); + list.mSize -= 4; + { + ALbyte *ptr; + RiffHdr smpl; + ALenum err; + + RiffHdr_read(&smpl, stream); + if(smpl.mCode != FOURCC('s','m','p','l')) + ERROR_GOTO(error, "Invalid Format, expected smpl got '"FOURCCFMT"'\n", FOURCCARGS(smpl.mCode)); + list.mSize -= 8; + + if(smpl.mSize > list.mSize) + ERROR_GOTO(error, "Invalid Format, sample chunk size mismatch\n"); + list.mSize -= smpl.mSize; + + buffer = NewBuffer(context); + if(!buffer) + SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, error); + /* Sample rate is unimportant, the individual fontsounds will specify it. */ + if((err=LoadData(buffer, 22050, AL_MONO16_SOFT, smpl.mSize/2, UserFmtMono, UserFmtShort, NULL, 1, AL_FALSE)) != AL_NO_ERROR) + SET_ERROR_AND_GOTO(context, err, error); + + ptr = buffer->data; + if(IS_LITTLE_ENDIAN) + smpl.mSize -= Reader_read(stream, ptr, smpl.mSize); + else + { + ALuint total = 0; + while(total < smpl.mSize && !READERR(stream)) + { + ALbyte buf[4096]; + ALuint todo = minu(smpl.mSize-total, sizeof(buf)); + ALuint i; + + smpl.mSize -= Reader_read(stream, buf, todo); + for(i = 0;i < todo;i++) + ptr[total+i] = buf[i^1]; + + total += todo; + } + } + + skip(stream, list.mSize); + } + + if(READERR(stream) != 0) + ERROR_GOTO(error, "Error reading sdta chunk\n"); + + RiffHdr_read(&list, stream); + if(list.mCode != FOURCC('L','I','S','T')) + ERROR_GOTO(error, "Invalid Format, expected LIST (pdta) got '"FOURCCFMT"'\n", FOURCCARGS(list.mCode)); + if((ltype=read_le32(stream)) != FOURCC('p','d','t','a')) + ERROR_GOTO(error, "Invalid Format, expected pdta got '"FOURCCFMT"'\n", FOURCCARGS(ltype)); + + // + RiffHdr_read(&list, stream); + if(list.mCode != FOURCC('p','h','d','r')) + ERROR_GOTO(error, "Invalid Format, expected phdr got '"FOURCCFMT"'\n", FOURCCARGS(list.mCode)); + if((list.mSize%38) != 0 || list.mSize == 0) + ERROR_GOTO(error, "Invalid Format, bad phdr size: %u\n", list.mSize); + sfont.phdr_size = list.mSize/38; + sfont.phdr = calloc(sfont.phdr_size, sizeof(sfont.phdr[0])); + for(i = 0;i < sfont.phdr_size;i++) + PresetHeader_read(&sfont.phdr[i], stream); + + RiffHdr_read(&list, stream); + if(list.mCode != FOURCC('p','b','a','g')) + ERROR_GOTO(error, "Invalid Format, expected pbag got '"FOURCCFMT"'\n", FOURCCARGS(list.mCode)); + if((list.mSize%4) != 0 || list.mSize == 0) + ERROR_GOTO(error, "Invalid Format, bad pbag size: %u\n", list.mSize); + sfont.pbag_size = list.mSize/4; + sfont.pbag = calloc(sfont.pbag_size, sizeof(sfont.pbag[0])); + for(i = 0;i < sfont.pbag_size;i++) + Zone_read(&sfont.pbag[i], stream); + + RiffHdr_read(&list, stream); + if(list.mCode != FOURCC('p','m','o','d')) + ERROR_GOTO(error, "Invalid Format, expected pmod got '"FOURCCFMT"'\n", FOURCCARGS(list.mCode)); + if((list.mSize%10) != 0 || list.mSize == 0) + ERROR_GOTO(error, "Invalid Format, bad pmod size: %u\n", list.mSize); + sfont.pmod_size = list.mSize/10; + sfont.pmod = calloc(sfont.pmod_size, sizeof(sfont.pmod[0])); + for(i = 0;i < sfont.pmod_size;i++) + Modulator_read(&sfont.pmod[i], stream); + + RiffHdr_read(&list, stream); + if(list.mCode != FOURCC('p','g','e','n')) + ERROR_GOTO(error, "Invalid Format, expected pgen got '"FOURCCFMT"'\n", FOURCCARGS(list.mCode)); + if((list.mSize%4) != 0 || list.mSize == 0) + ERROR_GOTO(error, "Invalid Format, bad pgen size: %u\n", list.mSize); + sfont.pgen_size = list.mSize/4; + sfont.pgen = calloc(sfont.pgen_size, sizeof(sfont.pgen[0])); + for(i = 0;i < sfont.pgen_size;i++) + Generator_read(&sfont.pgen[i], stream); + + // + RiffHdr_read(&list, stream); + if(list.mCode != FOURCC('i','n','s','t')) + ERROR_GOTO(error, "Invalid Format, expected inst got '"FOURCCFMT"'\n", FOURCCARGS(list.mCode)); + if((list.mSize%22) != 0 || list.mSize == 0) + ERROR_GOTO(error, "Invalid Format, bad inst size: %u\n", list.mSize); + sfont.inst_size = list.mSize/22; + sfont.inst = calloc(sfont.inst_size, sizeof(sfont.inst[0])); + for(i = 0;i < sfont.inst_size;i++) + InstrumentHeader_read(&sfont.inst[i], stream); + + RiffHdr_read(&list, stream); + if(list.mCode != FOURCC('i','b','a','g')) + ERROR_GOTO(error, "Invalid Format, expected ibag got '"FOURCCFMT"'\n", FOURCCARGS(list.mCode)); + if((list.mSize%4) != 0 || list.mSize == 0) + ERROR_GOTO(error, "Invalid Format, bad ibag size: %u\n", list.mSize); + sfont.ibag_size = list.mSize/4; + sfont.ibag = calloc(sfont.ibag_size, sizeof(sfont.ibag[0])); + for(i = 0;i < sfont.ibag_size;i++) + Zone_read(&sfont.ibag[i], stream); + + RiffHdr_read(&list, stream); + if(list.mCode != FOURCC('i','m','o','d')) + ERROR_GOTO(error, "Invalid Format, expected imod got '"FOURCCFMT"'\n", FOURCCARGS(list.mCode)); + if((list.mSize%10) != 0 || list.mSize == 0) + ERROR_GOTO(error, "Invalid Format, bad imod size: %u\n", list.mSize); + sfont.imod_size = list.mSize/10; + sfont.imod = calloc(sfont.imod_size, sizeof(sfont.imod[0])); + for(i = 0;i < sfont.imod_size;i++) + Modulator_read(&sfont.imod[i], stream); + + RiffHdr_read(&list, stream); + if(list.mCode != FOURCC('i','g','e','n')) + ERROR_GOTO(error, "Invalid Format, expected igen got '"FOURCCFMT"'\n", FOURCCARGS(list.mCode)); + if((list.mSize%4) != 0 || list.mSize == 0) + ERROR_GOTO(error, "Invalid Format, bad igen size: %u\n", list.mSize); + sfont.igen_size = list.mSize/4; + sfont.igen = calloc(sfont.igen_size, sizeof(sfont.igen[0])); + for(i = 0;i < sfont.igen_size;i++) + Generator_read(&sfont.igen[i], stream); + + // + RiffHdr_read(&list, stream); + if(list.mCode != FOURCC('s','h','d','r')) + ERROR_GOTO(error, "Invalid Format, expected shdr got '"FOURCCFMT"'\n", FOURCCARGS(list.mCode)); + if((list.mSize%46) != 0 || list.mSize == 0) + ERROR_GOTO(error, "Invalid Format, bad shdr size: %u\n", list.mSize); + sfont.shdr_size = list.mSize/46; + sfont.shdr = calloc(sfont.shdr_size, sizeof(sfont.shdr[0])); + for(i = 0;i < sfont.shdr_size;i++) + SampleHeader_read(&sfont.shdr[i], stream); + + if(READERR(stream) != 0) + ERROR_GOTO(error, "Error reading pdta chunk\n"); + + if(!ensureFontSanity(&sfont)) + goto error; + + presets = calloc(1, (soundfont->NumPresets+sfont.phdr_size-1)*sizeof(presets[0])); + if(!presets) + ERROR_GOTO(error, "Error allocating presets\n"); + memcpy(presets, soundfont->Presets, soundfont->NumPresets*sizeof(presets[0])); + presets_size = soundfont->NumPresets; + + for(i = 0;i < sfont.phdr_size-1;i++) + { + const Generator *gen, *gen_end; + const Modulator *mod, *mod_end; + const Zone *zone, *zone_end; + ALfontsound **sounds = NULL; + ALsizei sounds_size = 0; + GenModList gzone; + + if(sfont.phdr[i+1].mZoneIdx == sfont.phdr[i].mZoneIdx) + continue; + + GenModList_Construct(&gzone); + zone = sfont.pbag + sfont.phdr[i].mZoneIdx; + zone_end = sfont.pbag + sfont.phdr[i+1].mZoneIdx; + if(zone_end-zone > 1) + { + gen = sfont.pgen + zone->mGenIdx; + gen_end = sfont.pgen + (zone+1)->mGenIdx; + + // If no generators, or last generator is not an instrument, this is a global zone + for(;gen != gen_end;gen++) + { + if(gen->mGenerator == 41) + break; + } + + if(gen == gen_end) + { + gen = sfont.pgen + zone->mGenIdx; + gen_end = sfont.pgen + (zone+1)->mGenIdx; + for(;gen != gen_end;gen++) + GenModList_insertGen(&gzone, gen, AL_TRUE); + + mod = sfont.pmod + zone->mModIdx; + mod_end = sfont.pmod + (zone+1)->mModIdx; + for(;mod != mod_end;mod++) + GenModList_insertMod(&gzone, mod); + + zone++; + } + } + + for(;zone != zone_end;zone++) + { + GenModList lzone = GenModList_clone(&gzone); + + mod = sfont.pmod + zone->mModIdx; + mod_end = sfont.pmod + (zone+1)->mModIdx; + for(;mod != mod_end;mod++) + GenModList_insertMod(&lzone, mod); + + gen = sfont.pgen + zone->mGenIdx; + gen_end = sfont.pgen + (zone+1)->mGenIdx; + for(;gen != gen_end;gen++) + { + if(gen->mGenerator == 41) + { + if(gen->mAmount >= sfont.inst_size-1) + ERR("Generator %ld has invalid instrument ID (%d of %d)\n", + (long)(gen-sfont.pgen), gen->mAmount, sfont.inst_size-1); + else + processInstrument( + &sounds, &sounds_size, context, buffer, &sfont.inst[gen->mAmount], + &sfont.phdr[i], &sfont, &lzone + ); + break; + } + GenModList_insertGen(&lzone, gen, AL_TRUE); + } + GenModList_Destruct(&lzone); + } + + if(sounds_size > 0) + { + ALsizei j; + + presets[presets_size] = NewPreset(context); + presets[presets_size]->Preset = sfont.phdr[i].mPreset; + presets[presets_size]->Bank = sfont.phdr[i].mBank; + + for(j = 0;j < sounds_size;j++) + IncrementRef(&sounds[j]->ref); + sounds = ExchangePtr((XchgPtr*)&presets[presets_size]->Sounds, sounds); + ExchangeInt(&presets[presets_size]->NumSounds, sounds_size); + presets_size++; + } + free(sounds); + + GenModList_Destruct(&gzone); + } + + for(i = soundfont->NumPresets;i < presets_size;i++) + IncrementRef(&presets[i]->ref); + presets = ExchangePtr((XchgPtr*)&soundfont->Presets, presets); + ExchangeInt(&soundfont->NumPresets, presets_size); + + free(presets); + + Soundfont_Destruct(&sfont); + /* If the buffer ends up unused, delete it. */ + if(ReadRef(&buffer->ref) == 0) + { + TRACE("Deleting unused buffer...\n"); + DeleteBuffer(context->Device, buffer); + } + + return AL_TRUE; + +error: + if(presets) + { + ALCdevice *device = context->Device; + for(i = soundfont->NumPresets;i < presets_size;i++) + DeletePreset(device, presets[i]); + free(presets); + } + + Soundfont_Destruct(&sfont); + if(buffer) + DeleteBuffer(context->Device, buffer); + + return AL_FALSE; +} diff --git a/libs/openal-soft/Alc/midi/soft.c b/libs/openal-soft/Alc/midi/soft.c new file mode 100644 index 00000000..6566b411 --- /dev/null +++ b/libs/openal-soft/Alc/midi/soft.c @@ -0,0 +1,140 @@ + +#include "config.h" + +#include +#include +#include +#include + +#include "alMain.h" +#include "alError.h" +#include "evtqueue.h" +#include "alu.h" + +#include "midi/base.h" + + +typedef struct SSynth { + DERIVE_FROM_TYPE(MidiSynth); +} SSynth; + +static void SSynth_mixSamples(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]); + +static void SSynth_Construct(SSynth *self, ALCdevice *device); +static void SSynth_Destruct(SSynth *self); +static DECLARE_FORWARD3(SSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) +static DECLARE_FORWARD1(SSynth, MidiSynth, void, setGain, ALfloat) +static DECLARE_FORWARD(SSynth, MidiSynth, void, stop) +static DECLARE_FORWARD(SSynth, MidiSynth, void, reset) +static void SSynth_update(SSynth *self, ALCdevice *device); +static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]); +DECLARE_DEFAULT_ALLOCATORS(SSynth) +DEFINE_MIDISYNTH_VTABLE(SSynth); + + +static void SSynth_Construct(SSynth *self, ALCdevice *device) +{ + MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device); + SET_VTABLE2(SSynth, MidiSynth, self); +} + +static void SSynth_Destruct(SSynth* UNUSED(self)) +{ +} + + +static void SSynth_update(SSynth* UNUSED(self), ALCdevice* UNUSED(device)) +{ +} + + +static void SSynth_mixSamples(SSynth* UNUSED(self), ALuint UNUSED(SamplesToDo), ALfloatBUFFERSIZE *restrict UNUSED(DryBuffer)) +{ +} + + +static void SSynth_processQueue(SSynth *self, ALuint64 time) +{ + EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue; + + while(queue->pos < queue->size && queue->events[queue->pos].time <= time) + queue->pos++; +} + +static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]) +{ + MidiSynth *synth = STATIC_CAST(MidiSynth, self); + ALenum state = synth->State; + ALuint64 curtime; + ALuint total = 0; + + if(state == AL_INITIAL) + return; + if(state != AL_PLAYING) + { + SSynth_mixSamples(self, SamplesToDo, DryBuffer); + return; + } + + curtime = MidiSynth_getTime(synth); + while(total < SamplesToDo) + { + ALuint64 time, diff; + ALint tonext; + + time = MidiSynth_getNextEvtTime(synth); + diff = maxu64(time, curtime) - curtime; + if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX) + { + /* If there's no pending event, or if it's more than 1 second + * away, do as many samples as we can. */ + tonext = INT_MAX; + } + else + { + /* Figure out how many samples until the next event. */ + tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES); + tonext -= total; + /* For efficiency reasons, try to mix a multiple of 64 samples + * (~1ms @ 44.1khz) before processing the next event. */ + tonext = (tonext+63) & ~63; + } + + if(tonext > 0) + { + ALuint todo = mini(tonext, SamplesToDo-total); + SSynth_mixSamples(self, todo, DryBuffer); + total += todo; + tonext -= todo; + } + if(total < SamplesToDo && tonext <= 0) + SSynth_processQueue(self, time); + } + + synth->SamplesDone += SamplesToDo; + synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES; + synth->SamplesDone %= synth->SampleRate; +} + + +MidiSynth *SSynth_create(ALCdevice *device) +{ + SSynth *synth; + + /* This option is temporary. Once this synth is in a more usable state, a + * more generic selector should be used. */ + if(!GetConfigValueBool("midi", "internal-synth", 0)) + { + TRACE("Not using internal MIDI synth\n"); + return NULL; + } + + synth = SSynth_New(sizeof(*synth)); + if(!synth) + { + ERR("Failed to allocate SSynth\n"); + return NULL; + } + SSynth_Construct(synth, device); + return STATIC_CAST(MidiSynth, synth); +} diff --git a/libs/openal-soft/Alc/mixer.c b/libs/openal-soft/Alc/mixer.c new file mode 100644 index 00000000..f3b8f599 --- /dev/null +++ b/libs/openal-soft/Alc/mixer.c @@ -0,0 +1,510 @@ +/** + * 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 "mixer_defs.h" + + +extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size); + + +static inline HrtfMixerFunc SelectHrtfMixer(void) +{ +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return MixHrtf_SSE; +#endif +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return MixHrtf_Neon; +#endif + + return MixHrtf_C; +} + +static inline MixerFunc SelectMixer(void) +{ +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return Mix_SSE; +#endif +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return Mix_Neon; +#endif + + return Mix_C; +} + +static inline ResamplerFunc SelectResampler(enum Resampler Resampler, ALuint increment) +{ + if(increment == FRACTIONONE) + return Resample_copy32_C; + switch(Resampler) + { + case PointResampler: + return Resample_point32_C; + case LinearResampler: +#ifdef HAVE_SSE4_1 + if((CPUCapFlags&CPU_CAP_SSE4_1)) + return Resample_lerp32_SSE41; +#endif +#ifdef HAVE_SSE2 + if((CPUCapFlags&CPU_CAP_SSE2)) + return Resample_lerp32_SSE2; +#endif + return Resample_lerp32_C; + case CubicResampler: + return Resample_cubic32_C; + case ResamplerMax: + /* Shouldn't happen */ + break; + } + + return Resample_point32_C; +} + + +static inline ALfloat Sample_ALbyte(ALbyte val) +{ return val * (1.0f/127.0f); } + +static inline ALfloat Sample_ALshort(ALshort val) +{ return val * (1.0f/32767.0f); } + +static inline ALfloat Sample_ALfloat(ALfloat val) +{ return val; } + +#define DECL_TEMPLATE(T) \ +static void Load_##T(ALfloat *dst, const T *src, ALuint srcstep, ALuint samples)\ +{ \ + ALuint i; \ + for(i = 0;i < samples;i++) \ + dst[i] = Sample_##T(src[i*srcstep]); \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALfloat) + +#undef DECL_TEMPLATE + +static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum FmtType srctype, ALuint samples) +{ + switch(srctype) + { + case FmtByte: + Load_ALbyte(dst, src, srcstep, samples); + break; + case FmtShort: + Load_ALshort(dst, src, srcstep, samples); + break; + case FmtFloat: + Load_ALfloat(dst, src, srcstep, samples); + break; + } +} + +static void SilenceSamples(ALfloat *dst, ALuint samples) +{ + ALuint i; + for(i = 0;i < samples;i++) + dst[i] = 0.0f; +} + + +static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter, + ALfloat *restrict dst, const ALfloat *restrict src, + ALuint numsamples, enum ActiveFilters type) +{ + ALuint i; + switch(type) + { + case AF_None: + break; + + case AF_LowPass: + ALfilterState_process(lpfilter, dst, src, numsamples); + return dst; + case AF_HighPass: + ALfilterState_process(hpfilter, dst, src, numsamples); + return dst; + + case AF_BandPass: + for(i = 0;i < numsamples;) + { + ALfloat temp[64]; + ALuint todo = minu(64, numsamples-i); + + ALfilterState_process(lpfilter, temp, src+i, todo); + ALfilterState_process(hpfilter, dst+i, temp, todo); + i += todo; + } + return dst; + } + return src; +} + + +ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo) +{ + MixerFunc Mix; + HrtfMixerFunc HrtfMix; + ResamplerFunc Resample; + ALsource *Source = src->Source; + ALbufferlistitem *BufferListItem; + ALuint DataPosInt, DataPosFrac; + ALboolean Looping; + ALuint increment; + enum Resampler Resampler; + ALenum State; + ALuint OutPos; + ALuint NumChannels; + ALuint SampleSize; + ALint64 DataSize64; + ALuint chan, j; + + /* Get source info */ + State = Source->state; + BufferListItem = ATOMIC_LOAD(&Source->current_buffer); + DataPosInt = Source->position; + DataPosFrac = Source->position_fraction; + Looping = Source->Looping; + increment = src->Step; + Resampler = (increment==FRACTIONONE) ? PointResampler : Source->Resampler; + NumChannels = Source->NumChannels; + SampleSize = Source->SampleSize; + + Mix = SelectMixer(); + HrtfMix = SelectHrtfMixer(); + Resample = SelectResampler(Resampler, increment); + + OutPos = 0; + do { + const ALuint BufferPrePadding = ResamplerPrePadding[Resampler]; + const ALuint BufferPadding = ResamplerPadding[Resampler]; + ALuint SrcBufferSize, DstBufferSize; + + /* Figure out how many buffer samples will be needed */ + DataSize64 = SamplesToDo-OutPos; + DataSize64 *= increment; + DataSize64 += DataPosFrac+FRACTIONMASK; + DataSize64 >>= FRACTIONBITS; + DataSize64 += BufferPadding+BufferPrePadding; + + SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE); + + /* Figure out how many samples we can actually mix from this. */ + DataSize64 = SrcBufferSize; + DataSize64 -= BufferPadding+BufferPrePadding; + DataSize64 <<= FRACTIONBITS; + DataSize64 -= DataPosFrac; + + DstBufferSize = (ALuint)((DataSize64+(increment-1)) / increment); + DstBufferSize = minu(DstBufferSize, (SamplesToDo-OutPos)); + + /* Some mixers like having a multiple of 4, so try to give that unless + * this is the last update. */ + if(OutPos+DstBufferSize < SamplesToDo) + DstBufferSize &= ~3; + + for(chan = 0;chan < NumChannels;chan++) + { + const ALfloat *ResampledData; + ALfloat *SrcData = Device->SourceData; + ALuint SrcDataSize = 0; + + if(Source->SourceType == AL_STATIC) + { + const ALbuffer *ALBuffer = BufferListItem->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; + else + { + DataSize = BufferPrePadding - DataPosInt; + DataSize = minu(SrcBufferSize - SrcDataSize, DataSize); + + SilenceSamples(&SrcData[SrcDataSize], DataSize); + SrcDataSize += DataSize; + + pos = 0; + } + + /* Copy what's left to play in the source buffer, and clear the + * rest of the temp buffer */ + DataSize = minu(SrcBufferSize - SrcDataSize, ALBuffer->SampleLen - pos); + + LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize], + NumChannels, ALBuffer->FmtType, DataSize); + SrcDataSize += DataSize; + + SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize); + SrcDataSize += SrcBufferSize - SrcDataSize; + } + 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; + } + else if(DataPosInt >= BufferPrePadding) + pos = DataPosInt - BufferPrePadding; + else + { + DataSize = BufferPrePadding - DataPosInt; + DataSize = minu(SrcBufferSize - SrcDataSize, DataSize); + + SilenceSamples(&SrcData[SrcDataSize], DataSize); + SrcDataSize += DataSize; + + pos = 0; + } + + /* Copy what's left of this loop iteration, then copy repeats + * of the loop section */ + DataSize = LoopEnd - pos; + DataSize = minu(SrcBufferSize - SrcDataSize, DataSize); + + LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize], + NumChannels, ALBuffer->FmtType, DataSize); + SrcDataSize += DataSize; + + DataSize = LoopEnd-LoopStart; + while(SrcBufferSize > SrcDataSize) + { + DataSize = minu(SrcBufferSize - SrcDataSize, DataSize); + + LoadSamples(&SrcData[SrcDataSize], &Data[(LoopStart*NumChannels + chan)*SampleSize], + NumChannels, ALBuffer->FmtType, DataSize); + SrcDataSize += DataSize; + } + } + } + else + { + /* Crawl the buffer queue to fill in the temp buffer */ + ALbufferlistitem *tmpiter = BufferListItem; + ALuint pos; + + if(DataPosInt >= BufferPrePadding) + pos = DataPosInt - BufferPrePadding; + else + { + pos = BufferPrePadding - DataPosInt; + while(pos > 0) + { + ALbufferlistitem *prev; + if((prev=tmpiter->prev) != NULL) + tmpiter = prev; + else if(Looping) + { + while(tmpiter->next) + tmpiter = tmpiter->next; + } + else + { + ALuint DataSize = minu(SrcBufferSize - SrcDataSize, pos); + + SilenceSamples(&SrcData[SrcDataSize], DataSize); + SrcDataSize += DataSize; + + pos = 0; + break; + } + + if(tmpiter->buffer) + { + if((ALuint)tmpiter->buffer->SampleLen > pos) + { + pos = tmpiter->buffer->SampleLen - pos; + break; + } + pos -= tmpiter->buffer->SampleLen; + } + } + } + + while(tmpiter && SrcBufferSize > SrcDataSize) + { + const ALbuffer *ALBuffer; + if((ALBuffer=tmpiter->buffer) != NULL) + { + const ALubyte *Data = ALBuffer->data; + ALuint DataSize = ALBuffer->SampleLen; + + /* Skip the data already played */ + if(DataSize <= pos) + pos -= DataSize; + else + { + Data += (pos*NumChannels + chan)*SampleSize; + DataSize -= pos; + pos -= pos; + + DataSize = minu(SrcBufferSize - SrcDataSize, DataSize); + LoadSamples(&SrcData[SrcDataSize], Data, NumChannels, + ALBuffer->FmtType, DataSize); + SrcDataSize += DataSize; + } + } + tmpiter = tmpiter->next; + if(!tmpiter && Looping) + tmpiter = ATOMIC_LOAD(&Source->queue); + else if(!tmpiter) + { + SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize); + SrcDataSize += SrcBufferSize - SrcDataSize; + } + } + } + + /* Now resample, then filter and mix to the appropriate outputs. */ + ResampledData = Resample( + &SrcData[BufferPrePadding], DataPosFrac, increment, + Device->ResampledData, DstBufferSize + ); + { + DirectParams *parms = &src->Direct; + const ALfloat *samples; + + samples = DoFilters( + &parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass, + Device->FilteredData, ResampledData, DstBufferSize, + parms->Filters[chan].ActiveType + ); + if(!src->IsHrtf) + Mix(samples, MaxChannels, parms->OutBuffer, parms->Mix.Gains[chan], + parms->Counter, OutPos, DstBufferSize); + else + HrtfMix(parms->OutBuffer, samples, parms->Counter, src->Offset, + OutPos, parms->Mix.Hrtf.IrSize, &parms->Mix.Hrtf.Params[chan], + &parms->Mix.Hrtf.State[chan], DstBufferSize); + } + + for(j = 0;j < Device->NumAuxSends;j++) + { + SendParams *parms = &src->Send[j]; + const ALfloat *samples; + + if(!parms->OutBuffer) + continue; + + samples = DoFilters( + &parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass, + Device->FilteredData, ResampledData, DstBufferSize, + parms->Filters[chan].ActiveType + ); + Mix(samples, 1, parms->OutBuffer, &parms->Gain, + parms->Counter, OutPos, DstBufferSize); + } + } + /* Update positions */ + DataPosFrac += increment*DstBufferSize; + DataPosInt += DataPosFrac>>FRACTIONBITS; + DataPosFrac &= FRACTIONMASK; + + OutPos += DstBufferSize; + src->Offset += DstBufferSize; + src->Direct.Counter = maxu(src->Direct.Counter, DstBufferSize) - DstBufferSize; + for(j = 0;j < Device->NumAuxSends;j++) + src->Send[j].Counter = maxu(src->Send[j].Counter, DstBufferSize) - DstBufferSize; + + /* Handle looping sources */ + while(1) + { + const ALbuffer *ALBuffer; + ALuint DataSize = 0; + ALuint LoopStart = 0; + ALuint LoopEnd = 0; + + if((ALBuffer=BufferListItem->buffer) != NULL) + { + DataSize = ALBuffer->SampleLen; + LoopStart = ALBuffer->LoopStart; + LoopEnd = ALBuffer->LoopEnd; + if(LoopEnd > DataPosInt) + break; + } + + if(Looping && Source->SourceType == AL_STATIC) + { + assert(LoopEnd > LoopStart); + DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart; + break; + } + + if(DataSize > DataPosInt) + break; + + if(!(BufferListItem=BufferListItem->next)) + { + if(Looping) + BufferListItem = ATOMIC_LOAD(&Source->queue); + else + { + State = AL_STOPPED; + BufferListItem = NULL; + DataPosInt = 0; + DataPosFrac = 0; + break; + } + } + + DataPosInt -= DataSize; + } + } while(State == AL_PLAYING && OutPos < SamplesToDo); + + /* Update source info */ + Source->state = State; + ATOMIC_STORE(&Source->current_buffer, BufferListItem); + Source->position = DataPosInt; + Source->position_fraction = DataPosFrac; +} diff --git a/libs/openal-soft/Alc/mixer_c.c b/libs/openal-soft/Alc/mixer_c.c new file mode 100644 index 00000000..f3a229e5 --- /dev/null +++ b/libs/openal-soft/Alc/mixer_c.c @@ -0,0 +1,126 @@ +#include "config.h" + +#include + +#include "alMain.h" +#include "alu.h" +#include "alSource.h" +#include "alAuxEffectSlot.h" + + +static inline ALfloat point32(const ALfloat *vals, ALuint UNUSED(frac)) +{ return vals[0]; } +static inline ALfloat lerp32(const ALfloat *vals, ALuint frac) +{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); } +static inline ALfloat cubic32(const ALfloat *vals, ALuint frac) +{ return cubic(vals[-1], vals[0], vals[1], vals[2], frac * (1.0f/FRACTIONONE)); } + +const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint UNUSED(frac), + ALuint increment, ALfloat *restrict dst, ALuint numsamples) +{ + assert(increment==FRACTIONONE); +#if defined(HAVE_SSE) || defined(HAVE_NEON) + /* Avoid copying the source data if it's aligned like the destination. */ + if((((intptr_t)src)&15) == (((intptr_t)dst)&15)) + return src; +#endif + memcpy(dst, src, numsamples*sizeof(ALfloat)); + return dst; +} + +#define DECL_TEMPLATE(Sampler) \ +const ALfloat *Resample_##Sampler##_C(const ALfloat *src, ALuint frac, \ + ALuint increment, ALfloat *restrict dst, ALuint numsamples) \ +{ \ + ALuint i; \ + for(i = 0;i < numsamples;i++) \ + { \ + dst[i] = Sampler(src, frac); \ + \ + frac += increment; \ + src += frac>>FRACTIONBITS; \ + frac &= FRACTIONMASK; \ + } \ + return dst; \ +} + +DECL_TEMPLATE(point32) +DECL_TEMPLATE(lerp32) +DECL_TEMPLATE(cubic32) + +#undef DECL_TEMPLATE + + +void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples) +{ + ALuint i; + for(i = 0;i < numsamples;i++) + *(dst++) = ALfilterState_processSingle(filter, *(src++)); +} + + +static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2], + const ALuint IrSize, + ALfloat (*restrict Coeffs)[2], + const ALfloat (*restrict CoeffStep)[2], + ALfloat left, ALfloat right) +{ + ALuint c; + for(c = 0;c < IrSize;c++) + { + const ALuint off = (Offset+c)&HRIR_MASK; + Values[off][0] += Coeffs[c][0] * left; + Values[off][1] += Coeffs[c][1] * right; + Coeffs[c][0] += CoeffStep[c][0]; + Coeffs[c][1] += CoeffStep[c][1]; + } +} + +static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], + const ALuint IrSize, + ALfloat (*restrict Coeffs)[2], + ALfloat left, ALfloat right) +{ + ALuint c; + for(c = 0;c < IrSize;c++) + { + const ALuint off = (Offset+c)&HRIR_MASK; + Values[off][0] += Coeffs[c][0] * left; + Values[off][1] += Coeffs[c][1] * right; + } +} + +#define SUFFIX C +#include "mixer_inc.c" +#undef SUFFIX + + +void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], + MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize) +{ + ALfloat gain, step; + ALuint c; + + for(c = 0;c < OutChans;c++) + { + ALuint pos = 0; + gain = Gains[c].Current; + step = Gains[c].Step; + if(step != 1.0f && Counter > 0) + { + for(;pos < BufferSize && pos < Counter;pos++) + { + OutBuffer[c][OutPos+pos] += data[pos]*gain; + gain *= step; + } + if(pos == Counter) + gain = Gains[c].Target; + Gains[c].Current = gain; + } + + if(!(gain > GAIN_SILENCE_THRESHOLD)) + continue; + for(;pos < BufferSize;pos++) + OutBuffer[c][OutPos+pos] += data[pos]*gain; + } +} diff --git a/libs/openal-soft/Alc/mixer_defs.h b/libs/openal-soft/Alc/mixer_defs.h new file mode 100644 index 00000000..c1500ed2 --- /dev/null +++ b/libs/openal-soft/Alc/mixer_defs.h @@ -0,0 +1,65 @@ +#ifndef MIXER_DEFS_H +#define MIXER_DEFS_H + +#include "AL/alc.h" +#include "AL/al.h" +#include "alMain.h" +#include "alu.h" + +struct MixGains; + +struct HrtfParams; +struct HrtfState; + +/* C resamplers */ +const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); +const ALfloat *Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); +const ALfloat *Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); +const ALfloat *Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); + + +/* C mixers */ +void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, + const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate, + ALuint BufferSize); +void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], + struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize); + +/* SSE mixers */ +void MixHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, + const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate, + ALuint BufferSize); +void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], + struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize); + +/* SSE resamplers */ +inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size) +{ + ALuint i; + + pos_arr[0] = 0; + frac_arr[0] = frac; + for(i = 1;i < size;i++) + { + ALuint frac_tmp = frac_arr[i-1] + increment; + pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS); + frac_arr[i] = frac_tmp&FRACTIONMASK; + } +} + +const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment, + ALfloat *restrict dst, ALuint numsamples); +const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment, + ALfloat *restrict dst, ALuint numsamples); + +/* Neon mixers */ +void MixHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, + const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate, + ALuint BufferSize); +void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], + struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize); + +#endif /* MIXER_DEFS_H */ diff --git a/libs/openal-soft/Alc/mixer_inc.c b/libs/openal-soft/Alc/mixer_inc.c new file mode 100644 index 00000000..ab6f32c5 --- /dev/null +++ b/libs/openal-soft/Alc/mixer_inc.c @@ -0,0 +1,93 @@ +#include "config.h" + +#include "alMain.h" +#include "alSource.h" + +#include "hrtf.h" +#include "mixer_defs.h" +#include "align.h" + + +#define REAL_MERGE(a,b) a##b +#define MERGE(a,b) REAL_MERGE(a,b) + +#define MixHrtf MERGE(MixHrtf_,SUFFIX) + + +static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2], + const ALuint irSize, + ALfloat (*restrict Coeffs)[2], + const ALfloat (*restrict CoeffStep)[2], + ALfloat left, ALfloat right); +static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], + const ALuint irSize, + ALfloat (*restrict Coeffs)[2], + ALfloat left, ALfloat right); + + +void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, + ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, + const HrtfParams *hrtfparams, HrtfState *hrtfstate, ALuint BufferSize) +{ + alignas(16) ALfloat Coeffs[HRIR_LENGTH][2]; + ALuint Delay[2]; + ALfloat left, right; + ALuint pos; + ALuint c; + + for(c = 0;c < IrSize;c++) + { + Coeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter); + Coeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter); + } + Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter); + Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter); + + for(pos = 0;pos < BufferSize && pos < Counter;pos++) + { + hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos]; + left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK], + hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK], + (Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE)); + right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK], + hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK], + (Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE)); + + Delay[0] += hrtfparams->DelayStep[0]; + Delay[1] += hrtfparams->DelayStep[1]; + + hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f; + hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f; + Offset++; + + ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->CoeffStep, left, right); + OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0]; + OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1]; + OutPos++; + } + + Delay[0] >>= HRTFDELAY_BITS; + Delay[1] >>= HRTFDELAY_BITS; + for(;pos < BufferSize;pos++) + { + hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos]; + left = hrtfstate->History[(Offset-Delay[0])&SRC_HISTORY_MASK]; + right = hrtfstate->History[(Offset-Delay[1])&SRC_HISTORY_MASK]; + + hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f; + hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f; + Offset++; + + ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right); + OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0]; + OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1]; + + OutPos++; + } +} + + +#undef MixHrtf + +#undef MERGE +#undef REAL_MERGE diff --git a/libs/openal-soft/Alc/mixer_neon.c b/libs/openal-soft/Alc/mixer_neon.c new file mode 100644 index 00000000..7b6da2b9 --- /dev/null +++ b/libs/openal-soft/Alc/mixer_neon.c @@ -0,0 +1,118 @@ +#include "config.h" + +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "alMain.h" +#include "alu.h" +#include "hrtf.h" + + +static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2], + const ALuint IrSize, + ALfloat (*restrict Coeffs)[2], + const ALfloat (*restrict CoeffStep)[2], + ALfloat left, ALfloat right) +{ + 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 < IrSize;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]); + float32x4_t deltas = vld1q_f32(&CoeffStep[c][0]); + + vals = vmlaq_f32(vals, coefs, leftright4); + coefs = vaddq_f32(coefs, deltas); + + vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals)); + vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals)); + vst1q_f32(&Coeffs[c][0], coefs); + } +} + +static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], + const ALuint IrSize, + ALfloat (*restrict Coeffs)[2], + ALfloat left, ALfloat right) +{ + ALuint c; + float32x4_t leftright4; + { + 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 < IrSize;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)); + } +} + + +#define SUFFIX Neon +#include "mixer_inc.c" +#undef SUFFIX + + +void MixDirect_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], + MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize) +{ + ALfloat gain, step; + float32x4_t gain4; + ALuint c; + + for(c = 0;c < OutChans;c++) + { + ALuint pos = 0; + gain = Gains[c].Current; + step = Gains[c].Step; + if(step != 1.0f && Counter > 0) + { + for(;pos < BufferSize && pos < Counter;pos++) + { + OutBuffer[c][OutPos+pos] += data[pos]*gain; + gain *= step; + } + if(pos == Counter) + gain = Gains[c].Target; + Gains[c].Current = gain; + /* Mix until pos is aligned with 4 or the mix is done. */ + for(;pos < BufferSize && (pos&3) != 0;pos++) + OutBuffer[c][OutPos+pos] += data[pos]*gain; + } + + if(!(gain > GAIN_SILENCE_THRESHOLD)) + continue; + gain4 = vdupq_n_f32(gain); + for(;BufferSize-pos > 3;pos += 4) + { + const float32x4_t val4 = vld1q_f32(&data[pos]); + float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]); + dry4 = vaddq_f32(dry4, vmulq_f32(val4, gain4)); + vst1q_f32(&OutBuffer[c][OutPos+pos], dry4); + } + for(;pos < BufferSize;pos++) + OutBuffer[c][OutPos+pos] += data[pos]*gain; + } +} diff --git a/libs/openal-soft/Alc/mixer_sse.c b/libs/openal-soft/Alc/mixer_sse.c new file mode 100644 index 00000000..970619ec --- /dev/null +++ b/libs/openal-soft/Alc/mixer_sse.c @@ -0,0 +1,202 @@ +#include "config.h" + +#ifdef IN_IDE_PARSER +/* KDevelop's parser won't recognize these defines that get added by the -msse + * switch used to compile this source. Without them, xmmintrin.h fails to + * declare anything. */ +#define __MMX__ +#define __SSE__ +#endif +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "alMain.h" +#include "alu.h" + +#include "alSource.h" +#include "alAuxEffectSlot.h" +#include "mixer_defs.h" + + +static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2], + const ALuint IrSize, + ALfloat (*restrict Coeffs)[2], + const ALfloat (*restrict CoeffStep)[2], + ALfloat left, ALfloat right) +{ + const __m128 lrlr = _mm_setr_ps(left, right, left, right); + __m128 coeffs, deltas, imp0, imp1; + __m128 vals = _mm_setzero_ps(); + ALuint i; + + if((Offset&1)) + { + const ALuint o0 = Offset&HRIR_MASK; + const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK; + + coeffs = _mm_load_ps(&Coeffs[0][0]); + deltas = _mm_load_ps(&CoeffStep[0][0]); + vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]); + imp0 = _mm_mul_ps(lrlr, coeffs); + coeffs = _mm_add_ps(coeffs, deltas); + vals = _mm_add_ps(imp0, vals); + _mm_store_ps(&Coeffs[0][0], coeffs); + _mm_storel_pi((__m64*)&Values[o0][0], vals); + for(i = 1;i < IrSize-1;i += 2) + { + const ALuint o2 = (Offset+i)&HRIR_MASK; + + coeffs = _mm_load_ps(&Coeffs[i+1][0]); + deltas = _mm_load_ps(&CoeffStep[i+1][0]); + vals = _mm_load_ps(&Values[o2][0]); + imp1 = _mm_mul_ps(lrlr, coeffs); + coeffs = _mm_add_ps(coeffs, deltas); + imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2)); + vals = _mm_add_ps(imp0, vals); + _mm_store_ps(&Coeffs[i+1][0], coeffs); + _mm_store_ps(&Values[o2][0], vals); + imp0 = imp1; + } + vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]); + imp0 = _mm_movehl_ps(imp0, imp0); + vals = _mm_add_ps(imp0, vals); + _mm_storel_pi((__m64*)&Values[o1][0], vals); + } + else + { + for(i = 0;i < IrSize;i += 2) + { + const ALuint o = (Offset + i)&HRIR_MASK; + + coeffs = _mm_load_ps(&Coeffs[i][0]); + deltas = _mm_load_ps(&CoeffStep[i][0]); + vals = _mm_load_ps(&Values[o][0]); + imp0 = _mm_mul_ps(lrlr, coeffs); + coeffs = _mm_add_ps(coeffs, deltas); + vals = _mm_add_ps(imp0, vals); + _mm_store_ps(&Coeffs[i][0], coeffs); + _mm_store_ps(&Values[o][0], vals); + } + } +} + +static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], + const ALuint IrSize, + ALfloat (*restrict Coeffs)[2], + ALfloat left, ALfloat right) +{ + const __m128 lrlr = _mm_setr_ps(left, right, left, right); + __m128 vals = _mm_setzero_ps(); + __m128 coeffs; + ALuint i; + + if((Offset&1)) + { + const ALuint o0 = Offset&HRIR_MASK; + const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK; + __m128 imp0, imp1; + + coeffs = _mm_load_ps(&Coeffs[0][0]); + vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]); + imp0 = _mm_mul_ps(lrlr, coeffs); + vals = _mm_add_ps(imp0, vals); + _mm_storel_pi((__m64*)&Values[o0][0], vals); + for(i = 1;i < IrSize-1;i += 2) + { + const ALuint o2 = (Offset+i)&HRIR_MASK; + + coeffs = _mm_load_ps(&Coeffs[i+1][0]); + vals = _mm_load_ps(&Values[o2][0]); + imp1 = _mm_mul_ps(lrlr, coeffs); + imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2)); + vals = _mm_add_ps(imp0, vals); + _mm_store_ps(&Values[o2][0], vals); + imp0 = imp1; + } + vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]); + imp0 = _mm_movehl_ps(imp0, imp0); + vals = _mm_add_ps(imp0, vals); + _mm_storel_pi((__m64*)&Values[o1][0], vals); + } + else + { + for(i = 0;i < IrSize;i += 2) + { + const ALuint o = (Offset + i)&HRIR_MASK; + + coeffs = _mm_load_ps(&Coeffs[i][0]); + vals = _mm_load_ps(&Values[o][0]); + vals = _mm_add_ps(vals, _mm_mul_ps(lrlr, coeffs)); + _mm_store_ps(&Values[o][0], vals); + } + } +} + +#define SUFFIX SSE +#include "mixer_inc.c" +#undef SUFFIX + + +void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], + MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize) +{ + ALfloat gain, step; + __m128 gain4, step4; + ALuint c; + + for(c = 0;c < OutChans;c++) + { + ALuint pos = 0; + gain = Gains[c].Current; + step = Gains[c].Step; + if(step != 1.0f && Counter > 0) + { + /* Mix with applying gain steps in aligned multiples of 4. */ + if(BufferSize-pos > 3 && Counter-pos > 3) + { + gain4 = _mm_setr_ps( + gain, + gain * step, + gain * step * step, + gain * step * step * step + ); + step4 = _mm_set1_ps(step * step * step * step); + do { + const __m128 val4 = _mm_load_ps(&data[pos]); + __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]); + dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4)); + gain4 = _mm_mul_ps(gain4, step4); + _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4); + pos += 4; + } while(BufferSize-pos > 3 && Counter-pos > 3); + gain = _mm_cvtss_f32(gain4); + } + /* Mix with applying left over gain steps that aren't aligned multiples of 4. */ + for(;pos < BufferSize && pos < Counter;pos++) + { + OutBuffer[c][OutPos+pos] += data[pos]*gain; + gain *= step; + } + if(pos == Counter) + gain = Gains[c].Target; + Gains[c].Current = gain; + /* Mix until pos is aligned with 4 or the mix is done. */ + for(;pos < BufferSize && (pos&3) != 0;pos++) + OutBuffer[c][OutPos+pos] += data[pos]*gain; + } + + if(!(gain > GAIN_SILENCE_THRESHOLD)) + continue; + gain4 = _mm_set1_ps(gain); + for(;BufferSize-pos > 3;pos += 4) + { + const __m128 val4 = _mm_load_ps(&data[pos]); + __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]); + dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4)); + _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4); + } + for(;pos < BufferSize;pos++) + OutBuffer[c][OutPos+pos] += data[pos]*gain; + } +} diff --git a/libs/openal-soft/Alc/mixer_sse2.c b/libs/openal-soft/Alc/mixer_sse2.c new file mode 100644 index 00000000..7670add3 --- /dev/null +++ b/libs/openal-soft/Alc/mixer_sse2.c @@ -0,0 +1,78 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2014 by Timothy Arceri . + * 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 "alu.h" +#include "mixer_defs.h" + + +const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment, + ALfloat *restrict dst, ALuint numsamples) +{ + const __m128i increment4 = _mm_set1_epi32(increment*4); + const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE); + const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); + alignas(16) union { ALuint i[4]; float f[4]; } pos_; + alignas(16) union { ALuint i[4]; float f[4]; } frac_; + __m128i frac4, pos4; + ALuint pos; + ALuint i; + + InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); + + frac4 = _mm_castps_si128(_mm_load_ps(frac_.f)); + pos4 = _mm_castps_si128(_mm_load_ps(pos_.f)); + + for(i = 0;numsamples-i > 3;i += 4) + { + const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]); + const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]); + + /* val1 + (val2-val1)*mu */ + const __m128 r0 = _mm_sub_ps(val2, val1); + const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4); + const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0)); + + _mm_store_ps(&dst[i], out); + + frac4 = _mm_add_epi32(frac4, increment4); + pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS)); + frac4 = _mm_and_si128(frac4, fracMask4); + + _mm_store_ps(pos_.f, _mm_castsi128_ps(pos4)); + } + + pos = pos_.i[0]; + frac = _mm_cvtsi128_si32(frac4); + + for(;i < numsamples;i++) + { + dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE)); + + frac += increment; + pos += frac>>FRACTIONBITS; + frac &= FRACTIONMASK; + } + return dst; +} diff --git a/libs/openal-soft/Alc/mixer_sse41.c b/libs/openal-soft/Alc/mixer_sse41.c new file mode 100644 index 00000000..8ce8cd90 --- /dev/null +++ b/libs/openal-soft/Alc/mixer_sse41.c @@ -0,0 +1,82 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2014 by Timothy Arceri . + * 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 "alu.h" +#include "mixer_defs.h" + + +const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment, + ALfloat *restrict dst, ALuint numsamples) +{ + const __m128i increment4 = _mm_set1_epi32(increment*4); + const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE); + const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); + alignas(16) union { ALuint i[4]; float f[4]; } pos_; + alignas(16) union { ALuint i[4]; float f[4]; } frac_; + __m128i frac4, pos4; + ALuint pos; + ALuint i; + + InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); + + frac4 = _mm_castps_si128(_mm_load_ps(frac_.f)); + pos4 = _mm_castps_si128(_mm_load_ps(pos_.f)); + + for(i = 0;numsamples-i > 3;i += 4) + { + const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]); + const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]); + + /* val1 + (val2-val1)*mu */ + const __m128 r0 = _mm_sub_ps(val2, val1); + const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4); + const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0)); + + _mm_store_ps(&dst[i], out); + + frac4 = _mm_add_epi32(frac4, increment4); + pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS)); + frac4 = _mm_and_si128(frac4, fracMask4); + + pos_.i[0] = _mm_extract_epi32(pos4, 0); + pos_.i[1] = _mm_extract_epi32(pos4, 1); + pos_.i[2] = _mm_extract_epi32(pos4, 2); + pos_.i[3] = _mm_extract_epi32(pos4, 3); + } + + pos = pos_.i[0]; + frac = _mm_cvtsi128_si32(frac4); + + for(;i < numsamples;i++) + { + dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE)); + + frac += increment; + pos += frac>>FRACTIONBITS; + frac &= FRACTIONMASK; + } + return dst; +} diff --git a/libs/openal-soft/Alc/panning.c b/libs/openal-soft/Alc/panning.c new file mode 100644 index 00000000..30a1e571 --- /dev/null +++ b/libs/openal-soft/Alc/panning.c @@ -0,0 +1,450 @@ +/** + * 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" + +extern inline void SetGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MaxChannels]); + +static void SetSpeakerArrangement(const char *name, ALfloat SpeakerAngle[MaxChannels], + enum Channel Speaker2Chan[MaxChannels], ALint chans) +{ + char *confkey, *next; + char *layout_str; + char *sep, *end; + enum Channel val; + const char *str; + int i; + + if(!ConfigValueStr(NULL, name, &str) && !ConfigValueStr(NULL, "layout", &str)) + return; + + layout_str = strdup(str); + 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) + { + ERR("Malformed speaker key: %s\n", confkey); + continue; + } + + end = sep - 1; + while(isspace(*end) && end != confkey) + end--; + *(++end) = 0; + + if(strcmp(confkey, "fl") == 0 || strcmp(confkey, "front-left") == 0) + val = FrontLeft; + else if(strcmp(confkey, "fr") == 0 || strcmp(confkey, "front-right") == 0) + val = FrontRight; + else if(strcmp(confkey, "fc") == 0 || strcmp(confkey, "front-center") == 0) + val = FrontCenter; + else if(strcmp(confkey, "bl") == 0 || strcmp(confkey, "back-left") == 0) + val = BackLeft; + else if(strcmp(confkey, "br") == 0 || strcmp(confkey, "back-right") == 0) + val = BackRight; + else if(strcmp(confkey, "bc") == 0 || strcmp(confkey, "back-center") == 0) + val = BackCenter; + else if(strcmp(confkey, "sl") == 0 || strcmp(confkey, "side-left") == 0) + val = SideLeft; + else if(strcmp(confkey, "sr") == 0 || strcmp(confkey, "side-right") == 0) + val = SideRight; + 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] = DEG2RAD(angle); + else + ERR("Invalid angle for speaker \"%s\": %ld\n", confkey, angle); + break; + } + } + } + free(layout_str); + layout_str = NULL; + + 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; + } + } +} + + +void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat gains[MaxChannels]) +{ + ALfloat tmpgains[MaxChannels] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + enum Channel Speaker2Chan[MaxChannels]; + ALfloat SpeakerAngle[MaxChannels]; + ALfloat langle, rangle; + ALfloat a; + ALuint i; + + for(i = 0;i < device->NumChan;i++) + Speaker2Chan[i] = device->Speaker2Chan[i]; + for(i = 0;i < device->NumChan;i++) + SpeakerAngle[i] = device->SpeakerAngle[i]; + + /* Some easy special-cases first... */ + if(device->NumChan <= 1 || hwidth >= F_PI) + { + /* Full coverage for all speakers. */ + for(i = 0;i < MaxChannels;i++) + gains[i] = 0.0f; + for(i = 0;i < device->NumChan;i++) + { + enum Channel chan = Speaker2Chan[i]; + gains[chan] = ingain; + } + return; + } + if(hwidth <= 0.0f) + { + /* Infinitely small sound point. */ + for(i = 0;i < MaxChannels;i++) + gains[i] = 0.0f; + for(i = 0;i < device->NumChan-1;i++) + { + if(angle >= SpeakerAngle[i] && angle < SpeakerAngle[i+1]) + { + /* Sound is between speakers i and i+1 */ + a = (angle-SpeakerAngle[i]) / + (SpeakerAngle[i+1]-SpeakerAngle[i]); + gains[Speaker2Chan[i]] = sqrtf(1.0f-a) * ingain; + gains[Speaker2Chan[i+1]] = sqrtf( a) * ingain; + return; + } + } + /* Sound is between last and first speakers */ + if(angle < SpeakerAngle[0]) + angle += F_2PI; + a = (angle-SpeakerAngle[i]) / + (F_2PI + SpeakerAngle[0]-SpeakerAngle[i]); + gains[Speaker2Chan[i]] = sqrtf(1.0f-a) * ingain; + gains[Speaker2Chan[0]] = sqrtf( a) * ingain; + return; + } + + if(fabsf(angle)+hwidth > F_PI) + { + /* The coverage area would go outside of -pi...+pi. Instead, rotate the + * speaker angles so it would be as if angle=0, and keep them wrapped + * within -pi...+pi. */ + if(angle > 0.0f) + { + ALuint done; + ALuint i = 0; + while(i < device->NumChan && device->SpeakerAngle[i]-angle < -F_PI) + i++; + for(done = 0;i < device->NumChan;done++) + { + SpeakerAngle[done] = device->SpeakerAngle[i]-angle; + Speaker2Chan[done] = device->Speaker2Chan[i]; + i++; + } + for(i = 0;done < device->NumChan;i++) + { + SpeakerAngle[done] = device->SpeakerAngle[i]-angle + F_2PI; + Speaker2Chan[done] = device->Speaker2Chan[i]; + done++; + } + } + else + { + /* NOTE: '< device->NumChan' on the iterators is correct here since + * we need to handle index 0. Because the iterators are unsigned, + * they'll underflow and wrap to become 0xFFFFFFFF, which will + * break as expected. */ + ALuint done; + ALuint i = device->NumChan-1; + while(i < device->NumChan && device->SpeakerAngle[i]-angle > F_PI) + i--; + for(done = device->NumChan-1;i < device->NumChan;done--) + { + SpeakerAngle[done] = device->SpeakerAngle[i]-angle; + Speaker2Chan[done] = device->Speaker2Chan[i]; + i--; + } + for(i = device->NumChan-1;done < device->NumChan;i--) + { + SpeakerAngle[done] = device->SpeakerAngle[i]-angle - F_2PI; + Speaker2Chan[done] = device->Speaker2Chan[i]; + done--; + } + } + angle = 0.0f; + } + langle = angle - hwidth; + rangle = angle + hwidth; + + /* First speaker */ + i = 0; + do { + ALuint last = device->NumChan-1; + enum Channel chan = Speaker2Chan[i]; + + if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle) + { + tmpgains[chan] = 1.0f; + continue; + } + + if(SpeakerAngle[i] < langle && SpeakerAngle[i+1] > langle) + { + a = (langle-SpeakerAngle[i]) / + (SpeakerAngle[i+1]-SpeakerAngle[i]); + tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a); + } + if(SpeakerAngle[i] > rangle) + { + a = (F_2PI + rangle-SpeakerAngle[last]) / + (F_2PI + SpeakerAngle[i]-SpeakerAngle[last]); + tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a); + } + else if(SpeakerAngle[last] < rangle) + { + a = (rangle-SpeakerAngle[last]) / + (F_2PI + SpeakerAngle[i]-SpeakerAngle[last]); + tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a); + } + } while(0); + + for(i = 1;i < device->NumChan-1;i++) + { + enum Channel chan = Speaker2Chan[i]; + if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle) + { + tmpgains[chan] = 1.0f; + continue; + } + + if(SpeakerAngle[i] < langle && SpeakerAngle[i+1] > langle) + { + a = (langle-SpeakerAngle[i]) / + (SpeakerAngle[i+1]-SpeakerAngle[i]); + tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a); + } + if(SpeakerAngle[i] > rangle && SpeakerAngle[i-1] < rangle) + { + a = (rangle-SpeakerAngle[i-1]) / + (SpeakerAngle[i]-SpeakerAngle[i-1]); + tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a); + } + } + + /* Last speaker */ + i = device->NumChan-1; + do { + enum Channel chan = Speaker2Chan[i]; + if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle) + { + tmpgains[Speaker2Chan[i]] = 1.0f; + continue; + } + if(SpeakerAngle[i] > rangle && SpeakerAngle[i-1] < rangle) + { + a = (rangle-SpeakerAngle[i-1]) / + (SpeakerAngle[i]-SpeakerAngle[i-1]); + tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a); + } + if(SpeakerAngle[i] < langle) + { + a = (langle-SpeakerAngle[i]) / + (F_2PI + SpeakerAngle[0]-SpeakerAngle[i]); + tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a); + } + else if(SpeakerAngle[0] > langle) + { + a = (F_2PI + langle-SpeakerAngle[i]) / + (F_2PI + SpeakerAngle[0]-SpeakerAngle[i]); + tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a); + } + } while(0); + + for(i = 0;i < device->NumChan;i++) + { + enum Channel chan = device->Speaker2Chan[i]; + gains[chan] = sqrtf(tmpgains[chan]) * ingain; + } +} + + +ALvoid aluInitPanning(ALCdevice *Device) +{ + const char *layoutname = NULL; + enum Channel *Speaker2Chan; + ALfloat *SpeakerAngle; + + Speaker2Chan = Device->Speaker2Chan; + SpeakerAngle = Device->SpeakerAngle; + switch(Device->FmtChans) + { + case DevFmtMono: + Device->NumChan = 1; + Speaker2Chan[0] = FrontCenter; + SpeakerAngle[0] = DEG2RAD(0.0f); + layoutname = NULL; + break; + + case DevFmtStereo: + Device->NumChan = 2; + Speaker2Chan[0] = FrontLeft; + Speaker2Chan[1] = FrontRight; + SpeakerAngle[0] = DEG2RAD(-90.0f); + SpeakerAngle[1] = DEG2RAD( 90.0f); + layoutname = "layout_stereo"; + break; + + case DevFmtQuad: + Device->NumChan = 4; + Speaker2Chan[0] = BackLeft; + Speaker2Chan[1] = FrontLeft; + Speaker2Chan[2] = FrontRight; + Speaker2Chan[3] = BackRight; + SpeakerAngle[0] = DEG2RAD(-135.0f); + SpeakerAngle[1] = DEG2RAD( -45.0f); + SpeakerAngle[2] = DEG2RAD( 45.0f); + SpeakerAngle[3] = DEG2RAD( 135.0f); + layoutname = "layout_quad"; + break; + + case DevFmtX51: + Device->NumChan = 5; + Speaker2Chan[0] = BackLeft; + Speaker2Chan[1] = FrontLeft; + Speaker2Chan[2] = FrontCenter; + Speaker2Chan[3] = FrontRight; + Speaker2Chan[4] = BackRight; + SpeakerAngle[0] = DEG2RAD(-110.0f); + SpeakerAngle[1] = DEG2RAD( -30.0f); + SpeakerAngle[2] = DEG2RAD( 0.0f); + SpeakerAngle[3] = DEG2RAD( 30.0f); + SpeakerAngle[4] = DEG2RAD( 110.0f); + layoutname = "layout_surround51"; + break; + + case DevFmtX51Side: + Device->NumChan = 5; + Speaker2Chan[0] = SideLeft; + Speaker2Chan[1] = FrontLeft; + Speaker2Chan[2] = FrontCenter; + Speaker2Chan[3] = FrontRight; + Speaker2Chan[4] = SideRight; + SpeakerAngle[0] = DEG2RAD(-90.0f); + SpeakerAngle[1] = DEG2RAD(-30.0f); + SpeakerAngle[2] = DEG2RAD( 0.0f); + SpeakerAngle[3] = DEG2RAD( 30.0f); + SpeakerAngle[4] = DEG2RAD( 90.0f); + layoutname = "layout_side51"; + break; + + case DevFmtX61: + Device->NumChan = 6; + Speaker2Chan[0] = SideLeft; + Speaker2Chan[1] = FrontLeft; + Speaker2Chan[2] = FrontCenter; + Speaker2Chan[3] = FrontRight; + Speaker2Chan[4] = SideRight; + Speaker2Chan[5] = BackCenter; + SpeakerAngle[0] = DEG2RAD(-90.0f); + SpeakerAngle[1] = DEG2RAD(-30.0f); + SpeakerAngle[2] = DEG2RAD( 0.0f); + SpeakerAngle[3] = DEG2RAD( 30.0f); + SpeakerAngle[4] = DEG2RAD( 90.0f); + SpeakerAngle[5] = DEG2RAD(180.0f); + layoutname = "layout_surround61"; + break; + + case DevFmtX71: + Device->NumChan = 7; + Speaker2Chan[0] = BackLeft; + Speaker2Chan[1] = SideLeft; + Speaker2Chan[2] = FrontLeft; + Speaker2Chan[3] = FrontCenter; + Speaker2Chan[4] = FrontRight; + Speaker2Chan[5] = SideRight; + Speaker2Chan[6] = BackRight; + SpeakerAngle[0] = DEG2RAD(-150.0f); + SpeakerAngle[1] = DEG2RAD( -90.0f); + SpeakerAngle[2] = DEG2RAD( -30.0f); + SpeakerAngle[3] = DEG2RAD( 0.0f); + SpeakerAngle[4] = DEG2RAD( 30.0f); + SpeakerAngle[5] = DEG2RAD( 90.0f); + SpeakerAngle[6] = DEG2RAD( 150.0f); + layoutname = "layout_surround71"; + break; + } + if(layoutname && Device->Type != Loopback) + SetSpeakerArrangement(layoutname, SpeakerAngle, Speaker2Chan, Device->NumChan); +} diff --git a/libs/openal-soft/Alc/vector.h b/libs/openal-soft/Alc/vector.h new file mode 100644 index 00000000..9f28d0db --- /dev/null +++ b/libs/openal-soft/Alc/vector.h @@ -0,0 +1,87 @@ +#ifndef AL_VECTOR_H +#define AL_VECTOR_H + +#include + +#include + +/* "Base" vector type, designed to alias with the actual vector types. */ +typedef struct vector__s { + ALsizei Capacity; + ALsizei Size; +} *vector_; + +#define TYPEDEF_VECTOR(T, N) typedef struct { \ + ALsizei Capacity; \ + ALsizei Size; \ + T Data[]; \ +} _##N; \ +typedef _##N* N; \ +typedef const _##N* const_##N; + +#define VECTOR(T) struct { \ + ALsizei Capacity; \ + ALsizei Size; \ + T Data[]; \ +}* + +#define VECTOR_INIT(_x) do { (_x) = NULL; } while(0) +#define VECTOR_INIT_STATIC() NULL +#define VECTOR_DEINIT(_x) do { free((_x)); (_x) = NULL; } while(0) + +/* Helper to increase a vector's reserve. Do not call directly. */ +ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count, ALboolean exact); +#define VECTOR_RESERVE(_x, _c) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c), AL_TRUE)) + +ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count); +#define VECTOR_RESIZE(_x, _c) (vector_resize((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c))) + +#define VECTOR_CAPACITY(_x) ((_x) ? (_x)->Capacity : 0) +#define VECTOR_SIZE(_x) ((_x) ? (_x)->Size : 0) + +#define VECTOR_ITER_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL) +#define VECTOR_ITER_END(_x) ((_x) ? (_x)->Data + (_x)->Size : NULL) + +ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend); +#ifdef __GNUC__ +#define TYPE_CHECK(T1, T2) __builtin_types_compatible_p(T1, T2) +#define VECTOR_INSERT(_x, _i, _s, _e) __extension__({ \ + ALboolean _r; \ + static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_i))), "Incompatible insertion iterator"); \ + static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_s))), "Incompatible insertion source type"); \ + static_assert(TYPE_CHECK(__typeof(*(_s)), __typeof(*(_e))), "Incompatible iterator sources"); \ + _r = vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)); \ + _r; \ +}) +#else +#define VECTOR_INSERT(_x, _i, _s, _e) (vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e))) +#endif + +#define VECTOR_PUSH_BACK(_x, _obj) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), VECTOR_SIZE(_x)+1, AL_FALSE) && \ + (((_x)->Data[(_x)->Size++] = (_obj)),AL_TRUE)) +#define VECTOR_POP_BACK(_x) ((void)((_x)->Size--)) + +#define VECTOR_BACK(_x) ((_x)->Data[(_x)->Size-1]) +#define VECTOR_FRONT(_x) ((_x)->Data[0]) + +#define VECTOR_ELEM(_x, _o) ((_x)->Data[(_o)]) + +#define VECTOR_FOR_EACH(_t, _x, _f) do { \ + _t *_iter = VECTOR_ITER_BEGIN((_x)); \ + _t *_end = VECTOR_ITER_END((_x)); \ + for(;_iter != _end;++_iter) \ + _f(_iter); \ +} while(0) + +#define VECTOR_FIND_IF(_i, _t, _x, _f) do { \ + _t *_iter = VECTOR_ITER_BEGIN((_x)); \ + _t *_end = VECTOR_ITER_END((_x)); \ + for(;_iter != _end;++_iter) \ + { \ + if(_f(_iter)) \ + break; \ + } \ + (_i) = _iter; \ +} while(0) + +#endif /* AL_VECTOR_H */ diff --git a/libs/openal-soft/CMakeLists.txt b/libs/openal-soft/CMakeLists.txt new file mode 100644 index 00000000..0087edb2 --- /dev/null +++ b/libs/openal-soft/CMakeLists.txt @@ -0,0 +1,1249 @@ +# CMake build file list for OpenAL + +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +PROJECT(OpenAL) + +IF(COMMAND CMAKE_POLICY) + CMAKE_POLICY(SET CMP0003 NEW) + CMAKE_POLICY(SET CMP0005 NEW) +ENDIF(COMMAND CMAKE_POLICY) + +SET(CMAKE_MODULE_PATH "${OpenAL_SOURCE_DIR}/cmake") + +INCLUDE(CheckFunctionExists) +INCLUDE(CheckLibraryExists) +INCLUDE(CheckSharedFunctionExists) +INCLUDE(CheckIncludeFile) +INCLUDE(CheckIncludeFiles) +INCLUDE(CheckSymbolExists) +INCLUDE(CheckCCompilerFlag) +INCLUDE(CheckCSourceCompiles) +INCLUDE(CheckTypeSize) +include(CheckFileOffsetBits) + + +SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE) + + +OPTION(ALSOFT_DLOPEN "Check for the dlopen API for loading optional libs" ON) + +OPTION(ALSOFT_WERROR "Treat compile warnings as errors" OFF) + +OPTION(ALSOFT_UTILS "Build and install utility programs" ON) +OPTION(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF) + +OPTION(ALSOFT_EXAMPLES "Build and install example programs" ON) + +OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" ON) +OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" ON) + + +IF(WIN32) + SET(LIBNAME OpenAL32) + ADD_DEFINITIONS("-D_WIN32 -D_WIN32_WINNT=0x0502") + + # This option is mainly for static linking OpenAL Soft into another project + # that already defines the IDs. It is up to that project to ensure all + # required IDs are defined. + OPTION(ALSOFT_NO_UID_DEFS "Do not define GUIDs, IIDs, CLSIDs, or PropertyKeys" OFF) + + IF(MINGW) + OPTION(ALSOFT_BUILD_IMPORT_LIB "Build an import .lib using dlltool (requires sed)" ON) + IF(NOT DLLTOOL) + IF(HOST) + SET(DLLTOOL "${HOST}-dlltool") + ELSE() + SET(DLLTOOL "dlltool") + ENDIF() + ENDIF() + ENDIF() +ELSE() + SET(LIBNAME openal) + + # These are needed on non-Windows systems for extra features + ADD_DEFINITIONS(-D_GNU_SOURCE=1 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700) + SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_GNU_SOURCE=1 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700") +ENDIF() + +# Set defines for large file support +CHECK_FILE_OFFSET_BITS() +IF(_FILE_OFFSET_BITS) + ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=${_FILE_OFFSET_BITS}) + SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_FILE_OFFSET_BITS=${_FILE_OFFSET_BITS}") +ENDIF() +ADD_DEFINITIONS(-D_LARGEFILE_SOURCE -D_LARGE_FILES) +SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_LARGEFILE_SOURCE -D_LARGE_FILES") + + +# QNX's gcc do not uses /usr/include and /usr/lib pathes by default +IF ("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX") + ADD_DEFINITIONS("-I/usr/include") + SET(EXTRA_LIBS ${EXTRA_LIBS} -L/usr/lib) +ENDIF() + +IF(NOT LIBTYPE) + SET(LIBTYPE SHARED) +ENDIF() + +SET(LIB_MAJOR_VERSION "1") +SET(LIB_MINOR_VERSION "16") +SET(LIB_REVISION "0") +SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") + +SET(EXPORT_DECL "") +SET(ALIGN_DECL "") + + +CHECK_TYPE_SIZE("long" SIZEOF_LONG) +CHECK_TYPE_SIZE("long long" SIZEOF_LONG_LONG) + + +CHECK_C_COMPILER_FLAG(-std=c99 HAVE_STD_C99) +IF(HAVE_STD_C99) + SET(CMAKE_C_FLAGS "-std=c99 ${CMAKE_C_FLAGS}") +ENDIF() + +# MSVC may need workarounds for C99 restrict and inline +IF(MSVC) + # TODO: Once we truly require C99, these restrict and inline checks should go + # away. + CHECK_C_SOURCE_COMPILES("int *restrict foo; + int main() {return 0;}" HAVE_RESTRICT) + IF(NOT HAVE_RESTRICT) + ADD_DEFINITIONS("-Drestrict=") + SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Drestrict=") + ENDIF() + + CHECK_C_SOURCE_COMPILES("inline void foo(void) { } + int main() {return 0;}" HAVE_INLINE) + IF(NOT HAVE_INLINE) + CHECK_C_SOURCE_COMPILES("__inline void foo(void) { } + int main() {return 0;}" HAVE___INLINE) + IF(NOT HAVE___INLINE) + MESSAGE(FATAL_ERROR "No inline keyword found, please report!") + ENDIF() + + ADD_DEFINITIONS(-Dinline=__inline) + SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Dinline=__inline") + ENDIF() +ENDIF() + +# Make sure we have C99-style inline semantics with GCC (4.3 or newer). +IF(CMAKE_COMPILER_IS_GNUCC) + SET(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") + # Force no inlining for the next test. + SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -fno-inline") + + CHECK_C_SOURCE_COMPILES("extern inline int foo() { return 0; } + int main() {return foo();}" INLINE_IS_C99) + IF(NOT INLINE_IS_C99) + MESSAGE(FATAL_ERROR "Your compiler does not seem to have C99 inline semantics! + Please update your compiler for better C99 compliance.") + ENDIF() + + SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}") +ENDIF() + +# Check if we have C99 variable length arrays +CHECK_C_SOURCE_COMPILES( +"int main(int argc, char *argv[]) + { + volatile int tmp[argc]; + tmp[0] = argv[0][0]; + return tmp[0]; + }" +HAVE_C99_VLA) + +# Check if we have C99 bool +CHECK_C_SOURCE_COMPILES( +"int main(int argc, char *argv[]) + { + volatile _Bool ret; + ret = (argc > 1) ? 1 : 0; + return ret ? -1 : 0; + }" +HAVE_C99_BOOL) + +# Check if we have C11 static_assert +CHECK_C_SOURCE_COMPILES( +"int main() + { + _Static_assert(sizeof(int) == sizeof(int), \"What\"); + return 0; + }" +HAVE_C11_STATIC_ASSERT) + +# Check if we have C11 alignas +CHECK_C_SOURCE_COMPILES( +"_Alignas(16) int foo; + int main() + { + return 0; + }" +HAVE_C11_ALIGNAS) + +# Check if we have C11 _Atomic +CHECK_C_SOURCE_COMPILES( +"#include + int _Atomic foo; + int main() + { + return atomic_load(&foo); + }" +HAVE_C11_ATOMIC) + +# Add definitions, compiler switches, etc. +INCLUDE_DIRECTORIES("${OpenAL_SOURCE_DIR}/include" "${OpenAL_BINARY_DIR}") +IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES("${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc") + IF(WIN32 AND ALSOFT_NO_UID_DEFS) + ADD_DEFINITIONS("-DAL_NO_UID_DEFS") + ENDIF() +ENDIF() + +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) + ADD_DEFINITIONS("/wd4098") + + 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(ALSOFT_WERROR) + ADD_DEFINITIONS(-Werror) + ENDIF() + + # Force enable PIC if available. The static common library will be linked + # into the dynamic openal library, which requires all its code to be + # position-independent, and CMake doesn't automatically enable PIC for + # static library targets (Windows code is always position-independent). + CHECK_C_COMPILER_FLAG(-fPIC HAVE_FPIC_SWITCH) + IF(HAVE_FPIC_SWITCH AND NOT WIN32) + ADD_DEFINITIONS(-fPIC) + 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)") + IF(NOT MINGW) + SET(ALIGN_DECL "__declspec(align(x))") + ELSE() + SET(ALIGN_DECL "__declspec(aligned(x))") + ENDIF() +ELSE() + SET(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") + # Yes GCC, really don't accept visibility modes you don't support + SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror") + + CHECK_C_SOURCE_COMPILES("int foo() __attribute__((visibility(\"protected\"))); + int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY) + IF(HAVE_GCC_PROTECTED_VISIBILITY) + SET(EXPORT_DECL "__attribute__((visibility(\"protected\")))") + ELSE() + CHECK_C_SOURCE_COMPILES("int foo() __attribute__((visibility(\"default\"))); + int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY) + IF(HAVE_GCC_DEFAULT_VISIBILITY) + SET(EXPORT_DECL "__attribute__((visibility(\"default\")))") + ENDIF() + ENDIF() + + IF(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY) + CHECK_C_COMPILER_FLAG(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN_SWITCH) + IF(HAVE_VISIBILITY_HIDDEN_SWITCH) + ADD_DEFINITIONS(-fvisibility=hidden) + ENDIF() + ENDIF() + + CHECK_C_SOURCE_COMPILES("int foo __attribute__((aligned(16))); + int main() {return 0;}" HAVE_ATTRIBUTE_ALIGNED) + IF(HAVE_ATTRIBUTE_ALIGNED) + SET(ALIGN_DECL "__attribute__((aligned(x)))") + ENDIF() + + SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}") +ENDIF() + +SET(SSE_SWITCH "") +SET(SSE2_SWITCH "") +SET(SSE4_1_SWITCH "") +IF(NOT MSVC) + CHECK_C_COMPILER_FLAG(-msse HAVE_MSSE_SWITCH) + IF(HAVE_MSSE_SWITCH) + SET(SSE_SWITCH "-msse") + ENDIF() + CHECK_C_COMPILER_FLAG(-msse2 HAVE_MSSE2_SWITCH) + IF(HAVE_MSSE2_SWITCH) + SET(SSE2_SWITCH "-msse2") + ENDIF() + CHECK_C_COMPILER_FLAG(-msse4.1 HAVE_MSSE4_1_SWITCH) + IF(HAVE_MSSE4_1_SWITCH) + SET(SSE4_1_SWITCH "-msse4.1") + 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(stdbool.h HAVE_STDBOOL_H) +CHECK_INCLUDE_FILE(stdalign.h HAVE_STDALIGN_H) +CHECK_INCLUDE_FILE(malloc.h HAVE_MALLOC_H) +CHECK_INCLUDE_FILE(ftw.h HAVE_FTW_H) +CHECK_INCLUDE_FILE(io.h HAVE_IO_H) +CHECK_INCLUDE_FILE(strings.h HAVE_STRINGS_H) +CHECK_INCLUDE_FILE(cpuid.h HAVE_CPUID_H) +CHECK_INCLUDE_FILE(intrin.h HAVE_INTRIN_H) +CHECK_INCLUDE_FILE(sys/sysconf.h HAVE_SYS_SYSCONF_H) +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() + +IF(HAVE_CPUID_H) + CHECK_C_SOURCE_COMPILES("#include + int main() + { + unsigned int eax, ebx, ecx, edx; + return __get_cpuid(0, eax, ebx, ecx, edx); + }" HAVE_GCC_GET_CPUID) +ENDIF() + +IF(HAVE_INTRIN_H) + CHECK_C_SOURCE_COMPILES("#include + int main() + { + int regs[4]; + __cpuid(regs, 0); + return regs[0]; + }" HAVE_CPUID_INTRINSIC) +ENDIF() + +# Some systems need libm for some of the following math functions to work +CHECK_LIBRARY_EXISTS(m pow "" HAVE_LIBM) +IF(HAVE_LIBM) + SET(EXTRA_LIBS m ${EXTRA_LIBS}) + SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} m) +ENDIF() + + +CHECK_SYMBOL_EXISTS(aligned_alloc stdlib.h HAVE_ALIGNED_ALLOC) +CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) +CHECK_SYMBOL_EXISTS(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) +CHECK_SYMBOL_EXISTS(lrintf math.h HAVE_LRINTF) +IF(NOT HAVE_C99_VLA) + CHECK_SYMBOL_EXISTS(alloca malloc.h HAVE_ALLOCA) + IF(NOT HAVE_ALLOCA) + MESSAGE(FATAL_ERROR "No alloca function found, please report!") + ENDIF() +ENDIF() + +IF(HAVE_FLOAT_H) + CHECK_SYMBOL_EXISTS(_controlfp float.h HAVE__CONTROLFP) + CHECK_SYMBOL_EXISTS(__control87_2 float.h HAVE___CONTROL87_2) +ENDIF() +IF(HAVE_FTW_H) + CHECK_SYMBOL_EXISTS(ftw ftw.h HAVE_FTW) +ENDIF() +IF(HAVE_IO_H) + CHECK_SYMBOL_EXISTS(_wfindfirst io.h HAVE__WFINDFIRST) +ENDIF() + +CHECK_FUNCTION_EXISTS(strtof HAVE_STRTOF) +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_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(ALSOFT_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=0x0502) +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() + + # 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) + + CHECK_C_COMPILER_FLAG(-pthread HAVE_PTHREAD) + IF(HAVE_PTHREAD) + ADD_DEFINITIONS(-pthread) + SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -pthread") + SET(EXTRA_LIBS ${EXTRA_LIBS} -pthread) + ENDIF() + + CHECK_LIBRARY_EXISTS(pthread pthread_create "" HAVE_LIBPTHREAD) + IF(HAVE_LIBPTHREAD) + SET(EXTRA_LIBS pthread ${EXTRA_LIBS}) + ENDIF() + + CHECK_SYMBOL_EXISTS(pthread_setschedparam pthread.h HAVE_PTHREAD_SETSCHEDPARAM) + + CHECK_SYMBOL_EXISTS(pthread_setname_np pthread.h HAVE_PTHREAD_SETNAME_NP) + IF(NOT HAVE_PTHREAD_SETNAME_NP) + CHECK_SYMBOL_EXISTS(pthread_set_name_np pthread.h HAVE_PTHREAD_SET_NAME_NP) + ENDIF() + IF(HAVE_PTHREAD_NP_H) + CHECK_SYMBOL_EXISTS(pthread_mutexattr_setkind_np "pthread.h;pthread_np.h" HAVE_PTHREAD_MUTEXATTR_SETKIND_NP) + ENDIF() + + CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK) + + 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 0x0502 + #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() + + +SET(COMMON_OBJS common/atomic.c + common/rwlock.c + common/threads.c + common/uintmap.c +) +SET(OPENAL_OBJS OpenAL32/alAuxEffectSlot.c + OpenAL32/alBuffer.c + OpenAL32/alEffect.c + OpenAL32/alError.c + OpenAL32/alExtension.c + OpenAL32/alFilter.c + OpenAL32/alFontsound.c + OpenAL32/alListener.c + OpenAL32/alMidi.c + OpenAL32/alPreset.c + OpenAL32/alSoundfont.c + OpenAL32/alSource.c + OpenAL32/alState.c + OpenAL32/alThunk.c + OpenAL32/sample_cvt.c +) +SET(ALC_OBJS Alc/ALc.c + Alc/ALu.c + Alc/alcConfig.c + Alc/alcRing.c + Alc/bs2b.c + Alc/effects/autowah.c + Alc/effects/chorus.c + Alc/effects/compressor.c + Alc/effects/dedicated.c + Alc/effects/distortion.c + Alc/effects/echo.c + Alc/effects/equalizer.c + Alc/effects/flanger.c + Alc/effects/modulator.c + Alc/effects/null.c + Alc/effects/reverb.c + Alc/helpers.c + Alc/hrtf.c + Alc/panning.c + Alc/mixer.c + Alc/mixer_c.c +) + + +SET(CPU_EXTS "Default") +SET(HAVE_SSE 0) +SET(HAVE_SSE2 0) +SET(HAVE_SSE4_1 0) +SET(HAVE_NEON 0) + +SET(HAVE_FLUIDSYNTH 0) + +SET(HAVE_ALSA 0) +SET(HAVE_OSS 0) +SET(HAVE_SOLARIS 0) +SET(HAVE_SNDIO 0) +SET(HAVE_QSA 0) +SET(HAVE_DSOUND 0) +SET(HAVE_MMDEVAPI 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 SSE support +OPTION(ALSOFT_REQUIRE_SSE "Require SSE support" OFF) +CHECK_INCLUDE_FILE(xmmintrin.h HAVE_XMMINTRIN_H "${SSE_SWITCH}") +IF(HAVE_XMMINTRIN_H) + OPTION(ALSOFT_CPUEXT_SSE "Enable SSE support" ON) + IF(ALSOFT_CPUEXT_SSE) + IF(ALIGN_DECL OR HAVE_C11_ALIGNAS) + SET(HAVE_SSE 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_sse.c) + IF(SSE_SWITCH) + SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse.c PROPERTIES + COMPILE_FLAGS "${SSE_SWITCH}") + ENDIF() + SET(CPU_EXTS "${CPU_EXTS}, SSE") + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE) + MESSAGE(FATAL_ERROR "Failed to enabled required SSE CPU extensions") +ENDIF() + +OPTION(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF) +CHECK_INCLUDE_FILE(emmintrin.h HAVE_EMMINTRIN_H "${SSE2_SWITCH}") +IF(HAVE_EMMINTRIN_H) + OPTION(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON) + IF(HAVE_SSE AND ALSOFT_CPUEXT_SSE2) + IF(ALIGN_DECL OR HAVE_C11_ALIGNAS) + SET(HAVE_SSE2 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_sse2.c) + IF(SSE2_SWITCH) + SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse2.c PROPERTIES + COMPILE_FLAGS "${SSE2_SWITCH}") + ENDIF() + SET(CPU_EXTS "${CPU_EXTS}, SSE2") + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2) + MESSAGE(FATAL_ERROR "Failed to enable required SSE2 CPU extensions") +ENDIF() + +OPTION(ALSOFT_REQUIRE_SSE4_1 "Require SSE4.1 support" OFF) +CHECK_INCLUDE_FILE(smmintrin.h HAVE_SMMINTRIN_H "${SSE4_1_SWITCH}") +IF(HAVE_SMMINTRIN_H) + OPTION(ALSOFT_CPUEXT_SSE4_1 "Enable SSE4.1 support" ON) + IF(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE4_1) + IF(ALIGN_DECL OR HAVE_C11_ALIGNAS) + SET(HAVE_SSE4_1 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_sse41.c) + IF(SSE4_1_SWITCH) + SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse41.c PROPERTIES + COMPILE_FLAGS "${SSE4_1_SWITCH}") + ENDIF() + SET(CPU_EXTS "${CPU_EXTS}, SSE4.1") + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_SSE4_1 AND NOT HAVE_SSE4_1) + MESSAGE(FATAL_ERROR "Failed to enable required SSE4.1 CPU extensions") +ENDIF() + +# Check for ARM Neon support +OPTION(ALSOFT_REQUIRE_NEON "Require ARM Neon support" OFF) +CHECK_INCLUDE_FILE(arm_neon.h HAVE_ARM_NEON_H) +IF(HAVE_ARM_NEON_H) + OPTION(ALSOFT_CPUEXT_NEON "Enable ARM Neon support" ON) + IF(ALSOFT_CPUEXT_NEON) + SET(HAVE_NEON 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_neon.c) + SET(CPU_EXTS "${CPU_EXTS}, Neon") + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON) + MESSAGE(FATAL_ERROR "Failed to enabled required ARM Neon CPU extensions") +ENDIF() + + +IF(WIN32 OR HAVE_DLFCN_H) + SET(IS_LINKED "") + MACRO(ADD_BACKEND_LIBS _LIBS) + ENDMACRO() +ELSE() + SET(IS_LINKED " (linked)") + MACRO(ADD_BACKEND_LIBS _LIBS) + SET(EXTRA_LIBS ${_LIBS} ${EXTRA_LIBS}) + ENDMACRO() +ENDIF() + +SET(ALC_OBJS ${ALC_OBJS} + Alc/midi/base.c + Alc/midi/sf2load.c + Alc/midi/dummy.c + Alc/midi/fluidsynth.c + Alc/midi/soft.c +) + +# Check for FluidSynth support +OPTION(ALSOFT_REQUIRE_FLUIDSYNTH "Require FluidSynth MIDI" OFF) +FIND_PACKAGE(FluidSynth) +IF(FLUIDSYNTH_FOUND) + OPTION(ALSOFT_MIDI_FLUIDSYNTH "Enable FluidSynth MIDI" ON) + IF(ALSOFT_MIDI_FLUIDSYNTH) + SET(HAVE_FLUIDSYNTH 1) + ADD_BACKEND_LIBS(${FLUIDSYNTH_LIBRARIES}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${FLUIDSYNTH_INCLUDE_DIR}) + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_FLUIDSYNTH AND NOT HAVE_FLUIDSYNTH) + MESSAGE(FATAL_ERROR "Failed to enabled required FluidSynth support") +ENDIF() + + +SET(BACKENDS "") +SET(ALC_OBJS ${ALC_OBJS} + Alc/backends/base.c + # Default backends, always available + Alc/backends/loopback.c + Alc/backends/null.c +) + +# Check ALSA backend +OPTION(ALSOFT_REQUIRE_ALSA "Require ALSA backend" OFF) +FIND_PACKAGE(ALSA) +IF(ALSA_FOUND) + OPTION(ALSOFT_BACKEND_ALSA "Enable ALSA backend" ON) + IF(ALSOFT_BACKEND_ALSA) + SET(HAVE_ALSA 1) + SET(BACKENDS "${BACKENDS} ALSA${IS_LINKED},") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/alsa.c) + ADD_BACKEND_LIBS(${ALSA_LIBRARIES}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${ALSA_INCLUDE_DIRS}) + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA) + MESSAGE(FATAL_ERROR "Failed to enabled required ALSA backend") +ENDIF() + +# Check OSS backend +OPTION(ALSOFT_REQUIRE_OSS "Require OSS backend" OFF) +FIND_PACKAGE(OSS) +IF(OSS_FOUND) + OPTION(ALSOFT_BACKEND_OSS "Enable OSS backend" ON) + IF(ALSOFT_BACKEND_OSS) + SET(HAVE_OSS 1) + SET(BACKENDS "${BACKENDS} OSS,") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/oss.c) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${OSS_INCLUDE_DIRS}) + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS) + MESSAGE(FATAL_ERROR "Failed to enabled required OSS backend") +ENDIF() + +# Check Solaris backend +OPTION(ALSOFT_REQUIRE_SOLARIS "Require Solaris backend" OFF) +FIND_PACKAGE(AudioIO) +IF(AUDIOIO_FOUND) + OPTION(ALSOFT_BACKEND_SOLARIS "Enable Solaris backend" ON) + IF(ALSOFT_BACKEND_SOLARIS) + SET(HAVE_SOLARIS 1) + SET(BACKENDS "${BACKENDS} Solaris,") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/solaris.c) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${AUDIOIO_INCLUDE_DIRS}) + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS) + MESSAGE(FATAL_ERROR "Failed to enabled required Solaris backend") +ENDIF() + +# Check SndIO backend +OPTION(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF) +FIND_PACKAGE(SoundIO) +IF(SOUNDIO_FOUND) + OPTION(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON) + IF(ALSOFT_BACKEND_SNDIO) + SET(HAVE_SNDIO 1) + SET(BACKENDS "${BACKENDS} SndIO (linked),") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/sndio.c) + SET(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${SOUNDIO_INCLUDE_DIRS}) + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO) + MESSAGE(FATAL_ERROR "Failed to enabled required SndIO backend") +ENDIF() + +# Check QSA backend +OPTION(ALSOFT_REQUIRE_QSA "Require QSA backend" OFF) +FIND_PACKAGE(QSA) +IF(QSA_FOUND) + OPTION(ALSOFT_BACKEND_QSA "Enable QSA backend" ON) + IF(ALSOFT_BACKEND_QSA) + SET(HAVE_QSA 1) + SET(BACKENDS "${BACKENDS} QSA (linked),") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/qsa.c) + SET(EXTRA_LIBS ${QSA_LIBRARIES} ${EXTRA_LIBS}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${QSA_INCLUDE_DIRS}) + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_QSA AND NOT HAVE_QSA) + MESSAGE(FATAL_ERROR "Failed to enabled required QSA backend") +ENDIF() + +# Check Windows-only backends +OPTION(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) +OPTION(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) +OPTION(ALSOFT_REQUIRE_MMDEVAPI "Require MMDevApi backend" OFF) +IF(HAVE_WINDOWS_H) + # Check MMSystem backend + CHECK_INCLUDE_FILES("windows.h;mmsystem.h" HAVE_MMSYSTEM_H -D_WIN32_WINNT=0x0502) + IF(HAVE_MMSYSTEM_H) + CHECK_SHARED_FUNCTION_EXISTS(waveOutOpen "windows.h;mmsystem.h" winmm "" HAVE_LIBWINMM) + IF(HAVE_LIBWINMM) + OPTION(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON) + IF(ALSOFT_BACKEND_WINMM) + SET(HAVE_WINMM 1) + SET(BACKENDS "${BACKENDS} WinMM,") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/winmm.c) + SET(EXTRA_LIBS winmm ${EXTRA_LIBS}) + ENDIF() + ENDIF() + ENDIF() + + # Check DSound backend + FIND_PACKAGE(DSound) + IF(DSOUND_FOUND) + OPTION(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON) + IF(ALSOFT_BACKEND_DSOUND) + SET(HAVE_DSOUND 1) + SET(BACKENDS "${BACKENDS} DirectSound${IS_LINKED},") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/dsound.c) + ADD_BACKEND_LIBS(${DSOUND_LIBRARIES}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${DSOUND_INCLUDE_DIRS}) + ENDIF() + ENDIF() + ENDIF() + + # Check for MMDevApi backend + CHECK_INCLUDE_FILE(mmdeviceapi.h HAVE_MMDEVICEAPI_H) + IF(HAVE_MMDEVICEAPI_H) + OPTION(ALSOFT_BACKEND_MMDEVAPI "Enable MMDevApi backend" ON) + IF(ALSOFT_BACKEND_MMDEVAPI) + SET(HAVE_MMDEVAPI 1) + SET(BACKENDS "${BACKENDS} MMDevApi,") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/mmdevapi.c) + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM) + MESSAGE(FATAL_ERROR "Failed to enabled required WinMM backend") +ENDIF() +IF(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND) + MESSAGE(FATAL_ERROR "Failed to enabled required DSound backend") +ENDIF() +IF(ALSOFT_REQUIRE_MMDEVAPI AND NOT HAVE_MMDEVAPI) + MESSAGE(FATAL_ERROR "Failed to enabled required MMDevApi backend") +ENDIF() + +# Check PortAudio backend +OPTION(ALSOFT_REQUIRE_PORTAUDIO "Require PortAudio backend" OFF) +FIND_PACKAGE(PortAudio) +IF(PORTAUDIO_FOUND) + OPTION(ALSOFT_BACKEND_PORTAUDIO "Enable PortAudio backend" ON) + IF(ALSOFT_BACKEND_PORTAUDIO) + SET(HAVE_PORTAUDIO 1) + SET(BACKENDS "${BACKENDS} PortAudio${IS_LINKED},") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/portaudio.c) + ADD_BACKEND_LIBS(${PORTAUDIO_LIBRARIES}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${PORTAUDIO_INCLUDE_DIRS}) + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO) + MESSAGE(FATAL_ERROR "Failed to enabled required PortAudio backend") +ENDIF() + +# Check PulseAudio backend +OPTION(ALSOFT_REQUIRE_PULSEAUDIO "Require PulseAudio backend" OFF) +FIND_PACKAGE(PulseAudio) +IF(PULSEAUDIO_FOUND) + OPTION(ALSOFT_BACKEND_PULSEAUDIO "Enable PulseAudio backend" ON) + IF(ALSOFT_BACKEND_PULSEAUDIO) + SET(HAVE_PULSEAUDIO 1) + SET(BACKENDS "${BACKENDS} PulseAudio${IS_LINKED},") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/pulseaudio.c) + ADD_BACKEND_LIBS(${PULSEAUDIO_LIBRARIES}) + IF(CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${PULSEAUDIO_INCLUDE_DIRS}) + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO) + MESSAGE(FATAL_ERROR "Failed to enabled required PulseAudio backend") +ENDIF() + +# Check CoreAudio backend +OPTION(ALSOFT_REQUIRE_COREAUDIO "Require CoreAudio backend" OFF) +FIND_LIBRARY(COREAUDIO_FRAMEWORK + NAMES CoreAudio + PATHS /System/Library/Frameworks +) +IF(COREAUDIO_FRAMEWORK) + OPTION(ALSOFT_BACKEND_COREAUDIO "Enable CoreAudio backend" ON) + IF(ALSOFT_BACKEND_COREAUDIO) + SET(HAVE_COREAUDIO 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/coreaudio.c) + SET(BACKENDS "${BACKENDS} CoreAudio,") + SET(EXTRA_LIBS ${COREAUDIO_FRAMEWORK} ${EXTRA_LIBS}) + SET(EXTRA_LIBS /System/Library/Frameworks/AudioUnit.framework ${EXTRA_LIBS}) + SET(EXTRA_LIBS /System/Library/Frameworks/ApplicationServices.framework ${EXTRA_LIBS}) + + # Some versions of OSX may need the AudioToolbox framework. Add it if + # it's found. + FIND_LIBRARY(AUDIOTOOLBOX_LIBRARY + NAMES AudioToolbox + PATHS ~/Library/Frameworks + /Library/Frameworks + /System/Library/Frameworks + ) + IF(AUDIOTOOLBOX_LIBRARY) + SET(EXTRA_LIBS ${AUDIOTOOLBOX_LIBRARY} ${EXTRA_LIBS}) + ENDIF() + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO) + MESSAGE(FATAL_ERROR "Failed to enabled required CoreAudio backend") +ENDIF() + +# Check for OpenSL (Android) backend +OPTION(ALSOFT_REQUIRE_OPENSL "Require OpenSL backend" OFF) +CHECK_INCLUDE_FILES("SLES/OpenSLES.h;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) + OPTION(ALSOFT_BACKEND_OPENSL "Enable OpenSL backend" ON) + IF(ALSOFT_BACKEND_OPENSL) + 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(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL) + MESSAGE(FATAL_ERROR "Failed to enabled required OpenSL backend") +ENDIF() + +# Optionally enable the Wave Writer backend +OPTION(ALSOFT_BACKEND_WAVE "Enable Wave Writer backend" ON) +IF(ALSOFT_BACKEND_WAVE) + SET(HAVE_WAVE 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/wave.c) + SET(BACKENDS "${BACKENDS} WaveFile,") +ENDIF() + +# This is always available +SET(BACKENDS "${BACKENDS} Null") + +IF(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL) + add_subdirectory(utils/alsoft-config) +ENDIF() +IF(ALSOFT_EXAMPLES) + FIND_PACKAGE(SDL2) + IF(SDL2_FOUND) + FIND_PACKAGE(SDL_sound) + IF(SDL_SOUND_FOUND AND CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + ENDIF() + FIND_PACKAGE(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE) + IF(FFMPEG_FOUND AND CMAKE_VERSION VERSION_LESS "2.8.8") + INCLUDE_DIRECTORIES(${FFMPEG_INCLUDE_DIRS}) + ENDIF() + ENDIF() +ENDIF() + +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 common library with reusable helpers +ADD_LIBRARY(common STATIC ${COMMON_OBJS}) + +# Build main library +ADD_LIBRARY(${LIBNAME} ${LIBTYPE} ${OPENAL_OBJS} ${ALC_OBJS}) +SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES) +IF(WIN32 AND ALSOFT_NO_UID_DEFS) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY COMPILE_DEFINITIONS AL_NO_UID_DEFS) +ENDIF() +SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES "${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc") +IF(FLUIDSYNTH_FOUND) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${FLUIDSYNTH_INCLUDE_DIR}) +ENDIF() +IF(HAVE_ALSA) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${ALSA_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_OSS) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${OSS_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_SOLARIS) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${AUDIOIO_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_SNDIO) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${SOUNDIO_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_QSA) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${QSA_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_DSOUND) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${DSOUND_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_PORTAUDIO) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${PORTAUDIO_INCLUDE_DIRS}) +ENDIF() +IF(HAVE_PULSEAUDIO) + SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${PULSEAUDIO_INCLUDE_DIRS}) +ENDIF() +SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES VERSION ${LIB_VERSION} + SOVERSION ${LIB_MAJOR_VERSION}) +IF(WIN32 AND NOT LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES PREFIX "") + + IF(MINGW AND ALSOFT_BUILD_IMPORT_LIB) + FIND_PROGRAM(SED_EXECUTABLE NAMES sed DOC "sed executable") + FIND_PROGRAM(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable") + IF(NOT SED_EXECUTABLE OR NOT DLLTOOL_EXECUTABLE) + MESSAGE(STATUS "") + IF(NOT SED_EXECUTABLE) + MESSAGE(STATUS "WARNING: Cannot find sed, disabling .def/.lib generation") + ENDIF() + IF(NOT DLLTOOL_EXECUTABLE) + MESSAGE(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation") + ENDIF() + ELSE() + SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES LINK_FLAGS "-Wl,--output-def,${LIBNAME}.def") + ADD_CUSTOM_COMMAND(TARGET ${LIBNAME} POST_BUILD + COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" ${LIBNAME}.def + COMMAND "${DLLTOOL_EXECUTABLE}" -d ${LIBNAME}.def -l ${LIBNAME}.lib -D ${LIBNAME}.dll + COMMENT "Stripping ordinals from ${LIBNAME}.def and generating ${LIBNAME}.lib..." + VERBATIM + ) + ENDIF() + ENDIF() +ENDIF() + +TARGET_LINK_LIBRARIES(${LIBNAME} common ${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 + include/AL/efx-presets.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 "") +MESSAGE(STATUS "Building with support for CPU extensions:") +MESSAGE(STATUS " ${CPU_EXTS}") +MESSAGE(STATUS "") +IF(HAVE_FLUIDSYNTH) + MESSAGE(STATUS "FluidSynth support for ALC_SOFT_midi_interface enabled") + MESSAGE(STATUS "") +ENDIF() + +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 share/openal + ) + MESSAGE(STATUS "Installing sample configuration") + MESSAGE(STATUS "") +ENDIF() + +# Install HRTF definitions +IF(ALSOFT_HRTF_DEFS) + INSTALL(FILES hrtf/default-44100.mhr + hrtf/default-48000.mhr + DESTINATION share/openal/hrtf + ) + MESSAGE(STATUS "Installing HRTF definitions") + MESSAGE(STATUS "") +ENDIF() + +IF(ALSOFT_UTILS) + ADD_EXECUTABLE(openal-info utils/openal-info.c) + TARGET_LINK_LIBRARIES(openal-info ${LIBNAME}) + + ADD_EXECUTABLE(makehrtf utils/makehrtf.c) + IF(HAVE_LIBM) + TARGET_LINK_LIBRARIES(makehrtf m) + ENDIF() + + INSTALL(TARGETS openal-info makehrtf + RUNTIME DESTINATION bin + LIBRARY DESTINATION "lib${LIB_SUFFIX}" + ARCHIVE DESTINATION "lib${LIB_SUFFIX}" + ) + + MESSAGE(STATUS "Building utility programs") + IF(TARGET alsoft-config) + MESSAGE(STATUS "Building configuration program") + ENDIF() + MESSAGE(STATUS "") +ENDIF() + +IF(ALSOFT_EXAMPLES) + IF(SDL2_FOUND AND SDL_SOUND_FOUND) + ADD_LIBRARY(ex-common STATIC examples/common/alhelpers.c + examples/common/sdl_sound.c) + SET_PROPERTY(TARGET ex-common APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + ${SDL_SOUND_INCLUDE_DIR}) + + ADD_EXECUTABLE(alstream examples/alstream.c) + TARGET_LINK_LIBRARIES(alstream ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ${LIBNAME}) + SET_PROPERTY(TARGET alstream APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + ${SDL_SOUND_INCLUDE_DIR}) + + ADD_EXECUTABLE(alreverb examples/alreverb.c) + TARGET_LINK_LIBRARIES(alreverb ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ${LIBNAME}) + SET_PROPERTY(TARGET alreverb APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + ${SDL_SOUND_INCLUDE_DIR}) + + ADD_EXECUTABLE(allatency examples/allatency.c) + TARGET_LINK_LIBRARIES(allatency ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ${LIBNAME}) + SET_PROPERTY(TARGET allatency APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + ${SDL_SOUND_INCLUDE_DIR}) + + ADD_EXECUTABLE(alloopback examples/alloopback.c) + TARGET_LINK_LIBRARIES(alloopback ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ${LIBNAME}) + SET_PROPERTY(TARGET alloopback APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + ${SDL_SOUND_INCLUDE_DIR}) + + INSTALL(TARGETS alstream alreverb allatency alloopback + RUNTIME DESTINATION bin + LIBRARY DESTINATION "lib${LIB_SUFFIX}" + ARCHIVE DESTINATION "lib${LIB_SUFFIX}" + ) + + SET(FFVER_OK FALSE) + IF(FFMPEG_FOUND) + SET(FFVER_OK TRUE) + IF(AVFORMAT_VERSION VERSION_LESS "55.33.100") + MESSAGE(STATUS "libavformat is too old! (${AVFORMAT_VERSION}, wanted 55.33.100)") + SET(FFVER_OK FALSE) + ENDIF() + IF(AVCODEC_VERSION VERSION_LESS "55.52.102") + MESSAGE(STATUS "libavcodec is too old! (${AVCODEC_VERSION}, wanted 55.52.102)") + SET(FFVER_OK FALSE) + ENDIF() + IF(AVUTIL_VERSION VERSION_LESS "52.66.100") + MESSAGE(STATUS "libavutil is too old! (${AVUTIL_VERSION}, wanted 52.66.100)") + SET(FFVER_OK FALSE) + ENDIF() + IF(SWSCALE_VERSION VERSION_LESS "2.5.102") + MESSAGE(STATUS "libswscale is too old! (${SWSCALE_VERSION}, wanted 2.5.102)") + SET(FFVER_OK FALSE) + ENDIF() + IF(SWRESAMPLE_VERSION VERSION_LESS "0.18.100") + MESSAGE(STATUS "libswresample is too old! (${SWRESAMPLE_VERSION}, wanted 0.18.100)") + SET(FFVER_OK FALSE) + ENDIF() + ENDIF() + IF(FFVER_OK AND NOT MSVC) + ADD_EXECUTABLE(alffplay examples/alffplay.c) + TARGET_LINK_LIBRARIES(alffplay common ex-common ${SDL2_LIBRARY} ${LIBNAME} ${FFMPEG_LIBRARIES}) + SET_PROPERTY(TARGET alffplay APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + ${FFMPEG_INCLUDE_DIRS}) + + INSTALL(TARGETS alffplay + RUNTIME DESTINATION bin + LIBRARY DESTINATION "lib${LIB_SUFFIX}" + ARCHIVE DESTINATION "lib${LIB_SUFFIX}" + ) + MESSAGE(STATUS "Building SDL and FFmpeg example programs") + ELSE() + MESSAGE(STATUS "Building SDL example programs") + ENDIF() + MESSAGE(STATUS "") + ENDIF() +ENDIF() diff --git a/libs/openal-soft/COPYING b/libs/openal-soft/COPYING new file mode 100644 index 00000000..d0c89786 --- /dev/null +++ b/libs/openal-soft/COPYING @@ -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 --git a/libs/openal-soft/ChangeLog b/libs/openal-soft/ChangeLog new file mode 100644 index 00000000..8df50144 --- /dev/null +++ b/libs/openal-soft/ChangeLog @@ -0,0 +1,100 @@ +openal-soft-1.16.0: + + Implemented EFX Chorus, Flanger, Distortion, Equalizer, and Compressor + effects. + + Implemented high-pass and band-pass EFX filters. + + Implemented the high-pass filter for the EAXReverb effect. + + Implemented SSE2 and SSE4.1 linear resamplers. + + Implemented Neon-enhanced non-HRTF mixers. + + Implemented a QSA backend, for QNX. + + Implemented the ALC_SOFT_pause_device, AL_SOFT_deferred_updates, + AL_SOFT_block_alignment, AL_SOFT_MSADPCM, and AL_SOFT_source_length + extensions. + + Fixed resetting mmdevapi backend devices. + + Fixed clamping when converting 32-bit float samples to integer. + + Fixed modulation range in the Modulator effect. + + Several fixes for the OpenSL playback backend. + + Fixed device specifier names that have Unicode characters on Windows. + + Added support for filenames and paths with Unicode (UTF-8) characters on + Windows. + + Added support for alsoft.conf config files found in XDG Base Directory + Specification locations (XDG_CONFIG_DIRS and XDG_CONFIG_HOME, or their + defaults) on non-Windows systems. + + Added a GUI configuration utility (requires Qt 4.8). + + Added support for environment variable expansion in config options (not + keys or section names). + + Added an example that uses SDL2 and ffmpeg. + + Modified examples to use SDL_sound. + + Modified CMake config option names for better sorting. + + HRTF data sets specified in the hrtf_tables config option may now be + relative or absolute filenames. + + Made the default HRTF data set an external file, and added a data set for + 48khz playback in addition to 44.1khz. + + Added support for C11 atomic methods. + + Improved support for some non-GNU build systems. + +openal-soft-1.15.1: + + Fixed a regression with retrieving the source's AL_GAIN property. + +openal-soft-1.15: + + Fixed device enumeration with the OSS backend. + + Reorganized internal mixing logic, so unneeded steps can potentially be + skipped for better performance. + + Removed the lookup table for calculating the mixing pans. The panning is + now calculated directly for better precision. + + Improved the panning of stereo source channels when using stereo output. + + Improved source filter quality on send paths. + + Added a config option to allow PulseAudio to move streams between devices. + + The PulseAudio backend will now attempt to spawn a server by default. + + Added a workaround for a DirectSound bug relating to float32 output. + + Added SSE-based mixers, for HRTF and non-HRTF mixing. + + Added support for the new AL_SOFT_source_latency extension. + + Improved ALSA capture by avoiding an extra buffer when using sizes + supported by the underlying device. + + Improved the makehrtf utility to support new options and input formats. + + Modified the CFLAGS declared in the pkg-config file so the "AL/" portion of + the header includes can optionally be omitted. + + Added a couple example code programs to show how to apply reverb, and + retrieve latency. + + The configuration sample is now installed into the share/openal/ directory + instead of /etc/openal. + + The configuration sample now gets installed by default. diff --git a/libs/openal-soft/OpenAL32/Include/alAuxEffectSlot.h b/libs/openal-soft/OpenAL32/Include/alAuxEffectSlot.h new file mode 100644 index 00000000..286aa95d --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/alAuxEffectSlot.h @@ -0,0 +1,117 @@ +#ifndef _AL_AUXEFFECTSLOT_H_ +#define _AL_AUXEFFECTSLOT_H_ + +#include "alMain.h" +#include "alEffect.h" + +#include "align.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ALeffectStateVtable; +struct ALeffectslot; + +typedef struct ALeffectState { + const struct ALeffectStateVtable *vtbl; +} ALeffectState; + +struct ALeffectStateVtable { + void (*const Destruct)(ALeffectState *state); + + ALboolean (*const deviceUpdate)(ALeffectState *state, ALCdevice *device); + void (*const update)(ALeffectState *state, ALCdevice *device, const struct ALeffectslot *slot); + void (*const process)(ALeffectState *state, ALuint samplesToDo, const ALfloat *restrict samplesIn, ALfloat (*restrict samplesOut)[BUFFERSIZE]); + + void (*const Delete)(void *ptr); +}; + +#define DEFINE_ALEFFECTSTATE_VTABLE(T) \ +DECLARE_THUNK(T, ALeffectState, void, Destruct) \ +DECLARE_THUNK1(T, ALeffectState, ALboolean, deviceUpdate, ALCdevice*) \ +DECLARE_THUNK2(T, ALeffectState, void, update, ALCdevice*, const ALeffectslot*) \ +DECLARE_THUNK3(T, ALeffectState, void, process, ALuint, const ALfloat*restrict, ALfloatBUFFERSIZE*restrict) \ +static void T##_ALeffectState_Delete(void *ptr) \ +{ return T##_Delete(STATIC_UPCAST(T, ALeffectState, (ALeffectState*)ptr)); } \ + \ +static const struct ALeffectStateVtable T##_ALeffectState_vtable = { \ + T##_ALeffectState_Destruct, \ + \ + T##_ALeffectState_deviceUpdate, \ + T##_ALeffectState_update, \ + T##_ALeffectState_process, \ + \ + T##_ALeffectState_Delete, \ +} + + +struct ALeffectStateFactoryVtable; + +typedef struct ALeffectStateFactory { + const struct ALeffectStateFactoryVtable *vtbl; +} ALeffectStateFactory; + +struct ALeffectStateFactoryVtable { + ALeffectState *(*const create)(ALeffectStateFactory *factory); +}; + +#define DEFINE_ALEFFECTSTATEFACTORY_VTABLE(T) \ +DECLARE_THUNK(T, ALeffectStateFactory, ALeffectState*, create) \ + \ +static const struct ALeffectStateFactoryVtable T##_ALeffectStateFactory_vtable = { \ + T##_ALeffectStateFactory_create, \ +} + + +typedef struct ALeffectslot { + ALenum EffectType; + ALeffectProps EffectProps; + + volatile ALfloat Gain; + volatile ALboolean AuxSendAuto; + + ATOMIC(ALenum) NeedsUpdate; + ALeffectState *EffectState; + + alignas(16) ALfloat WetBuffer[1][BUFFERSIZE]; + + RefCount ref; + + /* Self ID */ + ALuint id; +} ALeffectslot; + +inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) +{ return (struct ALeffectslot*)LookupUIntMapKey(&context->EffectSlotMap, id); } +inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id) +{ return (struct ALeffectslot*)RemoveUIntMapKey(&context->EffectSlotMap, id); } + +ALenum InitEffectSlot(ALeffectslot *slot); +ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context); + + +ALeffectStateFactory *ALnullStateFactory_getFactory(void); +ALeffectStateFactory *ALreverbStateFactory_getFactory(void); +ALeffectStateFactory *ALautowahStateFactory_getFactory(void); +ALeffectStateFactory *ALchorusStateFactory_getFactory(void); +ALeffectStateFactory *ALcompressorStateFactory_getFactory(void); +ALeffectStateFactory *ALdistortionStateFactory_getFactory(void); +ALeffectStateFactory *ALechoStateFactory_getFactory(void); +ALeffectStateFactory *ALequalizerStateFactory_getFactory(void); +ALeffectStateFactory *ALflangerStateFactory_getFactory(void); +ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void); + +ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void); + + +ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect); + +void InitEffectFactoryMap(void); +void DeinitEffectFactoryMap(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/openal-soft/OpenAL32/Include/alBuffer.h b/libs/openal-soft/OpenAL32/Include/alBuffer.h new file mode 100644 index 00000000..557f2282 --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/alBuffer.h @@ -0,0 +1,116 @@ +#ifndef _AL_BUFFER_H_ +#define _AL_BUFFER_H_ + +#include "alMain.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* User formats */ +enum UserFmtType { + UserFmtByte = AL_BYTE_SOFT, + UserFmtUByte = AL_UNSIGNED_BYTE_SOFT, + UserFmtShort = AL_SHORT_SOFT, + UserFmtUShort = AL_UNSIGNED_SHORT_SOFT, + UserFmtInt = AL_INT_SOFT, + UserFmtUInt = AL_UNSIGNED_INT_SOFT, + UserFmtFloat = AL_FLOAT_SOFT, + UserFmtDouble = AL_DOUBLE_SOFT, + UserFmtByte3 = AL_BYTE3_SOFT, + UserFmtUByte3 = AL_UNSIGNED_BYTE3_SOFT, + UserFmtMulaw, + UserFmtAlaw, + UserFmtIMA4, + UserFmtMSADPCM, +}; +enum UserFmtChannels { + UserFmtMono = AL_MONO_SOFT, + UserFmtStereo = AL_STEREO_SOFT, + UserFmtRear = AL_REAR_SOFT, + UserFmtQuad = AL_QUAD_SOFT, + UserFmtX51 = AL_5POINT1_SOFT, /* (WFX order) */ + UserFmtX61 = AL_6POINT1_SOFT, /* (WFX order) */ + UserFmtX71 = AL_7POINT1_SOFT, /* (WFX order) */ +}; + +ALuint BytesFromUserFmt(enum UserFmtType type) DECL_CONST; +ALuint ChannelsFromUserFmt(enum UserFmtChannels chans) DECL_CONST; +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, +}; +#define MAX_INPUT_CHANNELS (8) + +ALuint BytesFromFmt(enum FmtType type) DECL_CONST; +ALuint ChannelsFromFmt(enum FmtChannels chans) DECL_CONST; +inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type) +{ + return ChannelsFromFmt(chans) * BytesFromFmt(type); +} + + +typedef struct ALbuffer { + ALvoid *data; + + ALsizei Frequency; + ALenum Format; + ALsizei SampleLen; + + enum FmtChannels FmtChannels; + enum FmtType FmtType; + + enum UserFmtChannels OriginalChannels; + enum UserFmtType OriginalType; + ALsizei OriginalSize; + ALsizei OriginalAlign; + + ALsizei LoopStart; + ALsizei LoopEnd; + + ALsizei UnpackAlign; + ALsizei PackAlign; + + /* Number of times buffer was attached to a source (deletion can only occur when 0) */ + RefCount ref; + + RWLock lock; + + /* Self ID */ + ALuint id; +} ALbuffer; + +ALbuffer *NewBuffer(ALCcontext *context); +void DeleteBuffer(ALCdevice *device, ALbuffer *buffer); + +ALenum LoadData(ALbuffer *buffer, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc); + +inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) +{ return (struct ALbuffer*)LookupUIntMapKey(&device->BufferMap, id); } +inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id) +{ return (struct ALbuffer*)RemoveUIntMapKey(&device->BufferMap, id); } + +ALvoid ReleaseALBuffers(ALCdevice *device); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/openal-soft/OpenAL32/Include/alEffect.h b/libs/openal-soft/OpenAL32/Include/alEffect.h new file mode 100644 index 00000000..f656c56a --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/alEffect.h @@ -0,0 +1,197 @@ +#ifndef _AL_EFFECT_H_ +#define _AL_EFFECT_H_ + +#include "alMain.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ALeffect; + +enum { + EAXREVERB = 0, + REVERB, + AUTOWAH, + CHORUS, + COMPRESSOR, + DISTORTION, + ECHO, + EQUALIZER, + FLANGER, + MODULATOR, + DEDICATED, + + MAX_EFFECTS +}; +extern ALboolean DisabledEffects[MAX_EFFECTS]; + +extern ALfloat ReverbBoost; +extern ALboolean EmulateEAXReverb; + +struct ALeffectVtable { + void (*const setParami)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALint val); + void (*const setParamiv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals); + void (*const setParamf)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val); + void (*const setParamfv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals); + + void (*const getParami)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *val); + void (*const getParamiv)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals); + void (*const getParamf)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val); + void (*const getParamfv)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals); +}; + +#define DEFINE_ALEFFECT_VTABLE(T) \ +const struct ALeffectVtable T##_vtable = { \ + T##_setParami, T##_setParamiv, \ + T##_setParamf, T##_setParamfv, \ + T##_getParami, T##_getParamiv, \ + T##_getParamf, T##_getParamfv, \ +} + +extern const struct ALeffectVtable ALeaxreverb_vtable; +extern const struct ALeffectVtable ALreverb_vtable; +extern const struct ALeffectVtable ALautowah_vtable; +extern const struct ALeffectVtable ALchorus_vtable; +extern const struct ALeffectVtable ALcompressor_vtable; +extern const struct ALeffectVtable ALdistortion_vtable; +extern const struct ALeffectVtable ALecho_vtable; +extern const struct ALeffectVtable ALequalizer_vtable; +extern const struct ALeffectVtable ALflanger_vtable; +extern const struct ALeffectVtable ALmodulator_vtable; +extern const struct ALeffectVtable ALnull_vtable; +extern const struct ALeffectVtable ALdedicated_vtable; + + +typedef union ALeffectProps { + 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 AttackTime; + ALfloat ReleaseTime; + ALfloat PeakGain; + ALfloat Resonance; + } Autowah; + + struct { + ALint Waveform; + ALint Phase; + ALfloat Rate; + ALfloat Depth; + ALfloat Feedback; + ALfloat Delay; + } Chorus; + + struct { + ALboolean OnOff; + } Compressor; + + struct { + ALfloat Edge; + ALfloat Gain; + ALfloat LowpassCutoff; + ALfloat EQCenter; + ALfloat EQBandwidth; + } Distortion; + + struct { + ALfloat Delay; + ALfloat LRDelay; + + ALfloat Damping; + ALfloat Feedback; + + ALfloat Spread; + } Echo; + + struct { + ALfloat Delay; + ALfloat LowCutoff; + ALfloat LowGain; + ALfloat Mid1Center; + ALfloat Mid1Gain; + ALfloat Mid1Width; + ALfloat Mid2Center; + ALfloat Mid2Gain; + ALfloat Mid2Width; + ALfloat HighCutoff; + ALfloat HighGain; + } Equalizer; + + struct { + ALint Waveform; + ALint Phase; + ALfloat Rate; + ALfloat Depth; + ALfloat Feedback; + ALfloat Delay; + } Flanger; + + struct { + ALfloat Frequency; + ALfloat HighPassCutoff; + ALint Waveform; + } Modulator; + + struct { + ALfloat Gain; + } Dedicated; +} ALeffectProps; + +typedef struct ALeffect { + // Effect type (AL_EFFECT_NULL, ...) + ALenum type; + + ALeffectProps Props; + + const struct ALeffectVtable *vtbl; + + /* Self ID */ + ALuint id; +} ALeffect; + +inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id) +{ return (struct ALeffect*)LookupUIntMapKey(&device->EffectMap, id); } +inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id) +{ return (struct ALeffect*)RemoveUIntMapKey(&device->EffectMap, id); } + +inline ALboolean IsReverbEffect(ALenum type) +{ return type == AL_EFFECT_REVERB || type == AL_EFFECT_EAXREVERB; } + +ALenum InitEffect(ALeffect *effect); +ALvoid ReleaseALEffects(ALCdevice *device); + +ALvoid LoadReverbPreset(const char *name, ALeffect *effect); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/openal-soft/OpenAL32/Include/alError.h b/libs/openal-soft/OpenAL32/Include/alError.h new file mode 100644 index 00000000..ab91d27b --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/alError.h @@ -0,0 +1,33 @@ +#ifndef _AL_ERROR_H_ +#define _AL_ERROR_H_ + +#include "alMain.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern ALboolean TrapALError; + +ALvoid alSetError(ALCcontext *Context, ALenum errorCode); + +#define SET_ERROR_AND_RETURN(ctx, err) do { \ + alSetError((ctx), (err)); \ + return; \ +} while(0) + +#define SET_ERROR_AND_RETURN_VALUE(ctx, err, val) do { \ + alSetError((ctx), (err)); \ + return (val); \ +} while(0) + +#define SET_ERROR_AND_GOTO(ctx, err, lbl) do { \ + alSetError((ctx), (err)); \ + goto lbl; \ +} while(0) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/openal-soft/OpenAL32/Include/alFilter.h b/libs/openal-soft/OpenAL32/Include/alFilter.h new file mode 100644 index 00000000..62b5fda8 --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/alFilter.h @@ -0,0 +1,112 @@ +#ifndef _AL_FILTER_H_ +#define _AL_FILTER_H_ + +#include "alMain.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LOWPASSFREQREF (5000.0f) +#define HIGHPASSFREQREF (250.0f) + + +/* Filters implementation is based on the "Cookbook formulae for audio * + * EQ biquad filter coefficients" by Robert Bristow-Johnson * + * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */ + +typedef enum ALfilterType { + /** EFX-style low-pass filter, specifying a gain and reference frequency. */ + ALfilterType_HighShelf, + /** EFX-style high-pass filter, specifying a gain and reference frequency. */ + ALfilterType_LowShelf, + /** Peaking filter, specifying a gain, reference frequency, and bandwidth. */ + ALfilterType_Peaking, + + /** Low-pass cut-off filter, specifying a cut-off frequency and bandwidth. */ + ALfilterType_LowPass, + /** High-pass cut-off filter, specifying a cut-off frequency and bandwidth. */ + ALfilterType_HighPass, + /** Band-pass filter, specifying a center frequency and bandwidth. */ + ALfilterType_BandPass, +} ALfilterType; + +typedef struct ALfilterState { + ALfloat x[2]; /* History of two last input samples */ + ALfloat y[2]; /* History of two last output samples */ + ALfloat a[3]; /* Transfer function coefficients "a" */ + ALfloat b[3]; /* Transfer function coefficients "b" */ + + void (*process)(struct ALfilterState *self, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples); +} ALfilterState; +#define ALfilterState_process(a, ...) ((a)->process((a), __VA_ARGS__)) + +void ALfilterState_clear(ALfilterState *filter); +void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat bandwidth); + +inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample) +{ + ALfloat outsmp; + + outsmp = filter->b[0] * sample + + filter->b[1] * filter->x[0] + + filter->b[2] * filter->x[1] - + filter->a[1] * filter->y[0] - + filter->a[2] * filter->y[1]; + filter->x[1] = filter->x[0]; + filter->x[0] = sample; + filter->y[1] = filter->y[0]; + filter->y[0] = outsmp; + + return outsmp; +} + +void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples); + + +typedef struct ALfilter { + // Filter type (AL_FILTER_NULL, ...) + ALenum type; + + ALfloat Gain; + ALfloat GainHF; + ALfloat HFReference; + ALfloat GainLF; + ALfloat LFReference; + + void (*SetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val); + void (*SetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals); + void (*SetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val); + void (*SetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals); + + void (*GetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *val); + void (*GetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *vals); + void (*GetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val); + void (*GetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals); + + /* Self ID */ + ALuint id; +} ALfilter; + +#define ALfilter_SetParami(x, c, p, v) ((x)->SetParami((x),(c),(p),(v))) +#define ALfilter_SetParamiv(x, c, p, v) ((x)->SetParamiv((x),(c),(p),(v))) +#define ALfilter_SetParamf(x, c, p, v) ((x)->SetParamf((x),(c),(p),(v))) +#define ALfilter_SetParamfv(x, c, p, v) ((x)->SetParamfv((x),(c),(p),(v))) + +#define ALfilter_GetParami(x, c, p, v) ((x)->GetParami((x),(c),(p),(v))) +#define ALfilter_GetParamiv(x, c, p, v) ((x)->GetParamiv((x),(c),(p),(v))) +#define ALfilter_GetParamf(x, c, p, v) ((x)->GetParamf((x),(c),(p),(v))) +#define ALfilter_GetParamfv(x, c, p, v) ((x)->GetParamfv((x),(c),(p),(v))) + +inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id) +{ return (struct ALfilter*)LookupUIntMapKey(&device->FilterMap, id); } +inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id) +{ return (struct ALfilter*)RemoveUIntMapKey(&device->FilterMap, id); } + +ALvoid ReleaseALFilters(ALCdevice *device); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/openal-soft/OpenAL32/Include/alListener.h b/libs/openal-soft/OpenAL32/Include/alListener.h new file mode 100644 index 00000000..ee07d87c --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/alListener.h @@ -0,0 +1,28 @@ +#ifndef _AL_LISTENER_H_ +#define _AL_LISTENER_H_ + +#include "alMain.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ALlistener { + volatile ALfloat Position[3]; + volatile ALfloat Velocity[3]; + volatile ALfloat Forward[3]; + volatile ALfloat Up[3]; + volatile ALfloat Gain; + volatile ALfloat MetersPerUnit; + + struct { + ALfloat Matrix[4][4]; + ALfloat Velocity[3]; + } Params; +} ALlistener; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/openal-soft/OpenAL32/Include/alMain.h b/libs/openal-soft/OpenAL32/Include/alMain.h new file mode 100644 index 00000000..111dde95 --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/alMain.h @@ -0,0 +1,895 @@ +#ifndef AL_MAIN_H +#define AL_MAIN_H + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_STRINGS_H +#include +#endif + +#ifdef HAVE_FENV_H +#include +#endif + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + + +#if defined(_WIN64) +#define SZFMT "%I64u" +#elif defined(_WIN32) +#define SZFMT "%u" +#else +#define SZFMT "%zu" +#endif + + +#include "static_assert.h" +#include "align.h" +#include "atomic.h" +#include "uintmap.h" +#include "vector.h" +#include "alstring.h" + +#ifndef ALC_SOFT_HRTF +#define ALC_SOFT_HRTF 1 +#define ALC_HRTF_SOFT 0x1992 +#endif + +#ifndef ALC_SOFT_midi_interface +#define ALC_SOFT_midi_interface 1 +/* Global properties */ +#define AL_MIDI_CLOCK_SOFT 0x9999 +#define AL_MIDI_STATE_SOFT 0x9986 +#define AL_MIDI_GAIN_SOFT 0x9998 +#define AL_SOUNDFONTS_SIZE_SOFT 0x9995 +#define AL_SOUNDFONTS_SOFT 0x9994 + +/* Soundfont properties */ +#define AL_PRESETS_SIZE_SOFT 0x9993 +#define AL_PRESETS_SOFT 0x9992 + +/* Preset properties */ +#define AL_MIDI_PRESET_SOFT 0x9997 +#define AL_MIDI_BANK_SOFT 0x9996 +#define AL_FONTSOUNDS_SIZE_SOFT 0x9991 +#define AL_FONTSOUNDS_SOFT 0x9990 + +/* Fontsound properties */ +/* AL_BUFFER */ +#define AL_SAMPLE_START_SOFT 0x2000 +#define AL_SAMPLE_END_SOFT 0x2001 +#define AL_SAMPLE_LOOP_START_SOFT 0x2002 +#define AL_SAMPLE_LOOP_END_SOFT 0x2003 +#define AL_SAMPLE_RATE_SOFT 0x2004 +#define AL_BASE_KEY_SOFT 0x2005 +#define AL_KEY_CORRECTION_SOFT 0x2006 +#define AL_SAMPLE_TYPE_SOFT 0x2007 +#define AL_FONTSOUND_LINK_SOFT 0x2008 +#define AL_MOD_LFO_TO_PITCH_SOFT 0x0005 +#define AL_VIBRATO_LFO_TO_PITCH_SOFT 0x0006 +#define AL_MOD_ENV_TO_PITCH_SOFT 0x0007 +#define AL_FILTER_CUTOFF_SOFT 0x0008 +#define AL_FILTER_RESONANCE_SOFT 0x0009 +#define AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT 0x000A +#define AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT 0x000B +#define AL_MOD_LFO_TO_VOLUME_SOFT 0x000D +#define AL_CHORUS_SEND_SOFT 0x000F +#define AL_REVERB_SEND_SOFT 0x0010 +#define AL_PAN_SOFT 0x0011 +#define AL_MOD_LFO_DELAY_SOFT 0x0015 +#define AL_MOD_LFO_FREQUENCY_SOFT 0x0016 +#define AL_VIBRATO_LFO_DELAY_SOFT 0x0017 +#define AL_VIBRATO_LFO_FREQUENCY_SOFT 0x0018 +#define AL_MOD_ENV_DELAYTIME_SOFT 0x0019 +#define AL_MOD_ENV_ATTACKTIME_SOFT 0x001A +#define AL_MOD_ENV_HOLDTIME_SOFT 0x001B +#define AL_MOD_ENV_DECAYTIME_SOFT 0x001C +#define AL_MOD_ENV_SUSTAINVOLUME_SOFT 0x001D +#define AL_MOD_ENV_RELEASETIME_SOFT 0x002E +#define AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT 0x001F +#define AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT 0x0020 +#define AL_VOLUME_ENV_DELAYTIME_SOFT 0x0021 +#define AL_VOLUME_ENV_ATTACKTIME_SOFT 0x0022 +#define AL_VOLUME_ENV_HOLDTIME_SOFT 0x0023 +#define AL_VOLUME_ENV_DECAYTIME_SOFT 0x0024 +#define AL_VOLUME_ENV_SUSTAINVOLUME_SOFT 0x0025 +#define AL_VOLUME_ENV_RELEASETIME_SOFT 0x0026 +#define AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT 0x0027 +#define AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT 0x0028 +#define AL_KEY_RANGE_SOFT 0x002B +#define AL_VELOCITY_RANGE_SOFT 0x002C +#define AL_ATTENUATION_SOFT 0x0030 +#define AL_TUNING_COARSE_SOFT 0x0033 +#define AL_TUNING_FINE_SOFT 0x0034 +#define AL_LOOP_MODE_SOFT 0x0036 +#define AL_TUNING_SCALE_SOFT 0x0038 +#define AL_EXCLUSIVE_CLASS_SOFT 0x0039 + +/* Sample Types */ +/* AL_MONO_SOFT */ +#define AL_RIGHT_SOFT 0x0002 +#define AL_LEFT_SOFT 0x0004 + +/* Loop Modes */ +/* AL_NONE */ +#define AL_LOOP_CONTINUOUS_SOFT 0x0001 +#define AL_LOOP_UNTIL_RELEASE_SOFT 0x0003 + +/* Fontsound modulator stage properties */ +#define AL_SOURCE0_INPUT_SOFT 0x998F +#define AL_SOURCE0_TYPE_SOFT 0x998E +#define AL_SOURCE0_FORM_SOFT 0x998D +#define AL_SOURCE1_INPUT_SOFT 0x998C +#define AL_SOURCE1_TYPE_SOFT 0x998B +#define AL_SOURCE1_FORM_SOFT 0x998A +#define AL_AMOUNT_SOFT 0x9989 +#define AL_TRANSFORM_OP_SOFT 0x9988 +#define AL_DESTINATION_SOFT 0x9987 + +/* Sounce Inputs */ +#define AL_ONE_SOFT 0x0080 +#define AL_NOTEON_VELOCITY_SOFT 0x0082 +#define AL_NOTEON_KEY_SOFT 0x0083 +/* AL_KEYPRESSURE_SOFT */ +/* AL_CHANNELPRESSURE_SOFT */ +/* AL_PITCHBEND_SOFT */ +#define AL_PITCHBEND_SENSITIVITY_SOFT 0x0090 +/* CC 0...127 */ + +/* Source Types */ +#define AL_UNORM_SOFT 0x0000 +#define AL_UNORM_REV_SOFT 0x0100 +#define AL_SNORM_SOFT 0x0200 +#define AL_SNORM_REV_SOFT 0x0300 + +/* Source Forms */ +#define AL_LINEAR_SOFT 0x0000 +#define AL_CONCAVE_SOFT 0x0400 +#define AL_CONVEX_SOFT 0x0800 +#define AL_SWITCH_SOFT 0x0C00 + +/* Transform Ops */ +/* AL_LINEAR_SOFT */ +#define AL_ABSOLUTE_SOFT 0x0002 + +/* Events */ +#define AL_NOTEOFF_SOFT 0x0080 +#define AL_NOTEON_SOFT 0x0090 +#define AL_KEYPRESSURE_SOFT 0x00A0 +#define AL_CONTROLLERCHANGE_SOFT 0x00B0 +#define AL_PROGRAMCHANGE_SOFT 0x00C0 +#define AL_CHANNELPRESSURE_SOFT 0x00D0 +#define AL_PITCHBEND_SOFT 0x00E0 + +typedef void (AL_APIENTRY*LPALGENSOUNDFONTSSOFT)(ALsizei n, ALuint *ids); +typedef void (AL_APIENTRY*LPALDELETESOUNDFONTSSOFT)(ALsizei n, const ALuint *ids); +typedef ALboolean (AL_APIENTRY*LPALISSOUNDFONTSOFT)(ALuint id); +typedef void (AL_APIENTRY*LPALGETSOUNDFONTIVSOFT)(ALuint id, ALenum param, ALint *values); +typedef void (AL_APIENTRY*LPALSOUNDFONTPRESETSSOFT)(ALuint id, ALsizei count, const ALuint *pids); +typedef void (AL_APIENTRY*LPALGENPRESETSSOFT)(ALsizei n, ALuint *ids); +typedef void (AL_APIENTRY*LPALDELETEPRESETSSOFT)(ALsizei n, const ALuint *ids); +typedef ALboolean (AL_APIENTRY*LPALISPRESETSOFT)(ALuint id); +typedef void (AL_APIENTRY*LPALPRESETISOFT)(ALuint id, ALenum param, ALint value); +typedef void (AL_APIENTRY*LPALPRESETIVSOFT)(ALuint id, ALenum param, const ALint *values); +typedef void (AL_APIENTRY*LPALPRESETFONTSOUNDSSOFT)(ALuint id, ALsizei count, const ALuint *fsids); +typedef void (AL_APIENTRY*LPALGETPRESETIVSOFT)(ALuint id, ALenum param, ALint *values); +typedef void (AL_APIENTRY*LPALGENFONTSOUNDSSOFT)(ALsizei n, ALuint *ids); +typedef void (AL_APIENTRY*LPALDELETEFONTSOUNDSSOFT)(ALsizei n, const ALuint *ids); +typedef ALboolean (AL_APIENTRY*LPALISFONTSOUNDSOFT)(ALuint id); +typedef void (AL_APIENTRY*LPALFONTSOUNDISOFT)(ALuint id, ALenum param, ALint value); +typedef void (AL_APIENTRY*LPALFONTSOUND2ISOFT)(ALuint id, ALenum param, ALint value1, ALint value2); +typedef void (AL_APIENTRY*LPALFONTSOUNDIVSOFT)(ALuint id, ALenum param, const ALint *values); +typedef void (AL_APIENTRY*LPALGETFONTSOUNDIVSOFT)(ALuint id, ALenum param, ALint *values); +typedef void (AL_APIENTRY*LPALFONTSOUNDMOFULATORISOFT)(ALuint id, ALsizei stage, ALenum param, ALint value); +typedef void (AL_APIENTRY*LPALGETFONTSOUNDMODULATORIVSOFT)(ALuint id, ALsizei stage, ALenum param, ALint *values); +typedef void (AL_APIENTRY*LPALMIDISOUNDFONTSOFT)(ALuint id); +typedef void (AL_APIENTRY*LPALMIDISOUNDFONTVSOFT)(ALsizei count, const ALuint *ids); +typedef void (AL_APIENTRY*LPALMIDIEVENTSOFT)(ALuint64SOFT time, ALenum event, ALsizei channel, ALsizei param1, ALsizei param2); +typedef void (AL_APIENTRY*LPALMIDISYSEXSOFT)(ALuint64SOFT time, const ALbyte *data, ALsizei size); +typedef void (AL_APIENTRY*LPALMIDIPLAYSOFT)(void); +typedef void (AL_APIENTRY*LPALMIDIPAUSESOFT)(void); +typedef void (AL_APIENTRY*LPALMIDISTOPSOFT)(void); +typedef void (AL_APIENTRY*LPALMIDIRESETSOFT)(void); +typedef void (AL_APIENTRY*LPALMIDIGAINSOFT)(ALfloat value); +typedef ALint64SOFT (AL_APIENTRY*LPALGETINTEGER64SOFT)(ALenum pname); +typedef void (AL_APIENTRY*LPALGETINTEGER64VSOFT)(ALenum pname, ALint64SOFT *values); +typedef void (AL_APIENTRY*LPALLOADSOUNDFONTSOFT)(ALuint id, size_t(*cb)(ALvoid*,size_t,ALvoid*), ALvoid *user); +#ifdef AL_ALEXT_PROTOTYPES +AL_API void AL_APIENTRY alGenSoundfontsSOFT(ALsizei n, ALuint *ids); +AL_API void AL_APIENTRY alDeleteSoundfontsSOFT(ALsizei n, const ALuint *ids); +AL_API ALboolean AL_APIENTRY alIsSoundfontSOFT(ALuint id); +AL_API void AL_APIENTRY alGetSoundfontivSOFT(ALuint id, ALenum param, ALint *values); +AL_API void AL_APIENTRY alSoundfontPresetsSOFT(ALuint id, ALsizei count, const ALuint *pids); + +AL_API void AL_APIENTRY alGenPresetsSOFT(ALsizei n, ALuint *ids); +AL_API void AL_APIENTRY alDeletePresetsSOFT(ALsizei n, const ALuint *ids); +AL_API ALboolean AL_APIENTRY alIsPresetSOFT(ALuint id); +AL_API void AL_APIENTRY alPresetiSOFT(ALuint id, ALenum param, ALint value); +AL_API void AL_APIENTRY alPresetivSOFT(ALuint id, ALenum param, const ALint *values); +AL_API void AL_APIENTRY alGetPresetivSOFT(ALuint id, ALenum param, ALint *values); +AL_API void AL_APIENTRY alPresetFontsoundsSOFT(ALuint id, ALsizei count, const ALuint *fsids); + +AL_API void AL_APIENTRY alGenFontsoundsSOFT(ALsizei n, ALuint *ids); +AL_API void AL_APIENTRY alDeleteFontsoundsSOFT(ALsizei n, const ALuint *ids); +AL_API ALboolean AL_APIENTRY alIsFontsoundSOFT(ALuint id); +AL_API void AL_APIENTRY alFontsoundiSOFT(ALuint id, ALenum param, ALint value); +AL_API void AL_APIENTRY alFontsound2iSOFT(ALuint id, ALenum param, ALint value1, ALint value2); +AL_API void AL_APIENTRY alFontsoundivSOFT(ALuint id, ALenum param, const ALint *values); +AL_API void AL_APIENTRY alGetFontsoundivSOFT(ALuint id, ALenum param, ALint *values); +AL_API void AL_APIENTRY alFontsoundModulatoriSOFT(ALuint id, ALsizei stage, ALenum param, ALint value); +AL_API void AL_APIENTRY alGetFontsoundModulatorivSOFT(ALuint id, ALsizei stage, ALenum param, ALint *values); + +AL_API void AL_APIENTRY alMidiSoundfontSOFT(ALuint id); +AL_API void AL_APIENTRY alMidiSoundfontvSOFT(ALsizei count, const ALuint *ids); +AL_API void AL_APIENTRY alMidiEventSOFT(ALuint64SOFT time, ALenum event, ALsizei channel, ALsizei param1, ALsizei param2); +AL_API void AL_APIENTRY alMidiSysExSOFT(ALuint64SOFT time, const ALbyte *data, ALsizei size); +AL_API void AL_APIENTRY alMidiPlaySOFT(void); +AL_API void AL_APIENTRY alMidiPauseSOFT(void); +AL_API void AL_APIENTRY alMidiStopSOFT(void); +AL_API void AL_APIENTRY alMidiResetSOFT(void); +AL_API void AL_APIENTRY alMidiGainSOFT(ALfloat value); +AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname); +AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values); +AL_API void AL_APIENTRY alLoadSoundfontSOFT(ALuint id, size_t(*cb)(ALvoid*,size_t,ALvoid*), ALvoid *user); +#endif +#endif + +#ifndef ALC_SOFT_device_clock +#define ALC_SOFT_device_clock 1 +typedef int64_t ALCint64SOFT; +typedef uint64_t ALCuint64SOFT; +#define ALC_DEVICE_CLOCK_SOFT 0x1600 +typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); +#ifdef AL_ALEXT_PROTOTYPES +ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); +#endif +#endif + + +#ifdef IN_IDE_PARSER +/* KDevelop's parser doesn't recognize the C99-standard restrict keyword, but + * recent versions (at least 4.5.1) do recognize GCC's __restrict. */ +#define restrict __restrict +#endif + + +typedef ALint64SOFT ALint64; +typedef ALuint64SOFT ALuint64; + +typedef ptrdiff_t ALintptrEXT; +typedef ptrdiff_t ALsizeiptrEXT; + +#ifndef U64 +#if defined(_MSC_VER) +#define U64(x) ((ALuint64)(x##ui64)) +#elif SIZEOF_LONG == 8 +#define U64(x) ((ALuint64)(x##ul)) +#elif SIZEOF_LONG_LONG == 8 +#define U64(x) ((ALuint64)(x##ull)) +#endif +#endif + +#ifndef UINT64_MAX +#define UINT64_MAX U64(18446744073709551615) +#endif + +#ifndef UNUSED +#if defined(__cplusplus) +#define UNUSED(x) +#elif defined(__GNUC__) +#define UNUSED(x) UNUSED_##x __attribute__((unused)) +#elif defined(__LCLINT__) +#define UNUSED(x) /*@unused@*/ x +#else +#define UNUSED(x) x +#endif +#endif + +#ifdef __GNUC__ +#define DECL_CONST __attribute__((const)) +#define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z)))) +#else +#define DECL_CONST +#define DECL_FORMAT(x, y, z) +#endif + +#if defined(__GNUC__) && defined(__i386__) +/* force_align_arg_pointer is required for proper function arguments aligning + * when SSE code is used. Some systems (Windows, QNX) do not guarantee our + * thread functions will be properly aligned on the stack, even though GCC may + * generate code with the assumption that it is. */ +#define FORCE_ALIGN __attribute__((force_align_arg_pointer)) +#else +#define FORCE_ALIGN +#endif + +#ifdef HAVE_C99_VLA +#define DECL_VLA(T, _name, _size) T _name[(_size)] +#else +#define DECL_VLA(T, _name, _size) T *_name = alloca((_size) * sizeof(T)) +#endif + +#ifndef PATH_MAX +#ifdef MAX_PATH +#define PATH_MAX MAX_PATH +#else +#define PATH_MAX 4096 +#endif +#endif + + +static const union { + ALuint u; + ALubyte b[sizeof(ALuint)]; +} EndianTest = { 1 }; +#define IS_LITTLE_ENDIAN (EndianTest.b[0] == 1) + +#define COUNTOF(x) (sizeof((x))/sizeof((x)[0])) + + +#define DERIVE_FROM_TYPE(t) t t##_parent +#define STATIC_CAST(to, obj) (&(obj)->to##_parent) +#ifdef __GNUC__ +#define STATIC_UPCAST(to, from, obj) __extension__({ \ + static_assert(__builtin_types_compatible_p(from, __typeof(*(obj))), \ + "Invalid upcast object from type"); \ + (to*)((char*)(obj) - offsetof(to, from##_parent)); \ +}) +#else +#define STATIC_UPCAST(to, from, obj) ((to*)((char*)(obj) - offsetof(to, from##_parent))) +#endif + +#define DECLARE_FORWARD(T1, T2, rettype, func) \ +rettype T1##_##func(T1 *obj) \ +{ return T2##_##func(STATIC_CAST(T2, obj)); } + +#define DECLARE_FORWARD1(T1, T2, rettype, func, argtype1) \ +rettype T1##_##func(T1 *obj, argtype1 a) \ +{ return T2##_##func(STATIC_CAST(T2, obj), a); } + +#define DECLARE_FORWARD2(T1, T2, rettype, func, argtype1, argtype2) \ +rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b) \ +{ return T2##_##func(STATIC_CAST(T2, obj), a, b); } + +#define DECLARE_FORWARD3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \ +rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b, argtype3 c) \ +{ return T2##_##func(STATIC_CAST(T2, obj), a, b, c); } + + +#define GET_VTABLE1(T1) (&(T1##_vtable)) +#define GET_VTABLE2(T1, T2) (&(T1##_##T2##_vtable)) + +#define SET_VTABLE1(T1, obj) ((obj)->vtbl = GET_VTABLE1(T1)) +#define SET_VTABLE2(T1, T2, obj) (STATIC_CAST(T2, obj)->vtbl = GET_VTABLE2(T1, T2)) + +#define DECLARE_THUNK(T1, T2, rettype, func) \ +static rettype T1##_##T2##_##func(T2 *obj) \ +{ return T1##_##func(STATIC_UPCAST(T1, T2, obj)); } + +#define DECLARE_THUNK1(T1, T2, rettype, func, argtype1) \ +static rettype T1##_##T2##_##func(T2 *obj, argtype1 a) \ +{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a); } + +#define DECLARE_THUNK2(T1, T2, rettype, func, argtype1, argtype2) \ +static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b) \ +{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b); } + +#define DECLARE_THUNK3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \ +static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c) \ +{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c); } + +#define DECLARE_DEFAULT_ALLOCATORS(T) \ +static void* T##_New(size_t size) { return malloc(size); } \ +static void T##_Delete(void *ptr) { free(ptr); } + +/* Helper to extract an argument list for VCALL. Not used directly. */ +#define EXTRACT_VCALL_ARGS(...) __VA_ARGS__)) + +/* Call a "virtual" method on an object, with arguments. */ +#define V(obj, func) ((obj)->vtbl->func((obj), EXTRACT_VCALL_ARGS +/* Call a "virtual" method on an object, with no arguments. */ +#define V0(obj, func) ((obj)->vtbl->func((obj) EXTRACT_VCALL_ARGS + +#define DELETE_OBJ(obj) do { \ + if((obj) != NULL) \ + { \ + V0((obj),Destruct)(); \ + V0((obj),Delete)(); \ + } \ +} while(0) + + +#ifdef __cplusplus +extern "C" { +#endif + +struct Hrtf; + + +#define DEFAULT_OUTPUT_RATE (44100) +#define MIN_OUTPUT_RATE (8000) + + +/* Find the next power-of-2 for non-power-of-2 numbers. */ +inline ALuint NextPowerOf2(ALuint value) +{ + if(value > 0) + { + value--; + value |= value>>1; + value |= value>>2; + value |= value>>4; + value |= value>>8; + value |= value>>16; + } + return value+1; +} + +/* Fast float-to-int conversion. Assumes the FPU is already in round-to-zero + * mode. */ +inline ALint fastf2i(ALfloat f) +{ +#ifdef HAVE_LRINTF + return lrintf(f); +#elif defined(_MSC_VER) && defined(_M_IX86) + ALint i; + __asm fld f + __asm fistp i + return i; +#else + return (ALint)f; +#endif +} + +/* Fast float-to-uint conversion. Assumes the FPU is already in round-to-zero + * mode. */ +inline ALuint fastf2u(ALfloat f) +{ return fastf2i(f); } + + +enum DevProbe { + ALL_DEVICE_PROBE, + CAPTURE_DEVICE_PROBE +}; + +typedef struct { + ALCenum (*OpenPlayback)(ALCdevice*, const ALCchar*); + void (*ClosePlayback)(ALCdevice*); + ALCboolean (*ResetPlayback)(ALCdevice*); + ALCboolean (*StartPlayback)(ALCdevice*); + void (*StopPlayback)(ALCdevice*); + + ALCenum (*OpenCapture)(ALCdevice*, const ALCchar*); + void (*CloseCapture)(ALCdevice*); + void (*StartCapture)(ALCdevice*); + void (*StopCapture)(ALCdevice*); + ALCenum (*CaptureSamples)(ALCdevice*, void*, ALCuint); + ALCuint (*AvailableSamples)(ALCdevice*); + + ALint64 (*GetLatency)(ALCdevice*); +} BackendFuncs; + +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 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_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_qsa_init(BackendFuncs *func_list); +void alc_qsa_deinit(void); +void alc_qsa_probe(enum DevProbe type); + +struct ALCbackend; + + +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, + + DefaultDistanceModel = InverseDistanceClamped +}; + +enum Resampler { + PointResampler, + LinearResampler, + CubicResampler, + + ResamplerMax, +}; + +enum Channel { + FrontLeft = 0, + FrontRight, + FrontCenter, + LFE, + BackLeft, + BackRight, + BackCenter, + SideLeft, + SideRight, + + MaxChannels, +}; + + +/* Device formats */ +enum DevFmtType { + DevFmtByte = ALC_BYTE_SOFT, + DevFmtUByte = ALC_UNSIGNED_BYTE_SOFT, + DevFmtShort = ALC_SHORT_SOFT, + DevFmtUShort = ALC_UNSIGNED_SHORT_SOFT, + DevFmtInt = ALC_INT_SOFT, + DevFmtUInt = ALC_UNSIGNED_INT_SOFT, + DevFmtFloat = ALC_FLOAT_SOFT, + + DevFmtTypeDefault = DevFmtFloat +}; +enum DevFmtChannels { + DevFmtMono = ALC_MONO_SOFT, + DevFmtStereo = ALC_STEREO_SOFT, + DevFmtQuad = ALC_QUAD_SOFT, + DevFmtX51 = ALC_5POINT1_SOFT, + DevFmtX61 = ALC_6POINT1_SOFT, + DevFmtX71 = ALC_7POINT1_SOFT, + + /* Similar to 5.1, except using the side channels instead of back */ + DevFmtX51Side = 0x80000000, + + DevFmtChannelsDefault = DevFmtStereo +}; + +ALuint BytesFromDevFmt(enum DevFmtType type) DECL_CONST; +ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) DECL_CONST; +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[]; + + +enum DeviceType { + Playback, + Capture, + Loopback +}; + + +/* Size for temporary storage of buffer data, in ALfloats. Larger values need + * more memory, while smaller values may need more iterations. The value needs + * to be a sensible size, however, as it constrains the max stepping value used + * for mixing, as well as the maximum number of samples per mixing iteration. + */ +#define BUFFERSIZE (2048u) + + +struct ALCdevice_struct +{ + RefCount ref; + + ALCboolean Connected; + enum DeviceType Type; + + ALuint Frequency; + ALuint UpdateSize; + ALuint NumUpdates; + enum DevFmtChannels FmtChans; + enum DevFmtType FmtType; + + al_string DeviceName; + + ATOMIC(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; + + // Map of Soundfonts for this device + UIntMap SfontMap; + + // Map of Presets for this device + UIntMap PresetMap; + + // Map of Fontsounds for this device + UIntMap FontsoundMap; + + /* Default soundfont (accessible as ID 0) */ + struct ALsoundfont *DefaultSfont; + + /* MIDI synth engine */ + struct MidiSynth *Synth; + + /* HRTF filter tables */ + const struct Hrtf *Hrtf; + + // Stereo-to-binaural filter + struct bs2b *Bs2b; + ALCint Bs2bLevel; + + // Device flags + ALuint Flags; + + ALuint ChannelOffsets[MaxChannels]; + + enum Channel Speaker2Chan[MaxChannels]; + ALfloat SpeakerAngle[MaxChannels]; + ALuint NumChan; + + ALuint64 ClockBase; + ALuint SamplesDone; + + /* Temp storage used for each source when mixing. */ + alignas(16) ALfloat SourceData[BUFFERSIZE]; + alignas(16) ALfloat ResampledData[BUFFERSIZE]; + alignas(16) ALfloat FilteredData[BUFFERSIZE]; + + // Dry path buffer mix + alignas(16) ALfloat DryBuffer[MaxChannels][BUFFERSIZE]; + + /* Running count of the mixer invocations, in 31.1 fixed point. This + * actually increments *twice* when mixing, first at the start and then at + * the end, so the bottom bit indicates if the device is currently mixing + * and the upper bits indicates how many mixes have been done. + */ + RefCount MixCount; + + /* Default effect slot */ + struct ALeffectslot *DefaultSlot; + + // Contexts created on this device + ATOMIC(ALCcontext*) ContextList; + + struct ALCbackend *Backend; + + void *ExtraData; // For the backend's use + + ALCdevice *volatile next; + + /* Memory space used by the default slot (Playback devices only) */ + alignas(16) ALCbyte _slot_mem[]; +}; + +// Frequency was requested by the app or config file +#define DEVICE_FREQUENCY_REQUEST (1<<1) +// Channel configuration was requested by the config file +#define DEVICE_CHANNELS_REQUEST (1<<2) +// Sample type was requested by the config file +#define DEVICE_SAMPLE_TYPE_REQUEST (1<<3) +// HRTF was requested by the app +#define DEVICE_HRTF_REQUEST (1<<4) + +// Stereo sources cover 120-degree angles around +/-90 +#define DEVICE_WIDE_STEREO (1<<16) + +// Specifies if the DSP is paused at user request +#define DEVICE_PAUSED (1<<30) + +// Specifies if the device is currently running +#define DEVICE_RUNNING (1<<31) + +/* Invalid channel offset */ +#define INVALID_OFFSET (~0u) + + +/* Nanosecond resolution for the device clock time. */ +#define DEVICE_CLOCK_RES U64(1000000000) + + +/* Must be less than 15 characters (16 including terminating null) for + * compatibility with pthread_setname_np limitations. */ +#define MIXER_THREAD_NAME "alsoft-mixer" + + +struct ALCcontext_struct +{ + RefCount ref; + + struct ALlistener *Listener; + + UIntMap SourceMap; + UIntMap EffectSlotMap; + + ATOMIC(ALenum) LastError; + + ATOMIC(ALenum) UpdateSources; + + volatile enum DistanceModel DistanceModel; + volatile ALboolean SourceDistanceModel; + + volatile ALfloat DopplerFactor; + volatile ALfloat DopplerVelocity; + volatile ALfloat SpeedOfSound; + volatile ALenum DeferUpdates; + + struct ALactivesource **ActiveSources; + ALsizei ActiveSourceCount; + ALsizei MaxActiveSources; + + VECTOR(struct ALeffectslot*) ActiveAuxSlots; + + ALCdevice *Device; + const ALCchar *ExtensionList; + + ALCcontext *volatile next; + + /* Memory space used by the listener */ + alignas(16) ALCbyte _listener_mem[]; +}; + +ALCcontext *GetContextRef(void); + +void ALCcontext_IncRef(ALCcontext *context); +void ALCcontext_DecRef(ALCcontext *context); + +void AppendAllDevicesList(const ALCchar *name); +void AppendCaptureDeviceList(const ALCchar *name); + +ALint64 ALCdevice_GetLatencyDefault(ALCdevice *device); + +void ALCdevice_Lock(ALCdevice *device); +void ALCdevice_Unlock(ALCdevice *device); +ALint64 ALCdevice_GetLatency(ALCdevice *device); + +inline void LockContext(ALCcontext *context) +{ ALCdevice_Lock(context->Device); } + +inline void UnlockContext(ALCcontext *context) +{ ALCdevice_Unlock(context->Device); } + + +void *al_malloc(size_t alignment, size_t size); +void *al_calloc(size_t alignment, size_t size); +void al_free(void *ptr); + + +typedef struct { +#ifdef HAVE_FENV_H + DERIVE_FROM_TYPE(fenv_t); +#else + int state; +#endif +#ifdef HAVE_SSE + int sse_state; +#endif +} FPUCtl; +void SetMixerFPUMode(FPUCtl *ctl); +void RestoreFPUMode(const FPUCtl *ctl); + + +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 GetConfigValueBool(const char *blockName, const char *keyName, int def); +int ConfigValueStr(const char *blockName, const char *keyName, const char **ret); +int ConfigValueInt(const char *blockName, const char *keyName, int *ret); +int ConfigValueUInt(const char *blockName, const char *keyName, unsigned int *ret); +int ConfigValueFloat(const char *blockName, const char *keyName, float *ret); + +void SetRTPriority(void); + +void SetDefaultChannelOrder(ALCdevice *device); +void SetDefaultWFXChannelOrder(ALCdevice *device); + +const ALCchar *DevFmtTypeString(enum DevFmtType type) DECL_CONST; +const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans) DECL_CONST; + + +extern FILE *LogFile; + +#if defined(__GNUC__) && !defined(IN_IDE_PARSER) +#define AL_PRINT(T, MSG, ...) fprintf(LogFile, "AL lib: %s %s: "MSG, T, __FUNCTION__ , ## __VA_ARGS__) +#else +void al_print(const char *type, const char *func, const char *fmt, ...) DECL_FORMAT(printf, 3,4); +#define AL_PRINT(T, ...) al_print((T), __FUNCTION__, __VA_ARGS__) +#endif + +enum LogLevel { + NoLog, + LogError, + LogWarning, + LogTrace, + LogRef +}; +extern enum LogLevel LogLevel; + +#define TRACEREF(...) do { \ + if(LogLevel >= LogRef) \ + AL_PRINT("(--)", __VA_ARGS__); \ +} while(0) + +#define TRACE(...) do { \ + if(LogLevel >= LogTrace) \ + AL_PRINT("(II)", __VA_ARGS__); \ +} while(0) + +#define WARN(...) do { \ + if(LogLevel >= LogWarning) \ + AL_PRINT("(WW)", __VA_ARGS__); \ +} while(0) + +#define ERR(...) do { \ + if(LogLevel >= LogError) \ + AL_PRINT("(EE)", __VA_ARGS__); \ +} while(0) + + +extern ALint RTPrioLevel; + + +extern ALuint CPUCapFlags; +enum { + CPU_CAP_SSE = 1<<0, + CPU_CAP_SSE2 = 1<<1, + CPU_CAP_SSE4_1 = 1<<2, + CPU_CAP_NEON = 1<<3, +}; + +void FillCPUCaps(ALuint capfilter); + +FILE *OpenDataFile(const char *fname, const char *subdir); + +/* Small hack to use a pointer-to-array type as a normal argument type. + * Shouldn't be used directly. */ +typedef ALfloat ALfloatBUFFERSIZE[BUFFERSIZE]; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/openal-soft/OpenAL32/Include/alMidi.h b/libs/openal-soft/OpenAL32/Include/alMidi.h new file mode 100644 index 00000000..9d9fa6c4 --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/alMidi.h @@ -0,0 +1,172 @@ +#ifndef ALMIDI_H +#define ALMIDI_H + +#include "alMain.h" +#include "atomic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ALsfmodulator { + struct { + ALenum Input; + ALenum Type; + ALenum Form; + } Source[2]; + ALint Amount; + ALenum TransformOp; + ALenum Dest; +} ALsfmodulator; + +typedef struct ALenvelope { + ALint DelayTime; + ALint AttackTime; + ALint HoldTime; + ALint DecayTime; + ALint SustainAttn; + ALint ReleaseTime; + ALint KeyToHoldTime; + ALint KeyToDecayTime; +} ALenvelope; + + +typedef struct ALfontsound { + RefCount ref; + + struct ALbuffer *Buffer; + + ALint MinKey, MaxKey; + ALint MinVelocity, MaxVelocity; + + ALint ModLfoToPitch; + ALint VibratoLfoToPitch; + ALint ModEnvToPitch; + + ALint FilterCutoff; + ALint FilterQ; + ALint ModLfoToFilterCutoff; + ALint ModEnvToFilterCutoff; + ALint ModLfoToVolume; + + ALint ChorusSend; + ALint ReverbSend; + + ALint Pan; + + struct { + ALint Delay; + ALint Frequency; + } ModLfo; + struct { + ALint Delay; + ALint Frequency; + } VibratoLfo; + + ALenvelope ModEnv; + ALenvelope VolEnv; + + ALint Attenuation; + + ALint CoarseTuning; + ALint FineTuning; + + ALenum LoopMode; + + ALint TuningScale; + + ALint ExclusiveClass; + + ALuint Start; + ALuint End; + ALuint LoopStart; + ALuint LoopEnd; + ALuint SampleRate; + ALubyte PitchKey; + ALbyte PitchCorrection; + ALenum SampleType; + struct ALfontsound *Link; + + /* NOTE: Each map entry contains *four* (4) ALsfmodulator objects. */ + UIntMap ModulatorMap; + + ALuint id; +} ALfontsound; + +void ALfontsound_setPropi(ALfontsound *self, ALCcontext *context, ALenum param, ALint value); +void ALfontsound_setModStagei(ALfontsound *self, ALCcontext *context, ALsizei stage, ALenum param, ALint value); + +ALfontsound *NewFontsound(ALCcontext *context); +void DeleteFontsound(ALCdevice *device, ALfontsound *sound); + +inline struct ALfontsound *LookupFontsound(ALCdevice *device, ALuint id) +{ return (struct ALfontsound*)LookupUIntMapKey(&device->FontsoundMap, id); } +inline struct ALfontsound *RemoveFontsound(ALCdevice *device, ALuint id) +{ return (struct ALfontsound*)RemoveUIntMapKey(&device->FontsoundMap, id); } + +void ReleaseALFontsounds(ALCdevice *device); + + +typedef struct ALsfpreset { + RefCount ref; + + ALint Preset; /* a.k.a. MIDI program number */ + ALint Bank; /* MIDI bank 0...127, or percussion (bank 128) */ + + ALfontsound **Sounds; + ALsizei NumSounds; + + ALuint id; +} ALsfpreset; + +ALsfpreset *NewPreset(ALCcontext *context); +void DeletePreset(ALCdevice *device, ALsfpreset *preset); + +inline struct ALsfpreset *LookupPreset(ALCdevice *device, ALuint id) +{ return (struct ALsfpreset*)LookupUIntMapKey(&device->PresetMap, id); } +inline struct ALsfpreset *RemovePreset(ALCdevice *device, ALuint id) +{ return (struct ALsfpreset*)RemoveUIntMapKey(&device->PresetMap, id); } + +void ReleaseALPresets(ALCdevice *device); + + +typedef struct ALsoundfont { + RefCount ref; + + ALsfpreset **Presets; + ALsizei NumPresets; + + RWLock Lock; + + ALuint id; +} ALsoundfont; + +ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context); +void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device); + +inline struct ALsoundfont *LookupSfont(ALCdevice *device, ALuint id) +{ return (struct ALsoundfont*)LookupUIntMapKey(&device->SfontMap, id); } +inline struct ALsoundfont *RemoveSfont(ALCdevice *device, ALuint id) +{ return (struct ALsoundfont*)RemoveUIntMapKey(&device->SfontMap, id); } + +void ReleaseALSoundfonts(ALCdevice *device); + + +inline ALboolean IsValidCtrlInput(int cc) +{ + /* These correspond to MIDI functions, not real controller values. */ + if(cc == 0 || cc == 6 || cc == 32 || cc == 38 || (cc >= 98 && cc <= 101) || cc >= 120) + return AL_FALSE; + /* These are the LSB components of CC0...CC31, which are automatically used when + * reading the MSB controller value. */ + if(cc >= 32 && cc <= 63) + return AL_FALSE; + /* All the rest are okay! */ + return AL_TRUE; +} + +#ifdef __cplusplus +} +#endif + +#endif /* ALMIDI_H */ diff --git a/libs/openal-soft/OpenAL32/Include/alSource.h b/libs/openal-soft/OpenAL32/Include/alSource.h new file mode 100644 index 00000000..8d74fc54 --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/alSource.h @@ -0,0 +1,147 @@ +#ifndef _AL_SOURCE_H_ +#define _AL_SOURCE_H_ + +#define MAX_SENDS 4 + +#include "alMain.h" +#include "alu.h" +#include "hrtf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern enum Resampler DefaultResampler; + +extern const ALsizei ResamplerPadding[ResamplerMax]; +extern const ALsizei ResamplerPrePadding[ResamplerMax]; + + +typedef struct ALbufferlistitem { + struct ALbuffer *buffer; + struct ALbufferlistitem *volatile next; + struct ALbufferlistitem *volatile prev; +} ALbufferlistitem; + + +typedef struct ALactivesource { + struct ALsource *Source; + + /** Method to update mixing parameters. */ + ALvoid (*Update)(struct ALactivesource *self, const ALCcontext *context); + + /** Current target parameters used for mixing. */ + ALint Step; + + ALboolean IsHrtf; + + ALuint Offset; /* Number of output samples mixed since starting. */ + + DirectParams Direct; + SendParams Send[MAX_SENDS]; +} ALactivesource; + + +typedef struct ALsource { + /** Source properties. */ + volatile ALfloat Pitch; + volatile ALfloat Gain; + volatile ALfloat OuterGain; + volatile ALfloat MinGain; + volatile ALfloat MaxGain; + volatile ALfloat InnerAngle; + volatile ALfloat OuterAngle; + volatile ALfloat RefDistance; + volatile ALfloat MaxDistance; + volatile ALfloat RollOffFactor; + volatile ALfloat Position[3]; + volatile ALfloat Velocity[3]; + volatile ALfloat Orientation[3]; + volatile ALboolean HeadRelative; + volatile ALboolean Looping; + volatile enum DistanceModel DistanceModel; + volatile ALboolean DirectChannels; + + volatile ALboolean DryGainHFAuto; + volatile ALboolean WetGainAuto; + volatile ALboolean WetGainHFAuto; + volatile ALfloat OuterGainHF; + + volatile ALfloat AirAbsorptionFactor; + volatile ALfloat RoomRolloffFactor; + volatile ALfloat DopplerFactor; + + volatile ALfloat Radius; + + enum Resampler Resampler; + + /** + * Last user-specified offset, and the offset type (bytes, samples, or + * seconds). + */ + ALdouble Offset; + ALenum OffsetType; + + /** Source type (static, streaming, or undetermined) */ + volatile ALint SourceType; + + /** Source state (initial, playing, paused, or stopped) */ + volatile ALenum state; + ALenum new_state; + + /** + * Source offset in samples, relative to the currently playing buffer, NOT + * the whole queue, and the fractional (fixed-point) offset to the next + * sample. + */ + ALuint position; + ALuint position_fraction; + + /** Source Buffer Queue info. */ + ATOMIC(ALbufferlistitem*) queue; + ATOMIC(ALbufferlistitem*) current_buffer; + RWLock queue_lock; + + /** Current buffer sample info. */ + ALuint NumChannels; + ALuint SampleSize; + + /** Direct filter and auxiliary send info. */ + struct { + ALfloat Gain; + ALfloat GainHF; + ALfloat HFReference; + ALfloat GainLF; + ALfloat LFReference; + } Direct; + struct { + struct ALeffectslot *Slot; + ALfloat Gain; + ALfloat GainHF; + ALfloat HFReference; + ALfloat GainLF; + ALfloat LFReference; + } Send[MAX_SENDS]; + + /** Source needs to update its mixing parameters. */ + ATOMIC(ALenum) NeedsUpdate; + + /** Self ID */ + ALuint id; +} ALsource; + +inline struct ALsource *LookupSource(ALCcontext *context, ALuint id) +{ return (struct ALsource*)LookupUIntMapKey(&context->SourceMap, id); } +inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id) +{ return (struct ALsource*)RemoveUIntMapKey(&context->SourceMap, id); } + +ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state); +ALboolean ApplyOffset(ALsource *Source); + +ALvoid ReleaseALSources(ALCcontext *Context); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/openal-soft/OpenAL32/Include/alThunk.h b/libs/openal-soft/OpenAL32/Include/alThunk.h new file mode 100644 index 00000000..adc77dec --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/alThunk.h @@ -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 --git a/libs/openal-soft/OpenAL32/Include/alu.h b/libs/openal-soft/OpenAL32/Include/alu.h new file mode 100644 index 00000000..7727c666 --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/alu.h @@ -0,0 +1,236 @@ +#ifndef _ALU_H_ +#define _ALU_H_ + +#include +#include +#ifdef HAVE_FLOAT_H +#include +#endif +#ifdef HAVE_IEEEFP_H +#include +#endif + +#include "alMain.h" +#include "alBuffer.h" +#include "alFilter.h" + +#include "hrtf.h" +#include "align.h" + + +#define F_PI (3.14159265358979323846f) +#define F_PI_2 (1.57079632679489661923f) +#define F_2PI (6.28318530717958647692f) + +#ifndef FLT_EPSILON +#define FLT_EPSILON (1.19209290e-07f) +#endif + +#define DEG2RAD(x) ((ALfloat)(x) * (F_PI/180.0f)) +#define RAD2DEG(x) ((ALfloat)(x) * (180.0f/F_PI)) + + +#define SRC_HISTORY_BITS (6) +#define SRC_HISTORY_LENGTH (1< b) ? b : a); } +inline ALfloat maxf(ALfloat a, ALfloat b) +{ return ((a > b) ? a : b); } +inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max) +{ return minf(max, maxf(min, val)); } + +inline ALdouble mind(ALdouble a, ALdouble b) +{ return ((a > b) ? b : a); } +inline ALdouble maxd(ALdouble a, ALdouble b) +{ return ((a > b) ? a : b); } +inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max) +{ return mind(max, maxd(min, val)); } + +inline ALuint minu(ALuint a, ALuint b) +{ return ((a > b) ? b : a); } +inline ALuint maxu(ALuint a, ALuint b) +{ return ((a > b) ? a : b); } +inline ALuint clampu(ALuint val, ALuint min, ALuint max) +{ return minu(max, maxu(min, val)); } + +inline ALint mini(ALint a, ALint b) +{ return ((a > b) ? b : a); } +inline ALint maxi(ALint a, ALint b) +{ return ((a > b) ? a : b); } +inline ALint clampi(ALint val, ALint min, ALint max) +{ return mini(max, maxi(min, val)); } + +inline ALint64 mini64(ALint64 a, ALint64 b) +{ return ((a > b) ? b : a); } +inline ALint64 maxi64(ALint64 a, ALint64 b) +{ return ((a > b) ? a : b); } +inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max) +{ return mini64(max, maxi64(min, val)); } + +inline ALuint64 minu64(ALuint64 a, ALuint64 b) +{ return ((a > b) ? b : a); } +inline ALuint64 maxu64(ALuint64 a, ALuint64 b) +{ return ((a > b) ? a : b); } +inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max) +{ return minu64(max, maxu64(min, val)); } + + +inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu) +{ + return val1 + (val2-val1)*mu; +} +inline ALfloat cubic(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat mu) +{ + ALfloat mu2 = mu*mu; + ALfloat a0 = -0.5f*val0 + 1.5f*val1 + -1.5f*val2 + 0.5f*val3; + ALfloat a1 = val0 + -2.5f*val1 + 2.0f*val2 + -0.5f*val3; + ALfloat a2 = -0.5f*val0 + 0.5f*val2; + ALfloat a3 = val1; + + return a0*mu*mu2 + a1*mu2 + a2*mu + a3; +} + + +ALvoid aluInitPanning(ALCdevice *Device); + +/** + * ComputeAngleGains + * + * Sets channel gains based on a given source's angle and its half-width. The + * angle and hwidth parameters are in radians. + */ +void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat gains[MaxChannels]); + +/** + * SetGains + * + * Helper to set the appropriate channels to the specified gain. + */ +inline void SetGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MaxChannels]) +{ + ComputeAngleGains(device, 0.0f, F_PI, ingain, gains); +} + + +ALvoid CalcSourceParams(struct ALactivesource *src, const ALCcontext *ALContext); +ALvoid CalcNonAttnSourceParams(struct ALactivesource *src, const ALCcontext *ALContext); + +ALvoid MixSource(struct ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo); + +ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size); +/* Caller must lock the device. */ +ALvoid aluHandleDisconnect(ALCdevice *device); + +extern ALfloat ConeScale; +extern ALfloat ZScale; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libs/openal-soft/OpenAL32/Include/bs2b.h b/libs/openal-soft/OpenAL32/Include/bs2b.h new file mode 100644 index 00000000..f1f3627a --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/bs2b.h @@ -0,0 +1,128 @@ +/*- + * 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 */ + float a0_lo; + float b1_lo; + + /* Highboost IIR filter coefficients */ + float a0_hi; + float a1_hi; + float b1_hi; + + /* Buffer of last filtered sample. + * [0] - first channel, [1] - second channel + */ + struct t_last_sample { + float asis[2]; + float lo[2]; + float 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); + +/* Crossfeeds one stereo sample that are pointed by sample. + * [0] - first channel, [1] - second channel. + * Returns crossfided sample by sample pointer. + */ +inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict sample) +{ +/* 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)) + + /* 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]; +#undef hi_filter +#undef lo_filter +} /* bs2b_cross_feed */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* BS2B_H */ diff --git a/libs/openal-soft/OpenAL32/Include/sample_cvt.h b/libs/openal-soft/OpenAL32/Include/sample_cvt.h new file mode 100644 index 00000000..12bb1fa6 --- /dev/null +++ b/libs/openal-soft/OpenAL32/Include/sample_cvt.h @@ -0,0 +1,9 @@ +#ifndef SAMPLE_CVT_H +#define SAMPLE_CVT_H + +#include "AL/al.h" +#include "alBuffer.h" + +void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len, ALsizei align); + +#endif /* SAMPLE_CVT_H */ diff --git a/libs/openal-soft/OpenAL32/alAuxEffectSlot.c b/libs/openal-soft/OpenAL32/alAuxEffectSlot.c new file mode 100644 index 00000000..ec8890b6 --- /dev/null +++ b/libs/openal-soft/OpenAL32/alAuxEffectSlot.c @@ -0,0 +1,550 @@ +/** + * 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" + + +extern inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id); +extern inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id); + +static ALenum AddEffectSlotArray(ALCcontext *Context, ALeffectslot **start, ALsizei count); +static void RemoveEffectSlotArray(ALCcontext *Context, const ALeffectslot *slot); + + +static UIntMap EffectStateFactoryMap; +static inline ALeffectStateFactory *getFactoryByType(ALenum type) +{ + ALeffectStateFactory* (*getFactory)(void) = LookupUIntMapKey(&EffectStateFactoryMap, type); + if(getFactory != NULL) + return getFactory(); + return NULL; +} + + +AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots) +{ + ALCcontext *context; + VECTOR(ALeffectslot*) slotvec; + ALsizei cur; + ALenum err; + + context = GetContextRef(); + if(!context) return; + + VECTOR_INIT(slotvec); + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(!VECTOR_RESERVE(slotvec, n)) + SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); + + for(cur = 0;cur < n;cur++) + { + ALeffectslot *slot = al_calloc(16, sizeof(ALeffectslot)); + err = AL_OUT_OF_MEMORY; + if(!slot || (err=InitEffectSlot(slot)) != AL_NO_ERROR) + { + al_free(slot); + alDeleteAuxiliaryEffectSlots(cur, effectslots); + SET_ERROR_AND_GOTO(context, err, done); + } + + err = NewThunkEntry(&slot->id); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&context->EffectSlotMap, slot->id, slot); + if(err != AL_NO_ERROR) + { + FreeThunkEntry(slot->id); + DELETE_OBJ(slot->EffectState); + al_free(slot); + + alDeleteAuxiliaryEffectSlots(cur, effectslots); + SET_ERROR_AND_GOTO(context, err, done); + } + + VECTOR_PUSH_BACK(slotvec, slot); + + effectslots[cur] = slot->id; + } + err = AddEffectSlotArray(context, VECTOR_ITER_BEGIN(slotvec), n); + if(err != AL_NO_ERROR) + { + alDeleteAuxiliaryEffectSlots(cur, effectslots); + SET_ERROR_AND_GOTO(context, err, done); + } + +done: + VECTOR_DEINIT(slotvec); + + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots) +{ + ALCcontext *context; + ALeffectslot *slot; + ALsizei i; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + for(i = 0;i < n;i++) + { + if((slot=LookupEffectSlot(context, effectslots[i])) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(ReadRef(&slot->ref) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + } + + // All effectslots are valid + for(i = 0;i < n;i++) + { + if((slot=RemoveEffectSlot(context, effectslots[i])) == NULL) + continue; + FreeThunkEntry(slot->id); + + RemoveEffectSlotArray(context, slot); + DELETE_OBJ(slot->EffectState); + + memset(slot, 0, sizeof(*slot)); + al_free(slot); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot) +{ + ALCcontext *context; + ALboolean ret; + + context = GetContextRef(); + if(!context) return AL_FALSE; + + ret = (LookupEffectSlot(context, effectslot) ? AL_TRUE : AL_FALSE); + + ALCcontext_DecRef(context); + + return ret; +} + +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value) +{ + ALCdevice *device; + ALCcontext *context; + ALeffectslot *slot; + ALeffect *effect = NULL; + ALenum err; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((slot=LookupEffectSlot(context, effectslot)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + switch(param) + { + case AL_EFFECTSLOT_EFFECT: + effect = (value ? LookupEffect(device, value) : NULL); + if(!(value == 0 || effect != NULL)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + err = InitializeEffect(device, slot, effect); + if(err != AL_NO_ERROR) + SET_ERROR_AND_GOTO(context, err, done); + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + break; + + case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: + if(!(value == AL_TRUE || value == AL_FALSE)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + slot->AuxSendAuto = value; + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values) +{ + ALCcontext *context; + + switch(param) + { + case AL_EFFECTSLOT_EFFECT: + case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: + alAuxiliaryEffectSloti(effectslot, param, values[0]); + return; + } + + context = GetContextRef(); + if(!context) return; + + if(LookupEffectSlot(context, effectslot) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value) +{ + ALCcontext *context; + ALeffectslot *slot; + + context = GetContextRef(); + if(!context) return; + + if((slot=LookupEffectSlot(context, effectslot)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + switch(param) + { + case AL_EFFECTSLOT_GAIN: + if(!(value >= 0.0f && value <= 1.0f)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + slot->Gain = value; + ATOMIC_STORE(&slot->NeedsUpdate, AL_TRUE); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values) +{ + ALCcontext *context; + + switch(param) + { + case AL_EFFECTSLOT_GAIN: + alAuxiliaryEffectSlotf(effectslot, param, values[0]); + return; + } + + context = GetContextRef(); + if(!context) return; + + if(LookupEffectSlot(context, effectslot) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value) +{ + ALCcontext *context; + ALeffectslot *slot; + + context = GetContextRef(); + if(!context) return; + + if((slot=LookupEffectSlot(context, effectslot)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + switch(param) + { + case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: + *value = slot->AuxSendAuto; + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values) +{ + ALCcontext *context; + + switch(param) + { + case AL_EFFECTSLOT_EFFECT: + case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: + alGetAuxiliaryEffectSloti(effectslot, param, values); + return; + } + + context = GetContextRef(); + if(!context) return; + + if(LookupEffectSlot(context, effectslot) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value) +{ + ALCcontext *context; + ALeffectslot *slot; + + context = GetContextRef(); + if(!context) return; + + if((slot=LookupEffectSlot(context, effectslot)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + switch(param) + { + case AL_EFFECTSLOT_GAIN: + *value = slot->Gain; + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values) +{ + ALCcontext *context; + + switch(param) + { + case AL_EFFECTSLOT_GAIN: + alGetAuxiliaryEffectSlotf(effectslot, param, values); + return; + } + + context = GetContextRef(); + if(!context) return; + + if(LookupEffectSlot(context, effectslot) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +static ALenum AddEffectSlotArray(ALCcontext *context, ALeffectslot **start, ALsizei count) +{ + ALenum err = AL_NO_ERROR; + + LockContext(context); + if(!VECTOR_INSERT(context->ActiveAuxSlots, VECTOR_ITER_END(context->ActiveAuxSlots), start, start+count)) + err = AL_OUT_OF_MEMORY; + UnlockContext(context); + + return err; +} + +static void RemoveEffectSlotArray(ALCcontext *context, const ALeffectslot *slot) +{ + ALeffectslot **iter; + + LockContext(context); +#define MATCH_SLOT(_i) (slot == *(_i)) + VECTOR_FIND_IF(iter, ALeffectslot*, context->ActiveAuxSlots, MATCH_SLOT); + if(iter != VECTOR_ITER_END(context->ActiveAuxSlots)) + { + *iter = VECTOR_BACK(context->ActiveAuxSlots); + VECTOR_POP_BACK(context->ActiveAuxSlots); + } +#undef MATCH_SLOT + UnlockContext(context); +} + + +void InitEffectFactoryMap(void) +{ + InitUIntMap(&EffectStateFactoryMap, ~0); + + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_NULL, ALnullStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EAXREVERB, ALreverbStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_REVERB, ALreverbStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_AUTOWAH, ALautowahStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_CHORUS, ALchorusStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_COMPRESSOR, ALcompressorStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DISTORTION, ALdistortionStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_ECHO, ALechoStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EQUALIZER, ALequalizerStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_FLANGER, ALflangerStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_RING_MODULATOR, ALmodulatorStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_DIALOGUE, ALdedicatedStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, ALdedicatedStateFactory_getFactory); +} + +void DeinitEffectFactoryMap(void) +{ + ResetUIntMap(&EffectStateFactoryMap); +} + + +ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect) +{ + ALenum newtype = (effect ? effect->type : AL_EFFECT_NULL); + ALeffectStateFactory *factory; + + if(newtype != EffectSlot->EffectType) + { + ALeffectState *State; + FPUCtl oldMode; + + factory = getFactoryByType(newtype); + if(!factory) + { + ERR("Failed to find factory for effect type 0x%04x\n", newtype); + return AL_INVALID_ENUM; + } + State = V0(factory,create)(); + if(!State) + return AL_OUT_OF_MEMORY; + + SetMixerFPUMode(&oldMode); + + ALCdevice_Lock(Device); + if(V(State,deviceUpdate)(Device) == AL_FALSE) + { + ALCdevice_Unlock(Device); + RestoreFPUMode(&oldMode); + DELETE_OBJ(State); + return AL_OUT_OF_MEMORY; + } + + State = ExchangePtr((XchgPtr*)&EffectSlot->EffectState, State); + if(!effect) + { + memset(&EffectSlot->EffectProps, 0, sizeof(EffectSlot->EffectProps)); + EffectSlot->EffectType = AL_EFFECT_NULL; + } + else + { + memcpy(&EffectSlot->EffectProps, &effect->Props, sizeof(effect->Props)); + EffectSlot->EffectType = effect->type; + } + + /* FIXME: This should be done asynchronously, but since the EffectState + * object was changed, it needs an update before its Process method can + * be called. */ + ATOMIC_STORE(&EffectSlot->NeedsUpdate, AL_FALSE); + V(EffectSlot->EffectState,update)(Device, EffectSlot); + ALCdevice_Unlock(Device); + + RestoreFPUMode(&oldMode); + + DELETE_OBJ(State); + State = NULL; + } + else + { + if(effect) + { + ALCdevice_Lock(Device); + memcpy(&EffectSlot->EffectProps, &effect->Props, sizeof(effect->Props)); + ALCdevice_Unlock(Device); + ATOMIC_STORE(&EffectSlot->NeedsUpdate, AL_TRUE); + } + } + + return AL_NO_ERROR; +} + + +ALenum InitEffectSlot(ALeffectslot *slot) +{ + ALeffectStateFactory *factory; + ALuint i, c; + + slot->EffectType = AL_EFFECT_NULL; + + factory = getFactoryByType(AL_EFFECT_NULL); + if(!(slot->EffectState=V0(factory,create)())) + return AL_OUT_OF_MEMORY; + + slot->Gain = 1.0; + slot->AuxSendAuto = AL_TRUE; + ATOMIC_INIT(&slot->NeedsUpdate, AL_FALSE); + for(c = 0;c < 1;c++) + { + for(i = 0;i < BUFFERSIZE;i++) + slot->WetBuffer[c][i] = 0.0f; + } + InitRef(&slot->ref, 0); + + return AL_NO_ERROR; +} + +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; + + DELETE_OBJ(temp->EffectState); + + FreeThunkEntry(temp->id); + memset(temp, 0, sizeof(ALeffectslot)); + al_free(temp); + } +} diff --git a/libs/openal-soft/OpenAL32/alBuffer.c b/libs/openal-soft/OpenAL32/alBuffer.c new file mode 100644 index 00000000..d03f30a4 --- /dev/null +++ b/libs/openal-soft/OpenAL32/alBuffer.c @@ -0,0 +1,1329 @@ +/** + * 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 +#ifdef HAVE_MALLOC_H +#include +#endif + +#include "alMain.h" +#include "alu.h" +#include "alError.h" +#include "alBuffer.h" +#include "alThunk.h" +#include "sample_cvt.h" + + +extern inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id); +extern inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id); +extern inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type); +extern inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type); + +static ALboolean IsValidType(ALenum type) DECL_CONST; +static ALboolean IsValidChannels(ALenum channels) DECL_CONST; +static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtType *type) DECL_CONST; +static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type) DECL_CONST; +static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align); + + +AL_API ALvoid AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) +{ + ALCcontext *context; + ALsizei cur = 0; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + for(cur = 0;cur < n;cur++) + { + ALbuffer *buffer = NewBuffer(context); + if(!buffer) + { + alDeleteBuffers(cur, buffers); + break; + } + + buffers[cur] = buffer->id; + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers) +{ + ALCdevice *device; + ALCcontext *context; + ALbuffer *ALBuf; + ALsizei i; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + for(i = 0;i < n;i++) + { + if(!buffers[i]) + continue; + + /* Check for valid Buffer ID */ + if((ALBuf=LookupBuffer(device, buffers[i])) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(ReadRef(&ALBuf->ref) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + } + + for(i = 0;i < n;i++) + { + if((ALBuf=LookupBuffer(device, buffers[i])) != NULL) + DeleteBuffer(device, ALBuf); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer) +{ + ALCcontext *context; + ALboolean ret; + + context = GetContextRef(); + if(!context) return AL_FALSE; + + ret = ((!buffer || LookupBuffer(context->Device, buffer)) ? + AL_TRUE : AL_FALSE); + + ALCcontext_DecRef(context); + + return ret; +} + + +AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq) +{ + enum UserFmtChannels srcchannels; + enum UserFmtType srctype; + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + ALenum newformat = AL_NONE; + ALuint framesize; + ALsizei align; + ALenum err; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((albuf=LookupBuffer(device, buffer)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(!(size >= 0 && freq > 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE) + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + + align = albuf->UnpackAlign; + if(SanitizeAlignment(srctype, &align) == AL_FALSE) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(srctype) + { + case UserFmtByte: + case UserFmtUByte: + case UserFmtShort: + case UserFmtUShort: + case UserFmtFloat: + framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align; + if((size%framesize) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + err = LoadData(albuf, freq, format, size/framesize*align, + srcchannels, srctype, data, align, AL_TRUE); + if(err != AL_NO_ERROR) + SET_ERROR_AND_GOTO(context, err, done); + break; + + case UserFmtInt: + case UserFmtUInt: + case UserFmtByte3: + case UserFmtUByte3: + case UserFmtDouble: + framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align; + if((size%framesize) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + 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; + } + err = LoadData(albuf, freq, newformat, size/framesize*align, + srcchannels, srctype, data, align, AL_TRUE); + if(err != AL_NO_ERROR) + SET_ERROR_AND_GOTO(context, err, done); + break; + + case UserFmtMulaw: + case UserFmtAlaw: + framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align; + if((size%framesize) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + 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; + } + err = LoadData(albuf, freq, newformat, size/framesize*align, + srcchannels, srctype, data, align, AL_TRUE); + if(err != AL_NO_ERROR) + SET_ERROR_AND_GOTO(context, err, done); + break; + + case UserFmtIMA4: + framesize = (align-1)/2 + 4; + framesize *= ChannelsFromUserFmt(srcchannels); + if((size%framesize) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + 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; + } + err = LoadData(albuf, freq, newformat, size/framesize*align, + srcchannels, srctype, data, align, AL_TRUE); + if(err != AL_NO_ERROR) + SET_ERROR_AND_GOTO(context, err, done); + break; + + case UserFmtMSADPCM: + framesize = (align-2)/2 + 7; + framesize *= ChannelsFromUserFmt(srcchannels); + if((size%framesize) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + 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; + } + err = LoadData(albuf, freq, newformat, size/framesize*align, + srcchannels, srctype, data, align, AL_TRUE); + if(err != AL_NO_ERROR) + SET_ERROR_AND_GOTO(context, err, done); + break; + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) +{ + enum UserFmtChannels srcchannels; + enum UserFmtType srctype; + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + ALuint byte_align; + ALuint channels; + ALuint bytes; + ALsizei align; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((albuf=LookupBuffer(device, buffer)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(!(length >= 0 && offset >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE) + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + + WriteLock(&albuf->lock); + align = albuf->UnpackAlign; + if(SanitizeAlignment(srctype, &align) == AL_FALSE) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + if(srcchannels != albuf->OriginalChannels || srctype != albuf->OriginalType) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + if(align != albuf->OriginalAlign) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + + if(albuf->OriginalType == UserFmtIMA4) + { + byte_align = (albuf->OriginalAlign-1)/2 + 4; + byte_align *= ChannelsFromUserFmt(albuf->OriginalChannels); + } + else if(albuf->OriginalType == UserFmtMSADPCM) + { + byte_align = (albuf->OriginalAlign-2)/2 + 7; + byte_align *= ChannelsFromUserFmt(albuf->OriginalChannels); + } + else + { + byte_align = albuf->OriginalAlign; + byte_align *= FrameSizeFromUserFmt(albuf->OriginalChannels, + albuf->OriginalType); + } + + if(offset > albuf->OriginalSize || length > albuf->OriginalSize-offset || + (offset%byte_align) != 0 || (length%byte_align) != 0) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + + channels = ChannelsFromFmt(albuf->FmtChannels); + bytes = BytesFromFmt(albuf->FmtType); + /* offset -> byte offset, length -> sample count */ + offset = offset/byte_align * channels*bytes; + length = length/byte_align * albuf->OriginalAlign; + + ConvertData((char*)albuf->data+offset, (enum UserFmtType)albuf->FmtType, + data, srctype, channels, length, align); + WriteUnlock(&albuf->lock); + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, + ALuint samplerate, ALenum internalformat, ALsizei samples, + ALenum channels, ALenum type, const ALvoid *data) +{ + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + ALsizei align; + ALenum err; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((albuf=LookupBuffer(device, buffer)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(!(samples >= 0 && samplerate != 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(IsValidType(type) == AL_FALSE || IsValidChannels(channels) == AL_FALSE) + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + + align = albuf->UnpackAlign; + if(SanitizeAlignment(type, &align) == AL_FALSE) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if((samples%align) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + err = LoadData(albuf, samplerate, internalformat, samples, + channels, type, data, align, AL_FALSE); + if(err != AL_NO_ERROR) + SET_ERROR_AND_GOTO(context, err, done); + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, + ALsizei offset, ALsizei samples, + ALenum channels, ALenum type, const ALvoid *data) +{ + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + ALsizei align; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((albuf=LookupBuffer(device, buffer)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(!(samples >= 0 && offset >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(IsValidType(type) == AL_FALSE) + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + + WriteLock(&albuf->lock); + align = albuf->UnpackAlign; + if(SanitizeAlignment(type, &align) == AL_FALSE) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + if(channels != (ALenum)albuf->FmtChannels) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + if(offset > albuf->SampleLen || samples > albuf->SampleLen-offset) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + if((samples%align) != 0) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + + /* offset -> byte offset */ + offset *= FrameSizeFromFmt(albuf->FmtChannels, albuf->FmtType); + ConvertData((char*)albuf->data+offset, (enum UserFmtType)albuf->FmtType, + data, type, ChannelsFromFmt(albuf->FmtChannels), samples, align); + WriteUnlock(&albuf->lock); + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, + ALsizei offset, ALsizei samples, + ALenum channels, ALenum type, ALvoid *data) +{ + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + ALsizei align; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((albuf=LookupBuffer(device, buffer)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(!(samples >= 0 && offset >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(IsValidType(type) == AL_FALSE) + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + + ReadLock(&albuf->lock); + align = albuf->PackAlign; + if(SanitizeAlignment(type, &align) == AL_FALSE) + { + ReadUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + if(channels != (ALenum)albuf->FmtChannels) + { + ReadUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + if(offset > albuf->SampleLen || samples > albuf->SampleLen-offset) + { + ReadUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + if((samples%align) != 0) + { + ReadUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + + /* offset -> byte offset */ + offset *= FrameSizeFromFmt(albuf->FmtChannels, albuf->FmtType); + ConvertData(data, type, (char*)albuf->data+offset, (enum UserFmtType)albuf->FmtType, + ChannelsFromFmt(albuf->FmtChannels), samples, align); + ReadUnlock(&albuf->lock); + +done: + ALCcontext_DecRef(context); +} + +AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format) +{ + enum FmtChannels dstchannels; + enum FmtType dsttype; + ALCcontext *context; + ALboolean ret; + + context = GetContextRef(); + if(!context) return AL_FALSE; + + ret = DecomposeFormat(format, &dstchannels, &dsttype); + + ALCcontext_DecRef(context); + + return ret; +} + + +AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat UNUSED(value)) +{ + ALCdevice *device; + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(LookupBuffer(device, buffer) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat UNUSED(value1), ALfloat UNUSED(value2), ALfloat UNUSED(value3)) +{ + ALCdevice *device; + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(LookupBuffer(device, buffer) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values) +{ + ALCdevice *device; + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(LookupBuffer(device, buffer) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value) +{ + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((albuf=LookupBuffer(device, buffer)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + switch(param) + { + case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: + if(!(value >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + ExchangeInt(&albuf->UnpackAlign, value); + break; + + case AL_PACK_BLOCK_ALIGNMENT_SOFT: + if(!(value >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + ExchangeInt(&albuf->PackAlign, value); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint UNUSED(value1), ALint UNUSED(value2), ALint UNUSED(value3)) +{ + ALCdevice *device; + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(LookupBuffer(device, buffer) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values) +{ + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + + if(values) + { + switch(param) + { + case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: + case AL_PACK_BLOCK_ALIGNMENT_SOFT: + alBufferi(buffer, param, values[0]); + return; + } + } + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((albuf=LookupBuffer(device, buffer)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + case AL_LOOP_POINTS_SOFT: + WriteLock(&albuf->lock); + if(ReadRef(&albuf->ref) != 0) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + } + if(values[0] >= values[1] || values[0] < 0 || + values[1] > albuf->SampleLen) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + + albuf->LoopStart = values[0]; + albuf->LoopEnd = values[1]; + WriteUnlock(&albuf->lock); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value) +{ + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((albuf=LookupBuffer(device, buffer)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + if(!(value)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + case AL_SEC_LENGTH_SOFT: + ReadLock(&albuf->lock); + if(albuf->SampleLen != 0) + *value = albuf->SampleLen / (ALfloat)albuf->Frequency; + else + *value = 0.0f; + ReadUnlock(&albuf->lock); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) +{ + ALCdevice *device; + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(LookupBuffer(device, buffer) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + if(!(value1 && value2 && value3)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values) +{ + ALCdevice *device; + ALCcontext *context; + + switch(param) + { + case AL_SEC_LENGTH_SOFT: + alGetBufferf(buffer, param, values); + return; + } + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(LookupBuffer(device, buffer) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value) +{ + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((albuf=LookupBuffer(device, buffer)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + if(!(value)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + case AL_FREQUENCY: + *value = albuf->Frequency; + break; + + case AL_BITS: + *value = BytesFromFmt(albuf->FmtType) * 8; + break; + + case AL_CHANNELS: + *value = ChannelsFromFmt(albuf->FmtChannels); + break; + + case AL_SIZE: + ReadLock(&albuf->lock); + *value = albuf->SampleLen * FrameSizeFromFmt(albuf->FmtChannels, + albuf->FmtType); + ReadUnlock(&albuf->lock); + break; + + case AL_INTERNAL_FORMAT_SOFT: + *value = albuf->Format; + break; + + case AL_BYTE_LENGTH_SOFT: + *value = albuf->OriginalSize; + break; + + case AL_SAMPLE_LENGTH_SOFT: + *value = albuf->SampleLen; + break; + + case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: + *value = albuf->UnpackAlign; + break; + + case AL_PACK_BLOCK_ALIGNMENT_SOFT: + *value = albuf->PackAlign; + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) +{ + ALCdevice *device; + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(LookupBuffer(device, buffer) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + if(!(value1 && value2 && value3)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values) +{ + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + + switch(param) + { + case AL_FREQUENCY: + case AL_BITS: + case AL_CHANNELS: + case AL_SIZE: + case AL_INTERNAL_FORMAT_SOFT: + case AL_BYTE_LENGTH_SOFT: + case AL_SAMPLE_LENGTH_SOFT: + case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: + case AL_PACK_BLOCK_ALIGNMENT_SOFT: + alGetBufferi(buffer, param, values); + return; + } + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((albuf=LookupBuffer(device, buffer)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + case AL_LOOP_POINTS_SOFT: + ReadLock(&albuf->lock); + values[0] = albuf->LoopStart; + values[1] = albuf->LoopEnd; + ReadUnlock(&albuf->lock); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +/* + * 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. + */ +ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, 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); + + newsize = frames; + newsize *= NewBytes; + newsize *= NewChannels; + if(newsize > INT_MAX) + return AL_OUT_OF_MEMORY; + + WriteLock(&ALBuf->lock); + if(ReadRef(&ALBuf->ref) != 0) + { + WriteUnlock(&ALBuf->lock); + return AL_INVALID_OPERATION; + } + + temp = realloc(ALBuf->data, (size_t)newsize); + if(!temp && newsize) + { + WriteUnlock(&ALBuf->lock); + return AL_OUT_OF_MEMORY; + } + ALBuf->data = temp; + + if(data != NULL) + ConvertData(ALBuf->data, (enum UserFmtType)DstType, data, SrcType, NewChannels, frames, align); + + if(storesrc) + { + ALBuf->OriginalChannels = SrcChannels; + ALBuf->OriginalType = SrcType; + if(SrcType == UserFmtIMA4) + { + ALsizei byte_align = ((align-1)/2 + 4) * ChannelsFromUserFmt(SrcChannels); + ALBuf->OriginalSize = frames / align * byte_align; + ALBuf->OriginalAlign = align; + } + else if(SrcType == UserFmtMSADPCM) + { + ALsizei byte_align = ((align-2)/2 + 7) * ChannelsFromUserFmt(SrcChannels); + ALBuf->OriginalSize = frames / align * byte_align; + ALBuf->OriginalAlign = align; + } + else + { + ALBuf->OriginalSize = frames * FrameSizeFromUserFmt(SrcChannels, SrcType); + ALBuf->OriginalAlign = 1; + } + } + else + { + ALBuf->OriginalChannels = (enum UserFmtChannels)DstChannels; + ALBuf->OriginalType = (enum UserFmtType)DstType; + ALBuf->OriginalSize = frames * NewBytes * NewChannels; + ALBuf->OriginalAlign = 1; + } + + ALBuf->Frequency = freq; + ALBuf->FmtChannels = DstChannels; + ALBuf->FmtType = DstType; + ALBuf->Format = NewFormat; + + ALBuf->SampleLen = frames; + ALBuf->LoopStart = 0; + ALBuf->LoopEnd = ALBuf->SampleLen; + + WriteUnlock(&ALBuf->lock); + 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(ALbyte[3]); + case UserFmtUByte3: return sizeof(ALubyte[3]); + case UserFmtMulaw: return sizeof(ALubyte); + case UserFmtAlaw: return sizeof(ALubyte); + case UserFmtIMA4: break; /* not handled here */ + case UserFmtMSADPCM: 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; +} +static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, + enum UserFmtType *type) +{ + static const struct { + ALenum format; + enum UserFmtChannels channels; + enum UserFmtType type; + } list[] = { + { AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte }, + { AL_FORMAT_MONO16, UserFmtMono, UserFmtShort }, + { AL_FORMAT_MONO_FLOAT32, UserFmtMono, UserFmtFloat }, + { AL_FORMAT_MONO_DOUBLE_EXT, UserFmtMono, UserFmtDouble }, + { AL_FORMAT_MONO_IMA4, UserFmtMono, UserFmtIMA4 }, + { AL_FORMAT_MONO_MSADPCM_SOFT, UserFmtMono, UserFmtMSADPCM }, + { AL_FORMAT_MONO_MULAW, UserFmtMono, UserFmtMulaw }, + { AL_FORMAT_MONO_ALAW_EXT, UserFmtMono, UserFmtAlaw }, + + { AL_FORMAT_STEREO8, UserFmtStereo, UserFmtUByte }, + { AL_FORMAT_STEREO16, UserFmtStereo, UserFmtShort }, + { AL_FORMAT_STEREO_FLOAT32, UserFmtStereo, UserFmtFloat }, + { AL_FORMAT_STEREO_DOUBLE_EXT, UserFmtStereo, UserFmtDouble }, + { AL_FORMAT_STEREO_IMA4, UserFmtStereo, UserFmtIMA4 }, + { AL_FORMAT_STEREO_MSADPCM_SOFT, UserFmtStereo, UserFmtMSADPCM }, + { AL_FORMAT_STEREO_MULAW, UserFmtStereo, UserFmtMulaw }, + { AL_FORMAT_STEREO_ALAW_EXT, UserFmtStereo, UserFmtAlaw }, + + { AL_FORMAT_REAR8, UserFmtRear, UserFmtUByte }, + { AL_FORMAT_REAR16, UserFmtRear, UserFmtShort }, + { AL_FORMAT_REAR32, UserFmtRear, UserFmtFloat }, + { AL_FORMAT_REAR_MULAW, UserFmtRear, UserFmtMulaw }, + + { AL_FORMAT_QUAD8_LOKI, UserFmtQuad, UserFmtUByte }, + { AL_FORMAT_QUAD16_LOKI, UserFmtQuad, UserFmtShort }, + + { AL_FORMAT_QUAD8, UserFmtQuad, UserFmtUByte }, + { AL_FORMAT_QUAD16, UserFmtQuad, UserFmtShort }, + { AL_FORMAT_QUAD32, UserFmtQuad, UserFmtFloat }, + { AL_FORMAT_QUAD_MULAW, UserFmtQuad, UserFmtMulaw }, + + { AL_FORMAT_51CHN8, UserFmtX51, UserFmtUByte }, + { AL_FORMAT_51CHN16, UserFmtX51, UserFmtShort }, + { AL_FORMAT_51CHN32, UserFmtX51, UserFmtFloat }, + { AL_FORMAT_51CHN_MULAW, UserFmtX51, UserFmtMulaw }, + + { AL_FORMAT_61CHN8, UserFmtX61, UserFmtUByte }, + { AL_FORMAT_61CHN16, UserFmtX61, UserFmtShort }, + { AL_FORMAT_61CHN32, UserFmtX61, UserFmtFloat }, + { AL_FORMAT_61CHN_MULAW, UserFmtX61, UserFmtMulaw }, + + { AL_FORMAT_71CHN8, UserFmtX71, UserFmtUByte }, + { AL_FORMAT_71CHN16, UserFmtX71, UserFmtShort }, + { AL_FORMAT_71CHN32, UserFmtX71, UserFmtFloat }, + { AL_FORMAT_71CHN_MULAW, UserFmtX71, UserFmtMulaw }, + }; + ALuint i; + + for(i = 0;i < COUNTOF(list);i++) + { + if(list[i].format == format) + { + *chans = list[i].channels; + *type = list[i].type; + 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; +} +static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type) +{ + static const struct { + ALenum format; + enum FmtChannels channels; + enum FmtType type; + } list[] = { + { AL_MONO8_SOFT, FmtMono, FmtByte }, + { AL_MONO16_SOFT, FmtMono, FmtShort }, + { AL_MONO32F_SOFT, FmtMono, FmtFloat }, + + { AL_STEREO8_SOFT, FmtStereo, FmtByte }, + { AL_STEREO16_SOFT, FmtStereo, FmtShort }, + { AL_STEREO32F_SOFT, FmtStereo, FmtFloat }, + + { AL_REAR8_SOFT, FmtRear, FmtByte }, + { AL_REAR16_SOFT, FmtRear, FmtShort }, + { AL_REAR32F_SOFT, FmtRear, FmtFloat }, + + { AL_FORMAT_QUAD8_LOKI, FmtQuad, FmtByte }, + { AL_FORMAT_QUAD16_LOKI, FmtQuad, FmtShort }, + + { AL_QUAD8_SOFT, FmtQuad, FmtByte }, + { AL_QUAD16_SOFT, FmtQuad, FmtShort }, + { AL_QUAD32F_SOFT, FmtQuad, FmtFloat }, + + { AL_5POINT1_8_SOFT, FmtX51, FmtByte }, + { AL_5POINT1_16_SOFT, FmtX51, FmtShort }, + { AL_5POINT1_32F_SOFT, FmtX51, FmtFloat }, + + { AL_6POINT1_8_SOFT, FmtX61, FmtByte }, + { AL_6POINT1_16_SOFT, FmtX61, FmtShort }, + { AL_6POINT1_32F_SOFT, FmtX61, FmtFloat }, + + { AL_7POINT1_8_SOFT, FmtX71, FmtByte }, + { AL_7POINT1_16_SOFT, FmtX71, FmtShort }, + { AL_7POINT1_32F_SOFT, FmtX71, FmtFloat }, + }; + ALuint i; + + for(i = 0;i < COUNTOF(list);i++) + { + if(list[i].format == format) + { + *chans = list[i].channels; + *type = list[i].type; + return AL_TRUE; + } + } + + return AL_FALSE; +} + +static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align) +{ + if(*align < 0) + return AL_FALSE; + + if(*align == 0) + { + if(type == 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 + */ + *align = 65; + } + else if(type == UserFmtMSADPCM) + *align = 64; + else + *align = 1; + return AL_TRUE; + } + + if(type == UserFmtIMA4) + { + /* IMA4 block alignment must be a multiple of 8, plus 1. */ + return ((*align)&7) == 1; + } + if(type == UserFmtMSADPCM) + { + /* MSADPCM block alignment must be a multiple of 2. */ + /* FIXME: Too strict? Might only require align*channels to be a + * multiple of 2. */ + return ((*align)&1) == 0; + } + + return AL_TRUE; +} + + +static ALboolean IsValidType(ALenum type) +{ + switch(type) + { + case AL_BYTE_SOFT: + case AL_UNSIGNED_BYTE_SOFT: + case AL_SHORT_SOFT: + case AL_UNSIGNED_SHORT_SOFT: + case AL_INT_SOFT: + case AL_UNSIGNED_INT_SOFT: + case AL_FLOAT_SOFT: + case AL_DOUBLE_SOFT: + case AL_BYTE3_SOFT: + case AL_UNSIGNED_BYTE3_SOFT: + return AL_TRUE; + } + return AL_FALSE; +} + +static ALboolean IsValidChannels(ALenum channels) +{ + switch(channels) + { + case AL_MONO_SOFT: + case AL_STEREO_SOFT: + case AL_REAR_SOFT: + case AL_QUAD_SOFT: + case AL_5POINT1_SOFT: + case AL_6POINT1_SOFT: + case AL_7POINT1_SOFT: + return AL_TRUE; + } + return AL_FALSE; +} + + +ALbuffer *NewBuffer(ALCcontext *context) +{ + ALCdevice *device = context->Device; + ALbuffer *buffer; + ALenum err; + + buffer = calloc(1, sizeof(ALbuffer)); + if(!buffer) + SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL); + RWLockInit(&buffer->lock); + + err = NewThunkEntry(&buffer->id); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&device->BufferMap, buffer->id, buffer); + if(err != AL_NO_ERROR) + { + FreeThunkEntry(buffer->id); + memset(buffer, 0, sizeof(ALbuffer)); + free(buffer); + + SET_ERROR_AND_RETURN_VALUE(context, err, NULL); + } + + return buffer; +} + +void DeleteBuffer(ALCdevice *device, ALbuffer *buffer) +{ + RemoveBuffer(device, buffer->id); + FreeThunkEntry(buffer->id); + + free(buffer->data); + + memset(buffer, 0, sizeof(*buffer)); + free(buffer); +} + + +/* + * 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->id); + memset(temp, 0, sizeof(ALbuffer)); + free(temp); + } +} diff --git a/libs/openal-soft/OpenAL32/alEffect.c b/libs/openal-soft/OpenAL32/alEffect.c new file mode 100644 index 00000000..c9e5928f --- /dev/null +++ b/libs/openal-soft/OpenAL32/alEffect.c @@ -0,0 +1,686 @@ +/** + * 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 "AL/al.h" +#include "AL/alc.h" +#include "alMain.h" +#include "alEffect.h" +#include "alThunk.h" +#include "alError.h" + + +ALboolean DisabledEffects[MAX_EFFECTS]; + +extern inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id); +extern inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id); +extern inline ALboolean IsReverbEffect(ALenum type); + +static void InitEffectParams(ALeffect *effect, ALenum type); + + +AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects) +{ + ALCdevice *device; + ALCcontext *context; + ALsizei cur; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + for(cur = 0;cur < n;cur++) + { + ALeffect *effect = calloc(1, sizeof(ALeffect)); + ALenum err = AL_OUT_OF_MEMORY; + if(!effect || (err=InitEffect(effect)) != AL_NO_ERROR) + { + free(effect); + alDeleteEffects(cur, effects); + SET_ERROR_AND_GOTO(context, err, done); + } + + err = NewThunkEntry(&effect->id); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&device->EffectMap, effect->id, effect); + if(err != AL_NO_ERROR) + { + FreeThunkEntry(effect->id); + memset(effect, 0, sizeof(ALeffect)); + free(effect); + + alDeleteEffects(cur, effects); + SET_ERROR_AND_GOTO(context, err, done); + } + + effects[cur] = effect->id; + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects) +{ + ALCdevice *device; + ALCcontext *context; + ALeffect *effect; + ALsizei i; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + for(i = 0;i < n;i++) + { + if(effects[i] && LookupEffect(device, effects[i]) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + } + for(i = 0;i < n;i++) + { + if((effect=RemoveEffect(device, effects[i])) == NULL) + continue; + FreeThunkEntry(effect->id); + + memset(effect, 0, sizeof(*effect)); + free(effect); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect) +{ + ALCcontext *Context; + ALboolean result; + + Context = GetContextRef(); + if(!Context) return AL_FALSE; + + result = ((!effect || LookupEffect(Context->Device, effect)) ? + AL_TRUE : AL_FALSE); + + ALCcontext_DecRef(Context); + + return result; +} + +AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device, effect)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + if(param == AL_EFFECT_TYPE) + { + ALboolean isOk = (value == AL_EFFECT_NULL); + ALint i; + for(i = 0;!isOk && EffectList[i].val;i++) + { + if(value == EffectList[i].val && + !DisabledEffects[EffectList[i].type]) + isOk = AL_TRUE; + } + + if(isOk) + InitEffectParams(ALEffect, value); + else + alSetError(Context, AL_INVALID_VALUE); + } + else + { + /* Call the appropriate handler */ + V(ALEffect,setParami)(Context, param, value); + } + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *values) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + switch(param) + { + case AL_EFFECT_TYPE: + alEffecti(effect, param, values[0]); + return; + } + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device, effect)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + /* Call the appropriate handler */ + V(ALEffect,setParamiv)(Context, param, values); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device, effect)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + /* Call the appropriate handler */ + V(ALEffect,setParamf)(Context, param, value); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *values) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device, effect)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + /* Call the appropriate handler */ + V(ALEffect,setParamfv)(Context, param, values); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device, effect)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + if(param == AL_EFFECT_TYPE) + *value = ALEffect->type; + else + { + /* Call the appropriate handler */ + V(ALEffect,getParami)(Context, param, value); + } + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *values) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + switch(param) + { + case AL_EFFECT_TYPE: + alGetEffecti(effect, param, values); + return; + } + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device, effect)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + /* Call the appropriate handler */ + V(ALEffect,getParamiv)(Context, param, values); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *value) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device, effect)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + /* Call the appropriate handler */ + V(ALEffect,getParamf)(Context, param, value); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *values) +{ + ALCcontext *Context; + ALCdevice *Device; + ALeffect *ALEffect; + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALEffect=LookupEffect(Device, effect)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + /* Call the appropriate handler */ + V(ALEffect,getParamfv)(Context, param, values); + } + + ALCcontext_DecRef(Context); +} + + +ALenum InitEffect(ALeffect *effect) +{ + InitEffectParams(effect, AL_EFFECT_NULL); + return AL_NO_ERROR; +} + +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->id); + memset(temp, 0, sizeof(ALeffect)); + free(temp); + } +} + + +static void InitEffectParams(ALeffect *effect, ALenum type) +{ + switch(type) + { + case AL_EFFECT_EAXREVERB: + effect->Props.Reverb.Density = AL_EAXREVERB_DEFAULT_DENSITY; + effect->Props.Reverb.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION; + effect->Props.Reverb.Gain = AL_EAXREVERB_DEFAULT_GAIN; + effect->Props.Reverb.GainHF = AL_EAXREVERB_DEFAULT_GAINHF; + effect->Props.Reverb.GainLF = AL_EAXREVERB_DEFAULT_GAINLF; + effect->Props.Reverb.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME; + effect->Props.Reverb.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO; + effect->Props.Reverb.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO; + effect->Props.Reverb.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN; + effect->Props.Reverb.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY; + effect->Props.Reverb.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + effect->Props.Reverb.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + effect->Props.Reverb.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + effect->Props.Reverb.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN; + effect->Props.Reverb.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY; + effect->Props.Reverb.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + effect->Props.Reverb.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + effect->Props.Reverb.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + effect->Props.Reverb.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME; + effect->Props.Reverb.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH; + effect->Props.Reverb.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME; + effect->Props.Reverb.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH; + effect->Props.Reverb.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF; + effect->Props.Reverb.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE; + effect->Props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE; + effect->Props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; + effect->Props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT; + SET_VTABLE1(ALeaxreverb, effect); + break; + case AL_EFFECT_REVERB: + effect->Props.Reverb.Density = AL_REVERB_DEFAULT_DENSITY; + effect->Props.Reverb.Diffusion = AL_REVERB_DEFAULT_DIFFUSION; + effect->Props.Reverb.Gain = AL_REVERB_DEFAULT_GAIN; + effect->Props.Reverb.GainHF = AL_REVERB_DEFAULT_GAINHF; + effect->Props.Reverb.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME; + effect->Props.Reverb.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO; + effect->Props.Reverb.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN; + effect->Props.Reverb.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY; + effect->Props.Reverb.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN; + effect->Props.Reverb.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY; + effect->Props.Reverb.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF; + effect->Props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; + effect->Props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT; + SET_VTABLE1(ALreverb, effect); + break; + case AL_EFFECT_AUTOWAH: + effect->Props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME; + effect->Props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN; + effect->Props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME; + effect->Props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE; + SET_VTABLE1(ALautowah, effect); + break; + case AL_EFFECT_CHORUS: + effect->Props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM; + effect->Props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE; + effect->Props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE; + effect->Props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH; + effect->Props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK; + effect->Props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY; + SET_VTABLE1(ALchorus, effect); + break; + case AL_EFFECT_COMPRESSOR: + effect->Props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF; + SET_VTABLE1(ALcompressor, effect); + break; + case AL_EFFECT_DISTORTION: + effect->Props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE; + effect->Props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN; + effect->Props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF; + effect->Props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER; + effect->Props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH; + SET_VTABLE1(ALdistortion, effect); + break; + case AL_EFFECT_ECHO: + effect->Props.Echo.Delay = AL_ECHO_DEFAULT_DELAY; + effect->Props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY; + effect->Props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING; + effect->Props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK; + effect->Props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD; + SET_VTABLE1(ALecho, effect); + break; + case AL_EFFECT_EQUALIZER: + effect->Props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF; + effect->Props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN; + effect->Props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER; + effect->Props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN; + effect->Props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH; + effect->Props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER; + effect->Props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN; + effect->Props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH; + effect->Props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF; + effect->Props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN; + SET_VTABLE1(ALequalizer, effect); + break; + case AL_EFFECT_FLANGER: + effect->Props.Flanger.Waveform = AL_FLANGER_DEFAULT_WAVEFORM; + effect->Props.Flanger.Phase = AL_FLANGER_DEFAULT_PHASE; + effect->Props.Flanger.Rate = AL_FLANGER_DEFAULT_RATE; + effect->Props.Flanger.Depth = AL_FLANGER_DEFAULT_DEPTH; + effect->Props.Flanger.Feedback = AL_FLANGER_DEFAULT_FEEDBACK; + effect->Props.Flanger.Delay = AL_FLANGER_DEFAULT_DELAY; + SET_VTABLE1(ALflanger, effect); + break; + case AL_EFFECT_RING_MODULATOR: + effect->Props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY; + effect->Props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF; + effect->Props.Modulator.Waveform = AL_RING_MODULATOR_DEFAULT_WAVEFORM; + SET_VTABLE1(ALmodulator, effect); + break; + case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: + case AL_EFFECT_DEDICATED_DIALOGUE: + effect->Props.Dedicated.Gain = 1.0f; + SET_VTABLE1(ALdedicated, effect); + break; + default: + SET_VTABLE1(ALnull, effect); + break; + } + effect->type = type; +} + + +#include "AL/efx-presets.h" + +#define DECL(x) { #x, EFX_REVERB_PRESET_##x } +static const struct { + const char name[32]; + EFXEAXREVERBPROPERTIES props; +} reverblist[] = { + DECL(GENERIC), + DECL(PADDEDCELL), + DECL(ROOM), + DECL(BATHROOM), + DECL(LIVINGROOM), + DECL(STONEROOM), + DECL(AUDITORIUM), + DECL(CONCERTHALL), + DECL(CAVE), + DECL(ARENA), + DECL(HANGAR), + DECL(CARPETEDHALLWAY), + DECL(HALLWAY), + DECL(STONECORRIDOR), + DECL(ALLEY), + DECL(FOREST), + DECL(CITY), + DECL(MOUNTAINS), + DECL(QUARRY), + DECL(PLAIN), + DECL(PARKINGLOT), + DECL(SEWERPIPE), + DECL(UNDERWATER), + DECL(DRUGGED), + DECL(DIZZY), + DECL(PSYCHOTIC), + + DECL(CASTLE_SMALLROOM), + DECL(CASTLE_SHORTPASSAGE), + DECL(CASTLE_MEDIUMROOM), + DECL(CASTLE_LARGEROOM), + DECL(CASTLE_LONGPASSAGE), + DECL(CASTLE_HALL), + DECL(CASTLE_CUPBOARD), + DECL(CASTLE_COURTYARD), + DECL(CASTLE_ALCOVE), + + DECL(FACTORY_SMALLROOM), + DECL(FACTORY_SHORTPASSAGE), + DECL(FACTORY_MEDIUMROOM), + DECL(FACTORY_LARGEROOM), + DECL(FACTORY_LONGPASSAGE), + DECL(FACTORY_HALL), + DECL(FACTORY_CUPBOARD), + DECL(FACTORY_COURTYARD), + DECL(FACTORY_ALCOVE), + + DECL(ICEPALACE_SMALLROOM), + DECL(ICEPALACE_SHORTPASSAGE), + DECL(ICEPALACE_MEDIUMROOM), + DECL(ICEPALACE_LARGEROOM), + DECL(ICEPALACE_LONGPASSAGE), + DECL(ICEPALACE_HALL), + DECL(ICEPALACE_CUPBOARD), + DECL(ICEPALACE_COURTYARD), + DECL(ICEPALACE_ALCOVE), + + DECL(SPACESTATION_SMALLROOM), + DECL(SPACESTATION_SHORTPASSAGE), + DECL(SPACESTATION_MEDIUMROOM), + DECL(SPACESTATION_LARGEROOM), + DECL(SPACESTATION_LONGPASSAGE), + DECL(SPACESTATION_HALL), + DECL(SPACESTATION_CUPBOARD), + DECL(SPACESTATION_ALCOVE), + + DECL(WOODEN_SMALLROOM), + DECL(WOODEN_SHORTPASSAGE), + DECL(WOODEN_MEDIUMROOM), + DECL(WOODEN_LARGEROOM), + DECL(WOODEN_LONGPASSAGE), + DECL(WOODEN_HALL), + DECL(WOODEN_CUPBOARD), + DECL(WOODEN_COURTYARD), + DECL(WOODEN_ALCOVE), + + DECL(SPORT_EMPTYSTADIUM), + DECL(SPORT_SQUASHCOURT), + DECL(SPORT_SMALLSWIMMINGPOOL), + DECL(SPORT_LARGESWIMMINGPOOL), + DECL(SPORT_GYMNASIUM), + DECL(SPORT_FULLSTADIUM), + DECL(SPORT_STADIUMTANNOY), + + DECL(PREFAB_WORKSHOP), + DECL(PREFAB_SCHOOLROOM), + DECL(PREFAB_PRACTISEROOM), + DECL(PREFAB_OUTHOUSE), + DECL(PREFAB_CARAVAN), + + DECL(DOME_TOMB), + DECL(PIPE_SMALL), + DECL(DOME_SAINTPAULS), + DECL(PIPE_LONGTHIN), + DECL(PIPE_LARGE), + DECL(PIPE_RESONANT), + + DECL(OUTDOORS_BACKYARD), + DECL(OUTDOORS_ROLLINGPLAINS), + DECL(OUTDOORS_DEEPCANYON), + DECL(OUTDOORS_CREEK), + DECL(OUTDOORS_VALLEY), + + DECL(MOOD_HEAVEN), + DECL(MOOD_HELL), + DECL(MOOD_MEMORY), + + DECL(DRIVING_COMMENTATOR), + DECL(DRIVING_PITGARAGE), + DECL(DRIVING_INCAR_RACER), + DECL(DRIVING_INCAR_SPORTS), + DECL(DRIVING_INCAR_LUXURY), + DECL(DRIVING_FULLGRANDSTAND), + DECL(DRIVING_EMPTYGRANDSTAND), + DECL(DRIVING_TUNNEL), + + DECL(CITY_STREETS), + DECL(CITY_SUBWAY), + DECL(CITY_MUSEUM), + DECL(CITY_LIBRARY), + DECL(CITY_UNDERPASS), + DECL(CITY_ABANDONED), + + DECL(DUSTYROOM), + DECL(CHAPEL), + DECL(SMALLWATERROOM), +}; +#undef DECL + +ALvoid LoadReverbPreset(const char *name, ALeffect *effect) +{ + size_t i; + + if(strcasecmp(name, "NONE") == 0) + { + InitEffectParams(effect, AL_EFFECT_NULL); + TRACE("Loading reverb '%s'\n", "NONE"); + return; + } + + if(!DisabledEffects[EAXREVERB]) + InitEffectParams(effect, AL_EFFECT_EAXREVERB); + else if(!DisabledEffects[REVERB]) + InitEffectParams(effect, AL_EFFECT_REVERB); + else + InitEffectParams(effect, AL_EFFECT_NULL); + for(i = 0;i < COUNTOF(reverblist);i++) + { + const EFXEAXREVERBPROPERTIES *props; + + if(strcasecmp(name, reverblist[i].name) != 0) + continue; + + TRACE("Loading reverb '%s'\n", reverblist[i].name); + props = &reverblist[i].props; + effect->Props.Reverb.Density = props->flDensity; + effect->Props.Reverb.Diffusion = props->flDiffusion; + effect->Props.Reverb.Gain = props->flGain; + effect->Props.Reverb.GainHF = props->flGainHF; + effect->Props.Reverb.GainLF = props->flGainLF; + effect->Props.Reverb.DecayTime = props->flDecayTime; + effect->Props.Reverb.DecayHFRatio = props->flDecayHFRatio; + effect->Props.Reverb.DecayLFRatio = props->flDecayLFRatio; + effect->Props.Reverb.ReflectionsGain = props->flReflectionsGain; + effect->Props.Reverb.ReflectionsDelay = props->flReflectionsDelay; + effect->Props.Reverb.ReflectionsPan[0] = props->flReflectionsPan[0]; + effect->Props.Reverb.ReflectionsPan[1] = props->flReflectionsPan[1]; + effect->Props.Reverb.ReflectionsPan[2] = props->flReflectionsPan[2]; + effect->Props.Reverb.LateReverbGain = props->flLateReverbGain; + effect->Props.Reverb.LateReverbDelay = props->flLateReverbDelay; + effect->Props.Reverb.LateReverbPan[0] = props->flLateReverbPan[0]; + effect->Props.Reverb.LateReverbPan[1] = props->flLateReverbPan[1]; + effect->Props.Reverb.LateReverbPan[2] = props->flLateReverbPan[2]; + effect->Props.Reverb.EchoTime = props->flEchoTime; + effect->Props.Reverb.EchoDepth = props->flEchoDepth; + effect->Props.Reverb.ModulationTime = props->flModulationTime; + effect->Props.Reverb.ModulationDepth = props->flModulationDepth; + effect->Props.Reverb.AirAbsorptionGainHF = props->flAirAbsorptionGainHF; + effect->Props.Reverb.HFReference = props->flHFReference; + effect->Props.Reverb.LFReference = props->flLFReference; + effect->Props.Reverb.RoomRolloffFactor = props->flRoomRolloffFactor; + effect->Props.Reverb.DecayHFLimit = props->iDecayHFLimit; + return; + } + + WARN("Reverb preset '%s' not found\n", name); +} diff --git a/libs/openal-soft/OpenAL32/alError.c b/libs/openal-soft/OpenAL32/alError.c new file mode 100644 index 00000000..9369af56 --- /dev/null +++ b/libs/openal-soft/OpenAL32/alError.c @@ -0,0 +1,77 @@ +/** + * 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 + +#ifdef HAVE_WINDOWS_H +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include "alMain.h" +#include "AL/alc.h" +#include "alError.h" + +ALboolean TrapALError = AL_FALSE; + +ALvoid alSetError(ALCcontext *Context, ALenum errorCode) +{ + ALenum curerr = AL_NO_ERROR; + if(TrapALError) + { +#ifdef _WIN32 + /* DebugBreak will cause an exception if there is no debugger */ + if(IsDebuggerPresent()) + DebugBreak(); +#elif defined(SIGTRAP) + raise(SIGTRAP); +#endif + } + ATOMIC_COMPARE_EXCHANGE_STRONG(ALenum, &Context->LastError, &curerr, errorCode); +} + +AL_API ALenum AL_APIENTRY alGetError(void) +{ + ALCcontext *Context; + ALenum errorCode; + + Context = GetContextRef(); + if(!Context) + { + if(TrapALError) + { +#ifdef _WIN32 + if(IsDebuggerPresent()) + DebugBreak(); +#elif defined(SIGTRAP) + raise(SIGTRAP); +#endif + } + return AL_INVALID_OPERATION; + } + + errorCode = ATOMIC_EXCHANGE(ALenum, &Context->LastError, AL_NO_ERROR); + + ALCcontext_DecRef(Context); + + return errorCode; +} diff --git a/libs/openal-soft/OpenAL32/alExtension.c b/libs/openal-soft/OpenAL32/alExtension.c new file mode 100644 index 00000000..29f21783 --- /dev/null +++ b/libs/openal-soft/OpenAL32/alExtension.c @@ -0,0 +1,106 @@ +/** + * 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" + + +const struct EffectList EffectList[] = { + { "eaxreverb", EAXREVERB, "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB }, + { "reverb", REVERB, "AL_EFFECT_REVERB", AL_EFFECT_REVERB }, +#if 0 + { "autowah", AUTOWAH, "AL_EFFECT_AUTOWAH", AL_EFFECT_AUTOWAH }, +#endif + { "chorus", CHORUS, "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS }, + { "compressor", COMPRESSOR, "AL_EFFECT_COMPRESSOR", AL_EFFECT_COMPRESSOR }, + { "distortion", DISTORTION, "AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION }, + { "echo", ECHO, "AL_EFFECT_ECHO", AL_EFFECT_ECHO }, + { "equalizer", EQUALIZER, "AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER }, + { "flanger", FLANGER, "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER }, + { "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 ret = AL_FALSE; + ALCcontext *context; + const char *ptr; + size_t len; + + context = GetContextRef(); + if(!context) return AL_FALSE; + + if(!(extName)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + len = strlen(extName); + ptr = context->ExtensionList; + while(ptr && *ptr) + { + if(strncasecmp(ptr, extName, len) == 0 && + (ptr[len] == '\0' || isspace(ptr[len]))) + { + ret = AL_TRUE; + break; + } + if((ptr=strchr(ptr, ' ')) != NULL) + { + do { + ++ptr; + } while(isspace(*ptr)); + } + } + +done: + ALCcontext_DecRef(context); + return ret; +} + + +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) +{ + if(!enumName) + return (ALenum)0; + return alcGetEnumValue(NULL, enumName); +} diff --git a/libs/openal-soft/OpenAL32/alFilter.c b/libs/openal-soft/OpenAL32/alFilter.c new file mode 100644 index 00000000..acfae8a6 --- /dev/null +++ b/libs/openal-soft/OpenAL32/alFilter.c @@ -0,0 +1,701 @@ +/** + * 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 "alu.h" +#include "alFilter.h" +#include "alThunk.h" +#include "alError.h" + + +extern inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id); +extern inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id); +extern inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample); + +static void InitFilterParams(ALfilter *filter, ALenum type); + + +AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters) +{ + ALCdevice *device; + ALCcontext *context; + ALsizei cur = 0; + ALenum err; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + for(cur = 0;cur < n;cur++) + { + ALfilter *filter = calloc(1, sizeof(ALfilter)); + if(!filter) + { + alDeleteFilters(cur, filters); + SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); + } + InitFilterParams(filter, AL_FILTER_NULL); + + err = NewThunkEntry(&filter->id); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&device->FilterMap, filter->id, filter); + if(err != AL_NO_ERROR) + { + FreeThunkEntry(filter->id); + memset(filter, 0, sizeof(ALfilter)); + free(filter); + + alDeleteFilters(cur, filters); + SET_ERROR_AND_GOTO(context, err, done); + } + + filters[cur] = filter->id; + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters) +{ + ALCdevice *device; + ALCcontext *context; + ALfilter *filter; + ALsizei i; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + for(i = 0;i < n;i++) + { + if(filters[i] && LookupFilter(device, filters[i]) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + } + for(i = 0;i < n;i++) + { + if((filter=RemoveFilter(device, filters[i])) == NULL) + continue; + FreeThunkEntry(filter->id); + + memset(filter, 0, sizeof(*filter)); + free(filter); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter) +{ + ALCcontext *Context; + ALboolean result; + + Context = GetContextRef(); + if(!Context) return AL_FALSE; + + result = ((!filter || LookupFilter(Context->Device, filter)) ? + AL_TRUE : AL_FALSE); + + ALCcontext_DecRef(Context); + + return result; +} + +AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value) +{ + ALCcontext *Context; + ALCdevice *Device; + ALfilter *ALFilter; + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALFilter=LookupFilter(Device, filter)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + if(param == AL_FILTER_TYPE) + { + if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS || + value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS) + InitFilterParams(ALFilter, value); + else + alSetError(Context, AL_INVALID_VALUE); + } + else + { + /* Call the appropriate handler */ + ALfilter_SetParami(ALFilter, Context, param, value); + } + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *values) +{ + ALCcontext *Context; + ALCdevice *Device; + ALfilter *ALFilter; + + switch(param) + { + case AL_FILTER_TYPE: + alFilteri(filter, param, values[0]); + return; + } + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALFilter=LookupFilter(Device, filter)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + /* Call the appropriate handler */ + ALfilter_SetParamiv(ALFilter, Context, param, values); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value) +{ + ALCcontext *Context; + ALCdevice *Device; + ALfilter *ALFilter; + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALFilter=LookupFilter(Device, filter)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + /* Call the appropriate handler */ + ALfilter_SetParamf(ALFilter, Context, param, value); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *values) +{ + ALCcontext *Context; + ALCdevice *Device; + ALfilter *ALFilter; + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALFilter=LookupFilter(Device, filter)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + /* Call the appropriate handler */ + ALfilter_SetParamfv(ALFilter, Context, param, values); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value) +{ + ALCcontext *Context; + ALCdevice *Device; + ALfilter *ALFilter; + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALFilter=LookupFilter(Device, filter)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + if(param == AL_FILTER_TYPE) + *value = ALFilter->type; + else + { + /* Call the appropriate handler */ + ALfilter_GetParami(ALFilter, Context, param, value); + } + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *values) +{ + ALCcontext *Context; + ALCdevice *Device; + ALfilter *ALFilter; + + switch(param) + { + case AL_FILTER_TYPE: + alGetFilteri(filter, param, values); + return; + } + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALFilter=LookupFilter(Device, filter)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + /* Call the appropriate handler */ + ALfilter_GetParamiv(ALFilter, Context, param, values); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *value) +{ + ALCcontext *Context; + ALCdevice *Device; + ALfilter *ALFilter; + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALFilter=LookupFilter(Device, filter)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + /* Call the appropriate handler */ + ALfilter_GetParamf(ALFilter, Context, param, value); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *values) +{ + ALCcontext *Context; + ALCdevice *Device; + ALfilter *ALFilter; + + Context = GetContextRef(); + if(!Context) return; + + Device = Context->Device; + if((ALFilter=LookupFilter(Device, filter)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else + { + /* Call the appropriate handler */ + ALfilter_GetParamfv(ALFilter, Context, param, values); + } + + ALCcontext_DecRef(Context); +} + + +void ALfilterState_clear(ALfilterState *filter) +{ + filter->x[0] = 0.0f; + filter->x[1] = 0.0f; + filter->y[0] = 0.0f; + filter->y[1] = 0.0f; +} + +void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat bandwidth) +{ + ALfloat alpha; + ALfloat w0; + + // Limit gain to -100dB + gain = maxf(gain, 0.00001f); + + w0 = F_2PI * freq_mult; + + /* Calculate filter coefficients depending on filter type */ + switch(type) + { + case ALfilterType_HighShelf: + alpha = sinf(w0)/2.0f*sqrtf((gain + 1.0f/gain)*(1.0f/0.75f - 1.0f) + 2.0f); + filter->b[0] = gain*((gain+1.0f) + (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha); + filter->b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cosf(w0) ); + filter->b[2] = gain*((gain+1.0f) + (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha); + filter->a[0] = (gain+1.0f) - (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha; + filter->a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cosf(w0) ); + filter->a[2] = (gain+1.0f) - (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha; + break; + case ALfilterType_LowShelf: + alpha = sinf(w0)/2.0f*sqrtf((gain + 1.0f/gain)*(1.0f/0.75f - 1.0f) + 2.0f); + filter->b[0] = gain*((gain+1.0f) - (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha); + filter->b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cosf(w0) ); + filter->b[2] = gain*((gain+1.0f) - (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha); + filter->a[0] = (gain+1.0f) + (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha; + filter->a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cosf(w0) ); + filter->a[2] = (gain+1.0f) + (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha; + break; + case ALfilterType_Peaking: + alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0)); + filter->b[0] = 1.0f + alpha * gain; + filter->b[1] = -2.0f * cosf(w0); + filter->b[2] = 1.0f - alpha * gain; + filter->a[0] = 1.0f + alpha / gain; + filter->a[1] = -2.0f * cosf(w0); + filter->a[2] = 1.0f - alpha / gain; + break; + + case ALfilterType_LowPass: + alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0)); + filter->b[0] = (1.0f - cosf(w0)) / 2.0f; + filter->b[1] = 1.0f - cosf(w0); + filter->b[2] = (1.0f - cosf(w0)) / 2.0f; + filter->a[0] = 1.0f + alpha; + filter->a[1] = -2.0f * cosf(w0); + filter->a[2] = 1.0f - alpha; + break; + case ALfilterType_HighPass: + alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0)); + filter->b[0] = (1.0f + cosf(w0)) / 2.0f; + filter->b[1] = 1.0f + cosf(w0); + filter->b[2] = (1.0f + cosf(w0)) / 2.0f; + filter->a[0] = 1.0f + alpha; + filter->a[1] = -2.0f * cosf(w0); + filter->a[2] = 1.0f - alpha; + break; + case ALfilterType_BandPass: + alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0)); + filter->b[0] = alpha; + filter->b[1] = 0; + filter->b[2] = -alpha; + filter->a[0] = 1.0f + alpha; + filter->a[1] = -2.0f * cosf(w0); + filter->a[2] = 1.0f - alpha; + break; + } + + filter->b[2] /= filter->a[0]; + filter->b[1] /= filter->a[0]; + filter->b[0] /= filter->a[0]; + filter->a[2] /= filter->a[0]; + filter->a[1] /= filter->a[0]; + filter->a[0] /= filter->a[0]; + + filter->process = ALfilterState_processC; +} + + +static void lp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void lp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void lp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val) +{ + switch(param) + { + case AL_LOWPASS_GAIN: + if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + filter->Gain = val; + break; + + case AL_LOWPASS_GAINHF: + if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + filter->GainHF = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +static void lp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + lp_SetParamf(filter, context, param, vals[0]); +} + +static void lp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void lp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void lp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val) +{ + switch(param) + { + case AL_LOWPASS_GAIN: + *val = filter->Gain; + break; + + case AL_LOWPASS_GAINHF: + *val = filter->GainHF; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +static void lp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals) +{ + lp_GetParamf(filter, context, param, vals); +} + + +static void hp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void hp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void hp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val) +{ + switch(param) + { + case AL_HIGHPASS_GAIN: + if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + filter->Gain = val; + break; + + case AL_HIGHPASS_GAINLF: + if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + filter->GainLF = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +static void hp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + hp_SetParamf(filter, context, param, vals[0]); +} + +static void hp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void hp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void hp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val) +{ + switch(param) + { + case AL_HIGHPASS_GAIN: + *val = filter->Gain; + break; + + case AL_HIGHPASS_GAINLF: + *val = filter->GainLF; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +static void hp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals) +{ + hp_GetParamf(filter, context, param, vals); +} + + +static void bp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void bp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void bp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val) +{ + switch(param) + { + case AL_BANDPASS_GAIN: + if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + filter->Gain = val; + break; + + case AL_BANDPASS_GAINHF: + if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + filter->GainHF = val; + break; + + case AL_BANDPASS_GAINLF: + if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + filter->GainLF = val; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +static void bp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals) +{ + bp_SetParamf(filter, context, param, vals[0]); +} + +static void bp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void bp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void bp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val) +{ + switch(param) + { + case AL_BANDPASS_GAIN: + *val = filter->Gain; + break; + + case AL_BANDPASS_GAINHF: + *val = filter->GainHF; + break; + + case AL_BANDPASS_GAINLF: + *val = filter->GainLF; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} +static void bp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals) +{ + bp_GetParamf(filter, context, param, vals); +} + + +static void null_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void null_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void null_SetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void null_SetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALfloat *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } + +static void null_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void null_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void null_GetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } +static void null_GetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(vals)) +{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } + + +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->id); + memset(temp, 0, sizeof(ALfilter)); + free(temp); + } +} + + +static void InitFilterParams(ALfilter *filter, ALenum type) +{ + if(type == AL_FILTER_LOWPASS) + { + filter->Gain = AL_LOWPASS_DEFAULT_GAIN; + filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF; + filter->HFReference = LOWPASSFREQREF; + filter->GainLF = 1.0f; + filter->LFReference = HIGHPASSFREQREF; + + filter->SetParami = lp_SetParami; + filter->SetParamiv = lp_SetParamiv; + filter->SetParamf = lp_SetParamf; + filter->SetParamfv = lp_SetParamfv; + filter->GetParami = lp_GetParami; + filter->GetParamiv = lp_GetParamiv; + filter->GetParamf = lp_GetParamf; + filter->GetParamfv = lp_GetParamfv; + } + else if(type == AL_FILTER_HIGHPASS) + { + filter->Gain = AL_HIGHPASS_DEFAULT_GAIN; + filter->GainHF = 1.0f; + filter->HFReference = LOWPASSFREQREF; + filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF; + filter->LFReference = HIGHPASSFREQREF; + + filter->SetParami = hp_SetParami; + filter->SetParamiv = hp_SetParamiv; + filter->SetParamf = hp_SetParamf; + filter->SetParamfv = hp_SetParamfv; + filter->GetParami = hp_GetParami; + filter->GetParamiv = hp_GetParamiv; + filter->GetParamf = hp_GetParamf; + filter->GetParamfv = hp_GetParamfv; + } + else if(type == AL_FILTER_BANDPASS) + { + filter->Gain = AL_BANDPASS_DEFAULT_GAIN; + filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF; + filter->HFReference = LOWPASSFREQREF; + filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF; + filter->LFReference = HIGHPASSFREQREF; + + filter->SetParami = bp_SetParami; + filter->SetParamiv = bp_SetParamiv; + filter->SetParamf = bp_SetParamf; + filter->SetParamfv = bp_SetParamfv; + filter->GetParami = bp_GetParami; + filter->GetParamiv = bp_GetParamiv; + filter->GetParamf = bp_GetParamf; + filter->GetParamfv = bp_GetParamfv; + } + else + { + filter->Gain = 1.0f; + filter->GainHF = 1.0f; + filter->HFReference = LOWPASSFREQREF; + filter->GainLF = 1.0f; + filter->LFReference = HIGHPASSFREQREF; + + filter->SetParami = null_SetParami; + filter->SetParamiv = null_SetParamiv; + filter->SetParamf = null_SetParamf; + filter->SetParamfv = null_SetParamfv; + filter->GetParami = null_GetParami; + filter->GetParamiv = null_GetParamiv; + filter->GetParamf = null_GetParamf; + filter->GetParamfv = null_GetParamfv; + } + filter->type = type; +} diff --git a/libs/openal-soft/OpenAL32/alFontsound.c b/libs/openal-soft/OpenAL32/alFontsound.c new file mode 100644 index 00000000..3a8f1460 --- /dev/null +++ b/libs/openal-soft/OpenAL32/alFontsound.c @@ -0,0 +1,1008 @@ + +#include "config.h" + +#include +#include + +#include "alMain.h" +#include "alMidi.h" +#include "alError.h" +#include "alThunk.h" +#include "alBuffer.h" + +#include "midi/base.h" + + +extern inline struct ALfontsound *LookupFontsound(ALCdevice *device, ALuint id); +extern inline struct ALfontsound *RemoveFontsound(ALCdevice *device, ALuint id); + + +static void ALfontsound_Construct(ALfontsound *self); +static void ALfontsound_Destruct(ALfontsound *self); +void ALfontsound_setPropi(ALfontsound *self, ALCcontext *context, ALenum param, ALint value); +static ALsfmodulator *ALfontsound_getModStage(ALfontsound *self, ALsizei stage); +void ALfontsound_setModStagei(ALfontsound *self, ALCcontext *context, ALsizei stage, ALenum param, ALint value); +static void ALfontsound_getModStagei(ALfontsound *self, ALCcontext *context, ALsizei stage, ALenum param, ALint *values); + +static inline struct ALsfmodulator *LookupModulator(ALfontsound *sound, ALuint id) +{ + ALsfmodulator *mod = LookupUIntMapKey(&sound->ModulatorMap, id>>2); + if(mod) mod += id&3; + return mod; +} + + +AL_API void AL_APIENTRY alGenFontsoundsSOFT(ALsizei n, ALuint *ids) +{ + ALCcontext *context; + ALsizei cur = 0; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + for(cur = 0;cur < n;cur++) + { + ALfontsound *sound = NewFontsound(context); + if(!sound) + { + alDeleteFontsoundsSOFT(cur, ids); + break; + } + + ids[cur] = sound->id; + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alDeleteFontsoundsSOFT(ALsizei n, const ALuint *ids) +{ + ALCdevice *device; + ALCcontext *context; + ALfontsound *inst; + ALsizei i; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + for(i = 0;i < n;i++) + { + /* Check for valid ID */ + if((inst=LookupFontsound(device, ids[i])) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(ReadRef(&inst->ref) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + } + + for(i = 0;i < n;i++) + { + if((inst=LookupFontsound(device, ids[i])) != NULL) + DeleteFontsound(device, inst); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALboolean AL_APIENTRY alIsFontsoundSOFT(ALuint id) +{ + ALCcontext *context; + ALboolean ret; + + context = GetContextRef(); + if(!context) return AL_FALSE; + + ret = LookupFontsound(context->Device, id) ? AL_TRUE : AL_FALSE; + + ALCcontext_DecRef(context); + + return ret; +} + +AL_API void AL_APIENTRY alFontsoundiSOFT(ALuint id, ALenum param, ALint value) +{ + ALCdevice *device; + ALCcontext *context; + ALfontsound *sound; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(!(sound=LookupFontsound(device, id))) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(ReadRef(&sound->ref) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + + ALfontsound_setPropi(sound, context, param, value); + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alFontsound2iSOFT(ALuint id, ALenum param, ALint value1, ALint value2) +{ + ALCdevice *device; + ALCcontext *context; + ALfontsound *sound; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(!(sound=LookupFontsound(device, id))) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(ReadRef(&sound->ref) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + switch(param) + { + case AL_KEY_RANGE_SOFT: + if(!(value1 >= 0 && value1 <= 127 && value2 >= 0 && value2 <= 127 && value2 >= value1)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + sound->MinKey = value1; + sound->MaxKey = value2; + break; + + case AL_VELOCITY_RANGE_SOFT: + if(!(value1 >= 0 && value1 <= 127 && value2 >= 0 && value2 <= 127 && value2 >= value1)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + sound->MinVelocity = value1; + sound->MaxVelocity = value2; + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alFontsoundivSOFT(ALuint id, ALenum param, const ALint *values) +{ + ALCdevice *device; + ALCcontext *context; + ALfontsound *sound; + + switch(param) + { + case AL_KEY_RANGE_SOFT: + case AL_VELOCITY_RANGE_SOFT: + alFontsound2iSOFT(id, param, values[0], values[1]); + return; + + case AL_MOD_LFO_TO_PITCH_SOFT: + case AL_VIBRATO_LFO_TO_PITCH_SOFT: + case AL_MOD_ENV_TO_PITCH_SOFT: + case AL_FILTER_CUTOFF_SOFT: + case AL_FILTER_RESONANCE_SOFT: + case AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT: + case AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT: + case AL_MOD_LFO_TO_VOLUME_SOFT: + case AL_CHORUS_SEND_SOFT: + case AL_REVERB_SEND_SOFT: + case AL_PAN_SOFT: + case AL_MOD_LFO_DELAY_SOFT: + case AL_MOD_LFO_FREQUENCY_SOFT: + case AL_VIBRATO_LFO_DELAY_SOFT: + case AL_VIBRATO_LFO_FREQUENCY_SOFT: + case AL_MOD_ENV_DELAYTIME_SOFT: + case AL_MOD_ENV_ATTACKTIME_SOFT: + case AL_MOD_ENV_HOLDTIME_SOFT: + case AL_MOD_ENV_DECAYTIME_SOFT: + case AL_MOD_ENV_SUSTAINVOLUME_SOFT: + case AL_MOD_ENV_RELEASETIME_SOFT: + case AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT: + case AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT: + case AL_VOLUME_ENV_DELAYTIME_SOFT: + case AL_VOLUME_ENV_ATTACKTIME_SOFT: + case AL_VOLUME_ENV_HOLDTIME_SOFT: + case AL_VOLUME_ENV_DECAYTIME_SOFT: + case AL_VOLUME_ENV_SUSTAINVOLUME_SOFT: + case AL_VOLUME_ENV_RELEASETIME_SOFT: + case AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT: + case AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT: + case AL_ATTENUATION_SOFT: + case AL_TUNING_COARSE_SOFT: + case AL_TUNING_FINE_SOFT: + case AL_LOOP_MODE_SOFT: + case AL_TUNING_SCALE_SOFT: + case AL_EXCLUSIVE_CLASS_SOFT: + case AL_SAMPLE_START_SOFT: + case AL_SAMPLE_END_SOFT: + case AL_SAMPLE_LOOP_START_SOFT: + case AL_SAMPLE_LOOP_END_SOFT: + case AL_SAMPLE_RATE_SOFT: + case AL_BASE_KEY_SOFT: + case AL_KEY_CORRECTION_SOFT: + case AL_SAMPLE_TYPE_SOFT: + case AL_FONTSOUND_LINK_SOFT: + alFontsoundiSOFT(id, param, values[0]); + return; + } + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(!(sound=LookupFontsound(device, id))) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(ReadRef(&sound->ref) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alGetFontsoundivSOFT(ALuint id, ALenum param, ALint *values) +{ + ALCdevice *device; + ALCcontext *context; + const ALfontsound *sound; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(!(sound=LookupFontsound(device, id))) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + switch(param) + { + case AL_BUFFER: + values[0] = (sound->Buffer ? sound->Buffer->id : 0); + break; + + case AL_MOD_LFO_TO_PITCH_SOFT: + values[0] = sound->ModLfoToPitch; + break; + + case AL_VIBRATO_LFO_TO_PITCH_SOFT: + values[0] = sound->VibratoLfoToPitch; + break; + + case AL_MOD_ENV_TO_PITCH_SOFT: + values[0] = sound->ModEnvToPitch; + break; + + case AL_FILTER_CUTOFF_SOFT: + values[0] = sound->FilterCutoff; + break; + + case AL_FILTER_RESONANCE_SOFT: + values[0] = sound->FilterQ; + break; + + case AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT: + values[0] = sound->ModLfoToFilterCutoff; + break; + + case AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT: + values[0] = sound->ModEnvToFilterCutoff; + break; + + case AL_MOD_LFO_TO_VOLUME_SOFT: + values[0] = sound->ModLfoToVolume; + break; + + case AL_CHORUS_SEND_SOFT: + values[0] = sound->ChorusSend; + break; + + case AL_REVERB_SEND_SOFT: + values[0] = sound->ReverbSend; + break; + + case AL_PAN_SOFT: + values[0] = sound->Pan; + break; + + case AL_MOD_LFO_DELAY_SOFT: + values[0] = sound->ModLfo.Delay; + break; + case AL_MOD_LFO_FREQUENCY_SOFT: + values[0] = sound->ModLfo.Frequency; + break; + + case AL_VIBRATO_LFO_DELAY_SOFT: + values[0] = sound->VibratoLfo.Delay; + break; + case AL_VIBRATO_LFO_FREQUENCY_SOFT: + values[0] = sound->VibratoLfo.Frequency; + break; + + case AL_MOD_ENV_DELAYTIME_SOFT: + values[0] = sound->ModEnv.DelayTime; + break; + case AL_MOD_ENV_ATTACKTIME_SOFT: + values[0] = sound->ModEnv.AttackTime; + break; + case AL_MOD_ENV_HOLDTIME_SOFT: + values[0] = sound->ModEnv.HoldTime; + break; + case AL_MOD_ENV_DECAYTIME_SOFT: + values[0] = sound->ModEnv.DecayTime; + break; + case AL_MOD_ENV_SUSTAINVOLUME_SOFT: + values[0] = sound->ModEnv.SustainAttn; + break; + case AL_MOD_ENV_RELEASETIME_SOFT: + values[0] = sound->ModEnv.ReleaseTime; + break; + case AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT: + values[0] = sound->ModEnv.KeyToHoldTime; + break; + case AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT: + values[0] = sound->ModEnv.KeyToDecayTime; + break; + + case AL_VOLUME_ENV_DELAYTIME_SOFT: + values[0] = sound->VolEnv.DelayTime; + break; + case AL_VOLUME_ENV_ATTACKTIME_SOFT: + values[0] = sound->VolEnv.AttackTime; + break; + case AL_VOLUME_ENV_HOLDTIME_SOFT: + values[0] = sound->VolEnv.HoldTime; + break; + case AL_VOLUME_ENV_DECAYTIME_SOFT: + values[0] = sound->VolEnv.DecayTime; + break; + case AL_VOLUME_ENV_SUSTAINVOLUME_SOFT: + values[0] = sound->VolEnv.SustainAttn; + break; + case AL_VOLUME_ENV_RELEASETIME_SOFT: + values[0] = sound->VolEnv.ReleaseTime; + break; + case AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT: + values[0] = sound->VolEnv.KeyToHoldTime; + break; + case AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT: + values[0] = sound->VolEnv.KeyToDecayTime; + break; + + case AL_KEY_RANGE_SOFT: + values[0] = sound->MinKey; + values[1] = sound->MaxKey; + break; + + case AL_VELOCITY_RANGE_SOFT: + values[0] = sound->MinVelocity; + values[1] = sound->MaxVelocity; + break; + + case AL_ATTENUATION_SOFT: + values[0] = sound->Attenuation; + break; + + case AL_TUNING_COARSE_SOFT: + values[0] = sound->CoarseTuning; + break; + case AL_TUNING_FINE_SOFT: + values[0] = sound->FineTuning; + break; + + case AL_LOOP_MODE_SOFT: + values[0] = sound->LoopMode; + break; + + case AL_TUNING_SCALE_SOFT: + values[0] = sound->TuningScale; + break; + + case AL_EXCLUSIVE_CLASS_SOFT: + values[0] = sound->ExclusiveClass; + break; + + case AL_SAMPLE_START_SOFT: + values[0] = sound->Start; + break; + + case AL_SAMPLE_END_SOFT: + values[0] = sound->End; + break; + + case AL_SAMPLE_LOOP_START_SOFT: + values[0] = sound->LoopStart; + break; + + case AL_SAMPLE_LOOP_END_SOFT: + values[0] = sound->LoopEnd; + break; + + case AL_SAMPLE_RATE_SOFT: + values[0] = sound->SampleRate; + break; + + case AL_BASE_KEY_SOFT: + values[0] = sound->PitchKey; + break; + + case AL_KEY_CORRECTION_SOFT: + values[0] = sound->PitchCorrection; + break; + + case AL_SAMPLE_TYPE_SOFT: + values[0] = sound->SampleType; + break; + + case AL_FONTSOUND_LINK_SOFT: + values[0] = (sound->Link ? sound->Link->id : 0); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alFontsoundModulatoriSOFT(ALuint id, ALsizei stage, ALenum param, ALint value) +{ + ALCdevice *device; + ALCcontext *context; + ALfontsound *sound; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(!(sound=LookupFontsound(device, id))) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + ALfontsound_setModStagei(sound, context, stage, param, value); + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alGetFontsoundModulatorivSOFT(ALuint id, ALsizei stage, ALenum param, ALint *values) +{ + ALCdevice *device; + ALCcontext *context; + ALfontsound *sound; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(!(sound=LookupFontsound(device, id))) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + ALfontsound_getModStagei(sound, context, stage, param, values); + +done: + ALCcontext_DecRef(context); +} + + +ALfontsound *NewFontsound(ALCcontext *context) +{ + ALCdevice *device = context->Device; + ALfontsound *sound; + ALenum err; + + sound = calloc(1, sizeof(*sound)); + if(!sound) + SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL); + ALfontsound_Construct(sound); + + err = NewThunkEntry(&sound->id); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&device->FontsoundMap, sound->id, sound); + if(err != AL_NO_ERROR) + { + ALfontsound_Destruct(sound); + memset(sound, 0, sizeof(*sound)); + free(sound); + + SET_ERROR_AND_RETURN_VALUE(context, err, NULL); + } + + return sound; +} + +void DeleteFontsound(ALCdevice *device, ALfontsound *sound) +{ + RemoveFontsound(device, sound->id); + + ALfontsound_Destruct(sound); + + memset(sound, 0, sizeof(*sound)); + free(sound); +} + + +static void ALfontsound_Construct(ALfontsound *self) +{ + InitRef(&self->ref, 0); + + self->Buffer = NULL; + + self->MinKey = 0; + self->MaxKey = 127; + self->MinVelocity = 0; + self->MaxVelocity = 127; + + self->ModLfoToPitch = 0; + self->VibratoLfoToPitch = 0; + self->ModEnvToPitch = 0; + + self->FilterCutoff = 13500; + self->FilterQ = 0; + self->ModLfoToFilterCutoff = 0; + self->ModEnvToFilterCutoff = 0; + self->ModLfoToVolume = 0; + + self->ChorusSend = 0; + self->ReverbSend = 0; + + self->Pan = 0; + + self->ModLfo.Delay = 0; + self->ModLfo.Frequency = 0; + + self->VibratoLfo.Delay = 0; + self->VibratoLfo.Frequency = 0; + + self->ModEnv.DelayTime = -12000; + self->ModEnv.AttackTime = -12000; + self->ModEnv.HoldTime = -12000; + self->ModEnv.DecayTime = -12000; + self->ModEnv.SustainAttn = 0; + self->ModEnv.ReleaseTime = -12000; + self->ModEnv.KeyToHoldTime = 0; + self->ModEnv.KeyToDecayTime = 0; + + self->VolEnv.DelayTime = -12000; + self->VolEnv.AttackTime = -12000; + self->VolEnv.HoldTime = -12000; + self->VolEnv.DecayTime = -12000; + self->VolEnv.SustainAttn = 0; + self->VolEnv.ReleaseTime = -12000; + self->VolEnv.KeyToHoldTime = 0; + self->VolEnv.KeyToDecayTime = 0; + + self->Attenuation = 0; + + self->CoarseTuning = 0; + self->FineTuning = 0; + + self->LoopMode = AL_NONE; + + self->TuningScale = 100; + + self->ExclusiveClass = 0; + + self->Start = 0; + self->End = 0; + self->LoopStart = 0; + self->LoopEnd = 0; + self->SampleRate = 0; + self->PitchKey = 0; + self->PitchCorrection = 0; + self->SampleType = AL_MONO_SOFT; + self->Link = NULL; + + InitUIntMap(&self->ModulatorMap, ~0); + + self->id = 0; +} + +static void ALfontsound_Destruct(ALfontsound *self) +{ + ALsizei i; + + FreeThunkEntry(self->id); + self->id = 0; + + if(self->Buffer) + DecrementRef(&self->Buffer->ref); + self->Buffer = NULL; + if(self->Link) + DecrementRef(&self->Link->ref); + self->Link = NULL; + + for(i = 0;i < self->ModulatorMap.size;i++) + { + free(self->ModulatorMap.array[i].value); + self->ModulatorMap.array[i].value = NULL; + } + ResetUIntMap(&self->ModulatorMap); +} + +void ALfontsound_setPropi(ALfontsound *self, ALCcontext *context, ALenum param, ALint value) +{ + ALfontsound *link; + ALbuffer *buffer; + + switch(param) + { + case AL_BUFFER: + buffer = value ? LookupBuffer(context->Device, value) : NULL; + if(value && !buffer) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + else if(buffer) + { + /* Buffer must have a non-0 length, and must be mono. */ + if(buffer->SampleLen <= 0 || buffer->FmtChannels != FmtMono) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + } + + if(buffer) IncrementRef(&buffer->ref); + if((buffer=ExchangePtr((XchgPtr*)&self->Buffer, buffer)) != NULL) + DecrementRef(&buffer->ref); + break; + + case AL_MOD_LFO_TO_PITCH_SOFT: + self->ModLfoToPitch = value; + break; + + case AL_VIBRATO_LFO_TO_PITCH_SOFT: + self->VibratoLfoToPitch = value; + break; + + case AL_MOD_ENV_TO_PITCH_SOFT: + self->ModEnvToPitch = value; + break; + + case AL_FILTER_CUTOFF_SOFT: + self->FilterCutoff = value; + break; + + case AL_FILTER_RESONANCE_SOFT: + if(!(value >= 0)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + self->FilterQ = value; + break; + + case AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT: + self->ModLfoToFilterCutoff = value; + break; + + case AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT: + self->ModEnvToFilterCutoff = value; + break; + + case AL_MOD_LFO_TO_VOLUME_SOFT: + self->ModLfoToVolume = value; + break; + + case AL_CHORUS_SEND_SOFT: + if(!(value >= 0 && value <= 1000)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + self->ChorusSend = value; + break; + case AL_REVERB_SEND_SOFT: + if(!(value >= 0 && value <= 1000)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + self->ReverbSend = value; + break; + + case AL_PAN_SOFT: + self->Pan = value; + break; + + case AL_MOD_LFO_DELAY_SOFT: + self->ModLfo.Delay = value; + break; + case AL_MOD_LFO_FREQUENCY_SOFT: + self->ModLfo.Frequency = value; + break; + + case AL_VIBRATO_LFO_DELAY_SOFT: + self->VibratoLfo.Delay = value; + break; + case AL_VIBRATO_LFO_FREQUENCY_SOFT: + self->VibratoLfo.Frequency = value; + break; + + case AL_MOD_ENV_DELAYTIME_SOFT: + self->ModEnv.DelayTime = value; + break; + case AL_MOD_ENV_ATTACKTIME_SOFT: + self->ModEnv.AttackTime = value; + break; + case AL_MOD_ENV_HOLDTIME_SOFT: + self->ModEnv.HoldTime = value; + break; + case AL_MOD_ENV_DECAYTIME_SOFT: + self->ModEnv.DecayTime = value; + break; + case AL_MOD_ENV_SUSTAINVOLUME_SOFT: + self->ModEnv.SustainAttn = value; + break; + case AL_MOD_ENV_RELEASETIME_SOFT: + self->ModEnv.ReleaseTime = value; + break; + case AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT: + self->ModEnv.KeyToHoldTime = value; + break; + case AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT: + self->ModEnv.KeyToDecayTime = value; + break; + + case AL_VOLUME_ENV_DELAYTIME_SOFT: + self->VolEnv.DelayTime = value; + break; + case AL_VOLUME_ENV_ATTACKTIME_SOFT: + self->VolEnv.AttackTime = value; + break; + case AL_VOLUME_ENV_HOLDTIME_SOFT: + self->VolEnv.HoldTime = value; + break; + case AL_VOLUME_ENV_DECAYTIME_SOFT: + self->VolEnv.DecayTime = value; + break; + case AL_VOLUME_ENV_SUSTAINVOLUME_SOFT: + self->VolEnv.SustainAttn = value; + break; + case AL_VOLUME_ENV_RELEASETIME_SOFT: + self->VolEnv.ReleaseTime = value; + break; + case AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT: + self->VolEnv.KeyToHoldTime = value; + break; + case AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT: + self->VolEnv.KeyToDecayTime = value; + break; + + case AL_ATTENUATION_SOFT: + if(!(value >= 0)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + self->Attenuation = value; + break; + + case AL_TUNING_COARSE_SOFT: + self->CoarseTuning = value; + break; + case AL_TUNING_FINE_SOFT: + self->FineTuning = value; + break; + + case AL_LOOP_MODE_SOFT: + if(!(value == AL_NONE || value == AL_LOOP_CONTINUOUS_SOFT || + value == AL_LOOP_UNTIL_RELEASE_SOFT)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + self->LoopMode = value; + break; + + case AL_TUNING_SCALE_SOFT: + self->TuningScale = value; + break; + + case AL_EXCLUSIVE_CLASS_SOFT: + self->ExclusiveClass = value; + break; + + case AL_SAMPLE_START_SOFT: + self->Start = value; + break; + + case AL_SAMPLE_END_SOFT: + self->End = value; + break; + + case AL_SAMPLE_LOOP_START_SOFT: + self->LoopStart = value; + break; + + case AL_SAMPLE_LOOP_END_SOFT: + self->LoopEnd = value; + break; + + case AL_SAMPLE_RATE_SOFT: + if(!(value > 0)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + self->SampleRate = value; + break; + + case AL_BASE_KEY_SOFT: + if(!((value >= 0 && value <= 127) || value == 255)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + self->PitchKey = value; + break; + + case AL_KEY_CORRECTION_SOFT: + if(!(value >= -99 && value <= 99)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + self->PitchCorrection = value; + break; + + case AL_SAMPLE_TYPE_SOFT: + if(!(value == AL_MONO_SOFT || value == AL_RIGHT_SOFT || value == AL_LEFT_SOFT)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + self->SampleType = value; + break; + + case AL_FONTSOUND_LINK_SOFT: + link = value ? LookupFontsound(context->Device, value) : NULL; + if(value && !link) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + + if(link) IncrementRef(&link->ref); + if((link=ExchangePtr((XchgPtr*)&self->Link, link)) != NULL) + DecrementRef(&link->ref); + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} + +static ALsfmodulator *ALfontsound_getModStage(ALfontsound *self, ALsizei stage) +{ + ALsfmodulator *ret = LookupModulator(self, stage); + if(!ret) + { + static const ALsfmodulator moddef = { + { { AL_ONE_SOFT, AL_UNORM_SOFT, AL_LINEAR_SOFT }, + { AL_ONE_SOFT, AL_UNORM_SOFT, AL_LINEAR_SOFT } }, + 0, + AL_LINEAR_SOFT, + AL_NONE + }; + ret = malloc(sizeof(ALsfmodulator[4])); + ret[0] = moddef; + ret[1] = moddef; + ret[2] = moddef; + ret[3] = moddef; + InsertUIntMapEntry(&self->ModulatorMap, stage>>2, ret); + ret += stage&3; + } + return ret; +} + +void ALfontsound_setModStagei(ALfontsound *self, ALCcontext *context, ALsizei stage, ALenum param, ALint value) +{ + ALint srcidx = 0; + + if(ReadRef(&self->ref) != 0) + SET_ERROR_AND_RETURN(context, AL_INVALID_OPERATION); + switch(param) + { + case AL_SOURCE1_INPUT_SOFT: + srcidx++; + /* fall-through */ + case AL_SOURCE0_INPUT_SOFT: + if(!(value == AL_ONE_SOFT || value == AL_NOTEON_VELOCITY_SOFT || + value == AL_NOTEON_KEY_SOFT || value == AL_KEYPRESSURE_SOFT || + value == AL_CHANNELPRESSURE_SOFT || value == AL_PITCHBEND_SOFT || + value == AL_PITCHBEND_SENSITIVITY_SOFT || + IsValidCtrlInput(value))) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + ALfontsound_getModStage(self, stage)->Source[srcidx].Input = value; + break; + + case AL_SOURCE1_TYPE_SOFT: + srcidx++; + /* fall-through */ + case AL_SOURCE0_TYPE_SOFT: + if(!(value == AL_UNORM_SOFT || value == AL_UNORM_REV_SOFT || + value == AL_SNORM_SOFT || value == AL_SNORM_REV_SOFT)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + ALfontsound_getModStage(self, stage)->Source[srcidx].Type = value; + break; + + case AL_SOURCE1_FORM_SOFT: + srcidx++; + /* fall-through */ + case AL_SOURCE0_FORM_SOFT: + if(!(value == AL_LINEAR_SOFT || value == AL_CONCAVE_SOFT || + value == AL_CONVEX_SOFT || value == AL_SWITCH_SOFT)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + ALfontsound_getModStage(self, stage)->Source[srcidx].Form = value; + break; + + case AL_AMOUNT_SOFT: + ALfontsound_getModStage(self, stage)->Amount = value; + break; + + case AL_TRANSFORM_OP_SOFT: + if(!(value == AL_LINEAR_SOFT || value == AL_ABSOLUTE_SOFT)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + ALfontsound_getModStage(self, stage)->TransformOp = value; + break; + + case AL_DESTINATION_SOFT: + if(!(value == AL_MOD_LFO_TO_PITCH_SOFT || value == AL_VIBRATO_LFO_TO_PITCH_SOFT || + value == AL_MOD_ENV_TO_PITCH_SOFT || value == AL_FILTER_CUTOFF_SOFT || + value == AL_FILTER_RESONANCE_SOFT || value == AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT || + value == AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT || value == AL_MOD_LFO_TO_VOLUME_SOFT || + value == AL_CHORUS_SEND_SOFT || value == AL_REVERB_SEND_SOFT || value == AL_PAN_SOFT || + value == AL_MOD_LFO_DELAY_SOFT || value == AL_MOD_LFO_FREQUENCY_SOFT || + value == AL_VIBRATO_LFO_DELAY_SOFT || value == AL_VIBRATO_LFO_FREQUENCY_SOFT || + value == AL_MOD_ENV_DELAYTIME_SOFT || value == AL_MOD_ENV_ATTACKTIME_SOFT || + value == AL_MOD_ENV_HOLDTIME_SOFT || value == AL_MOD_ENV_DECAYTIME_SOFT || + value == AL_MOD_ENV_SUSTAINVOLUME_SOFT || value == AL_MOD_ENV_RELEASETIME_SOFT || + value == AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT || value == AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT || + value == AL_VOLUME_ENV_DELAYTIME_SOFT || value == AL_VOLUME_ENV_ATTACKTIME_SOFT || + value == AL_VOLUME_ENV_HOLDTIME_SOFT || value == AL_VOLUME_ENV_DECAYTIME_SOFT || + value == AL_VOLUME_ENV_SUSTAINVOLUME_SOFT || value == AL_VOLUME_ENV_RELEASETIME_SOFT || + value == AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT || value == AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT || + value == AL_ATTENUATION_SOFT || value == AL_TUNING_COARSE_SOFT || + value == AL_TUNING_FINE_SOFT || value == AL_TUNING_SCALE_SOFT)) + SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + ALfontsound_getModStage(self, stage)->Dest = value; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} + +static void ALfontsound_getModStagei(ALfontsound *self, ALCcontext *context, ALsizei stage, ALenum param, ALint *values) +{ + ALsfmodulator *mod = LookupModulator(self, stage); + ALint srcidx = 0; + + switch(param) + { + case AL_SOURCE1_INPUT_SOFT: + srcidx++; + /* fall-through */ + case AL_SOURCE0_INPUT_SOFT: + values[0] = mod ? mod->Source[srcidx].Input : AL_ONE_SOFT; + break; + + case AL_SOURCE1_TYPE_SOFT: + srcidx++; + /* fall-through */ + case AL_SOURCE0_TYPE_SOFT: + values[0] = mod ? mod->Source[srcidx].Type : AL_UNORM_SOFT; + break; + + case AL_SOURCE1_FORM_SOFT: + srcidx++; + /* fall-through */ + case AL_SOURCE0_FORM_SOFT: + values[0] = mod ? mod->Source[srcidx].Form : AL_LINEAR_SOFT; + break; + + case AL_AMOUNT_SOFT: + values[0] = mod ? mod->Amount : 0; + break; + + case AL_TRANSFORM_OP_SOFT: + values[0] = mod ? mod->TransformOp : AL_LINEAR_SOFT; + break; + + case AL_DESTINATION_SOFT: + values[0] = mod ? mod->Dest : AL_NONE; + break; + + default: + SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + } +} + + +/* ReleaseALFontsounds + * + * Called to destroy any fontsounds that still exist on the device + */ +void ReleaseALFontsounds(ALCdevice *device) +{ + ALsizei i; + for(i = 0;i < device->FontsoundMap.size;i++) + { + ALfontsound *temp = device->FontsoundMap.array[i].value; + device->FontsoundMap.array[i].value = NULL; + + ALfontsound_Destruct(temp); + + memset(temp, 0, sizeof(*temp)); + free(temp); + } +} diff --git a/libs/openal-soft/OpenAL32/alListener.c b/libs/openal-soft/OpenAL32/alListener.c new file mode 100644 index 00000000..0baae82e --- /dev/null +++ b/libs/openal-soft/OpenAL32/alListener.c @@ -0,0 +1,446 @@ +/** + * 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 param, ALfloat value) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + switch(param) + { + case AL_GAIN: + if(!(value >= 0.0f && isfinite(value))) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + context->Listener->Gain = value; + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + break; + + case AL_METERS_PER_UNIT: + if(!(value >= 0.0f && isfinite(value))) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + context->Listener->MetersPerUnit = value; + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALvoid AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + switch(param) + { + case AL_POSITION: + if(!(isfinite(value1) && isfinite(value2) && isfinite(value3))) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + LockContext(context); + context->Listener->Position[0] = value1; + context->Listener->Position[1] = value2; + context->Listener->Position[2] = value3; + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + UnlockContext(context); + break; + + case AL_VELOCITY: + if(!(isfinite(value1) && isfinite(value2) && isfinite(value3))) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + LockContext(context); + context->Listener->Velocity[0] = value1; + context->Listener->Velocity[1] = value2; + context->Listener->Velocity[2] = value3; + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + UnlockContext(context); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values) +{ + ALCcontext *context; + + if(values) + { + switch(param) + { + case AL_GAIN: + case AL_METERS_PER_UNIT: + alListenerf(param, values[0]); + return; + + case AL_POSITION: + case AL_VELOCITY: + alListener3f(param, values[0], values[1], values[2]); + return; + } + } + + context = GetContextRef(); + if(!context) return; + + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + case AL_ORIENTATION: + if(!(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) && + isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5]))) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + LockContext(context); + /* AT then UP */ + context->Listener->Forward[0] = values[0]; + context->Listener->Forward[1] = values[1]; + context->Listener->Forward[2] = values[2]; + context->Listener->Up[0] = values[3]; + context->Listener->Up[1] = values[4]; + context->Listener->Up[2] = values[5]; + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + UnlockContext(context); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALvoid AL_APIENTRY alListeneri(ALenum param, ALint UNUSED(value)) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3) +{ + ALCcontext *context; + + switch(param) + { + case AL_POSITION: + case AL_VELOCITY: + alListener3f(param, (ALfloat)value1, (ALfloat)value2, (ALfloat)value3); + return; + } + + context = GetContextRef(); + if(!context) return; + + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values) +{ + ALCcontext *context; + + if(values) + { + ALfloat fvals[6]; + switch(param) + { + case AL_POSITION: + case AL_VELOCITY: + alListener3f(param, (ALfloat)values[0], (ALfloat)values[1], (ALfloat)values[2]); + return; + + case AL_ORIENTATION: + fvals[0] = (ALfloat)values[0]; + fvals[1] = (ALfloat)values[1]; + fvals[2] = (ALfloat)values[2]; + fvals[3] = (ALfloat)values[3]; + fvals[4] = (ALfloat)values[4]; + fvals[5] = (ALfloat)values[5]; + alListenerfv(param, fvals); + return; + } + } + + context = GetContextRef(); + if(!context) return; + + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + if(!(value)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + case AL_GAIN: + *value = context->Listener->Gain; + break; + + case AL_METERS_PER_UNIT: + *value = context->Listener->MetersPerUnit; + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + if(!(value1 && value2 && value3)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + case AL_POSITION: + LockContext(context); + *value1 = context->Listener->Position[0]; + *value2 = context->Listener->Position[1]; + *value3 = context->Listener->Position[2]; + UnlockContext(context); + break; + + case AL_VELOCITY: + LockContext(context); + *value1 = context->Listener->Velocity[0]; + *value2 = context->Listener->Velocity[1]; + *value3 = context->Listener->Velocity[2]; + UnlockContext(context); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values) +{ + ALCcontext *context; + + switch(param) + { + case AL_GAIN: + case AL_METERS_PER_UNIT: + alGetListenerf(param, values); + return; + + case AL_POSITION: + case AL_VELOCITY: + alGetListener3f(param, values+0, values+1, values+2); + return; + } + + context = GetContextRef(); + if(!context) return; + + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + case AL_ORIENTATION: + LockContext(context); + // AT then UP + values[0] = context->Listener->Forward[0]; + values[1] = context->Listener->Forward[1]; + values[2] = context->Listener->Forward[2]; + values[3] = context->Listener->Up[0]; + values[4] = context->Listener->Up[1]; + values[5] = context->Listener->Up[2]; + UnlockContext(context); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + if(!(value)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + if(!(value1 && value2 && value3)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch (param) + { + case AL_POSITION: + LockContext(context); + *value1 = (ALint)context->Listener->Position[0]; + *value2 = (ALint)context->Listener->Position[1]; + *value3 = (ALint)context->Listener->Position[2]; + UnlockContext(context); + break; + + case AL_VELOCITY: + LockContext(context); + *value1 = (ALint)context->Listener->Velocity[0]; + *value2 = (ALint)context->Listener->Velocity[1]; + *value3 = (ALint)context->Listener->Velocity[2]; + UnlockContext(context); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values) +{ + ALCcontext *context; + + switch(param) + { + case AL_POSITION: + case AL_VELOCITY: + alGetListener3i(param, values+0, values+1, values+2); + return; + } + + context = GetContextRef(); + if(!context) return; + + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(param) + { + case AL_ORIENTATION: + LockContext(context); + // AT then UP + values[0] = (ALint)context->Listener->Forward[0]; + values[1] = (ALint)context->Listener->Forward[1]; + values[2] = (ALint)context->Listener->Forward[2]; + values[3] = (ALint)context->Listener->Up[0]; + values[4] = (ALint)context->Listener->Up[1]; + values[5] = (ALint)context->Listener->Up[2]; + UnlockContext(context); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} diff --git a/libs/openal-soft/OpenAL32/alMidi.c b/libs/openal-soft/OpenAL32/alMidi.c new file mode 100644 index 00000000..015b1915 --- /dev/null +++ b/libs/openal-soft/OpenAL32/alMidi.c @@ -0,0 +1,217 @@ + +#include "config.h" + +#include +#include +#include +#include + +#include "alMain.h" +#include "alMidi.h" +#include "alError.h" +#include "alThunk.h" +#include "evtqueue.h" +#include "rwlock.h" +#include "alu.h" + +#include "midi/base.h" + + +MidiSynth *SynthCreate(ALCdevice *device) +{ + MidiSynth *synth = NULL; + if(!synth) synth = SSynth_create(device); + if(!synth) synth = FSynth_create(device); + if(!synth) synth = DSynth_create(device); + return synth; +} + + +AL_API void AL_APIENTRY alMidiSoundfontSOFT(ALuint id) +{ + alMidiSoundfontvSOFT(1, &id); +} + +AL_API void AL_APIENTRY alMidiSoundfontvSOFT(ALsizei count, const ALuint *ids) +{ + ALCdevice *device; + ALCcontext *context; + MidiSynth *synth; + ALenum err; + + context = GetContextRef(); + if(!context) return; + + if(count < 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + synth = device->Synth; + + WriteLock(&synth->Lock); + if(synth->State == AL_PLAYING || synth->State == AL_PAUSED) + alSetError(context, AL_INVALID_OPERATION); + else + { + err = V(synth,selectSoundfonts)(context, count, ids); + if(err != AL_NO_ERROR) + alSetError(context, err); + } + WriteUnlock(&synth->Lock); + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alMidiEventSOFT(ALuint64SOFT time, ALenum event, ALsizei channel, ALsizei param1, ALsizei param2) +{ + ALCdevice *device; + ALCcontext *context; + ALenum err; + + context = GetContextRef(); + if(!context) return; + + if(!(event == AL_NOTEOFF_SOFT || event == AL_NOTEON_SOFT || + event == AL_KEYPRESSURE_SOFT || event == AL_CONTROLLERCHANGE_SOFT || + event == AL_PROGRAMCHANGE_SOFT || event == AL_CHANNELPRESSURE_SOFT || + event == AL_PITCHBEND_SOFT)) + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + if(!(channel >= 0 && channel <= 15)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(!(param1 >= 0 && param1 <= 127)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(!(param2 >= 0 && param2 <= 127)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + ALCdevice_Lock(device); + err = MidiSynth_insertEvent(device->Synth, time, event|channel, param1, param2); + ALCdevice_Unlock(device); + if(err != AL_NO_ERROR) + alSetError(context, err); + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alMidiSysExSOFT(ALuint64SOFT time, const ALbyte *data, ALsizei size) +{ + ALCdevice *device; + ALCcontext *context; + ALenum err; + + context = GetContextRef(); + if(!context) return; + + if(!data || size < 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + ALCdevice_Lock(device); + err = MidiSynth_insertSysExEvent(device->Synth, time, data, size); + ALCdevice_Unlock(device); + if(err != AL_NO_ERROR) + alSetError(context, err); + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alMidiPlaySOFT(void) +{ + ALCcontext *context; + MidiSynth *synth; + + context = GetContextRef(); + if(!context) return; + + synth = context->Device->Synth; + WriteLock(&synth->Lock); + MidiSynth_setState(synth, AL_PLAYING); + WriteUnlock(&synth->Lock); + + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alMidiPauseSOFT(void) +{ + ALCcontext *context; + MidiSynth *synth; + + context = GetContextRef(); + if(!context) return; + + synth = context->Device->Synth; + WriteLock(&synth->Lock); + MidiSynth_setState(synth, AL_PAUSED); + WriteUnlock(&synth->Lock); + + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alMidiStopSOFT(void) +{ + ALCdevice *device; + ALCcontext *context; + MidiSynth *synth; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + synth = device->Synth; + + WriteLock(&synth->Lock); + MidiSynth_setState(synth, AL_STOPPED); + + ALCdevice_Lock(device); + V0(synth,stop)(); + ALCdevice_Unlock(device); + WriteUnlock(&synth->Lock); + + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alMidiResetSOFT(void) +{ + ALCdevice *device; + ALCcontext *context; + MidiSynth *synth; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + synth = device->Synth; + + WriteLock(&synth->Lock); + MidiSynth_setState(synth, AL_INITIAL); + + ALCdevice_Lock(device); + V0(synth,reset)(); + ALCdevice_Unlock(device); + WriteUnlock(&synth->Lock); + + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alMidiGainSOFT(ALfloat value) +{ + ALCdevice *device; + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + if(!(value >= 0.0f && isfinite(value))) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + V(device->Synth,setGain)(value); + +done: + ALCcontext_DecRef(context); +} diff --git a/libs/openal-soft/OpenAL32/alPreset.c b/libs/openal-soft/OpenAL32/alPreset.c new file mode 100644 index 00000000..1934ba05 --- /dev/null +++ b/libs/openal-soft/OpenAL32/alPreset.c @@ -0,0 +1,339 @@ + +#include "config.h" + +#include +#include + +#include "alMain.h" +#include "alMidi.h" +#include "alError.h" +#include "alThunk.h" + +#include "midi/base.h" + + +extern inline struct ALsfpreset *LookupPreset(ALCdevice *device, ALuint id); +extern inline struct ALsfpreset *RemovePreset(ALCdevice *device, ALuint id); + +static void ALsfpreset_Construct(ALsfpreset *self); +static void ALsfpreset_Destruct(ALsfpreset *self); + + +AL_API void AL_APIENTRY alGenPresetsSOFT(ALsizei n, ALuint *ids) +{ + ALCcontext *context; + ALsizei cur = 0; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + for(cur = 0;cur < n;cur++) + { + ALsfpreset *preset = NewPreset(context); + if(!preset) + { + alDeletePresetsSOFT(cur, ids); + break; + } + + ids[cur] = preset->id; + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alDeletePresetsSOFT(ALsizei n, const ALuint *ids) +{ + ALCdevice *device; + ALCcontext *context; + ALsfpreset *preset; + ALsizei i; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + for(i = 0;i < n;i++) + { + /* Check for valid ID */ + if((preset=LookupPreset(device, ids[i])) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(ReadRef(&preset->ref) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + } + + for(i = 0;i < n;i++) + { + if((preset=LookupPreset(device, ids[i])) != NULL) + DeletePreset(device, preset); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALboolean AL_APIENTRY alIsPresetSOFT(ALuint id) +{ + ALCcontext *context; + ALboolean ret; + + context = GetContextRef(); + if(!context) return AL_FALSE; + + ret = LookupPreset(context->Device, id) ? AL_TRUE : AL_FALSE; + + ALCcontext_DecRef(context); + + return ret; +} + +AL_API void AL_APIENTRY alPresetiSOFT(ALuint id, ALenum param, ALint value) +{ + ALCdevice *device; + ALCcontext *context; + ALsfpreset *preset; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((preset=LookupPreset(device, id)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(ReadRef(&preset->ref) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + switch(param) + { + case AL_MIDI_PRESET_SOFT: + if(!(value >= 0 && value <= 127)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + preset->Preset = value; + break; + + case AL_MIDI_BANK_SOFT: + if(!(value >= 0 && value <= 128)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + preset->Bank = value; + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alPresetivSOFT(ALuint id, ALenum param, const ALint *values) +{ + ALCdevice *device; + ALCcontext *context; + ALsfpreset *preset; + + switch(param) + { + case AL_MIDI_PRESET_SOFT: + case AL_MIDI_BANK_SOFT: + alPresetiSOFT(id, param, values[0]); + return; + } + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((preset=LookupPreset(device, id)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(ReadRef(&preset->ref) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + switch(param) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alGetPresetivSOFT(ALuint id, ALenum param, ALint *values) +{ + ALCdevice *device; + ALCcontext *context; + ALsfpreset *preset; + ALsizei i; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if((preset=LookupPreset(device, id)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + switch(param) + { + case AL_MIDI_PRESET_SOFT: + values[0] = preset->Preset; + break; + + case AL_MIDI_BANK_SOFT: + values[0] = preset->Bank; + break; + + case AL_FONTSOUNDS_SIZE_SOFT: + values[0] = preset->NumSounds; + break; + + case AL_FONTSOUNDS_SOFT: + for(i = 0;i < preset->NumSounds;i++) + values[i] = preset->Sounds[i]->id; + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alPresetFontsoundsSOFT(ALuint id, ALsizei count, const ALuint *fsids) +{ + ALCdevice *device; + ALCcontext *context; + ALsfpreset *preset; + ALfontsound **sounds; + ALsizei i; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(!(preset=LookupPreset(device, id))) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(count < 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + if(ReadRef(&preset->ref) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + + if(count == 0) + sounds = NULL; + else + { + sounds = calloc(count, sizeof(sounds[0])); + if(!sounds) + SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); + + for(i = 0;i < count;i++) + { + if(!(sounds[i]=LookupFontsound(device, fsids[i]))) + { + free(sounds); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + } + } + + for(i = 0;i < count;i++) + IncrementRef(&sounds[i]->ref); + + sounds = ExchangePtr((XchgPtr*)&preset->Sounds, sounds); + count = ExchangeInt(&preset->NumSounds, count); + + for(i = 0;i < count;i++) + DecrementRef(&sounds[i]->ref); + free(sounds); + +done: + ALCcontext_DecRef(context); +} + + +ALsfpreset *NewPreset(ALCcontext *context) +{ + ALCdevice *device = context->Device; + ALsfpreset *preset; + ALenum err; + + preset = calloc(1, sizeof(*preset)); + if(!preset) + SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL); + ALsfpreset_Construct(preset); + + err = NewThunkEntry(&preset->id); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&device->PresetMap, preset->id, preset); + if(err != AL_NO_ERROR) + { + ALsfpreset_Destruct(preset); + memset(preset, 0, sizeof(*preset)); + free(preset); + + SET_ERROR_AND_RETURN_VALUE(context, err, NULL); + } + + return preset; +} + +void DeletePreset(ALCdevice *device, ALsfpreset *preset) +{ + RemovePreset(device, preset->id); + + ALsfpreset_Destruct(preset); + memset(preset, 0, sizeof(*preset)); + free(preset); +} + + +static void ALsfpreset_Construct(ALsfpreset *self) +{ + InitRef(&self->ref, 0); + + self->Preset = 0; + self->Bank = 0; + + self->Sounds = NULL; + self->NumSounds = 0; + + self->id = 0; +} + +static void ALsfpreset_Destruct(ALsfpreset *self) +{ + ALsizei i; + + FreeThunkEntry(self->id); + self->id = 0; + + for(i = 0;i < self->NumSounds;i++) + DecrementRef(&self->Sounds[i]->ref); + free(self->Sounds); + self->Sounds = NULL; + self->NumSounds = 0; +} + + +/* ReleaseALPresets + * + * Called to destroy any presets that still exist on the device + */ +void ReleaseALPresets(ALCdevice *device) +{ + ALsizei i; + for(i = 0;i < device->PresetMap.size;i++) + { + ALsfpreset *temp = device->PresetMap.array[i].value; + device->PresetMap.array[i].value = NULL; + + ALsfpreset_Destruct(temp); + + memset(temp, 0, sizeof(*temp)); + free(temp); + } +} diff --git a/libs/openal-soft/OpenAL32/alSoundfont.c b/libs/openal-soft/OpenAL32/alSoundfont.c new file mode 100644 index 00000000..30c97a3a --- /dev/null +++ b/libs/openal-soft/OpenAL32/alSoundfont.c @@ -0,0 +1,454 @@ + +#include "config.h" + +#include +#include +#include +#include + +#include "alMain.h" +#include "alMidi.h" +#include "alThunk.h" +#include "alError.h" +#include + +#include "midi/base.h" + + +extern inline struct ALsoundfont *LookupSfont(ALCdevice *device, ALuint id); +extern inline struct ALsoundfont *RemoveSfont(ALCdevice *device, ALuint id); + +static void ALsoundfont_Construct(ALsoundfont *self); +static void ALsoundfont_Destruct(ALsoundfont *self); +void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device); +ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context); +static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr); + + +AL_API void AL_APIENTRY alGenSoundfontsSOFT(ALsizei n, ALuint *ids) +{ + ALCdevice *device; + ALCcontext *context; + ALsizei cur = 0; + ALenum err; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + for(cur = 0;cur < n;cur++) + { + ALsoundfont *sfont = calloc(1, sizeof(ALsoundfont)); + if(!sfont) + { + alDeleteSoundfontsSOFT(cur, ids); + SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); + } + ALsoundfont_Construct(sfont); + + err = NewThunkEntry(&sfont->id); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&device->SfontMap, sfont->id, sfont); + if(err != AL_NO_ERROR) + { + ALsoundfont_Destruct(sfont); + memset(sfont, 0, sizeof(ALsoundfont)); + free(sfont); + + alDeleteSoundfontsSOFT(cur, ids); + SET_ERROR_AND_GOTO(context, err, done); + } + + ids[cur] = sfont->id; + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alDeleteSoundfontsSOFT(ALsizei n, const ALuint *ids) +{ + ALCdevice *device; + ALCcontext *context; + ALsoundfont *sfont; + ALsizei i; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + device = context->Device; + for(i = 0;i < n;i++) + { + /* Check for valid soundfont ID */ + if(ids[i] == 0) + { + if(!(sfont=device->DefaultSfont)) + continue; + } + else if((sfont=LookupSfont(device, ids[i])) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(ReadRef(&sfont->ref) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + } + + for(i = 0;i < n;i++) + { + if(ids[i] == 0) + { + MidiSynth *synth = device->Synth; + WriteLock(&synth->Lock); + if(device->DefaultSfont != NULL) + ALsoundfont_deleteSoundfont(device->DefaultSfont, device); + device->DefaultSfont = NULL; + WriteUnlock(&synth->Lock); + continue; + } + else if((sfont=RemoveSfont(device, ids[i])) == NULL) + continue; + + ALsoundfont_Destruct(sfont); + + memset(sfont, 0, sizeof(*sfont)); + free(sfont); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALboolean AL_APIENTRY alIsSoundfontSOFT(ALuint id) +{ + ALCcontext *context; + ALboolean ret; + + context = GetContextRef(); + if(!context) return AL_FALSE; + + ret = ((!id || LookupSfont(context->Device, id)) ? + AL_TRUE : AL_FALSE); + + ALCcontext_DecRef(context); + + return ret; +} + +AL_API void AL_APIENTRY alGetSoundfontivSOFT(ALuint id, ALenum param, ALint *values) +{ + ALCdevice *device; + ALCcontext *context; + ALsoundfont *sfont; + ALsizei i; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(id == 0) + sfont = ALsoundfont_getDefSoundfont(context); + else if(!(sfont=LookupSfont(device, id))) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + switch(param) + { + case AL_PRESETS_SIZE_SOFT: + values[0] = sfont->NumPresets; + break; + + case AL_PRESETS_SOFT: + for(i = 0;i < sfont->NumPresets;i++) + values[i] = sfont->Presets[i]->id; + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alSoundfontPresetsSOFT(ALuint id, ALsizei count, const ALuint *pids) +{ + ALCdevice *device; + ALCcontext *context; + ALsoundfont *sfont; + ALsfpreset **presets; + ALsizei i; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(id == 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + if(!(sfont=LookupSfont(device, id))) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(count < 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + WriteLock(&sfont->Lock); + if(ReadRef(&sfont->ref) != 0) + { + WriteUnlock(&sfont->Lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + } + + if(count == 0) + presets = NULL; + else + { + presets = calloc(count, sizeof(presets[0])); + if(!presets) + { + WriteUnlock(&sfont->Lock); + SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); + } + + for(i = 0;i < count;i++) + { + if(!(presets[i]=LookupPreset(device, pids[i]))) + { + free(presets); + WriteUnlock(&sfont->Lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + } + } + + for(i = 0;i < count;i++) + IncrementRef(&presets[i]->ref); + + presets = ExchangePtr((XchgPtr*)&sfont->Presets, presets); + count = ExchangeInt(&sfont->NumPresets, count); + WriteUnlock(&sfont->Lock); + + for(i = 0;i < count;i++) + DecrementRef(&presets[i]->ref); + free(presets); + +done: + ALCcontext_DecRef(context); +} + + +AL_API void AL_APIENTRY alLoadSoundfontSOFT(ALuint id, size_t(*cb)(ALvoid*,size_t,ALvoid*), ALvoid *user) +{ + ALCdevice *device; + ALCcontext *context; + ALsoundfont *sfont; + Reader reader; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + if(id == 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + if(!(sfont=LookupSfont(device, id))) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + WriteLock(&sfont->Lock); + if(ReadRef(&sfont->ref) != 0) + { + WriteUnlock(&sfont->Lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + } + if(sfont->NumPresets > 0) + { + WriteUnlock(&sfont->Lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + } + + reader.cb = cb; + reader.ptr = user; + reader.error = 0; + loadSf2(&reader, sfont, context); + WriteUnlock(&sfont->Lock); + +done: + ALCcontext_DecRef(context); +} + + +static void ALsoundfont_Construct(ALsoundfont *self) +{ + InitRef(&self->ref, 0); + + self->Presets = NULL; + self->NumPresets = 0; + + RWLockInit(&self->Lock); + + self->id = 0; +} + +static void ALsoundfont_Destruct(ALsoundfont *self) +{ + ALsizei i; + + FreeThunkEntry(self->id); + self->id = 0; + + for(i = 0;i < self->NumPresets;i++) + { + DecrementRef(&self->Presets[i]->ref); + self->Presets[i] = NULL; + } + free(self->Presets); + self->Presets = NULL; + self->NumPresets = 0; +} + +ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context) +{ + ALCdevice *device = context->Device; + al_string fname = AL_STRING_INIT_STATIC(); + const char *namelist; + + if(device->DefaultSfont) + return device->DefaultSfont; + + device->DefaultSfont = calloc(1, sizeof(device->DefaultSfont[0])); + ALsoundfont_Construct(device->DefaultSfont); + + namelist = getenv("ALSOFT_SOUNDFONT"); + if(!namelist || !namelist[0]) + ConfigValueStr("midi", "soundfont", &namelist); + while(namelist && namelist[0]) + { + const char *next, *end; + FILE *f; + + while(*namelist && (isspace(*namelist) || *namelist == ',')) + namelist++; + if(!*namelist) + break; + next = strchr(namelist, ','); + end = next ? next++ : (namelist+strlen(namelist)); + while(--end != namelist && isspace(*end)) { + } + if(end == namelist) + continue; + al_string_append_range(&fname, namelist, end+1); + namelist = next; + + f = OpenDataFile(al_string_get_cstr(fname), "openal/soundfonts"); + if(f == NULL) + ERR("Failed to open %s\n", al_string_get_cstr(fname)); + else + { + Reader reader; + reader.cb = ALsoundfont_read; + reader.ptr = f; + reader.error = 0; + TRACE("Loading %s\n", al_string_get_cstr(fname)); + loadSf2(&reader, device->DefaultSfont, context); + fclose(f); + } + + al_string_clear(&fname); + } + AL_STRING_DEINIT(fname); + + return device->DefaultSfont; +} + +void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device) +{ + ALsfpreset **presets; + ALsizei num_presets; + VECTOR(ALbuffer*) buffers; + ALsizei i; + + VECTOR_INIT(buffers); + presets = ExchangePtr((XchgPtr*)&self->Presets, NULL); + num_presets = ExchangeInt(&self->NumPresets, 0); + + for(i = 0;i < num_presets;i++) + { + ALsfpreset *preset = presets[i]; + ALfontsound **sounds; + ALsizei num_sounds; + ALboolean deleting; + ALsizei j; + + sounds = ExchangePtr((XchgPtr*)&preset->Sounds, NULL); + num_sounds = ExchangeInt(&preset->NumSounds, 0); + + DeletePreset(device, preset); + preset = NULL; + + for(j = 0;j < num_sounds;j++) + DecrementRef(&sounds[j]->ref); + /* Some fontsounds may not be immediately deletable because they're + * linked to another fontsound. When those fontsounds are deleted + * they should become deletable, so use a loop until all fontsounds + * are deleted. */ + do { + deleting = AL_FALSE; + for(j = 0;j < num_sounds;j++) + { + if(sounds[j] && ReadRef(&sounds[j]->ref) == 0) + { + deleting = AL_TRUE; + if(sounds[j]->Buffer) + { + ALbuffer *buffer = sounds[j]->Buffer; + ALbuffer **iter; + +#define MATCH_BUFFER(_i) (buffer == *(_i)) + VECTOR_FIND_IF(iter, ALbuffer*, buffers, MATCH_BUFFER); + if(iter == VECTOR_ITER_END(buffers)) + VECTOR_PUSH_BACK(buffers, buffer); +#undef MATCH_BUFFER + } + DeleteFontsound(device, sounds[j]); + sounds[j] = NULL; + } + } + } while(deleting); + free(sounds); + } + + ALsoundfont_Destruct(self); + free(self); + +#define DELETE_BUFFER(iter) do { \ + assert(ReadRef(&(*(iter))->ref) == 0); \ + DeleteBuffer(device, *(iter)); \ +} while(0) + VECTOR_FOR_EACH(ALbuffer*, buffers, DELETE_BUFFER); + VECTOR_DEINIT(buffers); +#undef DELETE_BUFFER +} + + +static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr) +{ + return fread(buf, 1, bytes, (FILE*)ptr); +} + + +/* ReleaseALSoundfonts + * + * Called to destroy any soundfonts that still exist on the device + */ +void ReleaseALSoundfonts(ALCdevice *device) +{ + ALsizei i; + for(i = 0;i < device->SfontMap.size;i++) + { + ALsoundfont *temp = device->SfontMap.array[i].value; + device->SfontMap.array[i].value = NULL; + + ALsoundfont_Destruct(temp); + + memset(temp, 0, sizeof(*temp)); + free(temp); + } +} diff --git a/libs/openal-soft/OpenAL32/alSource.c b/libs/openal-soft/OpenAL32/alSource.c new file mode 100644 index 00000000..752e2e35 --- /dev/null +++ b/libs/openal-soft/OpenAL32/alSource.c @@ -0,0 +1,2912 @@ +/** + * 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 "AL/al.h" +#include "AL/alc.h" +#include "alMain.h" +#include "alError.h" +#include "alSource.h" +#include "alBuffer.h" +#include "alThunk.h" +#include "alAuxEffectSlot.h" + +#include "threads.h" + + +enum Resampler DefaultResampler = LinearResampler; +const ALsizei ResamplerPadding[ResamplerMax] = { + 0, /* Point */ + 1, /* Linear */ + 2, /* Cubic */ +}; +const ALsizei ResamplerPrePadding[ResamplerMax] = { + 0, /* Point */ + 0, /* Linear */ + 1, /* Cubic */ +}; + + +extern inline struct ALsource *LookupSource(ALCcontext *context, ALuint id); +extern inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id); + +static ALvoid InitSourceParams(ALsource *Source); +static ALint64 GetSourceOffset(const ALsource *Source); +static ALdouble GetSourceSecOffset(const ALsource *Source); +static ALvoid GetSourceOffsets(const ALsource *Source, ALenum name, ALdouble *offsets, ALdouble updateLen); +static ALint GetSampleOffset(ALsource *Source); + +typedef enum SrcFloatProp { + sfPitch = AL_PITCH, + sfGain = AL_GAIN, + sfMinGain = AL_MIN_GAIN, + sfMaxGain = AL_MAX_GAIN, + sfMaxDistance = AL_MAX_DISTANCE, + sfRolloffFactor = AL_ROLLOFF_FACTOR, + sfDopplerFactor = AL_DOPPLER_FACTOR, + sfConeOuterGain = AL_CONE_OUTER_GAIN, + sfSecOffset = AL_SEC_OFFSET, + sfSampleOffset = AL_SAMPLE_OFFSET, + sfByteOffset = AL_BYTE_OFFSET, + sfConeInnerAngle = AL_CONE_INNER_ANGLE, + sfConeOuterAngle = AL_CONE_OUTER_ANGLE, + sfRefDistance = AL_REFERENCE_DISTANCE, + + sfPosition = AL_POSITION, + sfVelocity = AL_VELOCITY, + sfDirection = AL_DIRECTION, + + sfSourceRelative = AL_SOURCE_RELATIVE, + sfLooping = AL_LOOPING, + sfBuffer = AL_BUFFER, + sfSourceState = AL_SOURCE_STATE, + sfBuffersQueued = AL_BUFFERS_QUEUED, + sfBuffersProcessed = AL_BUFFERS_PROCESSED, + sfSourceType = AL_SOURCE_TYPE, + + /* ALC_EXT_EFX */ + sfConeOuterGainHF = AL_CONE_OUTER_GAINHF, + sfAirAbsorptionFactor = AL_AIR_ABSORPTION_FACTOR, + sfRoomRolloffFactor = AL_ROOM_ROLLOFF_FACTOR, + sfDirectFilterGainHFAuto = AL_DIRECT_FILTER_GAINHF_AUTO, + sfAuxSendFilterGainAuto = AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, + sfAuxSendFilterGainHFAuto = AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, + + /* AL_SOFT_direct_channels */ + sfDirectChannelsSOFT = AL_DIRECT_CHANNELS_SOFT, + + /* AL_EXT_source_distance_model */ + sfDistanceModel = AL_DISTANCE_MODEL, + + sfSecLength = AL_SEC_LENGTH_SOFT, + + /* AL_SOFT_buffer_sub_data / AL_SOFT_buffer_samples */ + sfSampleRWOffsetsSOFT = AL_SAMPLE_RW_OFFSETS_SOFT, + sfByteRWOffsetsSOFT = AL_BYTE_RW_OFFSETS_SOFT, + + /* AL_SOFT_source_latency */ + sfSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT, +} SrcFloatProp; + +typedef enum SrcIntProp { + siMaxDistance = AL_MAX_DISTANCE, + siRolloffFactor = AL_ROLLOFF_FACTOR, + siRefDistance = AL_REFERENCE_DISTANCE, + siSourceRelative = AL_SOURCE_RELATIVE, + siConeInnerAngle = AL_CONE_INNER_ANGLE, + siConeOuterAngle = AL_CONE_OUTER_ANGLE, + siLooping = AL_LOOPING, + siBuffer = AL_BUFFER, + siSourceState = AL_SOURCE_STATE, + siBuffersQueued = AL_BUFFERS_QUEUED, + siBuffersProcessed = AL_BUFFERS_PROCESSED, + siSourceType = AL_SOURCE_TYPE, + siSecOffset = AL_SEC_OFFSET, + siSampleOffset = AL_SAMPLE_OFFSET, + siByteOffset = AL_BYTE_OFFSET, + siDopplerFactor = AL_DOPPLER_FACTOR, + siPosition = AL_POSITION, + siVelocity = AL_VELOCITY, + siDirection = AL_DIRECTION, + + /* ALC_EXT_EFX */ + siDirectFilterGainHFAuto = AL_DIRECT_FILTER_GAINHF_AUTO, + siAuxSendFilterGainAutio = AL_AUXILIARY_SEND_FILTER_GAIN_AUTO, + siAuxSendFilterGainHFAuto = AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO, + siDirectFilter = AL_DIRECT_FILTER, + siAuxSendFilter = AL_AUXILIARY_SEND_FILTER, + + /* AL_SOFT_direct_channels */ + siDirectChannelsSOFT = AL_DIRECT_CHANNELS_SOFT, + + /* AL_EXT_source_distance_model */ + siDistanceModel = AL_DISTANCE_MODEL, + + siByteLength = AL_BYTE_LENGTH_SOFT, + siSampleLength = AL_SAMPLE_LENGTH_SOFT, + + /* AL_SOFT_buffer_sub_data / AL_SOFT_buffer_samples */ + siSampleRWOffsetsSOFT = AL_SAMPLE_RW_OFFSETS_SOFT, + siByteRWOffsetsSOFT = AL_BYTE_RW_OFFSETS_SOFT, + + /* AL_SOFT_source_latency */ + siSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT, +} SrcIntProp; + +static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SrcFloatProp prop, const ALfloat *values); +static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp prop, const ALint *values); +static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SrcIntProp prop, const ALint64SOFT *values); + +static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SrcFloatProp prop, ALdouble *values); +static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp prop, ALint *values); +static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SrcIntProp prop, ALint64 *values); + +static ALint FloatValsByProp(ALenum prop) +{ + if(prop != (ALenum)((SrcFloatProp)prop)) + return 0; + switch((SrcFloatProp)prop) + { + case sfPitch: + case sfGain: + case sfMinGain: + case sfMaxGain: + case sfMaxDistance: + case sfRolloffFactor: + case sfDopplerFactor: + case sfConeOuterGain: + case sfSecOffset: + case sfSampleOffset: + case sfByteOffset: + case sfConeInnerAngle: + case sfConeOuterAngle: + case sfRefDistance: + case sfConeOuterGainHF: + case sfAirAbsorptionFactor: + case sfRoomRolloffFactor: + case sfDirectFilterGainHFAuto: + case sfAuxSendFilterGainAuto: + case sfAuxSendFilterGainHFAuto: + case sfDirectChannelsSOFT: + case sfDistanceModel: + case sfSourceRelative: + case sfLooping: + case sfBuffer: + case sfSourceState: + case sfBuffersQueued: + case sfBuffersProcessed: + case sfSourceType: + case sfSecLength: + return 1; + + case sfSampleRWOffsetsSOFT: + case sfByteRWOffsetsSOFT: + return 2; + + case sfPosition: + case sfVelocity: + case sfDirection: + return 3; + + case sfSecOffsetLatencySOFT: + break; /* Double only */ + } + return 0; +} +static ALint DoubleValsByProp(ALenum prop) +{ + if(prop != (ALenum)((SrcFloatProp)prop)) + return 0; + switch((SrcFloatProp)prop) + { + case sfPitch: + case sfGain: + case sfMinGain: + case sfMaxGain: + case sfMaxDistance: + case sfRolloffFactor: + case sfDopplerFactor: + case sfConeOuterGain: + case sfSecOffset: + case sfSampleOffset: + case sfByteOffset: + case sfConeInnerAngle: + case sfConeOuterAngle: + case sfRefDistance: + case sfConeOuterGainHF: + case sfAirAbsorptionFactor: + case sfRoomRolloffFactor: + case sfDirectFilterGainHFAuto: + case sfAuxSendFilterGainAuto: + case sfAuxSendFilterGainHFAuto: + case sfDirectChannelsSOFT: + case sfDistanceModel: + case sfSourceRelative: + case sfLooping: + case sfBuffer: + case sfSourceState: + case sfBuffersQueued: + case sfBuffersProcessed: + case sfSourceType: + case sfSecLength: + return 1; + + case sfSampleRWOffsetsSOFT: + case sfByteRWOffsetsSOFT: + case sfSecOffsetLatencySOFT: + return 2; + + case sfPosition: + case sfVelocity: + case sfDirection: + return 3; + } + return 0; +} + +static ALint IntValsByProp(ALenum prop) +{ + if(prop != (ALenum)((SrcIntProp)prop)) + return 0; + switch((SrcIntProp)prop) + { + case siMaxDistance: + case siRolloffFactor: + case siRefDistance: + case siSourceRelative: + case siConeInnerAngle: + case siConeOuterAngle: + case siLooping: + case siBuffer: + case siSourceState: + case siBuffersQueued: + case siBuffersProcessed: + case siSourceType: + case siSecOffset: + case siSampleOffset: + case siByteOffset: + case siDopplerFactor: + case siDirectFilterGainHFAuto: + case siAuxSendFilterGainAutio: + case siAuxSendFilterGainHFAuto: + case siDirectFilter: + case siDirectChannelsSOFT: + case siDistanceModel: + case siByteLength: + case siSampleLength: + return 1; + + case siSampleRWOffsetsSOFT: + case siByteRWOffsetsSOFT: + return 2; + + case siPosition: + case siVelocity: + case siDirection: + case siAuxSendFilter: + return 3; + + case siSampleOffsetLatencySOFT: + break; /* i64 only */ + } + return 0; +} +static ALint Int64ValsByProp(ALenum prop) +{ + if(prop != (ALenum)((SrcIntProp)prop)) + return 0; + switch((SrcIntProp)prop) + { + case siMaxDistance: + case siRolloffFactor: + case siRefDistance: + case siSourceRelative: + case siConeInnerAngle: + case siConeOuterAngle: + case siLooping: + case siBuffer: + case siSourceState: + case siBuffersQueued: + case siBuffersProcessed: + case siSourceType: + case siSecOffset: + case siSampleOffset: + case siByteOffset: + case siDopplerFactor: + case siDirectFilterGainHFAuto: + case siAuxSendFilterGainAutio: + case siAuxSendFilterGainHFAuto: + case siDirectFilter: + case siDirectChannelsSOFT: + case siDistanceModel: + case siByteLength: + case siSampleLength: + return 1; + + case siSampleRWOffsetsSOFT: + case siByteRWOffsetsSOFT: + case siSampleOffsetLatencySOFT: + return 2; + + case siPosition: + case siVelocity: + case siDirection: + case siAuxSendFilter: + return 3; + } + return 0; +} + + +#define CHECKVAL(x) do { \ + if(!(x)) \ + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); \ +} while(0) + +static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SrcFloatProp prop, const ALfloat *values) +{ + ALint ival; + + switch(prop) + { + case AL_PITCH: + CHECKVAL(*values >= 0.0f); + + Source->Pitch = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_CONE_INNER_ANGLE: + CHECKVAL(*values >= 0.0f && *values <= 360.0f); + + Source->InnerAngle = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_CONE_OUTER_ANGLE: + CHECKVAL(*values >= 0.0f && *values <= 360.0f); + + Source->OuterAngle = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_GAIN: + CHECKVAL(*values >= 0.0f); + + Source->Gain = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_MAX_DISTANCE: + CHECKVAL(*values >= 0.0f); + + Source->MaxDistance = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_ROLLOFF_FACTOR: + CHECKVAL(*values >= 0.0f); + + Source->RollOffFactor = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_REFERENCE_DISTANCE: + CHECKVAL(*values >= 0.0f); + + Source->RefDistance = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_MIN_GAIN: + CHECKVAL(*values >= 0.0f && *values <= 1.0f); + + Source->MinGain = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_MAX_GAIN: + CHECKVAL(*values >= 0.0f && *values <= 1.0f); + + Source->MaxGain = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_CONE_OUTER_GAIN: + CHECKVAL(*values >= 0.0f && *values <= 1.0f); + + Source->OuterGain = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_CONE_OUTER_GAINHF: + CHECKVAL(*values >= 0.0f && *values <= 1.0f); + + Source->OuterGainHF = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_AIR_ABSORPTION_FACTOR: + CHECKVAL(*values >= 0.0f && *values <= 10.0f); + + Source->AirAbsorptionFactor = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_ROOM_ROLLOFF_FACTOR: + CHECKVAL(*values >= 0.0f && *values <= 10.0f); + + Source->RoomRolloffFactor = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_DOPPLER_FACTOR: + CHECKVAL(*values >= 0.0f && *values <= 1.0f); + + Source->DopplerFactor = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + CHECKVAL(*values >= 0.0f); + + LockContext(Context); + Source->OffsetType = prop; + Source->Offset = *values; + + if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && + !Context->DeferUpdates) + { + if(ApplyOffset(Source) == AL_FALSE) + { + UnlockContext(Context); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); + } + } + UnlockContext(Context); + return AL_TRUE; + + + case sfSecLength: + case AL_SEC_OFFSET_LATENCY_SOFT: + /* Query only */ + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); + + + case AL_POSITION: + CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2])); + + LockContext(Context); + Source->Position[0] = values[0]; + Source->Position[1] = values[1]; + Source->Position[2] = values[2]; + UnlockContext(Context); + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_VELOCITY: + CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2])); + + LockContext(Context); + Source->Velocity[0] = values[0]; + Source->Velocity[1] = values[1]; + Source->Velocity[2] = values[2]; + UnlockContext(Context); + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_DIRECTION: + CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2])); + + LockContext(Context); + Source->Orientation[0] = values[0]; + Source->Orientation[1] = values[1]; + Source->Orientation[2] = values[2]; + UnlockContext(Context); + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + + case sfSampleRWOffsetsSOFT: + case sfByteRWOffsetsSOFT: + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); + + + case sfSourceRelative: + case sfLooping: + case sfSourceState: + case sfSourceType: + case sfDistanceModel: + case sfDirectFilterGainHFAuto: + case sfAuxSendFilterGainAuto: + case sfAuxSendFilterGainHFAuto: + case sfDirectChannelsSOFT: + ival = (ALint)values[0]; + return SetSourceiv(Source, Context, (SrcIntProp)prop, &ival); + + case sfBuffer: + case sfBuffersQueued: + case sfBuffersProcessed: + ival = (ALint)((ALuint)values[0]); + return SetSourceiv(Source, Context, (SrcIntProp)prop, &ival); + } + + ERR("Unexpected property: 0x%04x\n", prop); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); +} + +static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp prop, const ALint *values) +{ + ALCdevice *device = Context->Device; + ALbuffer *buffer = NULL; + ALfilter *filter = NULL; + ALeffectslot *slot = NULL; + ALbufferlistitem *oldlist; + ALbufferlistitem *newlist; + ALfloat fvals[3]; + + switch(prop) + { + case AL_SOURCE_RELATIVE: + CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); + + Source->HeadRelative = (ALboolean)*values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_LOOPING: + CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); + + Source->Looping = (ALboolean)*values; + return AL_TRUE; + + case AL_BUFFER: + CHECKVAL(*values == 0 || (buffer=LookupBuffer(device, *values)) != NULL); + + WriteLock(&Source->queue_lock); + if(!(Source->state == AL_STOPPED || Source->state == AL_INITIAL)) + { + WriteUnlock(&Source->queue_lock); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); + } + + if(buffer != NULL) + { + /* Add the selected buffer to a one-item queue */ + newlist = malloc(sizeof(ALbufferlistitem)); + newlist->buffer = buffer; + newlist->next = NULL; + newlist->prev = NULL; + IncrementRef(&buffer->ref); + + /* Source is now Static */ + Source->SourceType = AL_STATIC; + + ReadLock(&buffer->lock); + Source->NumChannels = ChannelsFromFmt(buffer->FmtChannels); + Source->SampleSize = BytesFromFmt(buffer->FmtType); + ReadUnlock(&buffer->lock); + } + else + { + /* Source is now Undetermined */ + Source->SourceType = AL_UNDETERMINED; + newlist = NULL; + } + oldlist = ATOMIC_EXCHANGE(ALbufferlistitem*, &Source->queue, newlist); + ATOMIC_STORE(&Source->current_buffer, newlist); + WriteUnlock(&Source->queue_lock); + + /* Delete all elements in the previous queue */ + while(oldlist != NULL) + { + ALbufferlistitem *temp = oldlist; + oldlist = temp->next; + + if(temp->buffer) + DecrementRef(&temp->buffer->ref); + free(temp); + } + return AL_TRUE; + + case siSourceState: + case siSourceType: + case siBuffersQueued: + case siBuffersProcessed: + /* Query only */ + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); + + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + CHECKVAL(*values >= 0); + + LockContext(Context); + Source->OffsetType = prop; + Source->Offset = *values; + + if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && + !Context->DeferUpdates) + { + if(ApplyOffset(Source) == AL_FALSE) + { + UnlockContext(Context); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); + } + } + UnlockContext(Context); + return AL_TRUE; + + + case siByteLength: + case siSampleLength: + case siSampleRWOffsetsSOFT: + case siByteRWOffsetsSOFT: + /* Query only */ + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); + + + case AL_DIRECT_FILTER: + CHECKVAL(*values == 0 || (filter=LookupFilter(device, *values)) != NULL); + + LockContext(Context); + if(!filter) + { + Source->Direct.Gain = 1.0f; + Source->Direct.GainHF = 1.0f; + Source->Direct.HFReference = LOWPASSFREQREF; + Source->Direct.GainLF = 1.0f; + Source->Direct.LFReference = HIGHPASSFREQREF; + } + else + { + Source->Direct.Gain = filter->Gain; + Source->Direct.GainHF = filter->GainHF; + Source->Direct.HFReference = filter->HFReference; + Source->Direct.GainLF = filter->GainLF; + Source->Direct.LFReference = filter->LFReference; + } + UnlockContext(Context); + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_DIRECT_FILTER_GAINHF_AUTO: + CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); + + Source->DryGainHFAuto = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); + + Source->WetGainAuto = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: + CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); + + Source->WetGainHFAuto = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_DIRECT_CHANNELS_SOFT: + CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); + + Source->DirectChannels = *values; + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + case AL_DISTANCE_MODEL: + CHECKVAL(*values == AL_NONE || + *values == AL_INVERSE_DISTANCE || + *values == AL_INVERSE_DISTANCE_CLAMPED || + *values == AL_LINEAR_DISTANCE || + *values == AL_LINEAR_DISTANCE_CLAMPED || + *values == AL_EXPONENT_DISTANCE || + *values == AL_EXPONENT_DISTANCE_CLAMPED); + + Source->DistanceModel = *values; + if(Context->SourceDistanceModel) + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + + case AL_AUXILIARY_SEND_FILTER: + LockContext(Context); + if(!((ALuint)values[1] < device->NumAuxSends && + (values[0] == 0 || (slot=LookupEffectSlot(Context, values[0])) != NULL) && + (values[2] == 0 || (filter=LookupFilter(device, values[2])) != NULL))) + { + UnlockContext(Context); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); + } + + /* Add refcount on the new slot, and release the previous slot */ + if(slot) IncrementRef(&slot->ref); + slot = ExchangePtr((XchgPtr*)&Source->Send[values[1]].Slot, slot); + if(slot) DecrementRef(&slot->ref); + + if(!filter) + { + /* Disable filter */ + Source->Send[values[1]].Gain = 1.0f; + Source->Send[values[1]].GainHF = 1.0f; + Source->Send[values[1]].HFReference = LOWPASSFREQREF; + Source->Send[values[1]].GainLF = 1.0f; + Source->Send[values[1]].LFReference = HIGHPASSFREQREF; + } + else + { + Source->Send[values[1]].Gain = filter->Gain; + Source->Send[values[1]].GainHF = filter->GainHF; + Source->Send[values[1]].HFReference = filter->HFReference; + Source->Send[values[1]].GainLF = filter->GainLF; + Source->Send[values[1]].LFReference = filter->LFReference; + } + UnlockContext(Context); + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + return AL_TRUE; + + + case AL_MAX_DISTANCE: + case AL_ROLLOFF_FACTOR: + case AL_CONE_INNER_ANGLE: + case AL_CONE_OUTER_ANGLE: + case AL_REFERENCE_DISTANCE: + case siDopplerFactor: + fvals[0] = (ALfloat)*values; + return SetSourcefv(Source, Context, (int)prop, fvals); + + case AL_POSITION: + case AL_VELOCITY: + case AL_DIRECTION: + fvals[0] = (ALfloat)values[0]; + fvals[1] = (ALfloat)values[1]; + fvals[2] = (ALfloat)values[2]; + return SetSourcefv(Source, Context, (int)prop, fvals); + + case siSampleOffsetLatencySOFT: + /* i64 only */ + break; + } + + ERR("Unexpected property: 0x%04x\n", prop); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); +} + +static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SrcIntProp prop, const ALint64SOFT *values) +{ + ALfloat fvals[3]; + ALint ivals[3]; + + switch(prop) + { + case siSampleRWOffsetsSOFT: + case siByteRWOffsetsSOFT: + case siSampleOffsetLatencySOFT: + /* Query only */ + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); + + + /* 1x int */ + case AL_SOURCE_RELATIVE: + case AL_LOOPING: + case AL_SOURCE_STATE: + case AL_BYTE_OFFSET: + case AL_SAMPLE_OFFSET: + case siByteLength: + case siSampleLength: + case siSourceType: + case siBuffersQueued: + case siBuffersProcessed: + case AL_DIRECT_FILTER_GAINHF_AUTO: + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: + case AL_DIRECT_CHANNELS_SOFT: + case AL_DISTANCE_MODEL: + CHECKVAL(*values <= INT_MAX && *values >= INT_MIN); + + ivals[0] = (ALint)*values; + return SetSourceiv(Source, Context, (int)prop, ivals); + + /* 1x uint */ + case AL_BUFFER: + case AL_DIRECT_FILTER: + CHECKVAL(*values <= UINT_MAX && *values >= 0); + + ivals[0] = (ALuint)*values; + return SetSourceiv(Source, Context, (int)prop, ivals); + + /* 3x uint */ + case AL_AUXILIARY_SEND_FILTER: + CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0 && + values[1] <= UINT_MAX && values[1] >= 0 && + values[2] <= UINT_MAX && values[2] >= 0); + + ivals[0] = (ALuint)values[0]; + ivals[1] = (ALuint)values[1]; + ivals[2] = (ALuint)values[2]; + return SetSourceiv(Source, Context, (int)prop, ivals); + + /* 1x float */ + case AL_MAX_DISTANCE: + case AL_ROLLOFF_FACTOR: + case AL_CONE_INNER_ANGLE: + case AL_CONE_OUTER_ANGLE: + case AL_REFERENCE_DISTANCE: + case AL_SEC_OFFSET: + case siDopplerFactor: + fvals[0] = (ALfloat)*values; + return SetSourcefv(Source, Context, (int)prop, fvals); + + /* 3x float */ + case AL_POSITION: + case AL_VELOCITY: + case AL_DIRECTION: + fvals[0] = (ALfloat)values[0]; + fvals[1] = (ALfloat)values[1]; + fvals[2] = (ALfloat)values[2]; + return SetSourcefv(Source, Context, (int)prop, fvals); + } + + ERR("Unexpected property: 0x%04x\n", prop); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); +} + +#undef CHECKVAL + + +static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SrcFloatProp prop, ALdouble *values) +{ + ALbufferlistitem *BufferList; + ALdouble offsets[2]; + ALdouble updateLen; + ALint ivals[3]; + ALboolean err; + + switch(prop) + { + case AL_GAIN: + *values = Source->Gain; + return AL_TRUE; + + case AL_PITCH: + *values = Source->Pitch; + return AL_TRUE; + + case AL_MAX_DISTANCE: + *values = Source->MaxDistance; + return AL_TRUE; + + case AL_ROLLOFF_FACTOR: + *values = Source->RollOffFactor; + return AL_TRUE; + + case AL_REFERENCE_DISTANCE: + *values = Source->RefDistance; + return AL_TRUE; + + case AL_CONE_INNER_ANGLE: + *values = Source->InnerAngle; + return AL_TRUE; + + case AL_CONE_OUTER_ANGLE: + *values = Source->OuterAngle; + return AL_TRUE; + + case AL_MIN_GAIN: + *values = Source->MinGain; + return AL_TRUE; + + case AL_MAX_GAIN: + *values = Source->MaxGain; + return AL_TRUE; + + case AL_CONE_OUTER_GAIN: + *values = Source->OuterGain; + return AL_TRUE; + + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + LockContext(Context); + ReadLock(&Source->queue_lock); + GetSourceOffsets(Source, prop, offsets, 0.0); + ReadUnlock(&Source->queue_lock); + UnlockContext(Context); + *values = offsets[0]; + return AL_TRUE; + + case AL_CONE_OUTER_GAINHF: + *values = Source->OuterGainHF; + return AL_TRUE; + + case AL_AIR_ABSORPTION_FACTOR: + *values = Source->AirAbsorptionFactor; + return AL_TRUE; + + case AL_ROOM_ROLLOFF_FACTOR: + *values = Source->RoomRolloffFactor; + return AL_TRUE; + + case AL_DOPPLER_FACTOR: + *values = Source->DopplerFactor; + return AL_TRUE; + + case sfSecLength: + ReadLock(&Source->queue_lock); + if(!(BufferList=ATOMIC_LOAD(&Source->queue))) + *values = 0; + else + { + ALint length = 0; + ALsizei freq = 1; + do { + ALbuffer *buffer = BufferList->buffer; + if(buffer && buffer->SampleLen > 0) + { + freq = buffer->Frequency; + length += buffer->SampleLen; + } + } while((BufferList=BufferList->next) != NULL); + *values = (ALdouble)length / (ALdouble)freq; + } + ReadUnlock(&Source->queue_lock); + return AL_TRUE; + + case AL_SAMPLE_RW_OFFSETS_SOFT: + case AL_BYTE_RW_OFFSETS_SOFT: + LockContext(Context); + ReadLock(&Source->queue_lock); + updateLen = (ALdouble)Context->Device->UpdateSize / + Context->Device->Frequency; + GetSourceOffsets(Source, prop, values, updateLen); + ReadUnlock(&Source->queue_lock); + UnlockContext(Context); + return AL_TRUE; + + case AL_SEC_OFFSET_LATENCY_SOFT: + LockContext(Context); + ReadLock(&Source->queue_lock); + values[0] = GetSourceSecOffset(Source); + ReadUnlock(&Source->queue_lock); + values[1] = (ALdouble)ALCdevice_GetLatency(Context->Device) / + 1000000000.0; + UnlockContext(Context); + return AL_TRUE; + + case AL_POSITION: + LockContext(Context); + values[0] = Source->Position[0]; + values[1] = Source->Position[1]; + values[2] = Source->Position[2]; + UnlockContext(Context); + return AL_TRUE; + + case AL_VELOCITY: + LockContext(Context); + values[0] = Source->Velocity[0]; + values[1] = Source->Velocity[1]; + values[2] = Source->Velocity[2]; + UnlockContext(Context); + return AL_TRUE; + + case AL_DIRECTION: + LockContext(Context); + values[0] = Source->Orientation[0]; + values[1] = Source->Orientation[1]; + values[2] = Source->Orientation[2]; + UnlockContext(Context); + return AL_TRUE; + + case AL_SOURCE_RELATIVE: + case AL_LOOPING: + case AL_BUFFER: + case AL_SOURCE_STATE: + case AL_BUFFERS_QUEUED: + case AL_BUFFERS_PROCESSED: + case AL_SOURCE_TYPE: + case AL_DIRECT_FILTER_GAINHF_AUTO: + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: + case AL_DIRECT_CHANNELS_SOFT: + case AL_DISTANCE_MODEL: + if((err=GetSourceiv(Source, Context, (int)prop, ivals)) != AL_FALSE) + *values = (ALdouble)ivals[0]; + return err; + } + + ERR("Unexpected property: 0x%04x\n", prop); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); +} + +static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SrcIntProp prop, ALint *values) +{ + ALbufferlistitem *BufferList; + ALdouble dvals[3]; + ALboolean err; + + switch(prop) + { + case AL_SOURCE_RELATIVE: + *values = Source->HeadRelative; + return AL_TRUE; + + case AL_LOOPING: + *values = Source->Looping; + return AL_TRUE; + + case AL_BUFFER: + ReadLock(&Source->queue_lock); + BufferList = (Source->SourceType == AL_STATIC) ? ATOMIC_LOAD(&Source->queue) : + ATOMIC_LOAD(&Source->current_buffer); + *values = (BufferList && BufferList->buffer) ? BufferList->buffer->id : 0; + ReadUnlock(&Source->queue_lock); + return AL_TRUE; + + case AL_SOURCE_STATE: + *values = Source->state; + return AL_TRUE; + + case siByteLength: + ReadLock(&Source->queue_lock); + if(!(BufferList=ATOMIC_LOAD(&Source->queue))) + *values = 0; + else + { + ALint length = 0; + do { + ALbuffer *buffer = BufferList->buffer; + if(buffer && buffer->SampleLen > 0) + { + ALuint byte_align, sample_align; + if(buffer->OriginalType == UserFmtIMA4) + { + ALsizei align = (buffer->OriginalAlign-1)/2 + 4; + byte_align = align * ChannelsFromFmt(buffer->FmtChannels); + sample_align = buffer->OriginalAlign; + } + else if(buffer->OriginalType == UserFmtMSADPCM) + { + ALsizei align = (buffer->OriginalAlign-2)/2 + 7; + byte_align = align * ChannelsFromFmt(buffer->FmtChannels); + sample_align = buffer->OriginalAlign; + } + else + { + ALsizei align = buffer->OriginalAlign; + byte_align = align * ChannelsFromFmt(buffer->FmtChannels); + sample_align = buffer->OriginalAlign; + } + + length += buffer->SampleLen / sample_align * byte_align; + } + } while((BufferList=BufferList->next) != NULL); + *values = length; + } + ReadUnlock(&Source->queue_lock); + return AL_TRUE; + + case siSampleLength: + ReadLock(&Source->queue_lock); + if(!(BufferList=ATOMIC_LOAD(&Source->queue))) + *values = 0; + else + { + ALint length = 0; + do { + ALbuffer *buffer = BufferList->buffer; + if(buffer) length += buffer->SampleLen; + } while((BufferList=BufferList->next) != NULL); + *values = length; + } + ReadUnlock(&Source->queue_lock); + return AL_TRUE; + + case AL_BUFFERS_QUEUED: + ReadLock(&Source->queue_lock); + if(!(BufferList=ATOMIC_LOAD(&Source->queue))) + *values = 0; + else + { + ALsizei count = 0; + do { + ++count; + } while((BufferList=BufferList->next) != NULL); + *values = count; + } + ReadUnlock(&Source->queue_lock); + return AL_TRUE; + + case AL_BUFFERS_PROCESSED: + ReadLock(&Source->queue_lock); + if(Source->Looping || Source->SourceType != AL_STREAMING) + { + /* Buffers on a looping source are in a perpetual state of + * PENDING, so don't report any as PROCESSED */ + *values = 0; + } + else + { + const ALbufferlistitem *BufferList = ATOMIC_LOAD(&Source->queue); + const ALbufferlistitem *Current = ATOMIC_LOAD(&Source->current_buffer); + ALsizei played = 0; + while(BufferList && BufferList != Current) + { + played++; + BufferList = BufferList->next; + } + *values = played; + } + ReadUnlock(&Source->queue_lock); + return AL_TRUE; + + case AL_SOURCE_TYPE: + *values = Source->SourceType; + return AL_TRUE; + + case AL_DIRECT_FILTER_GAINHF_AUTO: + *values = Source->DryGainHFAuto; + return AL_TRUE; + + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + *values = Source->WetGainAuto; + return AL_TRUE; + + case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: + *values = Source->WetGainHFAuto; + return AL_TRUE; + + case AL_DIRECT_CHANNELS_SOFT: + *values = Source->DirectChannels; + return AL_TRUE; + + case AL_DISTANCE_MODEL: + *values = Source->DistanceModel; + return AL_TRUE; + + case AL_MAX_DISTANCE: + case AL_ROLLOFF_FACTOR: + case AL_REFERENCE_DISTANCE: + case AL_CONE_INNER_ANGLE: + case AL_CONE_OUTER_ANGLE: + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + case AL_DOPPLER_FACTOR: + if((err=GetSourcedv(Source, Context, (int)prop, dvals)) != AL_FALSE) + *values = (ALint)dvals[0]; + return err; + + case AL_SAMPLE_RW_OFFSETS_SOFT: + case AL_BYTE_RW_OFFSETS_SOFT: + if((err=GetSourcedv(Source, Context, (int)prop, dvals)) != AL_FALSE) + { + values[0] = (ALint)dvals[0]; + values[1] = (ALint)dvals[1]; + } + return err; + + case AL_POSITION: + case AL_VELOCITY: + case AL_DIRECTION: + if((err=GetSourcedv(Source, Context, (int)prop, dvals)) != AL_FALSE) + { + values[0] = (ALint)dvals[0]; + values[1] = (ALint)dvals[1]; + values[2] = (ALint)dvals[2]; + } + return err; + + case siSampleOffsetLatencySOFT: + /* i64 only */ + break; + + case siDirectFilter: + case siAuxSendFilter: + /* ??? */ + break; + } + + ERR("Unexpected property: 0x%04x\n", prop); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); +} + +static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SrcIntProp prop, ALint64 *values) +{ + ALdouble dvals[3]; + ALint ivals[3]; + ALboolean err; + + switch(prop) + { + case AL_SAMPLE_OFFSET_LATENCY_SOFT: + LockContext(Context); + ReadLock(&Source->queue_lock); + values[0] = GetSourceOffset(Source); + ReadUnlock(&Source->queue_lock); + values[1] = ALCdevice_GetLatency(Context->Device); + UnlockContext(Context); + return AL_TRUE; + + case AL_MAX_DISTANCE: + case AL_ROLLOFF_FACTOR: + case AL_REFERENCE_DISTANCE: + case AL_CONE_INNER_ANGLE: + case AL_CONE_OUTER_ANGLE: + case AL_SEC_OFFSET: + case AL_SAMPLE_OFFSET: + case AL_BYTE_OFFSET: + case AL_DOPPLER_FACTOR: + if((err=GetSourcedv(Source, Context, (int)prop, dvals)) != AL_FALSE) + *values = (ALint64)dvals[0]; + return err; + + case AL_SAMPLE_RW_OFFSETS_SOFT: + case AL_BYTE_RW_OFFSETS_SOFT: + if((err=GetSourcedv(Source, Context, (int)prop, dvals)) != AL_FALSE) + { + values[0] = (ALint64)dvals[0]; + values[1] = (ALint64)dvals[1]; + } + return err; + + case AL_POSITION: + case AL_VELOCITY: + case AL_DIRECTION: + if((err=GetSourcedv(Source, Context, (int)prop, dvals)) != AL_FALSE) + { + values[0] = (ALint64)dvals[0]; + values[1] = (ALint64)dvals[1]; + values[2] = (ALint64)dvals[2]; + } + return err; + + case AL_SOURCE_RELATIVE: + case AL_LOOPING: + case AL_SOURCE_STATE: + case AL_BUFFERS_QUEUED: + case AL_BUFFERS_PROCESSED: + case siByteLength: + case siSampleLength: + case AL_SOURCE_TYPE: + case AL_DIRECT_FILTER_GAINHF_AUTO: + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: + case AL_DIRECT_CHANNELS_SOFT: + case AL_DISTANCE_MODEL: + if((err=GetSourceiv(Source, Context, (int)prop, ivals)) != AL_FALSE) + *values = ivals[0]; + return err; + + case siBuffer: + case siDirectFilter: + if((err=GetSourceiv(Source, Context, (int)prop, ivals)) != AL_FALSE) + *values = (ALuint)ivals[0]; + return err; + + case siAuxSendFilter: + if((err=GetSourceiv(Source, Context, (int)prop, ivals)) != AL_FALSE) + { + values[0] = (ALuint)ivals[0]; + values[1] = (ALuint)ivals[1]; + values[2] = (ALuint)ivals[2]; + } + return err; + } + + ERR("Unexpected property: 0x%04x\n", prop); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); +} + + +AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) +{ + ALCcontext *context; + ALsizei cur = 0; + ALenum err; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + for(cur = 0;cur < n;cur++) + { + ALsource *source = al_calloc(16, sizeof(ALsource)); + if(!source) + { + alDeleteSources(cur, sources); + SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); + } + InitSourceParams(source); + + err = NewThunkEntry(&source->id); + if(err == AL_NO_ERROR) + err = InsertUIntMapEntry(&context->SourceMap, source->id, source); + if(err != AL_NO_ERROR) + { + FreeThunkEntry(source->id); + memset(source, 0, sizeof(ALsource)); + al_free(source); + + alDeleteSources(cur, sources); + SET_ERROR_AND_GOTO(context, err, done); + } + + sources[cur] = source->id; + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) +{ + ALCcontext *context; + ALbufferlistitem *BufferList; + ALsource *Source; + ALsizei i, j; + + context = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + /* Check that all Sources are valid */ + for(i = 0;i < n;i++) + { + if(LookupSource(context, sources[i]) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + } + for(i = 0;i < n;i++) + { + ALactivesource **srclist, **srclistend; + + if((Source=RemoveSource(context, sources[i])) == NULL) + continue; + FreeThunkEntry(Source->id); + + LockContext(context); + srclist = context->ActiveSources; + srclistend = srclist + context->ActiveSourceCount; + while(srclist != srclistend) + { + if((*srclist)->Source == Source) + { + ALactivesource *temp = *(--srclistend); + *srclistend = *srclist; + *srclist = temp; + --(context->ActiveSourceCount); + break; + } + srclist++; + } + UnlockContext(context); + + BufferList = ATOMIC_EXCHANGE(ALbufferlistitem*, &Source->queue, NULL); + while(BufferList != NULL) + { + ALbufferlistitem *next = BufferList->next; + if(BufferList->buffer != NULL) + DecrementRef(&BufferList->buffer->ref); + free(BufferList); + BufferList = next; + } + + for(j = 0;j < MAX_SENDS;++j) + { + if(Source->Send[j].Slot) + DecrementRef(&Source->Send[j].Slot->ref); + Source->Send[j].Slot = NULL; + } + + memset(Source, 0, sizeof(*Source)); + al_free(Source); + } + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) +{ + ALCcontext *context; + ALboolean ret; + + context = GetContextRef(); + if(!context) return AL_FALSE; + + ret = (LookupSource(context, source) ? AL_TRUE : AL_FALSE); + + ALCcontext_DecRef(context); + + return ret; +} + + +AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!(FloatValsByProp(param) == 1)) + alSetError(Context, AL_INVALID_ENUM); + else + SetSourcefv(Source, Context, param, &value); + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!(FloatValsByProp(param) == 3)) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALfloat fvals[3] = { value1, value2, value3 }; + SetSourcefv(Source, Context, param, fvals); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!values) + alSetError(Context, AL_INVALID_VALUE); + else if(!(FloatValsByProp(param) > 0)) + alSetError(Context, AL_INVALID_ENUM); + else + SetSourcefv(Source, Context, param, values); + + ALCcontext_DecRef(Context); +} + + +AL_API ALvoid AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!(DoubleValsByProp(param) == 1)) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALfloat fval = (ALfloat)value; + SetSourcefv(Source, Context, param, &fval); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!(DoubleValsByProp(param) == 3)) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALfloat fvals[3] = { (ALfloat)value1, (ALfloat)value2, (ALfloat)value3 }; + SetSourcefv(Source, Context, param, fvals); + } + + ALCcontext_DecRef(Context); +} + +AL_API ALvoid AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values) +{ + ALCcontext *Context; + ALsource *Source; + ALint count; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!values) + alSetError(Context, AL_INVALID_VALUE); + else if(!((count=DoubleValsByProp(param)) > 0 && count <= 3)) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALfloat fvals[3]; + ALint i; + + for(i = 0;i < count;i++) + fvals[i] = (ALfloat)values[i]; + SetSourcefv(Source, Context, param, fvals); + } + + ALCcontext_DecRef(Context); +} + + +AL_API ALvoid AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!(IntValsByProp(param) == 1)) + alSetError(Context, AL_INVALID_ENUM); + else + SetSourceiv(Source, Context, param, &value); + + ALCcontext_DecRef(Context); +} + +AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!(IntValsByProp(param) == 3)) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALint ivals[3] = { value1, value2, value3 }; + SetSourceiv(Source, Context, param, ivals); + } + + ALCcontext_DecRef(Context); +} + +AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!values) + alSetError(Context, AL_INVALID_VALUE); + else if(!(IntValsByProp(param) > 0)) + alSetError(Context, AL_INVALID_ENUM); + else + SetSourceiv(Source, Context, param, values); + + ALCcontext_DecRef(Context); +} + + +AL_API ALvoid AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!(Int64ValsByProp(param) == 1)) + alSetError(Context, AL_INVALID_ENUM); + else + SetSourcei64v(Source, Context, param, &value); + + ALCcontext_DecRef(Context); +} + +AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!(Int64ValsByProp(param) == 3)) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALint64SOFT i64vals[3] = { value1, value2, value3 }; + SetSourcei64v(Source, Context, param, i64vals); + } + + ALCcontext_DecRef(Context); +} + +AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!values) + alSetError(Context, AL_INVALID_VALUE); + else if(!(Int64ValsByProp(param) > 0)) + alSetError(Context, AL_INVALID_ENUM); + else + SetSourcei64v(Source, Context, param, values); + + ALCcontext_DecRef(Context); +} + + +AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!value) + alSetError(Context, AL_INVALID_VALUE); + else if(!(FloatValsByProp(param) == 1)) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALdouble dval; + if(GetSourcedv(Source, Context, param, &dval)) + *value = (ALfloat)dval; + } + + ALCcontext_DecRef(Context); +} + + +AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!(value1 && value2 && value3)) + alSetError(Context, AL_INVALID_VALUE); + else if(!(FloatValsByProp(param) == 3)) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALdouble dvals[3]; + if(GetSourcedv(Source, Context, param, dvals)) + { + *value1 = (ALfloat)dvals[0]; + *value2 = (ALfloat)dvals[1]; + *value3 = (ALfloat)dvals[2]; + } + } + + ALCcontext_DecRef(Context); +} + + +AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values) +{ + ALCcontext *Context; + ALsource *Source; + ALint count; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!values) + alSetError(Context, AL_INVALID_VALUE); + else if(!((count=FloatValsByProp(param)) > 0 && count <= 3)) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALdouble dvals[3]; + if(GetSourcedv(Source, Context, param, dvals)) + { + ALint i; + for(i = 0;i < count;i++) + values[i] = (ALfloat)dvals[i]; + } + } + + ALCcontext_DecRef(Context); +} + + +AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!value) + alSetError(Context, AL_INVALID_VALUE); + else if(!(DoubleValsByProp(param) == 1)) + alSetError(Context, AL_INVALID_ENUM); + else + GetSourcedv(Source, Context, param, value); + + ALCcontext_DecRef(Context); +} + +AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!(value1 && value2 && value3)) + alSetError(Context, AL_INVALID_VALUE); + else if(!(DoubleValsByProp(param) == 3)) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALdouble dvals[3]; + if(GetSourcedv(Source, Context, param, dvals)) + { + *value1 = dvals[0]; + *value2 = dvals[1]; + *value3 = dvals[2]; + } + } + + ALCcontext_DecRef(Context); +} + +AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!values) + alSetError(Context, AL_INVALID_VALUE); + else if(!(DoubleValsByProp(param) > 0)) + alSetError(Context, AL_INVALID_ENUM); + else + GetSourcedv(Source, Context, param, values); + + ALCcontext_DecRef(Context); +} + + +AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!value) + alSetError(Context, AL_INVALID_VALUE); + else if(!(IntValsByProp(param) == 1)) + alSetError(Context, AL_INVALID_ENUM); + else + GetSourceiv(Source, Context, param, value); + + ALCcontext_DecRef(Context); +} + + +AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!(value1 && value2 && value3)) + alSetError(Context, AL_INVALID_VALUE); + else if(!(IntValsByProp(param) == 3)) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALint ivals[3]; + if(GetSourceiv(Source, Context, param, ivals)) + { + *value1 = ivals[0]; + *value2 = ivals[1]; + *value3 = ivals[2]; + } + } + + ALCcontext_DecRef(Context); +} + + +AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!values) + alSetError(Context, AL_INVALID_VALUE); + else if(!(IntValsByProp(param) > 0)) + alSetError(Context, AL_INVALID_ENUM); + else + GetSourceiv(Source, Context, param, values); + + ALCcontext_DecRef(Context); +} + + +AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!value) + alSetError(Context, AL_INVALID_VALUE); + else if(!(Int64ValsByProp(param) == 1)) + alSetError(Context, AL_INVALID_ENUM); + else + GetSourcei64v(Source, Context, param, value); + + ALCcontext_DecRef(Context); +} + +AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!(value1 && value2 && value3)) + alSetError(Context, AL_INVALID_VALUE); + else if(!(Int64ValsByProp(param) == 3)) + alSetError(Context, AL_INVALID_ENUM); + else + { + ALint64 i64vals[3]; + if(GetSourcei64v(Source, Context, param, i64vals)) + { + *value1 = i64vals[0]; + *value2 = i64vals[1]; + *value3 = i64vals[2]; + } + } + + ALCcontext_DecRef(Context); +} + +AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values) +{ + ALCcontext *Context; + ALsource *Source; + + Context = GetContextRef(); + if(!Context) return; + + if((Source=LookupSource(Context, source)) == NULL) + alSetError(Context, AL_INVALID_NAME); + else if(!values) + alSetError(Context, AL_INVALID_VALUE); + else if(!(Int64ValsByProp(param) > 0)) + alSetError(Context, AL_INVALID_ENUM); + else + GetSourcei64v(Source, Context, param, values); + + ALCcontext_DecRef(Context); +} + + +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 = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + for(i = 0;i < n;i++) + { + if(!LookupSource(context, sources[i])) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + } + + LockContext(context); + while(n > context->MaxActiveSources-context->ActiveSourceCount) + { + ALactivesource **temp = NULL; + ALsizei newcount; + + newcount = context->MaxActiveSources << 1; + if(newcount > 0) + temp = realloc(context->ActiveSources, + newcount * sizeof(context->ActiveSources[0])); + if(!temp) + { + UnlockContext(context); + SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); + } + for(i = context->MaxActiveSources;i < newcount;i++) + temp[i] = NULL; + + context->ActiveSources = temp; + context->MaxActiveSources = newcount; + } + + for(i = 0;i < n;i++) + { + source = LookupSource(context, sources[i]); + if(context->DeferUpdates) source->new_state = AL_PLAYING; + else SetSourceState(source, context, AL_PLAYING); + } + UnlockContext(context); + +done: + ALCcontext_DecRef(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 = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + for(i = 0;i < n;i++) + { + if(!LookupSource(context, sources[i])) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + } + + LockContext(context); + for(i = 0;i < n;i++) + { + source = LookupSource(context, sources[i]); + if(context->DeferUpdates) source->new_state = AL_PAUSED; + else SetSourceState(source, context, AL_PAUSED); + } + UnlockContext(context); + +done: + ALCcontext_DecRef(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 = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + for(i = 0;i < n;i++) + { + if(!LookupSource(context, sources[i])) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + } + + LockContext(context); + for(i = 0;i < n;i++) + { + source = LookupSource(context, sources[i]); + source->new_state = AL_NONE; + SetSourceState(source, context, AL_STOPPED); + } + UnlockContext(context); + +done: + ALCcontext_DecRef(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 = GetContextRef(); + if(!context) return; + + if(!(n >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + for(i = 0;i < n;i++) + { + if(!LookupSource(context, sources[i])) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + } + + LockContext(context); + for(i = 0;i < n;i++) + { + source = LookupSource(context, sources[i]); + source->new_state = AL_NONE; + SetSourceState(source, context, AL_INITIAL); + } + UnlockContext(context); + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers) +{ + ALCdevice *device; + ALCcontext *context; + ALsource *source; + ALsizei i; + ALbufferlistitem *BufferListStart; + ALbufferlistitem *BufferList; + ALbuffer *BufferFmt = NULL; + + if(nb == 0) + return; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + + if(!(nb >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if((source=LookupSource(context, src)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + WriteLock(&source->queue_lock); + if(source->SourceType == AL_STATIC) + { + WriteUnlock(&source->queue_lock); + /* Can't queue on a Static Source */ + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + } + + /* Check for a valid Buffer, for its frequency and format */ + BufferList = ATOMIC_LOAD(&source->queue); + while(BufferList) + { + if(BufferList->buffer) + { + BufferFmt = BufferList->buffer; + break; + } + BufferList = BufferList->next; + } + + BufferListStart = NULL; + BufferList = NULL; + for(i = 0;i < nb;i++) + { + ALbuffer *buffer = NULL; + if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == NULL) + { + WriteUnlock(&source->queue_lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, buffer_error); + } + + if(!BufferListStart) + { + BufferListStart = malloc(sizeof(ALbufferlistitem)); + BufferListStart->buffer = buffer; + BufferListStart->next = NULL; + BufferListStart->prev = NULL; + BufferList = BufferListStart; + } + else + { + BufferList->next = malloc(sizeof(ALbufferlistitem)); + BufferList->next->buffer = buffer; + BufferList->next->next = NULL; + BufferList->next->prev = BufferList; + BufferList = BufferList->next; + } + if(!buffer) continue; + + /* Hold a read lock on each buffer being queued while checking all + * provided buffers. This is done so other threads don't see an extra + * reference on some buffers if this operation ends up failing. */ + ReadLock(&buffer->lock); + IncrementRef(&buffer->ref); + + if(BufferFmt == NULL) + { + BufferFmt = buffer; + + source->NumChannels = ChannelsFromFmt(buffer->FmtChannels); + source->SampleSize = BytesFromFmt(buffer->FmtType); + } + else if(BufferFmt->Frequency != buffer->Frequency || + BufferFmt->OriginalChannels != buffer->OriginalChannels || + BufferFmt->OriginalType != buffer->OriginalType) + { + WriteUnlock(&source->queue_lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, buffer_error); + + buffer_error: + /* A buffer failed (invalid ID or format), so unlock and release + * each buffer we had. */ + while(BufferList != NULL) + { + ALbufferlistitem *prev = BufferList->prev; + if((buffer=BufferList->buffer) != NULL) + { + DecrementRef(&buffer->ref); + ReadUnlock(&buffer->lock); + } + free(BufferList); + BufferList = prev; + } + goto done; + } + } + /* All buffers good, unlock them now. */ + while(BufferList != NULL) + { + ALbuffer *buffer = BufferList->buffer; + if(buffer) ReadUnlock(&buffer->lock); + BufferList = BufferList->prev; + } + + /* Source is now streaming */ + source->SourceType = AL_STREAMING; + + BufferList = NULL; + if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALbufferlistitem*, &source->queue, &BufferList, BufferListStart)) + { + /* Queue head is not NULL, append to the end of the queue */ + while(BufferList->next != NULL) + BufferList = BufferList->next; + + BufferListStart->prev = BufferList; + BufferList->next = BufferListStart; + } + BufferList = NULL; + ATOMIC_COMPARE_EXCHANGE_STRONG(ALbufferlistitem*, &source->current_buffer, &BufferList, BufferListStart); + WriteUnlock(&source->queue_lock); + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers) +{ + ALCcontext *context; + ALsource *source; + ALbufferlistitem *NewHead; + ALbufferlistitem *OldHead; + ALbufferlistitem *Current; + ALsizei i; + + if(nb == 0) + return; + + context = GetContextRef(); + if(!context) return; + + if(!(nb >= 0)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + if((source=LookupSource(context, src)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + WriteLock(&source->queue_lock); + /* Find the new buffer queue head */ + NewHead = ATOMIC_LOAD(&source->queue); + Current = ATOMIC_LOAD(&source->current_buffer); + for(i = 0;i < nb && NewHead;i++) + { + if(NewHead == Current) + break; + NewHead = NewHead->next; + } + if(source->Looping || source->SourceType != AL_STREAMING || i != nb) + { + WriteUnlock(&source->queue_lock); + /* Trying to unqueue pending buffers, or a buffer that wasn't queued. */ + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + + /* Swap it, and cut the new head from the old. */ + OldHead = ATOMIC_EXCHANGE(ALbufferlistitem*, &source->queue, NewHead); + if(NewHead) + { + ALCdevice *device = context->Device; + ALbufferlistitem *OldTail = NewHead->prev; + uint count; + + /* Cut the new head's link back to the old body. The mixer is robust + * enough to handle the link back going away. Once the active mix (if + * any) is complete, it's safe to finish cutting the old tail from the + * new head. */ + NewHead->prev = NULL; + if(((count=ReadRef(&device->MixCount))&1) != 0) + { + while(count == ReadRef(&device->MixCount)) + althrd_yield(); + } + OldTail->next = NULL; + } + WriteUnlock(&source->queue_lock); + + while(OldHead != NULL) + { + ALbufferlistitem *next = OldHead->next; + ALbuffer *buffer = OldHead->buffer; + + if(!buffer) + *(buffers++) = 0; + else + { + *(buffers++) = buffer->id; + DecrementRef(&buffer->ref); + } + + free(OldHead); + OldHead = next; + } + +done: + ALCcontext_DecRef(context); +} + + +static ALvoid InitSourceParams(ALsource *Source) +{ + ALuint i; + + RWLockInit(&Source->queue_lock); + + Source->InnerAngle = 360.0f; + Source->OuterAngle = 360.0f; + Source->Pitch = 1.0f; + Source->Position[0] = 0.0f; + Source->Position[1] = 0.0f; + Source->Position[2] = 0.0f; + Source->Orientation[0] = 0.0f; + Source->Orientation[1] = 0.0f; + Source->Orientation[2] = 0.0f; + Source->Velocity[0] = 0.0f; + Source->Velocity[1] = 0.0f; + Source->Velocity[2] = 0.0f; + Source->RefDistance = 1.0f; + Source->MaxDistance = FLT_MAX; + Source->RollOffFactor = 1.0f; + Source->Looping = AL_FALSE; + Source->Gain = 1.0f; + Source->MinGain = 0.0f; + Source->MaxGain = 1.0f; + Source->OuterGain = 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->DirectChannels = AL_FALSE; + + Source->Radius = 0.0f; + + Source->DistanceModel = DefaultDistanceModel; + + Source->Resampler = DefaultResampler; + + Source->state = AL_INITIAL; + Source->new_state = AL_NONE; + Source->SourceType = AL_UNDETERMINED; + Source->Offset = -1.0; + + ATOMIC_INIT(&Source->queue, NULL); + ATOMIC_INIT(&Source->current_buffer, NULL); + + Source->Direct.Gain = 1.0f; + Source->Direct.GainHF = 1.0f; + Source->Direct.HFReference = LOWPASSFREQREF; + Source->Direct.GainLF = 1.0f; + Source->Direct.LFReference = HIGHPASSFREQREF; + for(i = 0;i < MAX_SENDS;i++) + { + Source->Send[i].Gain = 1.0f; + Source->Send[i].GainHF = 1.0f; + Source->Send[i].HFReference = LOWPASSFREQREF; + Source->Send[i].GainLF = 1.0f; + Source->Send[i].LFReference = HIGHPASSFREQREF; + } + + ATOMIC_INIT(&Source->NeedsUpdate, AL_TRUE); +} + + +/* SetSourceState + * + * Sets the source's new play state given its current state. + */ +ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state) +{ + ReadLock(&Source->queue_lock); + if(state == AL_PLAYING) + { + ALCdevice *device = Context->Device; + ALbufferlistitem *BufferList; + ALactivesource *src = NULL; + ALsizei j, k; + + /* Check that there is a queue containing at least one valid, non zero + * length Buffer. */ + BufferList = ATOMIC_LOAD(&Source->queue); + while(BufferList) + { + ALbuffer *buffer; + if((buffer=BufferList->buffer) != NULL && buffer->SampleLen > 0) + break; + BufferList = BufferList->next; + } + + if(Source->state != AL_PAUSED) + { + Source->state = AL_PLAYING; + Source->position = 0; + Source->position_fraction = 0; + ATOMIC_STORE(&Source->current_buffer, BufferList); + } + else + Source->state = AL_PLAYING; + + // Check if an Offset has been set + if(Source->Offset >= 0.0) + ApplyOffset(Source); + + /* If there's nothing to play, or device is disconnected, go right to + * stopped */ + if(!BufferList || !device->Connected) + goto do_stop; + + for(j = 0;j < Context->ActiveSourceCount;j++) + { + if(Context->ActiveSources[j]->Source == Source) + { + src = Context->ActiveSources[j]; + break; + } + } + if(src == NULL) + { + src = Context->ActiveSources[Context->ActiveSourceCount]; + if(src == NULL) + { + src = al_malloc(16, sizeof(src[0])); + Context->ActiveSources[Context->ActiveSourceCount] = src; + } + memset(src, 0, sizeof(*src)); + Context->ActiveSourceCount++; + + src->Source = Source; + } + else + { + ALuint i; + + src->Direct.Moving = AL_FALSE; + src->Direct.Counter = 0; + for(j = 0;j < MAX_INPUT_CHANNELS;j++) + { + for(k = 0;k < SRC_HISTORY_LENGTH;k++) + src->Direct.Mix.Hrtf.State[j].History[k] = 0.0f; + for(k = 0;k < HRIR_LENGTH;k++) + { + src->Direct.Mix.Hrtf.State[j].Values[k][0] = 0.0f; + src->Direct.Mix.Hrtf.State[j].Values[k][1] = 0.0f; + } + } + for(i = 0;i < device->NumAuxSends;i++) + { + src->Send[i].Counter = 0; + src->Send[i].Moving = AL_FALSE; + } + } + + if(BufferList->buffer->FmtChannels == FmtMono) + src->Update = CalcSourceParams; + else + src->Update = CalcNonAttnSourceParams; + + ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + } + else if(state == AL_PAUSED) + { + if(Source->state == AL_PLAYING) + Source->state = AL_PAUSED; + } + else if(state == AL_STOPPED) + { + do_stop: + if(Source->state != AL_INITIAL) + { + Source->state = AL_STOPPED; + ATOMIC_STORE(&Source->current_buffer, NULL); + } + Source->Offset = -1.0; + } + else if(state == AL_INITIAL) + { + if(Source->state != AL_INITIAL) + { + Source->state = AL_INITIAL; + Source->position = 0; + Source->position_fraction = 0; + ATOMIC_STORE(&Source->current_buffer, ATOMIC_LOAD(&Source->queue)); + } + Source->Offset = -1.0; + } + ReadUnlock(&Source->queue_lock); +} + +/* GetSourceOffset + * + * Gets the current read offset for the given Source, in 32.32 fixed-point + * samples. The offset is relative to the start of the queue (not the start of + * the current buffer). + */ +static ALint64 GetSourceOffset(const ALsource *Source) +{ + const ALbufferlistitem *BufferList; + const ALbufferlistitem *Current; + ALuint64 readPos; + + if(Source->state != AL_PLAYING && Source->state != AL_PAUSED) + return 0; + + /* NOTE: This is the offset into the *current* buffer, so add the length of + * any played buffers */ + readPos = (ALuint64)Source->position << 32; + readPos |= (ALuint64)Source->position_fraction << (32-FRACTIONBITS); + BufferList = ATOMIC_LOAD(&Source->queue); + Current = ATOMIC_LOAD(&Source->current_buffer); + while(BufferList && BufferList != Current) + { + if(BufferList->buffer) + readPos += (ALuint64)BufferList->buffer->SampleLen << 32; + BufferList = BufferList->next; + } + + return (ALint64)minu64(readPos, U64(0x7fffffffffffffff)); +} + +/* GetSourceSecOffset + * + * Gets the current read offset for the given Source, in seconds. The offset is + * relative to the start of the queue (not the start of the current buffer). + */ +static ALdouble GetSourceSecOffset(const ALsource *Source) +{ + const ALbufferlistitem *BufferList; + const ALbufferlistitem *Current; + const ALbuffer *Buffer = NULL; + ALuint64 readPos; + + if(Source->state != AL_PLAYING && Source->state != AL_PAUSED) + return 0.0; + + /* NOTE: This is the offset into the *current* buffer, so add the length of + * any played buffers */ + readPos = (ALuint64)Source->position << FRACTIONBITS; + readPos |= (ALuint64)Source->position_fraction; + BufferList = ATOMIC_LOAD(&Source->queue); + Current = ATOMIC_LOAD(&Source->current_buffer); + while(BufferList && BufferList != Current) + { + const ALbuffer *buffer = BufferList->buffer; + if(buffer != NULL) + { + if(!Buffer) Buffer = buffer; + readPos += (ALuint64)buffer->SampleLen << FRACTIONBITS; + } + BufferList = BufferList->next; + } + + while(BufferList && !Buffer) + { + Buffer = BufferList->buffer; + BufferList = BufferList->next; + } + assert(Buffer != NULL); + + return (ALdouble)readPos / (ALdouble)FRACTIONONE / (ALdouble)Buffer->Frequency; +} + +/* GetSourceOffsets + * + * Gets the current read and write offsets for the given Source, in the + * appropriate format (Bytes, Samples or Seconds). The offsets are relative to + * the start of the queue (not the start of the current buffer). + */ +static ALvoid GetSourceOffsets(const ALsource *Source, ALenum name, ALdouble *offset, ALdouble updateLen) +{ + const ALbufferlistitem *BufferList; + const ALbufferlistitem *Current; + const ALbuffer *Buffer = NULL; + ALboolean readFin = AL_FALSE; + ALuint readPos, writePos; + ALuint totalBufferLen; + + if(Source->state != AL_PLAYING && Source->state != AL_PAUSED) + { + offset[0] = 0.0; + offset[1] = 0.0; + return; + } + + if(updateLen > 0.0 && updateLen < 0.015) + updateLen = 0.015; + + /* NOTE: This is the offset into the *current* buffer, so add the length of + * any played buffers */ + totalBufferLen = 0; + readPos = Source->position; + BufferList = ATOMIC_LOAD(&Source->queue); + Current = ATOMIC_LOAD(&Source->current_buffer); + while(BufferList != NULL) + { + const ALbuffer *buffer; + readFin = readFin || (BufferList == Current); + if((buffer=BufferList->buffer) != NULL) + { + if(!Buffer) Buffer = buffer; + totalBufferLen += buffer->SampleLen; + if(!readFin) readPos += buffer->SampleLen; + } + BufferList = BufferList->next; + } + assert(Buffer != NULL); + + if(Source->state == AL_PLAYING) + writePos = readPos + (ALuint)(updateLen*Buffer->Frequency); + else + writePos = readPos; + + if(Source->Looping) + { + readPos %= totalBufferLen; + writePos %= totalBufferLen; + } + else + { + /* Wrap positions back to 0 */ + if(readPos >= totalBufferLen) + readPos = 0; + if(writePos >= totalBufferLen) + writePos = 0; + } + + switch(name) + { + case AL_SEC_OFFSET: + offset[0] = (ALdouble)readPos / Buffer->Frequency; + offset[1] = (ALdouble)writePos / Buffer->Frequency; + break; + + case AL_SAMPLE_OFFSET: + case AL_SAMPLE_RW_OFFSETS_SOFT: + offset[0] = (ALdouble)readPos; + offset[1] = (ALdouble)writePos; + break; + + case AL_BYTE_OFFSET: + case AL_BYTE_RW_OFFSETS_SOFT: + if(Buffer->OriginalType == UserFmtIMA4) + { + ALsizei align = (Buffer->OriginalAlign-1)/2 + 4; + ALuint BlockSize = align * ChannelsFromFmt(Buffer->FmtChannels); + ALuint FrameBlockSize = Buffer->OriginalAlign; + + /* 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 if(Buffer->OriginalType == UserFmtMSADPCM) + { + ALsizei align = (Buffer->OriginalAlign-2)/2 + 7; + ALuint BlockSize = align * ChannelsFromFmt(Buffer->FmtChannels); + ALuint FrameBlockSize = Buffer->OriginalAlign; + + /* 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 FrameSize = FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType); + offset[0] = (ALdouble)(readPos * FrameSize); + offset[1] = (ALdouble)(writePos * FrameSize); + } + break; + } +} + + +/* ApplyOffset + * + * Apply the stored playback offset to the Source. This function will update + * the number of buffers "played" given the stored offset. + */ +ALboolean ApplyOffset(ALsource *Source) +{ + ALbufferlistitem *BufferList; + const ALbuffer *Buffer; + ALint bufferLen, totalBufferLen; + ALint offset; + + /* Get sample frame offset */ + offset = GetSampleOffset(Source); + if(offset == -1) + return AL_FALSE; + + totalBufferLen = 0; + BufferList = ATOMIC_LOAD(&Source->queue); + while(BufferList && totalBufferLen <= offset) + { + Buffer = BufferList->buffer; + bufferLen = Buffer ? Buffer->SampleLen : 0; + + if(bufferLen > offset-totalBufferLen) + { + /* Offset is in this buffer */ + ATOMIC_STORE(&Source->current_buffer, BufferList); + + Source->position = offset - totalBufferLen; + Source->position_fraction = 0; + return AL_TRUE; + } + + totalBufferLen += bufferLen; + + BufferList = BufferList->next; + } + + /* Offset is out of range of the queue */ + return AL_FALSE; +} + + +/* GetSampleOffset + * + * Returns the sample offset into the Source's queue (from the Sample, Byte or + * Second offset supplied by the application). This takes into account the fact + * that the buffer format may have been modifed since. + */ +static ALint GetSampleOffset(ALsource *Source) +{ + const ALbuffer *Buffer = NULL; + const ALbufferlistitem *BufferList; + ALint Offset = -1; + + /* Find the first valid Buffer in the Queue */ + BufferList = ATOMIC_LOAD(&Source->queue); + while(BufferList) + { + if(BufferList->buffer) + { + Buffer = BufferList->buffer; + break; + } + BufferList = BufferList->next; + } + + if(!Buffer) + { + Source->Offset = -1.0; + return -1; + } + + switch(Source->OffsetType) + { + case AL_BYTE_OFFSET: + /* Determine the ByteOffset (and ensure it is block aligned) */ + Offset = (ALint)Source->Offset; + if(Buffer->OriginalType == UserFmtIMA4) + { + ALsizei align = (Buffer->OriginalAlign-1)/2 + 4; + Offset /= align * ChannelsFromUserFmt(Buffer->OriginalChannels); + Offset *= Buffer->OriginalAlign; + } + else if(Buffer->OriginalType == UserFmtMSADPCM) + { + ALsizei align = (Buffer->OriginalAlign-2)/2 + 7; + Offset /= align * ChannelsFromUserFmt(Buffer->OriginalChannels); + Offset *= Buffer->OriginalAlign; + } + else + Offset /= FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType); + break; + + case AL_SAMPLE_OFFSET: + Offset = (ALint)Source->Offset; + break; + + case AL_SEC_OFFSET: + Offset = (ALint)(Source->Offset * Buffer->Frequency); + break; + } + Source->Offset = -1.0; + + return Offset; +} + + +/* ReleaseALSources + * + * Destroys all sources in the source map. + */ +ALvoid ReleaseALSources(ALCcontext *Context) +{ + ALbufferlistitem *item; + 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; + + item = ATOMIC_EXCHANGE(ALbufferlistitem*, &temp->queue, NULL); + while(item != NULL) + { + ALbufferlistitem *next = item->next; + if(item->buffer != NULL) + DecrementRef(&item->buffer->ref); + free(item); + item = next; + } + + for(j = 0;j < MAX_SENDS;++j) + { + if(temp->Send[j].Slot) + DecrementRef(&temp->Send[j].Slot->ref); + temp->Send[j].Slot = NULL; + } + + FreeThunkEntry(temp->id); + memset(temp, 0, sizeof(*temp)); + al_free(temp); + } +} diff --git a/libs/openal-soft/OpenAL32/alState.c b/libs/openal-soft/OpenAL32/alState.c new file mode 100644 index 00000000..d48c268b --- /dev/null +++ b/libs/openal-soft/OpenAL32/alState.c @@ -0,0 +1,802 @@ +/** + * 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/al.h" +#include "AL/alext.h" +#include "alError.h" +#include "alSource.h" +#include "alAuxEffectSlot.h" +#include "alMidi.h" + +#include "midi/base.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 = GetContextRef(); + if(!context) return; + + switch(capability) + { + case AL_SOURCE_DISTANCE_MODEL: + context->SourceDistanceModel = AL_TRUE; + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alDisable(ALenum capability) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + switch(capability) + { + case AL_SOURCE_DISTANCE_MODEL: + context->SourceDistanceModel = AL_FALSE; + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability) +{ + ALCcontext *context; + ALboolean value=AL_FALSE; + + context = GetContextRef(); + if(!context) return AL_FALSE; + + switch(capability) + { + case AL_SOURCE_DISTANCE_MODEL: + value = context->SourceDistanceModel; + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); + + return value; +} + +AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname) +{ + ALCcontext *context; + ALboolean value=AL_FALSE; + + context = GetContextRef(); + 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->SpeedOfSound != 0.0f) + value = AL_TRUE; + break; + + case AL_DEFERRED_UPDATES_SOFT: + value = context->DeferUpdates; + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); + + return value; +} + +AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname) +{ + ALCdevice *device; + ALCcontext *context; + ALdouble value = 0.0; + + context = GetContextRef(); + if(!context) return 0.0; + + switch(pname) + { + case AL_DOPPLER_FACTOR: + value = (ALdouble)context->DopplerFactor; + break; + + case AL_DOPPLER_VELOCITY: + value = (ALdouble)context->DopplerVelocity; + break; + + case AL_DISTANCE_MODEL: + value = (ALdouble)context->DistanceModel; + break; + + case AL_SPEED_OF_SOUND: + value = (ALdouble)context->SpeedOfSound; + break; + + case AL_DEFERRED_UPDATES_SOFT: + value = (ALdouble)context->DeferUpdates; + break; + + case AL_MIDI_GAIN_SOFT: + device = context->Device; + value = (ALdouble)MidiSynth_getGain(device->Synth); + break; + + case AL_MIDI_STATE_SOFT: + device = context->Device; + value = (ALdouble)MidiSynth_getState(device->Synth); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); + + return value; +} + +AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname) +{ + ALCdevice *device; + ALCcontext *context; + ALfloat value = 0.0f; + + context = GetContextRef(); + 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 = (ALfloat)context->DistanceModel; + break; + + case AL_SPEED_OF_SOUND: + value = context->SpeedOfSound; + break; + + case AL_DEFERRED_UPDATES_SOFT: + value = (ALfloat)context->DeferUpdates; + break; + + case AL_MIDI_GAIN_SOFT: + device = context->Device; + value = MidiSynth_getGain(device->Synth); + break; + + case AL_MIDI_STATE_SOFT: + device = context->Device; + value = (ALfloat)MidiSynth_getState(device->Synth); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); + + return value; +} + +AL_API ALint AL_APIENTRY alGetInteger(ALenum pname) +{ + ALCcontext *context; + ALCdevice *device; + MidiSynth *synth; + ALint value = 0; + + context = GetContextRef(); + 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->SpeedOfSound; + break; + + case AL_DEFERRED_UPDATES_SOFT: + value = (ALint)context->DeferUpdates; + break; + + case AL_SOUNDFONTS_SIZE_SOFT: + device = context->Device; + synth = device->Synth; + value = synth->NumSoundfonts; + break; + + case AL_MIDI_STATE_SOFT: + device = context->Device; + value = MidiSynth_getState(device->Synth); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); + + return value; +} + +AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname) +{ + ALCcontext *context; + ALCdevice *device; + MidiSynth *synth; + ALint64SOFT value = 0; + + context = GetContextRef(); + if(!context) return 0; + + switch(pname) + { + case AL_DOPPLER_FACTOR: + value = (ALint64SOFT)context->DopplerFactor; + break; + + case AL_DOPPLER_VELOCITY: + value = (ALint64SOFT)context->DopplerVelocity; + break; + + case AL_DISTANCE_MODEL: + value = (ALint64SOFT)context->DistanceModel; + break; + + case AL_SPEED_OF_SOUND: + value = (ALint64SOFT)context->SpeedOfSound; + break; + + case AL_DEFERRED_UPDATES_SOFT: + value = (ALint64SOFT)context->DeferUpdates; + break; + + case AL_MIDI_CLOCK_SOFT: + device = context->Device; + ALCdevice_Lock(device); + value = MidiSynth_getTime(device->Synth); + ALCdevice_Unlock(device); + break; + + case AL_SOUNDFONTS_SIZE_SOFT: + device = context->Device; + synth = device->Synth; + value = (ALint64SOFT)synth->NumSoundfonts; + break; + + case AL_MIDI_STATE_SOFT: + device = context->Device; + value = (ALint64SOFT)MidiSynth_getState(device->Synth); + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); + + return value; +} + +AL_API ALvoid AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values) +{ + ALCcontext *context; + + if(values) + { + switch(pname) + { + case AL_DOPPLER_FACTOR: + case AL_DOPPLER_VELOCITY: + case AL_DISTANCE_MODEL: + case AL_SPEED_OF_SOUND: + case AL_DEFERRED_UPDATES_SOFT: + values[0] = alGetBoolean(pname); + return; + } + } + + context = GetContextRef(); + if(!context) return; + + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(pname) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values) +{ + ALCcontext *context; + + if(values) + { + switch(pname) + { + case AL_DOPPLER_FACTOR: + case AL_DOPPLER_VELOCITY: + case AL_DISTANCE_MODEL: + case AL_SPEED_OF_SOUND: + case AL_DEFERRED_UPDATES_SOFT: + case AL_MIDI_GAIN_SOFT: + case AL_MIDI_STATE_SOFT: + values[0] = alGetDouble(pname); + return; + } + } + + context = GetContextRef(); + if(!context) return; + + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(pname) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values) +{ + ALCcontext *context; + + if(values) + { + switch(pname) + { + case AL_DOPPLER_FACTOR: + case AL_DOPPLER_VELOCITY: + case AL_DISTANCE_MODEL: + case AL_SPEED_OF_SOUND: + case AL_DEFERRED_UPDATES_SOFT: + case AL_MIDI_GAIN_SOFT: + case AL_MIDI_STATE_SOFT: + values[0] = alGetFloat(pname); + return; + } + } + + context = GetContextRef(); + if(!context) return; + + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + switch(pname) + { + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values) +{ + ALCcontext *context; + ALCdevice *device; + MidiSynth *synth; + ALsizei i; + + if(values) + { + switch(pname) + { + case AL_DOPPLER_FACTOR: + case AL_DOPPLER_VELOCITY: + case AL_DISTANCE_MODEL: + case AL_SPEED_OF_SOUND: + case AL_DEFERRED_UPDATES_SOFT: + case AL_SOUNDFONTS_SIZE_SOFT: + case AL_MIDI_STATE_SOFT: + values[0] = alGetInteger(pname); + return; + } + } + + context = GetContextRef(); + if(!context) return; + + switch(pname) + { + case AL_SOUNDFONTS_SOFT: + device = context->Device; + synth = device->Synth; + if(synth->NumSoundfonts > 0) + { + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + for(i = 0;i < synth->NumSoundfonts;i++) + values[i] = synth->Soundfonts[i]->id; + } + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values) +{ + ALCcontext *context; + ALCdevice *device; + MidiSynth *synth; + ALsizei i; + + if(values) + { + switch(pname) + { + case AL_DOPPLER_FACTOR: + case AL_DOPPLER_VELOCITY: + case AL_DISTANCE_MODEL: + case AL_SPEED_OF_SOUND: + case AL_DEFERRED_UPDATES_SOFT: + case AL_MIDI_CLOCK_SOFT: + case AL_SOUNDFONTS_SIZE_SOFT: + case AL_MIDI_STATE_SOFT: + values[0] = alGetInteger64SOFT(pname); + return; + } + } + + context = GetContextRef(); + if(!context) return; + + switch(pname) + { + case AL_SOUNDFONTS_SOFT: + device = context->Device; + synth = device->Synth; + if(synth->NumSoundfonts > 0) + { + if(!(values)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + for(i = 0;i < synth->NumSoundfonts;i++) + values[i] = (ALint64SOFT)synth->Soundfonts[i]->id; + } + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); +} + +AL_API const ALchar* AL_APIENTRY alGetString(ALenum pname) +{ + const ALchar *value = NULL; + ALCcontext *context; + + context = GetContextRef(); + if(!context) 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 = context->ExtensionList; + 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: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); + + return value; +} + +AL_API ALvoid AL_APIENTRY alDopplerFactor(ALfloat value) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + if(!(value >= 0.0f && isfinite(value))) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + context->DopplerFactor = value; + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alDopplerVelocity(ALfloat value) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + if(!(value >= 0.0f && isfinite(value))) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + context->DopplerVelocity = value; + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alSpeedOfSound(ALfloat value) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + if(!(value > 0.0f && isfinite(value))) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + context->SpeedOfSound = value; + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + +done: + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alDistanceModel(ALenum value) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + if(!(value == AL_INVERSE_DISTANCE || value == AL_INVERSE_DISTANCE_CLAMPED || + value == AL_LINEAR_DISTANCE || value == AL_LINEAR_DISTANCE_CLAMPED || + value == AL_EXPONENT_DISTANCE || value == AL_EXPONENT_DISTANCE_CLAMPED || + value == AL_NONE)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + + context->DistanceModel = value; + if(!context->SourceDistanceModel) + ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + +done: + ALCcontext_DecRef(context); +} + + +AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + if(!context->DeferUpdates) + { + ALboolean UpdateSources; + ALactivesource **src, **src_end; + ALeffectslot **slot, **slot_end; + FPUCtl oldMode; + + SetMixerFPUMode(&oldMode); + + LockContext(context); + context->DeferUpdates = AL_TRUE; + + /* Make sure all pending updates are performed */ + UpdateSources = ATOMIC_EXCHANGE(ALenum, &context->UpdateSources, AL_FALSE); + + src = context->ActiveSources; + src_end = src + context->ActiveSourceCount; + while(src != src_end) + { + ALsource *source = (*src)->Source; + + if(source->state != AL_PLAYING && source->state != AL_PAUSED) + { + ALactivesource *temp = *(--src_end); + *src_end = *src; + *src = temp; + --(context->ActiveSourceCount); + continue; + } + + if(ATOMIC_EXCHANGE(ALenum, &source->NeedsUpdate, AL_FALSE) || UpdateSources) + (*src)->Update(*src, context); + + src++; + } + + slot = VECTOR_ITER_BEGIN(context->ActiveAuxSlots); + slot_end = VECTOR_ITER_END(context->ActiveAuxSlots); + while(slot != slot_end) + { + if(ATOMIC_EXCHANGE(ALenum, &(*slot)->NeedsUpdate, AL_FALSE)) + V((*slot)->EffectState,update)(context->Device, *slot); + slot++; + } + + UnlockContext(context); + RestoreFPUMode(&oldMode); + } + + ALCcontext_DecRef(context); +} + +AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + if(ExchangeInt(&context->DeferUpdates, AL_FALSE)) + { + ALsizei pos; + + LockContext(context); + LockUIntMapRead(&context->SourceMap); + for(pos = 0;pos < context->SourceMap.size;pos++) + { + ALsource *Source = context->SourceMap.array[pos].value; + ALenum new_state; + + if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && + Source->Offset >= 0.0) + { + ReadLock(&Source->queue_lock); + ApplyOffset(Source); + ReadUnlock(&Source->queue_lock); + } + + new_state = ExchangeInt(&Source->new_state, AL_NONE); + if(new_state) + SetSourceState(Source, context, new_state); + } + UnlockUIntMapRead(&context->SourceMap); + UnlockContext(context); + } + + ALCcontext_DecRef(context); +} diff --git a/libs/openal-soft/OpenAL32/alThunk.c b/libs/openal-soft/OpenAL32/alThunk.c new file mode 100644 index 00000000..c4b3b92b --- /dev/null +++ b/libs/openal-soft/OpenAL32/alThunk.c @@ -0,0 +1,89 @@ +/** + * 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 ALenum *ThunkArray; +static ALuint ThunkArraySize; +static RWLock ThunkLock; + +void ThunkInit(void) +{ + RWLockInit(&ThunkLock); + ThunkArraySize = 1; + ThunkArray = calloc(1, ThunkArraySize * sizeof(*ThunkArray)); +} + +void ThunkExit(void) +{ + free(ThunkArray); + ThunkArray = NULL; + ThunkArraySize = 0; +} + +ALenum NewThunkEntry(ALuint *index) +{ + ALenum *NewList; + ALuint i; + + ReadLock(&ThunkLock); + for(i = 0;i < ThunkArraySize;i++) + { + if(ExchangeInt(&ThunkArray[i], AL_TRUE) == AL_FALSE) + { + ReadUnlock(&ThunkLock); + *index = i+1; + return AL_NO_ERROR; + } + } + ReadUnlock(&ThunkLock); + + WriteLock(&ThunkLock); + NewList = realloc(ThunkArray, ThunkArraySize*2 * sizeof(*ThunkArray)); + if(!NewList) + { + WriteUnlock(&ThunkLock); + ERR("Realloc failed to increase to %u entries!\n", ThunkArraySize*2); + return AL_OUT_OF_MEMORY; + } + memset(&NewList[ThunkArraySize], 0, ThunkArraySize*sizeof(*ThunkArray)); + ThunkArraySize *= 2; + ThunkArray = NewList; + + ThunkArray[i] = AL_TRUE; + WriteUnlock(&ThunkLock); + + *index = i+1; + return AL_NO_ERROR; +} + +void FreeThunkEntry(ALuint index) +{ + ReadLock(&ThunkLock); + if(index > 0 && index <= ThunkArraySize) + ExchangeInt(&ThunkArray[index-1], AL_FALSE); + ReadUnlock(&ThunkLock); +} diff --git a/libs/openal-soft/OpenAL32/sample_cvt.c b/libs/openal-soft/OpenAL32/sample_cvt.c new file mode 100644 index 00000000..a02b217e --- /dev/null +++ b/libs/openal-soft/OpenAL32/sample_cvt.c @@ -0,0 +1,1269 @@ + +#include "config.h" + +#include "sample_cvt.h" + +#ifdef HAVE_ALLOCA_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif + +#include "AL/al.h" +#include "alu.h" +#include "alBuffer.h" + + +/* IMA ADPCM Stepsize table */ +static const int 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 int 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 int IMA4Index_adjust[16] = { + -1,-1,-1,-1, 2, 4, 6, 8, + -1,-1,-1,-1, 2, 4, 6, 8 +}; + + +/* MSADPCM Adaption table */ +static const int MSADPCMAdaption[16] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 +}; + +/* MSADPCM Adaption Coefficient tables */ +static const int MSADPCMAdaptionCoeff[7][2] = { + { 256, 0 }, + { 512, -256 }, + { 0, 0 }, + { 192, 64 }, + { 240, 0 }, + { 460, -208 }, + { 392, -232 } +}; + + +/* 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 +}; + + +/* A quick'n'dirty lookup table to decode an aLaw-encoded byte sample into a + * signed 16-bit sample */ +static const ALshort aLawDecompressionTable[256] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, + -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, + -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, + -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848 +}; + +/* Values used when encoding an aLaw sample */ +static const int aLawClip = 32635; +static const char aLawCompressTable[128] = { + 1,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4, + 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, + 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 +}; + + +typedef ALubyte ALmulaw; +typedef ALubyte ALalaw; +typedef ALubyte ALima4; +typedef ALubyte ALmsadpcm; +typedef struct { + ALbyte b[3]; +} ALbyte3; +static_assert(sizeof(ALbyte3)==sizeof(ALbyte[3]), "ALbyte3 size is not 3"); +typedef struct { + ALubyte b[3]; +} ALubyte3; +static_assert(sizeof(ALubyte3)==sizeof(ALubyte[3]), "ALubyte3 size is not 3"); + +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 inline ALshort DecodeALaw(ALalaw val) +{ return aLawDecompressionTable[val]; } + +static ALalaw EncodeALaw(ALshort val) +{ + ALint mant, exp, sign; + + sign = ((~val) >> 8) & 0x80; + if(!sign) + { + val = maxi(val, -32767); + val = -val; + } + val = mini(val, aLawClip); + + if(val >= 256) + { + exp = aLawCompressTable[(val>>8) & 0x7f]; + mant = (val >> (exp+3)) & 0x0f; + } + else + { + exp = 0; + mant = val >> 4; + } + + return ((exp<<4) | mant) ^ (sign^0x55); +} + +static void DecodeIMA4Block(ALshort *dst, const ALima4 *src, ALint numchans, ALsizei align) +{ + ALint sample[MAX_INPUT_CHANNELS], index[MAX_INPUT_CHANNELS]; + ALuint code[MAX_INPUT_CHANNELS]; + 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]; + } + + for(j = 1;j < align;j += 8) + { + 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++) + { + 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+k)*numchans + c] = sample[c]; + } + } + } +} + +static void EncodeIMA4Block(ALima4 *dst, const ALshort *src, ALint *sample, ALint *index, ALint numchans, ALsizei align) +{ + 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; + } + + for(j = 1;j < align;j += 8) + { + 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; + } + } + } +} + + +static void DecodeMSADPCMBlock(ALshort *dst, const ALmsadpcm *src, ALint numchans, ALsizei align) +{ + ALubyte blockpred[MAX_INPUT_CHANNELS]; + ALint delta[MAX_INPUT_CHANNELS]; + ALshort samples[MAX_INPUT_CHANNELS][2]; + ALint i, j; + + for(i = 0;i < numchans;i++) + { + blockpred[i] = *(src++); + blockpred[i] = minu(blockpred[i], 6); + } + for(i = 0;i < numchans;i++) + { + delta[i] = *(src++); + delta[i] |= *(src++) << 8; + delta[i] = (delta[i]^0x8000) - 0x8000; + } + for(i = 0;i < numchans;i++) + { + samples[i][0] = *(src++); + samples[i][0] |= *(src++) << 8; + samples[i][0] = (samples[i][0]^0x8000) - 0x8000; + } + for(i = 0;i < numchans;i++) + { + samples[i][1] = *(src++); + samples[i][1] |= *(src++) << 8; + samples[i][1] = (samples[i][1]^0x8000) - 0x8000; + } + + /* Second sample is written first. */ + for(i = 0;i < numchans;i++) + *(dst++) = samples[i][1]; + for(i = 0;i < numchans;i++) + *(dst++) = samples[i][0]; + + for(j = 2;j < align;j++) + { + for(i = 0;i < numchans;i++) + { + const ALint num = (j*numchans) + i; + ALint nibble, pred; + + /* Read the nibble (first is in the upper bits). */ + if(!(num&1)) + nibble = (*src>>4)&0x0f; + else + nibble = (*(src++))&0x0f; + + pred = (samples[i][0]*MSADPCMAdaptionCoeff[blockpred[i]][0] + + samples[i][1]*MSADPCMAdaptionCoeff[blockpred[i]][1]) / 256; + pred += ((nibble^0x08) - 0x08) * delta[i]; + pred = clampi(pred, -32768, 32767); + + samples[i][1] = samples[i][0]; + samples[i][0] = pred; + + delta[i] = (MSADPCMAdaption[nibble] * delta[i]) / 256; + delta[i] = maxi(16, delta[i]); + + *(dst++) = pred; + } + } +} + +/* NOTE: This encoder is pretty dumb/simplistic. Some kind of pre-processing + * that tries to find the optimal block predictors would be nice, at least. A + * multi-pass method that can generate better deltas would be good, too. */ +static void EncodeMSADPCMBlock(ALmsadpcm *dst, const ALshort *src, ALint *sample, ALint numchans, ALsizei align) +{ + ALubyte blockpred[MAX_INPUT_CHANNELS]; + ALint delta[MAX_INPUT_CHANNELS]; + ALshort samples[MAX_INPUT_CHANNELS][2]; + ALint i, j; + + /* Block predictor */ + for(i = 0;i < numchans;i++) + { + /* FIXME: Calculate something better. */ + blockpred[i] = 0; + *(dst++) = blockpred[i]; + } + /* Initial delta */ + for(i = 0;i < numchans;i++) + { + delta[i] = 16; + *(dst++) = (delta[i] ) & 0xff; + *(dst++) = (delta[i]>>8) & 0xff; + } + /* Initial sample 1 */ + for(i = 0;i < numchans;i++) + { + samples[i][0] = src[1*numchans + i]; + *(dst++) = (samples[i][0] ) & 0xff; + *(dst++) = (samples[i][0]>>8) & 0xff; + } + /* Initial sample 2 */ + for(i = 0;i < numchans;i++) + { + samples[i][1] = src[i]; + *(dst++) = (samples[i][1] ) & 0xff; + *(dst++) = (samples[i][1]>>8) & 0xff; + } + + for(j = 2;j < align;j++) + { + for(i = 0;i < numchans;i++) + { + const ALint num = (j*numchans) + i; + ALint nibble = 0; + ALint bias; + + sample[i] = (samples[i][0]*MSADPCMAdaptionCoeff[blockpred[i]][0] + + samples[i][1]*MSADPCMAdaptionCoeff[blockpred[i]][1]) / 256; + + nibble = src[num] - sample[i]; + if(nibble >= 0) + bias = delta[i] / 2; + else + bias = -delta[i] / 2; + + nibble = (nibble + bias) / delta[i]; + nibble = clampi(nibble, -8, 7)&0x0f; + + sample[i] += ((nibble^0x08)-0x08) * delta[i]; + sample[i] = clampi(sample[i], -32768, 32767); + + samples[i][1] = samples[i][0]; + samples[i][0] = sample[i]; + + delta[i] = (MSADPCMAdaption[nibble] * delta[i]) / 256; + delta[i] = maxi(16, delta[i]); + + if(!(num&1)) + *dst = nibble << 4; + else + { + *dst |= nibble; + dst++; + } + } + } +} + + +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_ALalaw(ALalaw val) +{ return Conv_ALbyte_ALshort(DecodeALaw(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_ALalaw(ALalaw val) +{ return Conv_ALubyte_ALshort(DecodeALaw(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_ALalaw(ALalaw val) +{ return Conv_ALshort_ALshort(DecodeALaw(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_ALalaw(ALalaw val) +{ return Conv_ALushort_ALshort(DecodeALaw(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*16777215.0f) << 7; +} +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_ALalaw(ALalaw val) +{ return Conv_ALint_ALshort(DecodeALaw(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*16777215.0f)<<7) + 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_ALalaw(ALalaw val) +{ return Conv_ALuint_ALshort(DecodeALaw(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 (ALfloat)(val>>7) * (1.0f/16777215.0f); } +static inline ALfloat Conv_ALfloat_ALuint(ALuint val) +{ return (ALfloat)((ALint)(val>>7)-16777216) * (1.0f/16777215.0f); } +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) ? (ALfloat)val : 0.0f; } +static inline ALfloat Conv_ALfloat_ALmulaw(ALmulaw val) +{ return Conv_ALfloat_ALshort(DecodeMuLaw(val)); } +static inline ALfloat Conv_ALfloat_ALalaw(ALalaw val) +{ return Conv_ALfloat_ALshort(DecodeALaw(val)); } +static inline ALfloat Conv_ALfloat_ALbyte3(ALbyte3 val) +{ return (ALfloat)(DecodeByte3(val) * (1.0/8388607.0)); } +static inline ALfloat Conv_ALfloat_ALubyte3(ALubyte3 val) +{ return (ALfloat)((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_ALalaw(ALalaw val) +{ return Conv_ALdouble_ALshort(DecodeALaw(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(ALalaw) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static inline ALalaw Conv_ALalaw_##T(T val) \ +{ return EncodeALaw(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) +DECL_TEMPLATE(ALmulaw) +static inline ALalaw Conv_ALalaw_ALalaw(ALalaw 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) +DECL_TEMPLATE(ALalaw) +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(ALalaw) +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, ALsizei UNUSED(align)) \ +{ \ + ALuint i, j; \ + for(i = 0;i < len;i++) \ + { \ + for(j = 0;j < numchans;j++) \ + *(dst++) = Conv_##T1##_##T2(*(src++)); \ + } \ +} + +#define DECL_TEMPLATE2(T) \ +DECL_TEMPLATE(T, ALbyte) \ +DECL_TEMPLATE(T, ALubyte) \ +DECL_TEMPLATE(T, ALshort) \ +DECL_TEMPLATE(T, ALushort) \ +DECL_TEMPLATE(T, ALint) \ +DECL_TEMPLATE(T, ALuint) \ +DECL_TEMPLATE(T, ALfloat) \ +DECL_TEMPLATE(T, ALdouble) \ +DECL_TEMPLATE(T, ALmulaw) \ +DECL_TEMPLATE(T, ALalaw) \ +DECL_TEMPLATE(T, ALbyte3) \ +DECL_TEMPLATE(T, ALubyte3) + +DECL_TEMPLATE2(ALbyte) +DECL_TEMPLATE2(ALubyte) +DECL_TEMPLATE2(ALshort) +DECL_TEMPLATE2(ALushort) +DECL_TEMPLATE2(ALint) +DECL_TEMPLATE2(ALuint) +DECL_TEMPLATE2(ALfloat) +DECL_TEMPLATE2(ALdouble) +DECL_TEMPLATE2(ALmulaw) +DECL_TEMPLATE2(ALalaw) +DECL_TEMPLATE2(ALbyte3) +DECL_TEMPLATE2(ALubyte3) + +#undef DECL_TEMPLATE2 +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static void Convert_##T##_ALima4(T *dst, const ALima4 *src, ALuint numchans, \ + ALuint len, ALuint align) \ +{ \ + ALsizei byte_align = ((align-1)/2 + 4) * numchans; \ + DECL_VLA(ALshort, tmp, align*numchans); \ + ALuint i, j, k; \ + \ + assert(align > 0 && (len%align) == 0); \ + for(i = 0;i < len;i += align) \ + { \ + DecodeIMA4Block(tmp, src, numchans, align); \ + src += byte_align; \ + \ + for(j = 0;j < align;j++) \ + { \ + for(k = 0;k < numchans;k++) \ + *(dst++) = Conv_##T##_ALshort(tmp[j*numchans + k]); \ + } \ + } \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +static void Convert_ALshort_ALima4(ALshort *dst, const ALima4 *src, ALuint numchans, + ALuint len, ALuint align) +{ + ALsizei byte_align = ((align-1)/2 + 4) * numchans; + ALuint i; + + assert(align > 0 && (len%align) == 0); + for(i = 0;i < len;i += align) + { + DecodeIMA4Block(dst, src, numchans, align); + src += byte_align; + dst += align*numchans; + } +} +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALalaw) +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 len, ALuint align) \ +{ \ + ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; \ + ALint index[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; \ + ALsizei byte_align = ((align-1)/2 + 4) * numchans; \ + DECL_VLA(ALshort, tmp, align*numchans); \ + ALuint i, j, k; \ + \ + assert(align > 0 && (len%align) == 0); \ + for(i = 0;i < len;i += align) \ + { \ + for(j = 0;j < align;j++) \ + { \ + for(k = 0;k < numchans;k++) \ + tmp[j*numchans + k] = Conv_ALshort_##T(*(src++)); \ + } \ + EncodeIMA4Block(dst, tmp, sample, index, numchans, align); \ + dst += byte_align; \ + } \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +static void Convert_ALima4_ALshort(ALima4 *dst, const ALshort *src, + ALuint numchans, ALuint len, ALuint align) +{ + ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; + ALint index[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; + ALsizei byte_align = ((align-1)/2 + 4) * numchans; + ALuint i; + + assert(align > 0 && (len%align) == 0); + for(i = 0;i < len;i += align) + { + EncodeIMA4Block(dst, src, sample, index, numchans, align); + src += align*numchans; + dst += byte_align; + } +} +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALalaw) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + + +#define DECL_TEMPLATE(T) \ +static void Convert_##T##_ALmsadpcm(T *dst, const ALmsadpcm *src, \ + ALuint numchans, ALuint len, \ + ALuint align) \ +{ \ + ALsizei byte_align = ((align-2)/2 + 7) * numchans; \ + DECL_VLA(ALshort, tmp, align*numchans); \ + ALuint i, j, k; \ + \ + assert(align > 1 && (len%align) == 0); \ + for(i = 0;i < len;i += align) \ + { \ + DecodeMSADPCMBlock(tmp, src, numchans, align); \ + src += byte_align; \ + \ + for(j = 0;j < align;j++) \ + { \ + for(k = 0;k < numchans;k++) \ + *(dst++) = Conv_##T##_ALshort(tmp[j*numchans + k]); \ + } \ + } \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +static void Convert_ALshort_ALmsadpcm(ALshort *dst, const ALmsadpcm *src, + ALuint numchans, ALuint len, + ALuint align) +{ + ALsizei byte_align = ((align-2)/2 + 7) * numchans; + ALuint i; + + assert(align > 1 && (len%align) == 0); + for(i = 0;i < len;i += align) + { + DecodeMSADPCMBlock(dst, src, numchans, align); + src += byte_align; + dst += align*numchans; + } +} +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALalaw) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T) \ +static void Convert_ALmsadpcm_##T(ALmsadpcm *dst, const T *src, \ + ALuint numchans, ALuint len, ALuint align) \ +{ \ + ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; \ + ALsizei byte_align = ((align-2)/2 + 7) * numchans; \ + DECL_VLA(ALshort, tmp, align*numchans); \ + ALuint i, j, k; \ + \ + assert(align > 1 && (len%align) == 0); \ + for(i = 0;i < len;i += align) \ + { \ + for(j = 0;j < align;j++) \ + { \ + for(k = 0;k < numchans;k++) \ + tmp[j*numchans + k] = Conv_ALshort_##T(*(src++)); \ + } \ + EncodeMSADPCMBlock(dst, tmp, sample, numchans, align); \ + dst += byte_align; \ + } \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +static void Convert_ALmsadpcm_ALshort(ALmsadpcm *dst, const ALshort *src, + ALuint numchans, ALuint len, ALuint align) +{ + ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; + ALsizei byte_align = ((align-2)/2 + 7) * numchans; + ALuint i; + + assert(align > 1 && (len%align) == 0); + for(i = 0;i < len;i += align) + { + EncodeMSADPCMBlock(dst, src, sample, numchans, align); + src += align*numchans; + dst += byte_align; + } +} +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALalaw) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + +/* NOTE: We don't store compressed samples internally, so these conversions + * should never happen. */ +static void Convert_ALima4_ALima4(ALima4* UNUSED(dst), const ALima4* UNUSED(src), + ALuint UNUSED(numchans), ALuint UNUSED(len), + ALuint UNUSED(align)) +{ + ERR("Unexpected IMA4-to-IMA4 conversion!\n"); +} + +static void Convert_ALmsadpcm_ALmsadpcm(ALmsadpcm* UNUSED(dst), const ALmsadpcm* UNUSED(src), + ALuint UNUSED(numchans), ALuint UNUSED(len), + ALuint UNUSED(align)) +{ + ERR("Unexpected MSADPCM-to-MSADPCM conversion!\n"); +} + +static void Convert_ALmsadpcm_ALima4(ALmsadpcm* UNUSED(dst), const ALima4* UNUSED(src), + ALuint UNUSED(numchans), ALuint UNUSED(len), + ALuint UNUSED(align)) +{ + ERR("Unexpected IMA4-to-MSADPCM conversion!\n"); +} + +static void Convert_ALima4_ALmsadpcm(ALima4* UNUSED(dst), const ALmsadpcm* UNUSED(src), + ALuint UNUSED(numchans), ALuint UNUSED(len), + ALuint UNUSED(align)) +{ + ERR("Unexpected MSADPCM-to-IMA4 conversion!\n"); +} + + +#define DECL_TEMPLATE(T) \ +static void Convert_##T(T *dst, const ALvoid *src, enum UserFmtType srcType, \ + ALsizei numchans, ALsizei len, ALsizei align) \ +{ \ + switch(srcType) \ + { \ + case UserFmtByte: \ + Convert_##T##_ALbyte(dst, src, numchans, len, align); \ + break; \ + case UserFmtUByte: \ + Convert_##T##_ALubyte(dst, src, numchans, len, align); \ + break; \ + case UserFmtShort: \ + Convert_##T##_ALshort(dst, src, numchans, len, align); \ + break; \ + case UserFmtUShort: \ + Convert_##T##_ALushort(dst, src, numchans, len, align); \ + break; \ + case UserFmtInt: \ + Convert_##T##_ALint(dst, src, numchans, len, align); \ + break; \ + case UserFmtUInt: \ + Convert_##T##_ALuint(dst, src, numchans, len, align); \ + break; \ + case UserFmtFloat: \ + Convert_##T##_ALfloat(dst, src, numchans, len, align); \ + break; \ + case UserFmtDouble: \ + Convert_##T##_ALdouble(dst, src, numchans, len, align); \ + break; \ + case UserFmtMulaw: \ + Convert_##T##_ALmulaw(dst, src, numchans, len, align); \ + break; \ + case UserFmtAlaw: \ + Convert_##T##_ALalaw(dst, src, numchans, len, align); \ + break; \ + case UserFmtIMA4: \ + Convert_##T##_ALima4(dst, src, numchans, len, align); \ + break; \ + case UserFmtMSADPCM: \ + Convert_##T##_ALmsadpcm(dst, src, numchans, len, align); \ + break; \ + case UserFmtByte3: \ + Convert_##T##_ALbyte3(dst, src, numchans, len, align); \ + break; \ + case UserFmtUByte3: \ + Convert_##T##_ALubyte3(dst, src, numchans, len, align); \ + 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(ALalaw) +DECL_TEMPLATE(ALima4) +DECL_TEMPLATE(ALmsadpcm) +DECL_TEMPLATE(ALbyte3) +DECL_TEMPLATE(ALubyte3) + +#undef DECL_TEMPLATE + + +void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len, ALsizei align) +{ + switch(dstType) + { + case UserFmtByte: + Convert_ALbyte(dst, src, srcType, numchans, len, align); + break; + case UserFmtUByte: + Convert_ALubyte(dst, src, srcType, numchans, len, align); + break; + case UserFmtShort: + Convert_ALshort(dst, src, srcType, numchans, len, align); + break; + case UserFmtUShort: + Convert_ALushort(dst, src, srcType, numchans, len, align); + break; + case UserFmtInt: + Convert_ALint(dst, src, srcType, numchans, len, align); + break; + case UserFmtUInt: + Convert_ALuint(dst, src, srcType, numchans, len, align); + break; + case UserFmtFloat: + Convert_ALfloat(dst, src, srcType, numchans, len, align); + break; + case UserFmtDouble: + Convert_ALdouble(dst, src, srcType, numchans, len, align); + break; + case UserFmtMulaw: + Convert_ALmulaw(dst, src, srcType, numchans, len, align); + break; + case UserFmtAlaw: + Convert_ALalaw(dst, src, srcType, numchans, len, align); + break; + case UserFmtIMA4: + Convert_ALima4(dst, src, srcType, numchans, len, align); + break; + case UserFmtMSADPCM: + Convert_ALmsadpcm(dst, src, srcType, numchans, len, align); + break; + case UserFmtByte3: + Convert_ALbyte3(dst, src, srcType, numchans, len, align); + break; + case UserFmtUByte3: + Convert_ALubyte3(dst, src, srcType, numchans, len, align); + break; + } +} diff --git a/libs/openal-soft/README b/libs/openal-soft/README new file mode 100644 index 00000000..ebca8f91 --- /dev/null +++ b/libs/openal-soft/README @@ -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 --git a/libs/openal-soft/XCompile.txt b/libs/openal-soft/XCompile.txt new file mode 100644 index 00000000..035f1e21 --- /dev/null +++ b/libs/openal-soft/XCompile.txt @@ -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 --git a/libs/openal-soft/alsoftrc.sample b/libs/openal-soft/alsoftrc.sample new file mode 100644 index 00000000..2e6dd92a --- /dev/null +++ b/libs/openal-soft/alsoftrc.sample @@ -0,0 +1,382 @@ +# OpenAL config file. +# +# Option blocks may appear multiple times, and duplicated options will take the +# last value specified. Environment variables may be specified within option +# values, and are automatically substituted when the config file is loaded. +# Environment variable names may only contain alpha-numeric characters (a-z, +# A-Z, 0-9) and underscores (_), and are prefixed with $. For example, +# specifying "$HOME/file.ext" would typically result in something like +# "/home/user/file.ext". To specify an actual "$" character, use "$$". +# +# The system-wide settings can be put in /etc/openal/alsoft.conf and user- +# specific override settings in $HOME/.alsoftrc. +# For Windows, these settings should go into $AppData\alsoft.ini +# +# 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: + +## +## General stuff +## +[general] + +## disable-cpu-exts: +# Disables use of specialized methods that use specific CPU intrinsics. +# Certain methods may utilize CPU extensions for improved performance, and +# this option is useful for preventing some or all of those methods from being +# used. The available extensions are: sse, sse2, sse4.1, and neon. Specifying +# 'all' disables use of all such specialized methods. +#disable-cpu-exts = + +## channels: +# Sets the output channel configuration. If left unspecified, one will try to +# be detected from the system, and defaulting to stereo. The available values +# are: mono, stereo, quad, surround51, surround61, surround71 +#channels = + +## sample-type: +# Sets the output sample type. Currently, all mixing is done with 32-bit float +# and converted to the output sample type as needed. Available values are: +# int8 - signed 8-bit int +# uint8 - unsigned 8-bit int +# int16 - signed 16-bit int +# uint16 - unsigned 16-bit int +# int32 - signed 32-bit int +# uint32 - unsigned 32-bit int +# float32 - 32-bit float +#sample-type = float32 + +## hrtf: +# Enables HRTF filters. These filters provide for better sound spatialization +# while using headphones. The default filter 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 + +## hrtf_tables +# Specifies a comma-separated list of files containing HRTF data sets. The +# format of the files are described in hrtf.txt. The filenames may contain +# these markers, which will be replaced as needed: +# %r - Device sampling rate +# %% - Percent sign (%) +# The listed files are relative to system-dependant data directories. On +# Windows this is: +# $AppData\openal\hrtf +# And on other systems, it's (in order): +# $XDG_DATA_HOME/openal/hrtf (defaults to $HOME/.local/share/openal/hrtf) +# $XDG_DATA_DIRS/openal/hrtf (defaults to /usr/local/share/openal/hrtf and +# /usr/share/openal/hrtf) +# An absolute path may also be specified, if the given file is elsewhere. +#hrtf_tables = default-%r.mhr + +## 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 + +## wide-stereo: +# Specifies that stereo sources are given a width of about 120 degrees on each +# channel, centering on -90 (left) and +90 (right), as opposed to being points +# placed at -30 (left) and +30 (right). This can be useful for surround-sound +# to give stereo sources a more encompassing sound. Note that the sound's +# overall volume will be slightly reduced to account for the extra output. +#wide-stereo = false + +## frequency: +# Sets the output frequency. If left unspecified it will try to detect a +# default from the system, otherwise it will default to 44100. +#frequency = + +## resampler: +# Selects the resampler used when mixing sources. Valid values are: +# point - nearest sample, no interpolation +# linear - extrapolates samples using a linear slope between samples +# cubic - extrapolates samples using a Catmull-Rom spline +# Specifying other values will result in using the default (linear). +#resampler = linear + +## 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. Acceptable values range between 64 and 8192. +#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. Acceptable values +# range between 2 and 16. +#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 + +## 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 (e.g. 'oss,' will try OSS first before +# other backends, while 'oss' will try OSS only). Backends prepended with - +# won't be considered for use (e.g. '-oss,' will try all available backends +# except OSS). An empty list means to try all backends. +#drivers = + +## 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,autowah,chorus, +# compressor,distortion,echo,equalizer,flanger,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_surround51 = fl=-30, fr=30, fc=0, bl=-110, br=110 +#layout_surround61 = fl=-30, fr=30, fc=0, sl=-90, sr=90, bc=180 +#layout_surround71 = fl=-30, fr=30, fc=0, sl=-90, sr=90, bl=-150, br=150 + +## default-reverb: +# A reverb preset that applies by default to all sources on send 0 +# (applications that set their own slots on send 0 will override this). +# Available presets are: None, Generic, PaddedCell, Room, Bathroom, +# Livingroom, Stoneroom, Auditorium, ConcertHall, Cave, Arena, Hangar, +# CarpetedHallway, Hallway, StoneCorridor, Alley, Forest, City, Moutains, +# Quarry, Plain, ParkingLot, SewerPipe, Underwater, Drugged, Dizzy, Psychotic. +#default-reverb = + +## trap-alc-error: +# Generates a SIGTRAP signal when an ALC device error is generated, on systems +# that support it. This helps when debugging, while trying to find the cause +# of a device error. On Windows, a breakpoint exception is generated. +#trap-alc-error = false + +## trap-al-error: +# Generates a SIGTRAP signal when an AL context error is generated, on systems +# that support it. This helps when debugging, while trying to find the cause +# of a context error. On Windows, a breakpoint exception is generated. +#trap-al-error = false + +## +## MIDI stuff (EXPERIMENTAL) +## +[midi] + +## soundfont: +# A default soundfont (sf2 format). Used when an app requests the system +# default. The listed file is relative to system-dependant data directories. +# On Windows this is: +# $AppData\openal\soundfonts +# And on other systems, it's (in order): +# $XDG_DATA_HOME/openal/soundfonts +# $XDG_DATA_DIRS/openal/soundfonts +# An absolute path may also be specified, if the given file is elsewhere. +#soundfont = + +## volume: +# Additional attenuation applied to MIDI output, expressed in decibels. This +# is used to help keep the mix from clipping, and so must be 0 or less. The +# value is logarithmic, so -6 will be about half amplitude, and -12 about +# 1/4th. The default is roughly -13.9794 (0.2, or 1/5th). +#volume = + +## +## 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 + +## +## PulseAudio backend stuff +## +[pulse] + +## spawn-server: +# Attempts to autospawn a PulseAudio server whenever needed (initializing the +# backend, enumerating devices, etc). Setting autospawn to false in Pulse's +# client.conf will still prevent autospawning even if this is set to true. +#spawn-server = true + +## allow-moves: +# Allows PulseAudio to move active streams to different devices. Note that the +# device specifier (seen by applications) will not be updated when this +# occurs, and neither will the AL device configuration (sample rate, format, +# etc). +#allow-moves = 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: + +## device-prefix-*: +# Card- and device-specific prefixes may be used to override the device-prefix +# option. The option may specify the card id (eg, device-prefix-NVidia), or +# the card id and device index (eg, device-prefix-NVidia-0). The card id is +# case-sensitive. +#device-prefix- = + +## 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 id and d is the +# device number for the requested device name. +#capture-prefix = plughw: + +## capture-prefix-*: +# Card- and device-specific prefixes may be used to override the +# capture-prefix option. The option may specify the card id (eg, +# capture-prefix-NVidia), or the card id and device index (eg, +# capture-prefix-NVidia-0). The card id is case-sensitive. +#capture-prefix- = + +## 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 + +## +## QSA backend stuff +## +[qsa] + +## device: +# Sets the device name for the default playback device. +#device = default + +## capture: +# Sets the device name for the default capture device. +#capture = default + +## +## 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 + +## +## 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 --git a/libs/openal-soft/cmake/CheckFileOffsetBits.c b/libs/openal-soft/cmake/CheckFileOffsetBits.c new file mode 100644 index 00000000..de98296e --- /dev/null +++ b/libs/openal-soft/cmake/CheckFileOffsetBits.c @@ -0,0 +1,9 @@ +#include + +#define KB ((off_t)(1024)) +#define MB ((off_t)(KB*1024)) +#define GB ((off_t)(MB*1024)) +int tb[((GB+GB+GB) > GB) ? 1 : -1]; + +int main() +{ return 0; } diff --git a/libs/openal-soft/cmake/CheckFileOffsetBits.cmake b/libs/openal-soft/cmake/CheckFileOffsetBits.cmake new file mode 100644 index 00000000..8b549b69 --- /dev/null +++ b/libs/openal-soft/cmake/CheckFileOffsetBits.cmake @@ -0,0 +1,39 @@ +# - Check if the _FILE_OFFSET_BITS macro is needed for large files +# CHECK_FILE_OFFSET_BITS() +# +# 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 +# Copyright (c) 2009, Chris Robinson +# +# Redistribution and use is allowed according to the terms of the LGPL license. + + +MACRO(CHECK_FILE_OFFSET_BITS) + + IF(NOT DEFINED _FILE_OFFSET_BITS) + MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files") + TRY_COMPILE(__WITHOUT_FILE_OFFSET_BITS_64 + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/cmake/CheckFileOffsetBits.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}) + IF(NOT __WITHOUT_FILE_OFFSET_BITS_64) + TRY_COMPILE(__WITH_FILE_OFFSET_BITS_64 + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/cmake/CheckFileOffsetBits.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_FILE_OFFSET_BITS=64) + ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64) + + IF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) + SET(_FILE_OFFSET_BITS 64 CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files") + MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files - 64") + ELSE(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) + SET(_FILE_OFFSET_BITS "" CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files") + MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files - not needed") + ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) + ENDIF(NOT DEFINED _FILE_OFFSET_BITS) + +ENDMACRO(CHECK_FILE_OFFSET_BITS) \ No newline at end of file diff --git a/libs/openal-soft/cmake/CheckSharedFunctionExists.cmake b/libs/openal-soft/cmake/CheckSharedFunctionExists.cmake new file mode 100644 index 00000000..4980effa --- /dev/null +++ b/libs/openal-soft/cmake/CheckSharedFunctionExists.cmake @@ -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 --git a/libs/openal-soft/cmake/FindALSA.cmake b/libs/openal-soft/cmake/FindALSA.cmake new file mode 100644 index 00000000..519304d6 --- /dev/null +++ b/libs/openal-soft/cmake/FindALSA.cmake @@ -0,0 +1,73 @@ +# - Find alsa +# Find the alsa libraries (asound) +# +# This module defines the following variables: +# ALSA_FOUND - True if ALSA_INCLUDE_DIR & ALSA_LIBRARY are found +# ALSA_LIBRARIES - Set when ALSA_LIBRARY is found +# ALSA_INCLUDE_DIRS - Set when ALSA_INCLUDE_DIR is found +# +# ALSA_INCLUDE_DIR - where to find asoundlib.h, etc. +# ALSA_LIBRARY - the asound library +# ALSA_VERSION_STRING - the version of alsa found (since CMake 2.8.8) +# + +#============================================================================= +# Copyright 2009-2011 Kitware, Inc. +# Copyright 2009-2011 Philip Lowman +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * The names of Kitware, Inc., the Insight Consortium, or the names of +# any consortium members, or of any contributors, may not be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +find_path(ALSA_INCLUDE_DIR NAMES alsa/asoundlib.h + DOC "The ALSA (asound) include directory" +) + +find_library(ALSA_LIBRARY NAMES asound + DOC "The ALSA (asound) library" +) + +if(ALSA_INCLUDE_DIR AND EXISTS "${ALSA_INCLUDE_DIR}/alsa/version.h") + file(STRINGS "${ALSA_INCLUDE_DIR}/alsa/version.h" alsa_version_str REGEX "^#define[\t ]+SND_LIB_VERSION_STR[\t ]+\".*\"") + + string(REGEX REPLACE "^.*SND_LIB_VERSION_STR[\t ]+\"([^\"]*)\".*$" "\\1" ALSA_VERSION_STRING "${alsa_version_str}") + unset(alsa_version_str) +endif() + +# handle the QUIETLY and REQUIRED arguments and set ALSA_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(ALSA + REQUIRED_VARS ALSA_LIBRARY ALSA_INCLUDE_DIR + VERSION_VAR ALSA_VERSION_STRING) + +if(ALSA_FOUND) + set( ALSA_LIBRARIES ${ALSA_LIBRARY} ) + set( ALSA_INCLUDE_DIRS ${ALSA_INCLUDE_DIR} ) +endif() + +mark_as_advanced(ALSA_INCLUDE_DIR ALSA_LIBRARY) diff --git a/libs/openal-soft/cmake/FindAudioIO.cmake b/libs/openal-soft/cmake/FindAudioIO.cmake new file mode 100644 index 00000000..f0f8b2a5 --- /dev/null +++ b/libs/openal-soft/cmake/FindAudioIO.cmake @@ -0,0 +1,21 @@ +# - Find AudioIO includes and libraries +# +# AUDIOIO_FOUND - True if AUDIOIO_INCLUDE_DIR is found +# AUDIOIO_INCLUDE_DIRS - Set when AUDIOIO_INCLUDE_DIR is found +# +# AUDIOIO_INCLUDE_DIR - where to find sys/audioio.h, etc. +# + +find_path(AUDIOIO_INCLUDE_DIR + NAMES sys/audioio.h + DOC "The AudioIO include directory" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(AudioIO REQUIRED_VARS AUDIOIO_INCLUDE_DIR) + +if(AUDIOIO_FOUND) + set(AUDIOIO_INCLUDE_DIRS ${AUDIOIO_INCLUDE_DIR}) +endif() + +mark_as_advanced(AUDIOIO_INCLUDE_DIR) diff --git a/libs/openal-soft/cmake/FindDSound.cmake b/libs/openal-soft/cmake/FindDSound.cmake new file mode 100644 index 00000000..36cdf4b5 --- /dev/null +++ b/libs/openal-soft/cmake/FindDSound.cmake @@ -0,0 +1,33 @@ +# - Find DirectSound includes and libraries +# +# DSOUND_FOUND - True if DSOUND_INCLUDE_DIR & DSOUND_LIBRARY are found +# DSOUND_LIBRARIES - Set when DSOUND_LIBRARY is found +# DSOUND_INCLUDE_DIRS - Set when DSOUND_INCLUDE_DIR is found +# +# DSOUND_INCLUDE_DIR - where to find dsound.h, etc. +# DSOUND_LIBRARY - the dsound library +# + +find_path(DSOUND_INCLUDE_DIR + PATHS "${DXSDK_DIR}/include" + NAMES dsound.h + DOC "The DirectSound include directory" +) + +find_library(DSOUND_LIBRARY + PATHS "${DXSDK_DIR}/lib" + NAMES dsound + DOC "The DirectSound library" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(DSound + REQUIRED_VARS DSOUND_LIBRARY DSOUND_INCLUDE_DIR +) + +if(DSOUND_FOUND) + set(DSOUND_LIBRARIES ${DSOUND_LIBRARY}) + set(DSOUND_INCLUDE_DIRS ${DSOUND_INCLUDE_DIR}) +endif() + +mark_as_advanced(DSOUND_INCLUDE_DIR DSOUND_LIBRARY) diff --git a/libs/openal-soft/cmake/FindFFmpeg.cmake b/libs/openal-soft/cmake/FindFFmpeg.cmake new file mode 100644 index 00000000..96cbb6ed --- /dev/null +++ b/libs/openal-soft/cmake/FindFFmpeg.cmake @@ -0,0 +1,173 @@ +# vim: ts=2 sw=2 +# - Try to find the required ffmpeg components(default: AVFORMAT, AVUTIL, AVCODEC) +# +# Once done this will define +# FFMPEG_FOUND - System has the all required components. +# FFMPEG_INCLUDE_DIRS - Include directory necessary for using the required components headers. +# FFMPEG_LIBRARIES - Link these to use the required ffmpeg components. +# FFMPEG_DEFINITIONS - Compiler switches required for using the required ffmpeg components. +# +# For each of the components it will additionaly set. +# - AVCODEC +# - AVDEVICE +# - AVFORMAT +# - AVUTIL +# - POSTPROC +# - SWSCALE +# - SWRESAMPLE +# the following variables will be defined +# _FOUND - System has +# _INCLUDE_DIRS - Include directory necessary for using the headers +# _LIBRARIES - Link these to use +# _DEFINITIONS - Compiler switches required for using +# _VERSION - The components version +# +# Copyright (c) 2006, Matthias Kretz, +# Copyright (c) 2008, Alexander Neundorf, +# Copyright (c) 2011, Michael Jansen, +# +# Redistribution and use is allowed according to the terms of the BSD license. + +include(FindPackageHandleStandardArgs) + +if(NOT FFmpeg_FIND_COMPONENTS) + set(FFmpeg_FIND_COMPONENTS AVFORMAT AVCODEC AVUTIL) +endif() + +# +### Macro: set_component_found +# +# Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present. +# +macro(set_component_found _component) + if(${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS) + # message(STATUS " - ${_component} found.") + set(${_component}_FOUND TRUE) + else() + # message(STATUS " - ${_component} not found.") + endif() +endmacro() + +# +### Macro: find_component +# +# Checks for the given component by invoking pkgconfig and then looking up the libraries and +# include directories. +# +macro(find_component _component _pkgconfig _library _header) + if(NOT WIN32) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_${_component} ${_pkgconfig}) + endif() + endif() + + find_path(${_component}_INCLUDE_DIRS ${_header} + HINTS + ${FFMPEGSDK_INC} + ${PC_LIB${_component}_INCLUDEDIR} + ${PC_LIB${_component}_INCLUDE_DIRS} + PATH_SUFFIXES + ffmpeg + ) + + find_library(${_component}_LIBRARIES NAMES ${_library} + HINTS + ${FFMPEGSDK_LIB} + ${PC_LIB${_component}_LIBDIR} + ${PC_LIB${_component}_LIBRARY_DIRS} + ) + + STRING(REGEX REPLACE "/.*" "/version.h" _ver_header ${_header}) + if(EXISTS "${${_component}_INCLUDE_DIRS}/${_ver_header}") + file(STRINGS "${${_component}_INCLUDE_DIRS}/${_ver_header}" version_str REGEX "^#define[\t ]+LIB${_component}_VERSION_M.*") + + foreach(_str "${version_str}") + if(NOT version_maj) + string(REGEX REPLACE "^.*LIB${_component}_VERSION_MAJOR[\t ]+([0-9]*).*$" "\\1" version_maj "${_str}") + endif() + if(NOT version_min) + string(REGEX REPLACE "^.*LIB${_component}_VERSION_MINOR[\t ]+([0-9]*).*$" "\\1" version_min "${_str}") + endif() + if(NOT version_mic) + string(REGEX REPLACE "^.*LIB${_component}_VERSION_MICRO[\t ]+([0-9]*).*$" "\\1" version_mic "${_str}") + endif() + endforeach() + unset(version_str) + + set(${_component}_VERSION "${version_maj}.${version_min}.${version_mic}" CACHE STRING "The ${_component} version number.") + unset(version_maj) + unset(version_min) + unset(version_mic) + endif(EXISTS "${${_component}_INCLUDE_DIRS}/${_ver_header}") + set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.") + set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.") + + set_component_found(${_component}) + + mark_as_advanced( + ${_component}_INCLUDE_DIRS + ${_component}_LIBRARIES + ${_component}_DEFINITIONS + ${_component}_VERSION) +endmacro() + + +set(FFMPEGSDK $ENV{FFMPEG_HOME}) +if(FFMPEGSDK) + set(FFMPEGSDK_INC "${FFMPEGSDK}/include") + set(FFMPEGSDK_LIB "${FFMPEGSDK}/lib") +endif() + +# Check for all possible components. +find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h) +find_component(AVFORMAT libavformat avformat libavformat/avformat.h) +find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h) +find_component(AVUTIL libavutil avutil libavutil/avutil.h) +find_component(SWSCALE libswscale swscale libswscale/swscale.h) +find_component(SWRESAMPLE libswresample swresample libswresample/swresample.h) +find_component(POSTPROC libpostproc postproc libpostproc/postprocess.h) + +# Check if the required components were found and add their stuff to the FFMPEG_* vars. +foreach(_component ${FFmpeg_FIND_COMPONENTS}) + if(${_component}_FOUND) + # message(STATUS "Required component ${_component} present.") + set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARIES}) + set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS}) + list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS}) + else() + # message(STATUS "Required component ${_component} missing.") + endif() +endforeach() + +# Build the include path and library list with duplicates removed. +if(FFMPEG_INCLUDE_DIRS) + list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) +endif() + +if(FFMPEG_LIBRARIES) + list(REMOVE_DUPLICATES FFMPEG_LIBRARIES) +endif() + +# cache the vars. +set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE) +set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE) +set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE) + +mark_as_advanced(FFMPEG_INCLUDE_DIRS FFMPEG_LIBRARIES FFMPEG_DEFINITIONS) + +# Now set the noncached _FOUND vars for the components. +foreach(_component AVCODEC AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWRESAMPLE SWSCALE) + set_component_found(${_component}) +endforeach () + +# Compile the list of required vars +set(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS) +foreach(_component ${FFmpeg_FIND_COMPONENTS}) + list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS) +endforeach() + +# Give a nice error message if some of the required vars are missing. +find_package_handle_standard_args(FFmpeg DEFAULT_MSG ${_FFmpeg_REQUIRED_VARS}) diff --git a/libs/openal-soft/cmake/FindFluidSynth.cmake b/libs/openal-soft/cmake/FindFluidSynth.cmake new file mode 100644 index 00000000..fe96b225 --- /dev/null +++ b/libs/openal-soft/cmake/FindFluidSynth.cmake @@ -0,0 +1,19 @@ +# - Find fluidsynth +# Find the native fluidsynth includes and library +# +# FLUIDSYNTH_INCLUDE_DIR - where to find fluidsynth.h +# FLUIDSYNTH_LIBRARIES - List of libraries when using fluidsynth. +# FLUIDSYNTH_FOUND - True if fluidsynth found. + + +FIND_PATH(FLUIDSYNTH_INCLUDE_DIR fluidsynth.h) + +FIND_LIBRARY(FLUIDSYNTH_LIBRARIES NAMES fluidsynth ) +MARK_AS_ADVANCED( FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR ) + +# handle the QUIETLY and REQUIRED arguments and set FLUIDSYNTH_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(FluidSynth + REQUIRED_VARS FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR) + diff --git a/libs/openal-soft/cmake/FindOSS.cmake b/libs/openal-soft/cmake/FindOSS.cmake new file mode 100644 index 00000000..88ee66ad --- /dev/null +++ b/libs/openal-soft/cmake/FindOSS.cmake @@ -0,0 +1,21 @@ +# - Find OSS includes +# +# OSS_FOUND - True if OSS_INCLUDE_DIR is found +# OSS_INCLUDE_DIRS - Set when OSS_INCLUDE_DIR is found +# +# OSS_INCLUDE_DIR - where to find sys/soundcard.h, etc. +# + +find_path(OSS_INCLUDE_DIR + NAMES sys/soundcard.h + DOC "The OSS include directory" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(OSS REQUIRED_VARS OSS_INCLUDE_DIR) + +if(OSS_FOUND) + set(OSS_INCLUDE_DIRS ${OSS_INCLUDE_DIR}) +endif() + +mark_as_advanced(OSS_INCLUDE_DIR) diff --git a/libs/openal-soft/cmake/FindPortAudio.cmake b/libs/openal-soft/cmake/FindPortAudio.cmake new file mode 100644 index 00000000..fad2313d --- /dev/null +++ b/libs/openal-soft/cmake/FindPortAudio.cmake @@ -0,0 +1,32 @@ +# - Find PortAudio includes and libraries +# +# PORTAUDIO_FOUND - True if PORTAUDIO_INCLUDE_DIR & PORTAUDIO_LIBRARY +# are found +# PORTAUDIO_LIBRARIES - Set when PORTAUDIO_LIBRARY is found +# PORTAUDIO_INCLUDE_DIRS - Set when PORTAUDIO_INCLUDE_DIR is found +# +# PORTAUDIO_INCLUDE_DIR - where to find portaudio.h, etc. +# PORTAUDIO_LIBRARY - the portaudio library +# + +find_path(PORTAUDIO_INCLUDE_DIR + NAMES portaudio.h + DOC "The PortAudio include directory" +) + +find_library(PORTAUDIO_LIBRARY + NAMES portaudio + DOC "The PortAudio library" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PortAudio + REQUIRED_VARS PORTAUDIO_LIBRARY PORTAUDIO_INCLUDE_DIR +) + +if(PORTAUDIO_FOUND) + set(PORTAUDIO_LIBRARIES ${PORTAUDIO_LIBRARY}) + set(PORTAUDIO_INCLUDE_DIRS ${PORTAUDIO_INCLUDE_DIR}) +endif() + +mark_as_advanced(PORTAUDIO_INCLUDE_DIR PORTAUDIO_LIBRARY) diff --git a/libs/openal-soft/cmake/FindPulseAudio.cmake b/libs/openal-soft/cmake/FindPulseAudio.cmake new file mode 100644 index 00000000..1f6f843a --- /dev/null +++ b/libs/openal-soft/cmake/FindPulseAudio.cmake @@ -0,0 +1,43 @@ +# - Find PulseAudio includes and libraries +# +# PULSEAUDIO_FOUND - True if PULSEAUDIO_INCLUDE_DIR & +# PULSEAUDIO_LIBRARY are found +# PULSEAUDIO_LIBRARIES - Set when PULSEAUDIO_LIBRARY is found +# PULSEAUDIO_INCLUDE_DIRS - Set when PULSEAUDIO_INCLUDE_DIR is found +# +# PULSEAUDIO_INCLUDE_DIR - where to find pulse/pulseaudio.h, etc. +# PULSEAUDIO_LIBRARY - the pulse library +# PULSEAUDIO_VERSION_STRING - the version of PulseAudio found +# + +find_path(PULSEAUDIO_INCLUDE_DIR + NAMES pulse/pulseaudio.h + DOC "The PulseAudio include directory" +) + +find_library(PULSEAUDIO_LIBRARY + NAMES pulse + DOC "The PulseAudio library" +) + +if(PULSEAUDIO_INCLUDE_DIR AND EXISTS "${PULSEAUDIO_INCLUDE_DIR}/pulse/version.h") + file(STRINGS "${PULSEAUDIO_INCLUDE_DIR}/pulse/version.h" pulse_version_str + REGEX "^#define[\t ]+pa_get_headers_version\\(\\)[\t ]+\\(\".*\"\\)") + + string(REGEX REPLACE "^.*pa_get_headers_version\\(\\)[\t ]+\\(\"([^\"]*)\"\\).*$" "\\1" + PULSEAUDIO_VERSION_STRING "${pulse_version_str}") + unset(pulse_version_str) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PulseAudio + REQUIRED_VARS PULSEAUDIO_LIBRARY PULSEAUDIO_INCLUDE_DIR + VERSION_VAR PULSEAUDIO_VERSION_STRING +) + +if(PULSEAUDIO_FOUND) + set(PULSEAUDIO_LIBRARIES ${PULSEAUDIO_LIBRARY}) + set(PULSEAUDIO_INCLUDE_DIRS ${PULSEAUDIO_INCLUDE_DIR}) +endif() + +mark_as_advanced(PULSEAUDIO_INCLUDE_DIR PULSEAUDIO_LIBRARY) diff --git a/libs/openal-soft/cmake/FindQSA.cmake b/libs/openal-soft/cmake/FindQSA.cmake new file mode 100644 index 00000000..0ad1fd43 --- /dev/null +++ b/libs/openal-soft/cmake/FindQSA.cmake @@ -0,0 +1,34 @@ +# - Find QSA includes and libraries +# +# QSA_FOUND - True if QSA_INCLUDE_DIR & QSA_LIBRARY are found +# QSA_LIBRARIES - Set when QSA_LIBRARY is found +# QSA_INCLUDE_DIRS - Set when QSA_INCLUDE_DIR is found +# +# QSA_INCLUDE_DIR - where to find sys/asoundlib.h, etc. +# QSA_LIBRARY - the asound library +# + +# Only check for QSA on QNX, because it conflicts with ALSA. +if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX") + find_path(QSA_INCLUDE_DIR + NAMES sys/asoundlib.h + DOC "The QSA include directory" + ) + + find_library(QSA_LIBRARY + NAMES asound + DOC "The QSA library" + ) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(QSA + REQUIRED_VARS QSA_LIBRARY QSA_INCLUDE_DIR +) + +if(QSA_FOUND) + set(QSA_LIBRARIES ${QSA_LIBRARY}) + set(QSA_INCLUDE_DIRS ${QSA_INCLUDE_DIR}) +endif() + +mark_as_advanced(QSA_INCLUDE_DIR QSA_LIBRARY) diff --git a/libs/openal-soft/cmake/FindSDL2.cmake b/libs/openal-soft/cmake/FindSDL2.cmake new file mode 100644 index 00000000..70e607a8 --- /dev/null +++ b/libs/openal-soft/cmake/FindSDL2.cmake @@ -0,0 +1,193 @@ +# Locate SDL2 library +# This module defines +# SDL2_LIBRARY, the name of the library to link against +# SDL2_FOUND, if false, do not try to link to SDL2 +# SDL2_INCLUDE_DIR, where to find SDL.h +# +# This module responds to the the flag: +# SDL2_BUILDING_LIBRARY +# If this is defined, then no SDL2_main will be linked in because +# only applications need main(). +# Otherwise, it is assumed you are building an application and this +# module will attempt to locate and set the the proper link flags +# as part of the returned SDL2_LIBRARY variable. +# +# Don't forget to include SDL2main.h and SDL2main.m your project for the +# OS X framework based version. (Other versions link to -lSDL2main which +# this module will try to find on your behalf.) Also for OS X, this +# module will automatically add the -framework Cocoa on your behalf. +# +# +# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration +# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library +# (SDL2.dll, libsdl2.so, SDL2.framework, etc). +# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. +# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value +# as appropriate. These values are used to generate the final SDL2_LIBRARY +# variable, but when these values are unset, SDL2_LIBRARY does not get created. +# +# +# $SDL2DIR is an environment variable that would +# correspond to the ./configure --prefix=$SDL2DIR +# used in building SDL2. +# l.e.galup 9-20-02 +# +# Modified by Eric Wing. +# Added code to assist with automated building by using environmental variables +# and providing a more controlled/consistent search behavior. +# Added new modifications to recognize OS X frameworks and +# additional Unix paths (FreeBSD, etc). +# Also corrected the header search path to follow "proper" SDL2 guidelines. +# Added a search for SDL2main which is needed by some platforms. +# Added a search for threads which is needed by some platforms. +# Added needed compile switches for MinGW. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of +# SDL2_LIBRARY to override this selection or set the CMake environment +# CMAKE_INCLUDE_PATH to modify the search paths. +# +# Note that the header path has changed from SDL2/SDL.h to just SDL.h +# This needed to change because "proper" SDL2 convention +# is #include "SDL.h", not . This is done for portability +# reasons because not all systems place things in SDL2/ (see FreeBSD). +# +# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake +# module with the minor edit of changing "SDL" to "SDL2" where necessary. This +# was not created for redistribution, and exists temporarily pending official +# SDL2 CMake modules. + +#============================================================================= +# Copyright 2003-2009 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.) + + +FIND_PATH(SDL2_INCLUDE_DIR SDL.h + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES include/SDL2 include + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include/SDL2 + /usr/include/SDL2 + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) +#MESSAGE("SDL2_INCLUDE_DIR is ${SDL2_INCLUDE_DIR}") + +FIND_LIBRARY(SDL2_LIBRARY_TEMP + NAMES SDL2 + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS + /sw + /opt/local + /opt/csw + /opt +) + +#MESSAGE("SDL2_LIBRARY_TEMP is ${SDL2_LIBRARY_TEMP}") + +IF(NOT SDL2_BUILDING_LIBRARY) + IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDL2main for compatibility even though they don't + # necessarily need it. + FIND_LIBRARY(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) + ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") +ENDIF(NOT SDL2_BUILDING_LIBRARY) + +# SDL2 may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + +# MinGW needs an additional library, mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows +# (Actually on second look, I think it only needs one of the m* libraries.) +IF(MINGW) + SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") +ENDIF(MINGW) + +SET(SDL2_FOUND "NO") +IF(SDL2_LIBRARY_TEMP) + # For SDL2main + IF(NOT SDL2_BUILDING_LIBRARY) + IF(SDL2MAIN_LIBRARY) + SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(SDL2MAIN_LIBRARY) + ENDIF(NOT SDL2_BUILDING_LIBRARY) + + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + IF(APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") + ENDIF(APPLE) + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + IF(NOT APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(NOT APPLE) + + # For MinGW library + IF(MINGW) + SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(MINGW) + + IF(WIN32) + SET(SDL2_LIBRARY_TEMP winmm imm32 version msimg32 ${SDL2_LIBRARY_TEMP}) + ENDIF(WIN32) + + # Set the final string here so the GUI reflects the final state. + SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") + # Set the temp variable to INTERNAL so it is not seen in the CMake GUI + SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") + + SET(SDL2_FOUND "YES") +ENDIF(SDL2_LIBRARY_TEMP) + +INCLUDE(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 + REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) + +IF(SDL2_STATIC) + if (UNIX AND NOT APPLE) + EXECUTE_PROCESS(COMMAND sdl2-config --static-libs OUTPUT_VARIABLE SDL2_LINK_FLAGS) + STRING(REGEX REPLACE "(\r?\n)+$" "" SDL2_LINK_FLAGS "${SDL2_LINK_FLAGS}") + SET(SDL2_LIBRARY ${SDL2_LINK_FLAGS}) + ENDIF() +ENDIF(SDL2_STATIC) diff --git a/libs/openal-soft/cmake/FindSDL_sound.cmake b/libs/openal-soft/cmake/FindSDL_sound.cmake new file mode 100644 index 00000000..2dab1a1c --- /dev/null +++ b/libs/openal-soft/cmake/FindSDL_sound.cmake @@ -0,0 +1,380 @@ +# - Locates the SDL_sound library +# +# This module depends on SDL being found and +# must be called AFTER FindSDL.cmake or FindSDL2.cmake is called. +# +# This module defines +# SDL_SOUND_INCLUDE_DIR, where to find SDL_sound.h +# SDL_SOUND_FOUND, if false, do not try to link to SDL_sound +# SDL_SOUND_LIBRARIES, this contains the list of libraries that you need +# to link against. This is a read-only variable and is marked INTERNAL. +# SDL_SOUND_EXTRAS, this is an optional variable for you to add your own +# flags to SDL_SOUND_LIBRARIES. This is prepended to SDL_SOUND_LIBRARIES. +# This is available mostly for cases this module failed to anticipate for +# and you must add additional flags. This is marked as ADVANCED. +# SDL_SOUND_VERSION_STRING, human-readable string containing the version of SDL_sound +# +# This module also defines (but you shouldn't need to use directly) +# SDL_SOUND_LIBRARY, the name of just the SDL_sound library you would link +# against. Use SDL_SOUND_LIBRARIES for you link instructions and not this one. +# And might define the following as needed +# MIKMOD_LIBRARY +# MODPLUG_LIBRARY +# OGG_LIBRARY +# VORBIS_LIBRARY +# SMPEG_LIBRARY +# FLAC_LIBRARY +# SPEEX_LIBRARY +# +# Typically, you should not use these variables directly, and you should use +# SDL_SOUND_LIBRARIES which contains SDL_SOUND_LIBRARY and the other audio libraries +# (if needed) to successfully compile on your system. +# +# Created by Eric Wing. +# This module is a bit more complicated than the other FindSDL* family modules. +# The reason is that SDL_sound can be compiled in a large variety of different ways +# which are independent of platform. SDL_sound may dynamically link against other 3rd +# party libraries to get additional codec support, such as Ogg Vorbis, SMPEG, ModPlug, +# MikMod, FLAC, Speex, and potentially others. +# Under some circumstances which I don't fully understand, +# there seems to be a requirement +# that dependent libraries of libraries you use must also be explicitly +# linked against in order to successfully compile. SDL_sound does not currently +# have any system in place to know how it was compiled. +# So this CMake module does the hard work in trying to discover which 3rd party +# libraries are required for building (if any). +# This module uses a brute force approach to create a test program that uses SDL_sound, +# and then tries to build it. If the build fails, it parses the error output for +# known symbol names to figure out which libraries are needed. +# +# Responds to the $SDLDIR and $SDLSOUNDDIR environmental variable that would +# correspond to the ./configure --prefix=$SDLDIR used in building SDL. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of +# SDL_LIBRARY or SDL2_LIBRARY to override this selection or set the CMake +# environment CMAKE_INCLUDE_PATH to modify the search paths. + +#============================================================================= +# Copyright 2005-2009 Kitware, Inc. +# Copyright 2012 Benjamin Eikel +# +# 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.) + +set(SDL_SOUND_EXTRAS "" CACHE STRING "SDL_sound extra flags") +mark_as_advanced(SDL_SOUND_EXTRAS) + +# Find SDL_sound.h +find_path(SDL_SOUND_INCLUDE_DIR SDL_sound.h + HINTS + ENV SDLSOUNDDIR + ENV SDLDIR + PATH_SUFFIXES SDL SDL12 SDL11 +) + +find_library(SDL_SOUND_LIBRARY + NAMES SDL_sound + HINTS + ENV SDLSOUNDDIR + ENV SDLDIR +) + +if(SDL2_FOUND OR SDL_FOUND) + if(SDL_SOUND_INCLUDE_DIR AND SDL_SOUND_LIBRARY) + # CMake is giving me problems using TRY_COMPILE with the CMAKE_FLAGS + # for the :STRING syntax if I have multiple values contained in a + # single variable. This is a problem for the SDL2_LIBRARY variable + # because it does just that. When I feed this variable to the command, + # only the first value gets the appropriate modifier (e.g. -I) and + # the rest get dropped. + # To get multiple single variables to work, I must separate them with a "\;" + # I could go back and modify the FindSDL2.cmake module, but that's kind of painful. + # The solution would be to try something like: + # set(SDL2_TRY_COMPILE_LIBRARY_LIST "${SDL2_TRY_COMPILE_LIBRARY_LIST}\;${CMAKE_THREAD_LIBS_INIT}") + # Instead, it was suggested on the mailing list to write a temporary CMakeLists.txt + # with a temporary test project and invoke that with TRY_COMPILE. + # See message thread "Figuring out dependencies for a library in order to build" + # 2005-07-16 + # try_compile( + # MY_RESULT + # ${CMAKE_BINARY_DIR} + # ${PROJECT_SOURCE_DIR}/DetermineSoundLibs.c + # CMAKE_FLAGS + # -DINCLUDE_DIRECTORIES:STRING=${SDL2_INCLUDE_DIR}\;${SDL_SOUND_INCLUDE_DIR} + # -DLINK_LIBRARIES:STRING=${SDL_SOUND_LIBRARY}\;${SDL2_LIBRARY} + # OUTPUT_VARIABLE MY_OUTPUT + # ) + + # To minimize external dependencies, create a sdlsound test program + # which will be used to figure out if additional link dependencies are + # required for the link phase. + file(WRITE ${PROJECT_BINARY_DIR}/CMakeTmp/DetermineSoundLibs.c + "#include \"SDL_sound.h\" + #include \"SDL.h\" + int main(int argc, char* argv[]) + { + Sound_AudioInfo desired; + Sound_Sample* sample; + + SDL_Init(0); + Sound_Init(); + + /* This doesn't actually have to work, but Init() is a no-op + * for some of the decoders, so this should force more symbols + * to be pulled in. + */ + sample = Sound_NewSampleFromFile(argv[1], &desired, 4096); + + Sound_Quit(); + SDL_Quit(); + return 0; + }" + ) + + # Calling + # target_link_libraries(DetermineSoundLibs "${SDL_SOUND_LIBRARY} ${SDL2_LIBRARY}) + # causes problems when SDL2_LIBRARY looks like + # /Library/Frameworks/SDL2.framework;-framework Cocoa + # The ;-framework Cocoa seems to be confusing CMake once the OS X + # framework support was added. I was told that breaking up the list + # would fix the problem. + set(TMP_TRY_LIBS) + if(SDL2_FOUND) + foreach(lib ${SDL_SOUND_LIBRARY} ${SDL2_LIBRARY}) + set(TMP_TRY_LIBS "${TMP_TRY_LIBS} \"${lib}\"") + endforeach() + set(TMP_INCLUDE_DIRS ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + else() + foreach(lib ${SDL_SOUND_LIBRARY} ${SDL_LIBRARY}) + set(TMP_TRY_LIBS "${TMP_TRY_LIBS} \"${lib}\"") + endforeach() + set(TMP_INCLUDE_DIRS ${SDL_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + endif() + + # message("TMP_TRY_LIBS ${TMP_TRY_LIBS}") + + # Write the CMakeLists.txt and test project + # Weird, this is still sketchy. If I don't quote the variables + # in the TARGET_LINK_LIBRARIES, I seem to loose everything + # in the SDL2_LIBRARY string after the "-framework". + # But if I quote the stuff in INCLUDE_DIRECTORIES, it doesn't work. + file(WRITE ${PROJECT_BINARY_DIR}/CMakeTmp/CMakeLists.txt + "cmake_minimum_required(VERSION 2.8) + project(DetermineSoundLibs C) + include_directories(${TMP_INCLUDE_DIRS}) + add_executable(DetermineSoundLibs DetermineSoundLibs.c) + target_link_libraries(DetermineSoundLibs ${TMP_TRY_LIBS})" + ) + unset(TMP_INCLUDE_DIRS) + unset(TMP_TRY_LIBS) + + try_compile( + MY_RESULT + ${PROJECT_BINARY_DIR}/CMakeTmp + ${PROJECT_BINARY_DIR}/CMakeTmp + DetermineSoundLibs + OUTPUT_VARIABLE MY_OUTPUT + ) + # message("${MY_RESULT}") + # message(${MY_OUTPUT}) + + if(NOT MY_RESULT) + # I expect that MPGLIB, VOC, WAV, AIFF, and SHN are compiled in statically. + # I think Timidity is also compiled in statically. + # I've never had to explcitly link against Quicktime, so I'll skip that for now. + + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARY}) + + # Find MikMod + if("${MY_OUTPUT}" MATCHES "MikMod_") + find_library(MIKMOD_LIBRARY + NAMES libmikmod-coreaudio mikmod + PATHS + ENV MIKMODDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(MIKMOD_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MIKMOD_LIBRARY}) + endif(MIKMOD_LIBRARY) + endif("${MY_OUTPUT}" MATCHES "MikMod_") + + # Find ModPlug + if("${MY_OUTPUT}" MATCHES "MODPLUG_") + find_library(MODPLUG_LIBRARY + NAMES modplug + PATHS + ENV MODPLUGDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(MODPLUG_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MODPLUG_LIBRARY}) + endif() + endif() + + # Find Ogg and Vorbis + if("${MY_OUTPUT}" MATCHES "ov_") + find_library(VORBIS_LIBRARY + NAMES vorbis Vorbis VORBIS + PATHS + ENV VORBISDIR + ENV OGGDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(VORBIS_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${VORBIS_LIBRARY}) + endif() + find_library(OGG_LIBRARY + NAMES ogg Ogg OGG + PATHS + ENV OGGDIR + ENV VORBISDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(OGG_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${OGG_LIBRARY}) + endif() + endif() + + # Find SMPEG + if("${MY_OUTPUT}" MATCHES "SMPEG_") + find_library(SMPEG_LIBRARY + NAMES smpeg SMPEG Smpeg SMpeg + PATHS + ENV SMPEGDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(SMPEG_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${SMPEG_LIBRARY}) + endif() + endif() + + + # Find FLAC + if("${MY_OUTPUT}" MATCHES "FLAC_") + find_library(FLAC_LIBRARY + NAMES flac FLAC + PATHS + ENV FLACDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(FLAC_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${FLAC_LIBRARY}) + endif() + endif() + + + # Hmmm...Speex seems to depend on Ogg. This might be a problem if + # the TRY_COMPILE attempt gets blocked at SPEEX before it can pull + # in the Ogg symbols. I'm not sure if I should duplicate the ogg stuff + # above for here or if two ogg entries will screw up things. + if("${MY_OUTPUT}" MATCHES "speex_") + find_library(SPEEX_LIBRARY + NAMES speex SPEEX + PATHS + ENV SPEEXDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(SPEEX_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${SPEEX_LIBRARY}) + endif() + + # Find OGG (needed for Speex) + # We might have already found Ogg for Vorbis, so skip it if so. + if(NOT OGG_LIBRARY) + find_library(OGG_LIBRARY + NAMES ogg Ogg OGG + PATHS + ENV OGGDIR + ENV VORBISDIR + ENV SPEEXDIR + ENV SDLSOUNDDIR + ENV SDLDIR + /sw + /opt/local + /opt/csw + /opt + PATH_SUFFIXES lib + ) + if(OGG_LIBRARY) + set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${OGG_LIBRARY}) + endif() + endif() + endif() + + set(SDL_SOUND_LIBRARIES ${SDL_SOUND_EXTRAS} ${SDL_SOUND_LIBRARIES_TMP} CACHE INTERNAL "SDL_sound and dependent libraries") + else() + set(SDL_SOUND_LIBRARIES ${SDL_SOUND_EXTRAS} ${SDL_SOUND_LIBRARY} CACHE INTERNAL "SDL_sound and dependent libraries") + endif() + endif() +endif() + +if(SDL_SOUND_INCLUDE_DIR AND EXISTS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h") + file(STRINGS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h" SDL_SOUND_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SOUND_VER_MAJOR[ \t]+[0-9]+$") + file(STRINGS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h" SDL_SOUND_VERSION_MINOR_LINE REGEX "^#define[ \t]+SOUND_VER_MINOR[ \t]+[0-9]+$") + file(STRINGS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h" SDL_SOUND_VERSION_PATCH_LINE REGEX "^#define[ \t]+SOUND_VER_PATCH[ \t]+[0-9]+$") + string(REGEX REPLACE "^#define[ \t]+SOUND_VER_MAJOR[ \t]+([0-9]+)$" "\\1" SDL_SOUND_VERSION_MAJOR "${SDL_SOUND_VERSION_MAJOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SOUND_VER_MINOR[ \t]+([0-9]+)$" "\\1" SDL_SOUND_VERSION_MINOR "${SDL_SOUND_VERSION_MINOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SOUND_VER_PATCH[ \t]+([0-9]+)$" "\\1" SDL_SOUND_VERSION_PATCH "${SDL_SOUND_VERSION_PATCH_LINE}") + set(SDL_SOUND_VERSION_STRING ${SDL_SOUND_VERSION_MAJOR}.${SDL_SOUND_VERSION_MINOR}.${SDL_SOUND_VERSION_PATCH}) + unset(SDL_SOUND_VERSION_MAJOR_LINE) + unset(SDL_SOUND_VERSION_MINOR_LINE) + unset(SDL_SOUND_VERSION_PATCH_LINE) + unset(SDL_SOUND_VERSION_MAJOR) + unset(SDL_SOUND_VERSION_MINOR) + unset(SDL_SOUND_VERSION_PATCH) +endif() + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL_sound + REQUIRED_VARS SDL_SOUND_LIBRARIES SDL_SOUND_INCLUDE_DIR + VERSION_VAR SDL_SOUND_VERSION_STRING) diff --git a/libs/openal-soft/cmake/FindSoundIO.cmake b/libs/openal-soft/cmake/FindSoundIO.cmake new file mode 100644 index 00000000..10450254 --- /dev/null +++ b/libs/openal-soft/cmake/FindSoundIO.cmake @@ -0,0 +1,32 @@ +# - Find SoundIO (sndio) includes and libraries +# +# SOUNDIO_FOUND - True if SOUNDIO_INCLUDE_DIR & SOUNDIO_LIBRARY are +# found +# SOUNDIO_LIBRARIES - Set when SOUNDIO_LIBRARY is found +# SOUNDIO_INCLUDE_DIRS - Set when SOUNDIO_INCLUDE_DIR is found +# +# SOUNDIO_INCLUDE_DIR - where to find sndio.h, etc. +# SOUNDIO_LIBRARY - the sndio library +# + +find_path(SOUNDIO_INCLUDE_DIR + NAMES sndio.h + DOC "The SoundIO include directory" +) + +find_library(SOUNDIO_LIBRARY + NAMES sndio + DOC "The SoundIO library" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SoundIO + REQUIRED_VARS SOUNDIO_LIBRARY SOUNDIO_INCLUDE_DIR +) + +if(SOUNDIO_FOUND) + set(SOUNDIO_LIBRARIES ${SOUNDIO_LIBRARY}) + set(SOUNDIO_INCLUDE_DIRS ${SOUNDIO_INCLUDE_DIR}) +endif() + +mark_as_advanced(SOUNDIO_INCLUDE_DIR SOUNDIO_LIBRARY) diff --git a/libs/openal-soft/common/atomic.c b/libs/openal-soft/common/atomic.c new file mode 100644 index 00000000..3cdb77f4 --- /dev/null +++ b/libs/openal-soft/common/atomic.c @@ -0,0 +1,13 @@ + +#include "config.h" + +#include "atomic.h" + + +extern inline void InitRef(RefCount *ptr, uint value); +extern inline uint ReadRef(RefCount *ptr); +extern inline uint IncrementRef(RefCount *ptr); +extern inline uint DecrementRef(RefCount *ptr); + +extern inline int ExchangeInt(volatile int *ptr, int newval); +extern inline void *ExchangePtr(XchgPtr *ptr, void *newval); diff --git a/libs/openal-soft/common/rwlock.c b/libs/openal-soft/common/rwlock.c new file mode 100644 index 00000000..cfa3aee4 --- /dev/null +++ b/libs/openal-soft/common/rwlock.c @@ -0,0 +1,57 @@ + +#include "config.h" + +#include "rwlock.h" + +#include "bool.h" +#include "atomic.h" +#include "threads.h" + + +/* A simple spinlock. Yield the thread while the given integer is set by + * another. Could probably be improved... */ +#define LOCK(l) do { \ + while(ATOMIC_EXCHANGE(int, &(l), true) == true) \ + althrd_yield(); \ +} while(0) +#define UNLOCK(l) ATOMIC_STORE(&(l), false) + + +void RWLockInit(RWLock *lock) +{ + InitRef(&lock->read_count, 0); + InitRef(&lock->write_count, 0); + ATOMIC_INIT(&lock->read_lock, false); + ATOMIC_INIT(&lock->read_entry_lock, false); + ATOMIC_INIT(&lock->write_lock, false); +} + +void ReadLock(RWLock *lock) +{ + LOCK(lock->read_entry_lock); + LOCK(lock->read_lock); + if(IncrementRef(&lock->read_count) == 1) + LOCK(lock->write_lock); + UNLOCK(lock->read_lock); + UNLOCK(lock->read_entry_lock); +} + +void ReadUnlock(RWLock *lock) +{ + if(DecrementRef(&lock->read_count) == 0) + UNLOCK(lock->write_lock); +} + +void WriteLock(RWLock *lock) +{ + if(IncrementRef(&lock->write_count) == 1) + LOCK(lock->read_lock); + LOCK(lock->write_lock); +} + +void WriteUnlock(RWLock *lock) +{ + UNLOCK(lock->write_lock); + if(DecrementRef(&lock->write_count) == 0) + UNLOCK(lock->read_lock); +} diff --git a/libs/openal-soft/common/threads.c b/libs/openal-soft/common/threads.c new file mode 100644 index 00000000..cd9f6755 --- /dev/null +++ b/libs/openal-soft/common/threads.c @@ -0,0 +1,744 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include "threads.h" + +#include +#include +#include + +#include "uintmap.h" + + +extern inline althrd_t althrd_current(void); +extern inline int althrd_equal(althrd_t thr0, althrd_t thr1); +extern inline void althrd_exit(int res); +extern inline void althrd_yield(void); + +extern inline int almtx_lock(almtx_t *mtx); +extern inline int almtx_unlock(almtx_t *mtx); +extern inline int almtx_trylock(almtx_t *mtx); + +extern inline void *altss_get(altss_t tss_id); +extern inline int altss_set(altss_t tss_id, void *val); + + +#ifndef UNUSED +#if defined(__cplusplus) +#define UNUSED(x) +#elif defined(__GNUC__) +#define UNUSED(x) UNUSED_##x __attribute__((unused)) +#elif defined(__LCLINT__) +#define UNUSED(x) /*@unused@*/ x +#else +#define UNUSED(x) x +#endif +#endif + + +#define THREAD_STACK_SIZE (1*1024*1024) /* 1MB */ + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include +#include + + +void althrd_setname(althrd_t thr, const char *name) +{ +#if defined(_MSC_VER) +#define MS_VC_EXCEPTION 0x406D1388 +#pragma pack(push,8) + struct { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. + } info; +#pragma pack(pop) + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = thr; + info.dwFlags = 0; + + __try { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } + __except(EXCEPTION_CONTINUE_EXECUTION) { + } +#undef MS_VC_EXCEPTION +#else + (void)thr; + (void)name; +#endif +} + + +static UIntMap ThrdIdHandle = UINTMAP_STATIC_INITIALIZE; + +static void NTAPI althrd_callback(void* UNUSED(handle), DWORD reason, void* UNUSED(reserved)) +{ + if(reason == DLL_PROCESS_DETACH) + ResetUIntMap(&ThrdIdHandle); +} +#ifdef _MSC_VER +#pragma section(".CRT$XLC",read) +__declspec(allocate(".CRT$XLC")) PIMAGE_TLS_CALLBACK althrd_callback_ = althrd_callback; +#elif defined(__GNUC__) +PIMAGE_TLS_CALLBACK althrd_callback_ __attribute__((section(".CRT$XLC"))) = althrd_callback; +#else +PIMAGE_TLS_CALLBACK althrd_callback_ = althrd_callback; +#endif + + +typedef struct thread_cntr { + althrd_start_t func; + void *arg; +} thread_cntr; + +static DWORD WINAPI althrd_starter(void *arg) +{ + thread_cntr cntr; + memcpy(&cntr, arg, sizeof(cntr)); + free(arg); + + return (DWORD)((*cntr.func)(cntr.arg)); +} + + +int althrd_create(althrd_t *thr, althrd_start_t func, void *arg) +{ + thread_cntr *cntr; + DWORD thrid; + HANDLE hdl; + + cntr = malloc(sizeof(*cntr)); + if(!cntr) return althrd_nomem; + + cntr->func = func; + cntr->arg = arg; + + hdl = CreateThread(NULL, THREAD_STACK_SIZE, althrd_starter, cntr, 0, &thrid); + if(!hdl) + { + free(cntr); + return althrd_error; + } + InsertUIntMapEntry(&ThrdIdHandle, thrid, hdl); + + *thr = thrid; + return althrd_success; +} + +int althrd_detach(althrd_t thr) +{ + HANDLE hdl = RemoveUIntMapKey(&ThrdIdHandle, thr); + if(!hdl) return althrd_error; + + CloseHandle(hdl); + return althrd_success; +} + +int althrd_join(althrd_t thr, int *res) +{ + DWORD code; + + HANDLE hdl = RemoveUIntMapKey(&ThrdIdHandle, thr); + if(!hdl) return althrd_error; + + WaitForSingleObject(hdl, INFINITE); + GetExitCodeThread(hdl, &code); + CloseHandle(hdl); + + if(res != NULL) + *res = (int)code; + return althrd_success; +} + +int althrd_sleep(const struct timespec *ts, struct timespec* UNUSED(rem)) +{ + DWORD msec; + + if(ts->tv_sec < 0 || ts->tv_sec >= (0x7fffffff / 1000) || + ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000) + return -2; + + msec = (DWORD)(ts->tv_sec * 1000); + msec += (DWORD)((ts->tv_nsec+999999) / 1000000); + Sleep(msec); + + return 0; +} + + +int almtx_init(almtx_t *mtx, int type) +{ + if(!mtx) return althrd_error; + type &= ~(almtx_recursive|almtx_timed); + if(type != almtx_plain) + return althrd_error; + + InitializeCriticalSection(mtx); + return althrd_success; +} + +void almtx_destroy(almtx_t *mtx) +{ + DeleteCriticalSection(mtx); +} + +int almtx_timedlock(almtx_t *mtx, const struct timespec *ts) +{ + int ret; + + if(!mtx || !ts) + return althrd_error; + + while((ret=almtx_trylock(mtx)) == althrd_busy) + { + struct timespec now; + + if(ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000 || + altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) + return althrd_error; + if(now.tv_sec > ts->tv_sec || (now.tv_sec == ts->tv_sec && now.tv_nsec >= ts->tv_nsec)) + return althrd_timedout; + + althrd_yield(); + } + + return ret; +} + +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 +int alcnd_init(alcnd_t *cond) +{ + InitializeConditionVariable(cond); + return althrd_success; +} + +int alcnd_signal(alcnd_t *cond) +{ + WakeConditionVariable(cond); + return althrd_success; +} + +int alcnd_broadcast(alcnd_t *cond) +{ + WakeAllConditionVariable(cond); + return althrd_success; +} + +int alcnd_wait(alcnd_t *cond, almtx_t *mtx) +{ + if(SleepConditionVariableCS(cond, mtx, INFINITE) != 0) + return althrd_success; + return althrd_error; +} + +int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point) +{ + struct timespec curtime; + DWORD sleeptime; + + if(altimespec_get(&curtime, AL_TIME_UTC) != AL_TIME_UTC) + return althrd_error; + + sleeptime = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000; + sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000; + if(SleepConditionVariableCS(cond, mtx, sleeptime) != 0) + return althrd_success; + return (GetLastError()==ERROR_TIMEOUT) ? althrd_timedout : althrd_error; +} + +void alcnd_destroy(alcnd_t* UNUSED(cond)) +{ + /* Nothing to delete? */ +} + +#else + +/* WARNING: This is a rather poor implementation of condition variables, with + * known problems. However, it's simple, efficient, and good enough for now to + * not require Vista. Based on "Strategies for Implementing POSIX Condition + * Variables" by Douglas C. Schmidt and Irfan Pyarali: + * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html + */ +/* A better solution may be using Wine's implementation. It requires internals + * (NtCreateKeyedEvent, NtReleaseKeyedEvent, and NtWaitForKeyedEvent) from + * ntdll, and implemention of exchange and compare-exchange for RefCounts. + */ + +typedef struct { + RefCount wait_count; + + HANDLE events[2]; +} _int_alcnd_t; +enum { + SIGNAL = 0, + BROADCAST = 1 +}; + +int alcnd_init(alcnd_t *cond) +{ + _int_alcnd_t *icond = calloc(1, sizeof(*icond)); + if(!icond) return althrd_nomem; + + InitRef(&icond->wait_count, 0); + + icond->events[SIGNAL] = CreateEvent(NULL, FALSE, FALSE, NULL); + icond->events[BROADCAST] = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!icond->events[SIGNAL] || !icond->events[BROADCAST]) + { + if(icond->events[SIGNAL]) + CloseHandle(icond->events[SIGNAL]); + if(icond->events[BROADCAST]) + CloseHandle(icond->events[BROADCAST]); + free(icond); + return althrd_error; + } + + cond->Ptr = icond; + return althrd_success; +} + +int alcnd_signal(alcnd_t *cond) +{ + _int_alcnd_t *icond = cond->Ptr; + if(ReadRef(&icond->wait_count) > 0) + SetEvent(icond->events[SIGNAL]); + return althrd_success; +} + +int alcnd_broadcast(alcnd_t *cond) +{ + _int_alcnd_t *icond = cond->Ptr; + if(ReadRef(&icond->wait_count) > 0) + SetEvent(icond->events[BROADCAST]); + return althrd_success; +} + +int alcnd_wait(alcnd_t *cond, almtx_t *mtx) +{ + _int_alcnd_t *icond = cond->Ptr; + int res; + + IncrementRef(&icond->wait_count); + LeaveCriticalSection(mtx); + + res = WaitForMultipleObjects(2, icond->events, FALSE, INFINITE); + + if(DecrementRef(&icond->wait_count) == 0 && res == WAIT_OBJECT_0+BROADCAST) + ResetEvent(icond->events[BROADCAST]); + EnterCriticalSection(mtx); + + return althrd_success; +} + +int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point) +{ + _int_alcnd_t *icond = cond->Ptr; + struct timespec curtime; + DWORD sleeptime; + int res; + + if(altimespec_get(&curtime, AL_TIME_UTC) != AL_TIME_UTC) + return althrd_error; + sleeptime = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000; + sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000; + + IncrementRef(&icond->wait_count); + LeaveCriticalSection(mtx); + + res = WaitForMultipleObjects(2, icond->events, FALSE, sleeptime); + + if(DecrementRef(&icond->wait_count) == 0 && res == WAIT_OBJECT_0+BROADCAST) + ResetEvent(icond->events[BROADCAST]); + EnterCriticalSection(mtx); + + return (res == WAIT_TIMEOUT) ? althrd_timedout : althrd_success; +} + +void alcnd_destroy(alcnd_t *cond) +{ + _int_alcnd_t *icond = cond->Ptr; + CloseHandle(icond->events[SIGNAL]); + CloseHandle(icond->events[BROADCAST]); + free(icond); +} +#endif /* defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 */ + + +/* An associative map of uint:void* pairs. The key is the TLS index (given by + * TlsAlloc), and the value is the altss_dtor_t callback. When a thread exits, + * we iterate over the TLS indices for their thread-local value and call the + * destructor function with it if they're both not NULL. To avoid using + * DllMain, a PIMAGE_TLS_CALLBACK function pointer is placed in a ".CRT$XLx" + * section (where x is a character A to Z) which will be called by the CRT. + */ +static UIntMap TlsDestructors = UINTMAP_STATIC_INITIALIZE; + +static void NTAPI altss_callback(void* UNUSED(handle), DWORD reason, void* UNUSED(reserved)) +{ + ALsizei i; + + if(reason == DLL_PROCESS_DETACH) + { + ResetUIntMap(&TlsDestructors); + return; + } + if(reason != DLL_THREAD_DETACH) + return; + + LockUIntMapRead(&TlsDestructors); + for(i = 0;i < TlsDestructors.size;i++) + { + void *ptr = altss_get(TlsDestructors.array[i].key); + altss_dtor_t callback = (altss_dtor_t)TlsDestructors.array[i].value; + if(ptr && callback) + callback(ptr); + } + UnlockUIntMapRead(&TlsDestructors); +} +#ifdef _MSC_VER +#pragma section(".CRT$XLB",read) +__declspec(allocate(".CRT$XLB")) PIMAGE_TLS_CALLBACK altss_callback_ = altss_callback; +#elif defined(__GNUC__) +PIMAGE_TLS_CALLBACK altss_callback_ __attribute__((section(".CRT$XLB"))) = altss_callback; +#else +#warning "No TLS callback support, thread-local contexts may leak references on poorly written applications." +PIMAGE_TLS_CALLBACK altss_callback_ = altss_callback; +#endif + +int altss_create(altss_t *tss_id, altss_dtor_t callback) +{ + DWORD key = TlsAlloc(); + if(key == TLS_OUT_OF_INDEXES) + return althrd_error; + + *tss_id = key; + if(callback != NULL) + InsertUIntMapEntry(&TlsDestructors, key, callback); + return althrd_success; +} + +void altss_delete(altss_t tss_id) +{ + RemoveUIntMapKey(&TlsDestructors, tss_id); + TlsFree(tss_id); +} + + +int altimespec_get(struct timespec *ts, int base) +{ + static_assert(sizeof(FILETIME) == sizeof(ULARGE_INTEGER), + "Size of FILETIME does not match ULARGE_INTEGER"); + if(base == AL_TIME_UTC) + { + union { + FILETIME ftime; + ULARGE_INTEGER ulint; + } systime; + GetSystemTimeAsFileTime(&systime.ftime); + /* FILETIME is in 100-nanosecond units, or 1/10th of a microsecond. */ + ts->tv_sec = systime.ulint.QuadPart/10000000; + ts->tv_nsec = (systime.ulint.QuadPart%10000000) * 100; + return base; + } + + return 0; +} + + +void alcall_once(alonce_flag *once, void (*callback)(void)) +{ + LONG ret; + while((ret=InterlockedExchange(once, 1)) == 1) + althrd_yield(); + if(ret == 0) + (*callback)(); + InterlockedExchange(once, 2); +} + +#else + +#include +#include +#include +#ifdef HAVE_PTHREAD_NP_H +#include +#endif + + +extern inline int althrd_sleep(const struct timespec *ts, struct timespec *rem); +extern inline void alcall_once(alonce_flag *once, void (*callback)(void)); + + +void althrd_setname(althrd_t thr, const char *name) +{ +#if defined(HAVE_PTHREAD_SETNAME_NP) +#if defined(__GNUC__) + pthread_setname_np(thr, name); +#elif defined(__APPLE__) + if(althrd_equal(thr, althrd_current()) + pthread_setname_np(name); +#endif +#elif defined(HAVE_PTHREAD_SET_NAME_NP) + pthread_set_name_np(thr, name); +#else + (void)thr; + (void)name; +#endif +} + + +typedef struct thread_cntr { + althrd_start_t func; + void *arg; +} thread_cntr; + +static void *althrd_starter(void *arg) +{ + thread_cntr cntr; + memcpy(&cntr, arg, sizeof(cntr)); + free(arg); + + return (void*)(intptr_t)((*cntr.func)(cntr.arg)); +} + + +int althrd_create(althrd_t *thr, althrd_start_t func, void *arg) +{ + thread_cntr *cntr; + pthread_attr_t attr; + + cntr = malloc(sizeof(*cntr)); + if(!cntr) return althrd_nomem; + + if(pthread_attr_init(&attr) != 0) + { + free(cntr); + return althrd_error; + } + if(pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE) != 0) + { + pthread_attr_destroy(&attr); + free(cntr); + return althrd_error; + } + + cntr->func = func; + cntr->arg = arg; + if(pthread_create(thr, &attr, althrd_starter, cntr) != 0) + { + pthread_attr_destroy(&attr); + free(cntr); + return althrd_error; + } + pthread_attr_destroy(&attr); + + return althrd_success; +} + +int althrd_detach(althrd_t thr) +{ + if(pthread_detach(thr) != 0) + return althrd_error; + return althrd_success; +} + +int althrd_join(althrd_t thr, int *res) +{ + void *code; + + if(pthread_join(thr, &code) != 0) + return althrd_error; + if(res != NULL) + *res = (int)(intptr_t)code; + return althrd_success; +} + + +int almtx_init(almtx_t *mtx, int type) +{ + int ret; + + if(!mtx) return althrd_error; + if((type&~(almtx_recursive|almtx_timed)) != 0) + return althrd_error; + + type &= ~almtx_timed; + if(type == almtx_plain) + ret = pthread_mutex_init(mtx, NULL); + else + { + pthread_mutexattr_t attr; + + ret = pthread_mutexattr_init(&attr); + if(ret) return althrd_error; + + if(type == almtx_recursive) + { + ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#ifdef HAVE_PTHREAD_MUTEXATTR_SETKIND_NP + if(ret != 0) + ret = pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_RECURSIVE); +#endif + } + else + ret = 1; + if(ret == 0) + ret = pthread_mutex_init(mtx, &attr); + pthread_mutexattr_destroy(&attr); + } + return ret ? althrd_error : althrd_success; +} + +void almtx_destroy(almtx_t *mtx) +{ + pthread_mutex_destroy(mtx); +} + +int almtx_timedlock(almtx_t *mtx, const struct timespec *ts) +{ + int ret; + +#ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK + ret = pthread_mutex_timedlock(mtx, ts); + switch(ret) + { + case 0: return althrd_success; + case ETIMEDOUT: return althrd_timedout; + case EBUSY: return althrd_busy; + } + return althrd_error; +#else + if(!mtx || !ts) + return althrd_error; + + while((ret=almtx_trylock(mtx)) == althrd_busy) + { + struct timespec now; + + if(ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000 || + altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) + return althrd_error; + if(now.tv_sec > ts->tv_sec || (now.tv_sec == ts->tv_sec && now.tv_nsec >= ts->tv_nsec)) + return althrd_timedout; + + althrd_yield(); + } + + return ret; +#endif +} + +int alcnd_init(alcnd_t *cond) +{ + if(pthread_cond_init(cond, NULL) == 0) + return althrd_success; + return althrd_error; +} + +int alcnd_signal(alcnd_t *cond) +{ + if(pthread_cond_signal(cond) == 0) + return althrd_success; + return althrd_error; +} + +int alcnd_broadcast(alcnd_t *cond) +{ + if(pthread_cond_broadcast(cond) == 0) + return althrd_success; + return althrd_error; +} + +int alcnd_wait(alcnd_t *cond, almtx_t *mtx) +{ + if(pthread_cond_wait(cond, mtx) == 0) + return althrd_success; + return althrd_error; +} + +int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point) +{ + if(pthread_cond_timedwait(cond, mtx, time_point) == 0) + return althrd_success; + return althrd_error; +} + +void alcnd_destroy(alcnd_t *cond) +{ + pthread_cond_destroy(cond); +} + + +int altss_create(altss_t *tss_id, altss_dtor_t callback) +{ + if(pthread_key_create(tss_id, callback) != 0) + return althrd_error; + return althrd_success; +} + +void altss_delete(altss_t tss_id) +{ + pthread_key_delete(tss_id); +} + + +int altimespec_get(struct timespec *ts, int base) +{ + if(base == AL_TIME_UTC) + { + int ret; +#if _POSIX_TIMERS > 0 + ret = clock_gettime(CLOCK_REALTIME, ts); + if(ret == 0) return base; +#else /* _POSIX_TIMERS > 0 */ + struct timeval tv; + ret = gettimeofday(&tv, NULL); + if(ret == 0) + { + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; + return base; + } +#endif + } + + return 0; +} + +#endif + + +void al_nssleep(time_t sec, long nsec) +{ + struct timespec ts, rem; + ts.tv_sec = sec; + ts.tv_nsec = nsec; + + while(althrd_sleep(&ts, &rem) == -1) + ts = rem; +} diff --git a/libs/openal-soft/common/uintmap.c b/libs/openal-soft/common/uintmap.c new file mode 100644 index 00000000..b7a9a29c --- /dev/null +++ b/libs/openal-soft/common/uintmap.c @@ -0,0 +1,144 @@ + +#include "config.h" + +#include "uintmap.h" + +#include +#include + + +extern inline void LockUIntMapRead(UIntMap *map); +extern inline void UnlockUIntMapRead(UIntMap *map); +extern inline void LockUIntMapWrite(UIntMap *map); +extern inline void UnlockUIntMapWrite(UIntMap *map); + + +void InitUIntMap(UIntMap *map, ALsizei limit) +{ + map->array = NULL; + map->size = 0; + map->maxsize = 0; + map->limit = limit; + RWLockInit(&map->lock); +} + +void ResetUIntMap(UIntMap *map) +{ + WriteLock(&map->lock); + free(map->array); + map->array = NULL; + map->size = 0; + map->maxsize = 0; + WriteUnlock(&map->lock); +} + +ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value) +{ + ALsizei pos = 0; + + WriteLock(&map->lock); + 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->limit) + { + WriteUnlock(&map->lock); + return AL_OUT_OF_MEMORY; + } + + if(map->size == map->maxsize) + { + ALvoid *temp = NULL; + ALsizei newsize; + + newsize = (map->maxsize ? (map->maxsize<<1) : 4); + if(newsize >= map->maxsize) + temp = realloc(map->array, newsize*sizeof(map->array[0])); + if(!temp) + { + WriteUnlock(&map->lock); + return AL_OUT_OF_MEMORY; + } + map->array = temp; + map->maxsize = newsize; + } + + if(pos < map->size) + memmove(&map->array[pos+1], &map->array[pos], + (map->size-pos)*sizeof(map->array[0])); + map->size++; + } + map->array[pos].key = key; + map->array[pos].value = value; + WriteUnlock(&map->lock); + + return AL_NO_ERROR; +} + +ALvoid *RemoveUIntMapKey(UIntMap *map, ALuint key) +{ + ALvoid *ptr = NULL; + WriteLock(&map->lock); + 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) + { + ptr = map->array[low].value; + if(low < map->size-1) + memmove(&map->array[low], &map->array[low+1], + (map->size-1-low)*sizeof(map->array[0])); + map->size--; + } + } + WriteUnlock(&map->lock); + return ptr; +} + +ALvoid *LookupUIntMapKey(UIntMap *map, ALuint key) +{ + ALvoid *ptr = NULL; + ReadLock(&map->lock); + 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) + ptr = map->array[low].value; + } + ReadUnlock(&map->lock); + return ptr; +} diff --git a/libs/openal-soft/config.h.in b/libs/openal-soft/config.h.in new file mode 100644 index 00000000..af0d8bfd --- /dev/null +++ b/libs/openal-soft/config.h.in @@ -0,0 +1,203 @@ +/* API declaration export attribute */ +#define AL_API ${EXPORT_DECL} +#define ALC_API ${EXPORT_DECL} + +/* Define to the library version */ +#define ALSOFT_VERSION "${LIB_VERSION}" + +#ifdef IN_IDE_PARSER +/* KDevelop's parser doesn't recognize the C99-standard restrict keyword, but + * recent versions (at least 4.5.1) do recognize GCC's __restrict. */ +#define restrict __restrict +#endif + +/* Define any available alignment declaration */ +#define ALIGN(x) ${ALIGN_DECL} + +/* Define if we have the C11 aligned_alloc function */ +#cmakedefine HAVE_ALIGNED_ALLOC + +/* Define if we have the posix_memalign function */ +#cmakedefine HAVE_POSIX_MEMALIGN + +/* Define if we have the _aligned_malloc function */ +#cmakedefine HAVE__ALIGNED_MALLOC + +/* Define if we have SSE CPU extensions */ +#cmakedefine HAVE_SSE +#cmakedefine HAVE_SSE2 +#cmakedefine HAVE_SSE4_1 + +/* Define if we have ARM Neon CPU extensions */ +#cmakedefine HAVE_NEON + +/* Define if we have FluidSynth support */ +#cmakedefine HAVE_FLUIDSYNTH + +/* 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 QSA backend */ +#cmakedefine HAVE_QSA + +/* 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 + +/* Define if we have the stat function */ +#cmakedefine HAVE_STAT + +/* Define if we have the lrintf function */ +#cmakedefine HAVE_LRINTF + +/* Define if we have the strtof function */ +#cmakedefine HAVE_STRTOF + +/* 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 if we have C99 variable-length array support */ +#cmakedefine HAVE_C99_VLA + +/* Define if we have C99 _Bool support */ +#cmakedefine HAVE_C99_BOOL + +/* Define if we have C11 _Static_assert support */ +#cmakedefine HAVE_C11_STATIC_ASSERT + +/* Define if we have C11 _Alignas support */ +#cmakedefine HAVE_C11_ALIGNAS + +/* Define if we have C11 _Atomic support */ +#cmakedefine HAVE_C11_ATOMIC + +/* 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 stdint.h */ +#cmakedefine HAVE_STDINT_H + +/* Define if we have stdbool.h */ +#cmakedefine HAVE_STDBOOL_H + +/* Define if we have stdalign.h */ +#cmakedefine HAVE_STDALIGN_H + +/* Define if we have windows.h */ +#cmakedefine HAVE_WINDOWS_H + +/* Define if we have dlfcn.h */ +#cmakedefine HAVE_DLFCN_H + +/* Define if we have pthread_np.h */ +#cmakedefine HAVE_PTHREAD_NP_H + +/* Define if we have alloca.h */ +#cmakedefine HAVE_ALLOCA_H + +/* Define if we have malloc.h */ +#cmakedefine HAVE_MALLOC_H + +/* Define if we have ftw.h */ +#cmakedefine HAVE_FTW_H + +/* Define if we have io.h */ +#cmakedefine HAVE_IO_H + +/* Define if we have strings.h */ +#cmakedefine HAVE_STRINGS_H + +/* Define if we have cpuid.h */ +#cmakedefine HAVE_CPUID_H + +/* Define if we have intrin.h */ +#cmakedefine HAVE_INTRIN_H + +/* Define if we have sys/sysconf.h */ +#cmakedefine HAVE_SYS_SYSCONF_H + +/* Define if we have guiddef.h */ +#cmakedefine HAVE_GUIDDEF_H + +/* Define if we have initguid.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 GCC's __get_cpuid() */ +#cmakedefine HAVE_GCC_GET_CPUID + +/* Define if we have the __cpuid() intrinsic */ +#cmakedefine HAVE_CPUID_INTRINSIC + +/* Define if we have _controlfp() */ +#cmakedefine HAVE__CONTROLFP + +/* Define if we have __control87_2() */ +#cmakedefine HAVE___CONTROL87_2 + +/* Define if we have ftw() */ +#cmakedefine HAVE_FTW + +/* Define if we have _wfindfirst() */ +#cmakedefine HAVE__WFINDFIRST + +/* Define if we have pthread_setschedparam() */ +#cmakedefine HAVE_PTHREAD_SETSCHEDPARAM + +/* Define if we have pthread_setname_np() */ +#cmakedefine HAVE_PTHREAD_SETNAME_NP + +/* Define if we have pthread_set_name_np() */ +#cmakedefine HAVE_PTHREAD_SET_NAME_NP + +/* Define if we have pthread_mutexattr_setkind_np() */ +#cmakedefine HAVE_PTHREAD_MUTEXATTR_SETKIND_NP + +/* Define if we have pthread_mutex_timedlock() */ +#cmakedefine HAVE_PTHREAD_MUTEX_TIMEDLOCK diff --git a/libs/openal-soft/env-vars.txt b/libs/openal-soft/env-vars.txt new file mode 100644 index 00000000..47dce032 --- /dev/null +++ b/libs/openal-soft/env-vars.txt @@ -0,0 +1,71 @@ +Useful Environment Variables + +Below is a list of environment variables that can be set to aid with running or +debugging apps that use OpenAL Soft. They should be set before the app is run. + +*** Logging *** + +ALSOFT_LOGLEVEL +Specifies the amount of logging OpenAL Soft will write out: +0 - Effectively disables all logging +1 - Prints out errors only +2 - Prints out warnings and errors +3 - Prints out additional information, as well as warnings and errors +4 - Same as 3, but also device and context reference count changes. This will + print out *a lot* of info, and is generally not useful unless you're trying + to track a reference leak within the library. + +ALSOFT_LOGFILE +Specifies a filename that logged output will be written to. Note that the file +will be first cleared when logging is initialized. + +*** Overrides *** + +ALSOFT_CONF +Specifies an additional configuration file to load settings from. These +settings will take precedence over the global and user configs, but not other +environment variable settings. + +ALSOFT_DRIVERS +Overrides the drivers config option. This specifies which backend drivers to +consider or not consider for use. Please see the drivers option in +alsoftrc.sample for a list of available drivers. + +ALSOFT_DEFAULT_REVERB +Specifies the default reverb preset to apply to sources. Please see the +default-reverb option in alsoftrc.sample for additional information and a list +of available presets. + +ALSOFT_TRAP_AL_ERROR +Set to "true" or "1" to force trapping AL errors. Like the trap-al-error config +option, this will raise a SIGTRAP signal (or a breakpoint exception under +Windows) when a context-level error is generated. Useful when run under a +debugger as it will break execution right when the error occurs, making it +easier to track the cause. + +ALSOFT_TRAP_ALC_ERROR +Set to "true" or "1" to force trapping ALC errors. Like the trap-alc-error +config option, this will raise a SIGTRAP signal (or a breakpoint exception +under Windows) when a device-level error is generated. Useful when run under a +debugger as it will break execution right when the error occurs, making it +easier to track the cause. + +ALSOFT_TRAP_ERROR +Set to "true" or "1" to force trapping both ALC and AL errors. + +*** Compatibility *** + +__ALSOFT_HALF_ANGLE_CONES +Older versions of OpenAL Soft incorrectly calculated the cone angles to range +between 0 and 180 degrees, instead of the expected range of 0 to 360 degrees. +Setting this to "true" or "1" restores the old buggy behavior, for apps that +were written to expect the incorrect range. + +__ALSOFT_REVERSE_Z +Applications that don't natively use OpenAL's coordinate system have to convert +to it before passing in 3D coordinates. Depending on how exactly this is done, +it can cause correct output for stereo but incorrect Z panning for surround +sound (i.e., sounds that are supposed to be behind you sound like they're in +front, and vice-versa). Setting this to "true" or "1" will negate the localized +Z coordinate to attempt to fix output for apps that have incorrect front/back +panning. diff --git a/libs/openal-soft/examples/alffplay.c b/libs/openal-soft/examples/alffplay.c new file mode 100644 index 00000000..d8ef0e57 --- /dev/null +++ b/libs/openal-soft/examples/alffplay.c @@ -0,0 +1,1479 @@ +/* + * alffplay.c + * + * A pedagogical video player that really works! Now with seeking features. + * + * Code based on FFplay, Copyright (c) 2003 Fabrice Bellard, and a tutorial by + * Martin Bohme . + * + * Requires C99. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "threads.h" +#include "bool.h" + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + + +static bool has_latency_check = false; +static LPALGETSOURCEDVSOFT alGetSourcedvSOFT; + +#define AUDIO_BUFFER_TIME 100 /* In milliseconds, per-buffer */ +#define AUDIO_BUFFER_QUEUE_SIZE 8 /* Number of buffers to queue */ +#define MAX_AUDIOQ_SIZE (5 * 16 * 1024) /* Bytes of compressed audio data to keep queued */ +#define MAX_VIDEOQ_SIZE (5 * 256 * 1024) /* Bytes of compressed video data to keep queued */ +#define AV_SYNC_THRESHOLD 0.01 +#define AV_NOSYNC_THRESHOLD 10.0 +#define SAMPLE_CORRECTION_MAX_DIFF 0.1 +#define AUDIO_DIFF_AVG_NB 20 +#define VIDEO_PICTURE_QUEUE_SIZE 16 + +enum { + FF_UPDATE_EVENT = SDL_USEREVENT, + FF_REFRESH_EVENT, + FF_QUIT_EVENT +}; + + +typedef struct PacketQueue { + AVPacketList *first_pkt, *last_pkt; + volatile int nb_packets; + volatile int size; + volatile bool flushing; + almtx_t mutex; + alcnd_t cond; +} PacketQueue; + +typedef struct VideoPicture { + SDL_Texture *bmp; + int width, height; /* Logical image size (actual size may be larger) */ + volatile bool updated; + double pts; +} VideoPicture; + +typedef struct AudioState { + AVStream *st; + + PacketQueue q; + AVPacket pkt; + + /* Used for clock difference average computation */ + double diff_accum; + double diff_avg_coef; + double diff_threshold; + + /* Time (in seconds) of the next sample to be buffered */ + double current_pts; + + /* Decompressed sample frame, and swresample context for conversion */ + AVFrame *decoded_aframe; + struct SwrContext *swres_ctx; + + /* Conversion format, for what gets fed to OpenAL */ + int dst_ch_layout; + enum AVSampleFormat dst_sample_fmt; + + /* Storage of converted samples */ + uint8_t *samples; + ssize_t samples_len; /* In samples */ + ssize_t samples_pos; + int samples_max; + + /* OpenAL format */ + ALenum format; + ALint frame_size; + + ALuint source; + ALuint buffer[AUDIO_BUFFER_QUEUE_SIZE]; + ALuint buffer_idx; + almtx_t src_mutex; + + althrd_t thread; +} AudioState; + +typedef struct VideoState { + AVStream *st; + + PacketQueue q; + + double clock; + double frame_timer; + double frame_last_pts; + double frame_last_delay; + double current_pts; + /* time (av_gettime) at which we updated current_pts - used to have running video pts */ + int64_t current_pts_time; + + /* Decompressed video frame, and swscale context for conversion */ + AVFrame *decoded_vframe; + struct SwsContext *swscale_ctx; + + VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; + int pictq_size, pictq_rindex, pictq_windex; + almtx_t pictq_mutex; + alcnd_t pictq_cond; + + althrd_t thread; +} VideoState; + +typedef struct MovieState { + AVFormatContext *pFormatCtx; + int videoStream, audioStream; + + volatile bool seek_req; + int64_t seek_pos; + + int av_sync_type; + + int64_t external_clock_base; + + AudioState audio; + VideoState video; + + althrd_t parse_thread; + + char filename[1024]; + + volatile bool quit; +} MovieState; + +enum { + AV_SYNC_AUDIO_MASTER, + AV_SYNC_VIDEO_MASTER, + AV_SYNC_EXTERNAL_MASTER, + + DEFAULT_AV_SYNC_TYPE = AV_SYNC_EXTERNAL_MASTER +}; + +static AVPacket flush_pkt = { .data = (uint8_t*)"FLUSH" }; + +static void packet_queue_init(PacketQueue *q) +{ + memset(q, 0, sizeof(PacketQueue)); + almtx_init(&q->mutex, almtx_plain); + alcnd_init(&q->cond); +} +static int packet_queue_put(PacketQueue *q, AVPacket *pkt) +{ + AVPacketList *pkt1; + if(pkt != &flush_pkt && !pkt->buf && av_dup_packet(pkt) < 0) + return -1; + + pkt1 = av_malloc(sizeof(AVPacketList)); + if(!pkt1) return -1; + pkt1->pkt = *pkt; + pkt1->next = NULL; + + almtx_lock(&q->mutex); + if(!q->last_pkt) + q->first_pkt = pkt1; + else + q->last_pkt->next = pkt1; + q->last_pkt = pkt1; + q->nb_packets++; + q->size += pkt1->pkt.size; + almtx_unlock(&q->mutex); + + alcnd_signal(&q->cond); + return 0; +} +static int packet_queue_get(PacketQueue *q, AVPacket *pkt, MovieState *state) +{ + AVPacketList *pkt1; + int ret = -1; + + almtx_lock(&q->mutex); + while(!state->quit) + { + pkt1 = q->first_pkt; + if(pkt1) + { + q->first_pkt = pkt1->next; + if(!q->first_pkt) + q->last_pkt = NULL; + q->nb_packets--; + q->size -= pkt1->pkt.size; + *pkt = pkt1->pkt; + av_free(pkt1); + ret = 1; + break; + } + + if(q->flushing) + { + ret = 0; + break; + } + alcnd_wait(&q->cond, &q->mutex); + } + almtx_unlock(&q->mutex); + return ret; +} +static void packet_queue_clear(PacketQueue *q) +{ + AVPacketList *pkt, *pkt1; + + almtx_lock(&q->mutex); + for(pkt = q->first_pkt;pkt != NULL;pkt = pkt1) + { + pkt1 = pkt->next; + if(pkt->pkt.data != flush_pkt.data) + av_free_packet(&pkt->pkt); + av_freep(&pkt); + } + q->last_pkt = NULL; + q->first_pkt = NULL; + q->nb_packets = 0; + q->size = 0; + almtx_unlock(&q->mutex); +} +static void packet_queue_flush(PacketQueue *q) +{ + almtx_lock(&q->mutex); + q->flushing = true; + almtx_unlock(&q->mutex); + alcnd_signal(&q->cond); +} +static void packet_queue_deinit(PacketQueue *q) +{ + packet_queue_clear(q); + alcnd_destroy(&q->cond); + almtx_destroy(&q->mutex); +} + + +static double get_audio_clock(AudioState *state) +{ + double pts; + + almtx_lock(&state->src_mutex); + /* The audio clock is the timestamp of the sample currently being heard. + * It's based on 4 components: + * 1 - The timestamp of the next sample to buffer (state->current_pts) + * 2 - The length of the source's buffer queue (AL_SEC_LENGTH_SOFT) + * 3 - The offset OpenAL is currently at in the source (the first value + * from AL_SEC_OFFSET_LATENCY_SOFT) + * 4 - The latency between OpenAL and the DAC (the second value from + * AL_SEC_OFFSET_LATENCY_SOFT) + * + * Subtracting the length of the source queue from the next sample's + * timestamp gives the timestamp of the sample at start of the source + * queue. Adding the source offset to that results in the timestamp for + * OpenAL's current position, and subtracting the source latency from that + * gives the timestamp of the sample currently at the DAC. + */ + pts = state->current_pts; + if(state->source) + { + ALdouble offset[2] = { 0.0, 0.0 }; + ALdouble queue_len = 0.0; + ALint status; + + /* NOTE: The source state must be checked last, in case an underrun + * occurs and the source stops between retrieving the offset+latency + * and getting the state. */ + if(has_latency_check) + { + alGetSourcedvSOFT(state->source, AL_SEC_OFFSET_LATENCY_SOFT, offset); + alGetSourcedvSOFT(state->source, AL_SEC_LENGTH_SOFT, &queue_len); + } + else + { + ALint ioffset, ilen; + alGetSourcei(state->source, AL_SAMPLE_OFFSET, &ioffset); + alGetSourcei(state->source, AL_SAMPLE_LENGTH_SOFT, &ilen); + offset[0] = (double)ioffset / state->st->codec->sample_rate; + queue_len = (double)ilen / state->st->codec->sample_rate; + } + alGetSourcei(state->source, AL_SOURCE_STATE, &status); + + /* If the source is AL_STOPPED, then there was an underrun and all + * buffers are processed, so ignore the source queue. The audio thread + * will put the source into an AL_INITIAL state and clear the queue + * when it starts recovery. */ + if(status != AL_STOPPED) + pts = pts - queue_len + offset[0]; + if(status == AL_PLAYING) + pts = pts - offset[1]; + } + almtx_unlock(&state->src_mutex); + + return (pts >= 0.0) ? pts : 0.0; +} +static double get_video_clock(VideoState *state) +{ + double delta = (av_gettime() - state->current_pts_time) / 1000000.0; + return state->current_pts + delta; +} +static double get_external_clock(MovieState *movState) +{ + return (av_gettime()-movState->external_clock_base) / 1000000.0; +} + +double get_master_clock(MovieState *movState) +{ + if(movState->av_sync_type == AV_SYNC_VIDEO_MASTER) + return get_video_clock(&movState->video); + if(movState->av_sync_type == AV_SYNC_AUDIO_MASTER) + return get_audio_clock(&movState->audio); + return get_external_clock(movState); +} + +/* Return how many samples to skip to maintain sync (negative means to + * duplicate samples). */ +static int synchronize_audio(MovieState *movState) +{ + double diff, avg_diff; + double ref_clock; + + if(movState->av_sync_type == AV_SYNC_AUDIO_MASTER) + return 0; + + ref_clock = get_master_clock(movState); + diff = ref_clock - get_audio_clock(&movState->audio); + + if(!(diff < AV_NOSYNC_THRESHOLD)) + { + /* Difference is TOO big; reset diff stuff */ + movState->audio.diff_accum = 0.0; + return 0; + } + + /* Accumulate the diffs */ + movState->audio.diff_accum = movState->audio.diff_accum*movState->audio.diff_avg_coef + diff; + avg_diff = movState->audio.diff_accum*(1.0 - movState->audio.diff_avg_coef); + if(fabs(avg_diff) < movState->audio.diff_threshold) + return 0; + + /* Constrain the per-update difference to avoid exceedingly large skips */ + if(!(diff <= SAMPLE_CORRECTION_MAX_DIFF)) + diff = SAMPLE_CORRECTION_MAX_DIFF; + else if(!(diff >= -SAMPLE_CORRECTION_MAX_DIFF)) + diff = -SAMPLE_CORRECTION_MAX_DIFF; + return (int)(diff*movState->audio.st->codec->sample_rate); +} + +static int audio_decode_frame(MovieState *movState) +{ + AVPacket *pkt = &movState->audio.pkt; + + while(!movState->quit) + { + while(!movState->quit && pkt->size == 0) + { + av_free_packet(pkt); + + /* Get the next packet */ + int err; + if((err=packet_queue_get(&movState->audio.q, pkt, movState)) <= 0) + { + if(err == 0) + break; + return err; + } + if(pkt->data == flush_pkt.data) + { + avcodec_flush_buffers(movState->audio.st->codec); + movState->audio.diff_accum = 0.0; + movState->audio.current_pts = av_q2d(movState->audio.st->time_base)*pkt->pts; + + alSourceRewind(movState->audio.source); + alSourcei(movState->audio.source, AL_BUFFER, 0); + + av_new_packet(pkt, 0); + + return -1; + } + + /* If provided, update w/ pts */ + if(pkt->pts != AV_NOPTS_VALUE) + movState->audio.current_pts = av_q2d(movState->audio.st->time_base)*pkt->pts; + } + + AVFrame *frame = movState->audio.decoded_aframe; + int got_frame = 0; + int len1 = avcodec_decode_audio4(movState->audio.st->codec, frame, + &got_frame, pkt); + if(len1 < 0) break; + + if(len1 <= pkt->size) + { + /* Move the unread data to the front and clear the end bits */ + int remaining = pkt->size - len1; + memmove(pkt->data, &pkt->data[len1], remaining); + av_shrink_packet(pkt, remaining); + } + + if(!got_frame || frame->nb_samples <= 0) + { + av_frame_unref(frame); + continue; + } + + if(frame->nb_samples > movState->audio.samples_max) + { + av_freep(&movState->audio.samples); + av_samples_alloc( + &movState->audio.samples, NULL, movState->audio.st->codec->channels, + frame->nb_samples, movState->audio.dst_sample_fmt, 0 + ); + movState->audio.samples_max = frame->nb_samples; + } + /* Return the amount of sample frames converted */ + int data_size = swr_convert(movState->audio.swres_ctx, + &movState->audio.samples, frame->nb_samples, + (const uint8_t**)frame->data, frame->nb_samples + ); + + av_frame_unref(frame); + return data_size; + } + + return -1; +} + +static int read_audio(MovieState *movState, uint8_t *samples, int length) +{ + int sample_skip = synchronize_audio(movState); + int audio_size = 0; + + /* Read the next chunk of data, refill the buffer, and queue it + * on the source */ + length /= movState->audio.frame_size; + while(audio_size < length) + { + if(movState->audio.samples_len <= 0 || movState->audio.samples_pos >= movState->audio.samples_len) + { + int frame_len = audio_decode_frame(movState); + if(frame_len < 0) return -1; + + movState->audio.samples_len = frame_len; + if(movState->audio.samples_len == 0) + break; + + movState->audio.samples_pos = (movState->audio.samples_len < sample_skip) ? + movState->audio.samples_len : sample_skip; + sample_skip -= movState->audio.samples_pos; + + movState->audio.current_pts += (double)movState->audio.samples_pos / + (double)movState->audio.st->codec->sample_rate; + continue; + } + + int rem = length - audio_size; + if(movState->audio.samples_pos >= 0) + { + int n = movState->audio.frame_size; + int len = movState->audio.samples_len - movState->audio.samples_pos; + if(rem > len) rem = len; + memcpy(samples + audio_size*n, + movState->audio.samples + movState->audio.samples_pos*n, + rem*n); + } + else + { + int n = movState->audio.frame_size; + int len = -movState->audio.samples_pos; + if(rem > len) rem = len; + + /* Add samples by copying the first sample */ + if(n == 1) + { + uint8_t sample = ((uint8_t*)movState->audio.samples)[0]; + uint8_t *q = (uint8_t*)samples + audio_size; + for(int i = 0;i < rem;i++) + *(q++) = sample; + } + else if(n == 2) + { + uint16_t sample = ((uint16_t*)movState->audio.samples)[0]; + uint16_t *q = (uint16_t*)samples + audio_size; + for(int i = 0;i < rem;i++) + *(q++) = sample; + } + else if(n == 4) + { + uint32_t sample = ((uint32_t*)movState->audio.samples)[0]; + uint32_t *q = (uint32_t*)samples + audio_size; + for(int i = 0;i < rem;i++) + *(q++) = sample; + } + else if(n == 8) + { + uint64_t sample = ((uint64_t*)movState->audio.samples)[0]; + uint64_t *q = (uint64_t*)samples + audio_size; + for(int i = 0;i < rem;i++) + *(q++) = sample; + } + else + { + uint8_t *sample = movState->audio.samples; + uint8_t *q = samples + audio_size*n; + for(int i = 0;i < rem;i++) + { + memcpy(q, sample, n); + q += n; + } + } + } + + movState->audio.samples_pos += rem; + movState->audio.current_pts += (double)rem / movState->audio.st->codec->sample_rate; + audio_size += rem; + } + + return audio_size * movState->audio.frame_size; +} + +static int audio_thread(void *userdata) +{ + MovieState *movState = (MovieState*)userdata; + uint8_t *samples = NULL; + ALsizei buffer_len; + + alGenBuffers(AUDIO_BUFFER_QUEUE_SIZE, movState->audio.buffer); + alGenSources(1, &movState->audio.source); + + alSourcei(movState->audio.source, AL_SOURCE_RELATIVE, AL_TRUE); + alSourcei(movState->audio.source, AL_ROLLOFF_FACTOR, 0); + + av_new_packet(&movState->audio.pkt, 0); + + /* Find a suitable format for OpenAL. Currently does not handle surround + * sound (everything non-mono becomes stereo). */ + if(movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_U8 || + movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_U8P) + { + movState->audio.dst_sample_fmt = AV_SAMPLE_FMT_U8; + movState->audio.frame_size = 1; + if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_MONO) + { + movState->audio.dst_ch_layout = AV_CH_LAYOUT_MONO; + movState->audio.frame_size *= 1; + movState->audio.format = AL_FORMAT_MONO8; + } + else + { + movState->audio.dst_ch_layout = AV_CH_LAYOUT_STEREO; + movState->audio.frame_size *= 2; + movState->audio.format = AL_FORMAT_STEREO8; + } + } + else if((movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_FLT || + movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_FLTP) && + alIsExtensionPresent("AL_EXT_FLOAT32")) + { + movState->audio.dst_sample_fmt = AV_SAMPLE_FMT_FLT; + movState->audio.frame_size = 4; + if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_MONO) + { + movState->audio.dst_ch_layout = AV_CH_LAYOUT_MONO; + movState->audio.frame_size *= 1; + movState->audio.format = AL_FORMAT_MONO_FLOAT32; + } + else + { + movState->audio.dst_ch_layout = AV_CH_LAYOUT_STEREO; + movState->audio.frame_size *= 2; + movState->audio.format = AL_FORMAT_STEREO_FLOAT32; + } + } + else + { + movState->audio.dst_sample_fmt = AV_SAMPLE_FMT_S16; + movState->audio.frame_size = 2; + if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_MONO) + { + movState->audio.dst_ch_layout = AV_CH_LAYOUT_MONO; + movState->audio.frame_size *= 1; + movState->audio.format = AL_FORMAT_MONO16; + } + else + { + movState->audio.dst_ch_layout = AV_CH_LAYOUT_STEREO; + movState->audio.frame_size *= 2; + movState->audio.format = AL_FORMAT_STEREO16; + } + } + buffer_len = AUDIO_BUFFER_TIME * movState->audio.st->codec->sample_rate / 1000 * + movState->audio.frame_size; + samples = av_malloc(buffer_len); + + movState->audio.samples = NULL; + movState->audio.samples_max = 0; + movState->audio.samples_pos = 0; + movState->audio.samples_len = 0; + + if(!(movState->audio.decoded_aframe=av_frame_alloc())) + { + fprintf(stderr, "Failed to allocate audio frame\n"); + goto finish; + } + + movState->audio.swres_ctx = swr_alloc_set_opts(NULL, + movState->audio.dst_ch_layout, + movState->audio.dst_sample_fmt, + movState->audio.st->codec->sample_rate, + movState->audio.st->codec->channel_layout, + movState->audio.st->codec->sample_fmt, + movState->audio.st->codec->sample_rate, + 0, NULL + ); + if(!movState->audio.swres_ctx || swr_init(movState->audio.swres_ctx) != 0) + { + fprintf(stderr, "Failed to initialize audio converter\n"); + goto finish; + } + + almtx_lock(&movState->audio.src_mutex); + while(alGetError() == AL_NO_ERROR && !movState->quit) + { + /* First remove any processed buffers. */ + ALint processed; + alGetSourcei(movState->audio.source, AL_BUFFERS_PROCESSED, &processed); + alSourceUnqueueBuffers(movState->audio.source, processed, (ALuint[AUDIO_BUFFER_QUEUE_SIZE]){}); + + /* Refill the buffer queue. */ + ALint queued; + alGetSourcei(movState->audio.source, AL_BUFFERS_QUEUED, &queued); + while(queued < AUDIO_BUFFER_QUEUE_SIZE) + { + int audio_size; + + /* Read the next chunk of data, fill the buffer, and queue it on + * the source */ + audio_size = read_audio(movState, samples, buffer_len); + if(audio_size < 0) break; + + ALuint bufid = movState->audio.buffer[movState->audio.buffer_idx++]; + movState->audio.buffer_idx %= AUDIO_BUFFER_QUEUE_SIZE; + + alBufferData(bufid, movState->audio.format, samples, audio_size, + movState->audio.st->codec->sample_rate); + alSourceQueueBuffers(movState->audio.source, 1, &bufid); + queued++; + } + + /* Check that the source is playing. */ + ALint state; + alGetSourcei(movState->audio.source, AL_SOURCE_STATE, &state); + if(state == AL_STOPPED) + { + /* AL_STOPPED means there was an underrun. Double-check that all + * processed buffers are removed, then rewind the source to get it + * back into an AL_INITIAL state. */ + alGetSourcei(movState->audio.source, AL_BUFFERS_PROCESSED, &processed); + alSourceUnqueueBuffers(movState->audio.source, processed, (ALuint[AUDIO_BUFFER_QUEUE_SIZE]){}); + alSourceRewind(movState->audio.source); + continue; + } + + almtx_unlock(&movState->audio.src_mutex); + + /* (re)start the source if needed, and wait for a buffer to finish */ + if(state != AL_PLAYING && state != AL_PAUSED) + { + alGetSourcei(movState->audio.source, AL_BUFFERS_QUEUED, &queued); + if(queued > 0) alSourcePlay(movState->audio.source); + } + SDL_Delay(AUDIO_BUFFER_TIME); + + almtx_lock(&movState->audio.src_mutex); + } + almtx_unlock(&movState->audio.src_mutex); + +finish: + av_frame_free(&movState->audio.decoded_aframe); + swr_free(&movState->audio.swres_ctx); + + av_freep(&samples); + av_freep(&movState->audio.samples); + + alDeleteSources(1, &movState->audio.source); + alDeleteBuffers(AUDIO_BUFFER_QUEUE_SIZE, movState->audio.buffer); + + return 0; +} + + +static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) +{ + (void)interval; + + SDL_PushEvent(&(SDL_Event){ .user={.type=FF_REFRESH_EVENT, .data1=opaque} }); + return 0; /* 0 means stop timer */ +} + +/* Schedule a video refresh in 'delay' ms */ +static void schedule_refresh(MovieState *movState, int delay) +{ + SDL_AddTimer(delay, sdl_refresh_timer_cb, movState); +} + +static void video_display(MovieState *movState, SDL_Window *screen, SDL_Renderer *renderer) +{ + VideoPicture *vp = &movState->video.pictq[movState->video.pictq_rindex]; + + if(!vp->bmp) + return; + + float aspect_ratio; + int win_w, win_h; + int w, h, x, y; + + if(movState->video.st->codec->sample_aspect_ratio.num == 0) + aspect_ratio = 0.0f; + else + { + aspect_ratio = av_q2d(movState->video.st->codec->sample_aspect_ratio) * + movState->video.st->codec->width / + movState->video.st->codec->height; + } + if(aspect_ratio <= 0.0f) + { + aspect_ratio = (float)movState->video.st->codec->width / + (float)movState->video.st->codec->height; + } + + SDL_GetWindowSize(screen, &win_w, &win_h); + h = win_h; + w = ((int)rint(h * aspect_ratio) + 3) & ~3; + if(w > win_w) + { + w = win_w; + h = ((int)rint(w / aspect_ratio) + 3) & ~3; + } + x = (win_w - w) / 2; + y = (win_h - h) / 2; + + SDL_RenderCopy(renderer, vp->bmp, + &(SDL_Rect){ .x=0, .y=0, .w=vp->width, .h=vp->height }, + &(SDL_Rect){ .x=x, .y=y, .w=w, .h=h } + ); + SDL_RenderPresent(renderer); +} + +static void video_refresh_timer(MovieState *movState, SDL_Window *screen, SDL_Renderer *renderer) +{ + if(!movState->video.st) + { + schedule_refresh(movState, 100); + return; + } + + almtx_lock(&movState->video.pictq_mutex); +retry: + if(movState->video.pictq_size == 0) + schedule_refresh(movState, 1); + else + { + VideoPicture *vp = &movState->video.pictq[movState->video.pictq_rindex]; + double actual_delay, delay, sync_threshold, ref_clock, diff; + + movState->video.current_pts = vp->pts; + movState->video.current_pts_time = av_gettime(); + + delay = vp->pts - movState->video.frame_last_pts; /* the pts from last time */ + if(delay <= 0 || delay >= 1.0) + { + /* if incorrect delay, use previous one */ + delay = movState->video.frame_last_delay; + } + /* save for next time */ + movState->video.frame_last_delay = delay; + movState->video.frame_last_pts = vp->pts; + + /* Update delay to sync to clock if not master source. */ + if(movState->av_sync_type != AV_SYNC_VIDEO_MASTER) + { + ref_clock = get_master_clock(movState); + diff = vp->pts - ref_clock; + + /* Skip or repeat the frame. Take delay into account. */ + sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; + if(fabs(diff) < AV_NOSYNC_THRESHOLD) + { + if(diff <= -sync_threshold) + delay = 0; + else if(diff >= sync_threshold) + delay = 2 * delay; + } + } + + movState->video.frame_timer += delay; + /* Compute the REAL delay. */ + actual_delay = movState->video.frame_timer - (av_gettime() / 1000000.0); + if(!(actual_delay >= 0.010)) + { + /* We don't have time to handle this picture, just skip to the next one. */ + movState->video.pictq_rindex = (movState->video.pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE; + movState->video.pictq_size--; + alcnd_signal(&movState->video.pictq_cond); + goto retry; + } + schedule_refresh(movState, (int)(actual_delay*1000.0 + 0.5)); + + /* Show the picture! */ + video_display(movState, screen, renderer); + + /* Update queue for next picture. */ + movState->video.pictq_rindex = (movState->video.pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE; + movState->video.pictq_size--; + alcnd_signal(&movState->video.pictq_cond); + } + almtx_unlock(&movState->video.pictq_mutex); +} + + +static void update_picture(MovieState *movState, bool *first_update, SDL_Window *screen, SDL_Renderer *renderer) +{ + VideoPicture *vp = &movState->video.pictq[movState->video.pictq_windex]; + + /* allocate or resize the buffer! */ + if(!vp->bmp || vp->width != movState->video.st->codec->width || + vp->height != movState->video.st->codec->height) + { + if(vp->bmp) + SDL_DestroyTexture(vp->bmp); + vp->bmp = SDL_CreateTexture( + renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, + movState->video.st->codec->coded_width, movState->video.st->codec->coded_height + ); + if(!vp->bmp) + fprintf(stderr, "Failed to create YV12 texture!\n"); + vp->width = movState->video.st->codec->width; + vp->height = movState->video.st->codec->height; + + if(*first_update && vp->width > 0 && vp->height > 0) + { + /* For the first update, set the window size to the video size. */ + *first_update = false; + + int w = vp->width; + int h = vp->height; + if(movState->video.st->codec->sample_aspect_ratio.num != 0 && + movState->video.st->codec->sample_aspect_ratio.den != 0) + { + double aspect_ratio = av_q2d(movState->video.st->codec->sample_aspect_ratio); + if(aspect_ratio >= 1.0) + w = (int)(w*aspect_ratio + 0.5); + else if(aspect_ratio > 0.0) + h = (int)(h/aspect_ratio + 0.5); + } + SDL_SetWindowSize(screen, w, h); + } + } + + if(vp->bmp) + { + AVFrame *frame = movState->video.decoded_vframe; + void *pixels = NULL; + int pitch = 0; + + if(movState->video.st->codec->pix_fmt == PIX_FMT_YUV420P) + SDL_UpdateYUVTexture(vp->bmp, NULL, + frame->data[0], frame->linesize[0], + frame->data[1], frame->linesize[1], + frame->data[2], frame->linesize[2] + ); + else if(SDL_LockTexture(vp->bmp, NULL, &pixels, &pitch) != 0) + fprintf(stderr, "Failed to lock texture\n"); + else + { + // Convert the image into YUV format that SDL uses + int coded_w = movState->video.st->codec->coded_width; + int coded_h = movState->video.st->codec->coded_height; + int w = movState->video.st->codec->width; + int h = movState->video.st->codec->height; + if(!movState->video.swscale_ctx) + movState->video.swscale_ctx = sws_getContext( + w, h, movState->video.st->codec->pix_fmt, + w, h, PIX_FMT_YUV420P, SWS_X, NULL, NULL, NULL + ); + + /* point pict at the queue */ + AVPicture pict; + pict.data[0] = pixels; + pict.data[2] = pict.data[0] + coded_w*coded_h; + pict.data[1] = pict.data[2] + coded_w*coded_h/4; + + pict.linesize[0] = pitch; + pict.linesize[2] = pitch / 2; + pict.linesize[1] = pitch / 2; + + sws_scale(movState->video.swscale_ctx, (const uint8_t**)frame->data, + frame->linesize, 0, h, pict.data, pict.linesize); + SDL_UnlockTexture(vp->bmp); + } + } + + almtx_lock(&movState->video.pictq_mutex); + vp->updated = true; + almtx_unlock(&movState->video.pictq_mutex); + alcnd_signal(&movState->video.pictq_cond); +} + +static int queue_picture(MovieState *movState, double pts) +{ + /* Wait until we have space for a new pic */ + almtx_lock(&movState->video.pictq_mutex); + while(movState->video.pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !movState->quit) + alcnd_wait(&movState->video.pictq_cond, &movState->video.pictq_mutex); + almtx_unlock(&movState->video.pictq_mutex); + + if(movState->quit) + return -1; + + VideoPicture *vp = &movState->video.pictq[movState->video.pictq_windex]; + + /* We have to create/update the picture in the main thread */ + vp->updated = false; + SDL_PushEvent(&(SDL_Event){ .user={.type=FF_UPDATE_EVENT, .data1=movState} }); + + /* Wait until the picture is updated. */ + almtx_lock(&movState->video.pictq_mutex); + while(!vp->updated && !movState->quit) + alcnd_wait(&movState->video.pictq_cond, &movState->video.pictq_mutex); + almtx_unlock(&movState->video.pictq_mutex); + if(movState->quit) + return -1; + vp->pts = pts; + + movState->video.pictq_windex = (movState->video.pictq_windex+1)%VIDEO_PICTURE_QUEUE_SIZE; + almtx_lock(&movState->video.pictq_mutex); + movState->video.pictq_size++; + almtx_unlock(&movState->video.pictq_mutex); + + return 0; +} + +static double synchronize_video(MovieState *movState, double pts) +{ + double frame_delay; + + if(pts == 0.0) /* if we aren't given a pts, set it to the clock */ + pts = movState->video.clock; + else /* if we have pts, set video clock to it */ + movState->video.clock = pts; + + /* update the video clock */ + frame_delay = av_q2d(movState->video.st->codec->time_base); + /* if we are repeating a frame, adjust clock accordingly */ + frame_delay += movState->video.decoded_vframe->repeat_pict * (frame_delay * 0.5); + movState->video.clock += frame_delay; + return pts; +} + +int video_thread(void *arg) +{ + MovieState *movState = (MovieState*)arg; + AVPacket *packet = (AVPacket[1]){}; + int64_t saved_pts, pkt_pts; + int frameFinished; + + movState->video.decoded_vframe = av_frame_alloc(); + while(packet_queue_get(&movState->video.q, packet, movState) >= 0) + { + if(packet->data == flush_pkt.data) + { + avcodec_flush_buffers(movState->video.st->codec); + + almtx_lock(&movState->video.pictq_mutex); + movState->video.pictq_size = 0; + movState->video.pictq_rindex = 0; + movState->video.pictq_windex = 0; + almtx_unlock(&movState->video.pictq_mutex); + + movState->video.clock = av_q2d(movState->video.st->time_base)*packet->pts; + movState->video.current_pts = movState->video.clock; + movState->video.current_pts_time = av_gettime(); + continue; + } + + pkt_pts = packet->pts; + + /* Decode video frame */ + avcodec_decode_video2(movState->video.st->codec, movState->video.decoded_vframe, + &frameFinished, packet); + if(pkt_pts != AV_NOPTS_VALUE && !movState->video.decoded_vframe->opaque) + { + /* Store the packet's original pts in the frame, in case the frame + * is not finished decoding yet. */ + saved_pts = pkt_pts; + movState->video.decoded_vframe->opaque = &saved_pts; + } + + av_free_packet(packet); + + if(frameFinished) + { + double pts = av_q2d(movState->video.st->time_base); + if(packet->dts != AV_NOPTS_VALUE) + pts *= packet->dts; + else if(movState->video.decoded_vframe->opaque) + pts *= *(int64_t*)movState->video.decoded_vframe->opaque; + else + pts *= 0.0; + movState->video.decoded_vframe->opaque = NULL; + + pts = synchronize_video(movState, pts); + if(queue_picture(movState, pts) < 0) + break; + } + } + + sws_freeContext(movState->video.swscale_ctx); + movState->video.swscale_ctx = NULL; + av_frame_free(&movState->video.decoded_vframe); + return 0; +} + + +static int stream_component_open(MovieState *movState, int stream_index) +{ + AVFormatContext *pFormatCtx = movState->pFormatCtx; + AVCodecContext *codecCtx; + AVCodec *codec; + + if(stream_index < 0 || (unsigned int)stream_index >= pFormatCtx->nb_streams) + return -1; + + /* Get a pointer to the codec context for the video stream, and open the + * associated codec */ + codecCtx = pFormatCtx->streams[stream_index]->codec; + + codec = avcodec_find_decoder(codecCtx->codec_id); + if(!codec || avcodec_open2(codecCtx, codec, NULL) < 0) + { + fprintf(stderr, "Unsupported codec!\n"); + return -1; + } + + /* Initialize and start the media type handler */ + switch(codecCtx->codec_type) + { + case AVMEDIA_TYPE_AUDIO: + movState->audioStream = stream_index; + movState->audio.st = pFormatCtx->streams[stream_index]; + + /* Averaging filter for audio sync */ + movState->audio.diff_avg_coef = exp(log(0.01) / AUDIO_DIFF_AVG_NB); + /* Correct audio only if larger error than this */ + movState->audio.diff_threshold = 2.0 * 0.050/* 50 ms */; + + memset(&movState->audio.pkt, 0, sizeof(movState->audio.pkt)); + if(althrd_create(&movState->audio.thread, audio_thread, movState) != althrd_success) + { + movState->audioStream = -1; + movState->audio.st = NULL; + } + break; + + case AVMEDIA_TYPE_VIDEO: + movState->videoStream = stream_index; + movState->video.st = pFormatCtx->streams[stream_index]; + + movState->video.current_pts_time = av_gettime(); + movState->video.frame_timer = (double)movState->video.current_pts_time / + 1000000.0; + movState->video.frame_last_delay = 40e-3; + + if(althrd_create(&movState->video.thread, video_thread, movState) != althrd_success) + { + movState->videoStream = -1; + movState->video.st = NULL; + } + break; + + default: + break; + } + + return 0; +} + +static int decode_interrupt_cb(void *ctx) +{ + return ((MovieState*)ctx)->quit; +} + +int decode_thread(void *arg) +{ + MovieState *movState = (MovieState *)arg; + AVFormatContext *fmtCtx = movState->pFormatCtx; + AVPacket *packet = (AVPacket[1]){}; + int video_index = -1; + int audio_index = -1; + + movState->videoStream = -1; + movState->audioStream = -1; + + /* Dump information about file onto standard error */ + av_dump_format(fmtCtx, 0, movState->filename, 0); + + /* Find the first video and audio streams */ + for(unsigned int i = 0;i < fmtCtx->nb_streams;i++) + { + if(fmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + video_index = i; + else if(fmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + audio_index = i; + } + movState->external_clock_base = av_gettime(); + if(audio_index >= 0) + stream_component_open(movState, audio_index); + if(video_index >= 0) + stream_component_open(movState, video_index); + + if(movState->videoStream < 0 && movState->audioStream < 0) + { + fprintf(stderr, "%s: could not open codecs\n", movState->filename); + goto fail; + } + + /* Main packet handling loop */ + while(!movState->quit) + { + if(movState->seek_req) + { + int64_t seek_target = movState->seek_pos; + int stream_index= -1; + + /* Prefer seeking on the video stream. */ + if(movState->videoStream >= 0) + stream_index = movState->videoStream; + else if(movState->audioStream >= 0) + stream_index = movState->audioStream; + + /* Get a seek timestamp for the appropriate stream. */ + int64_t timestamp = seek_target; + if(stream_index >= 0) + timestamp = av_rescale_q(seek_target, AV_TIME_BASE_Q, fmtCtx->streams[stream_index]->time_base); + + if(av_seek_frame(movState->pFormatCtx, stream_index, timestamp, 0) < 0) + fprintf(stderr, "%s: error while seeking\n", movState->pFormatCtx->filename); + else + { + /* Seek successful, clear the packet queues and send a special + * 'flush' packet with the new stream clock time. */ + if(movState->audioStream >= 0) + { + packet_queue_clear(&movState->audio.q); + flush_pkt.pts = av_rescale_q(seek_target, AV_TIME_BASE_Q, + fmtCtx->streams[movState->audioStream]->time_base + ); + packet_queue_put(&movState->audio.q, &flush_pkt); + } + if(movState->videoStream >= 0) + { + packet_queue_clear(&movState->video.q); + flush_pkt.pts = av_rescale_q(seek_target, AV_TIME_BASE_Q, + fmtCtx->streams[movState->videoStream]->time_base + ); + packet_queue_put(&movState->video.q, &flush_pkt); + } + movState->external_clock_base = av_gettime() - seek_target; + } + movState->seek_req = false; + } + + if(movState->audio.q.size >= MAX_AUDIOQ_SIZE || + movState->video.q.size >= MAX_VIDEOQ_SIZE) + { + SDL_Delay(10); + continue; + } + + if(av_read_frame(movState->pFormatCtx, packet) < 0) + { + packet_queue_flush(&movState->video.q); + packet_queue_flush(&movState->audio.q); + break; + } + + /* Place the packet in the queue it's meant for, or discard it. */ + if(packet->stream_index == movState->videoStream) + packet_queue_put(&movState->video.q, packet); + else if(packet->stream_index == movState->audioStream) + packet_queue_put(&movState->audio.q, packet); + else + av_free_packet(packet); + } + + /* all done - wait for it */ + while(!movState->quit) + { + if(movState->audio.q.nb_packets == 0 && movState->video.q.nb_packets == 0) + break; + SDL_Delay(100); + } + +fail: + movState->quit = true; + packet_queue_flush(&movState->video.q); + packet_queue_flush(&movState->audio.q); + + if(movState->videoStream >= 0) + althrd_join(movState->video.thread, NULL); + if(movState->audioStream >= 0) + althrd_join(movState->audio.thread, NULL); + + SDL_PushEvent(&(SDL_Event){ .user={.type=FF_QUIT_EVENT, .data1=movState} }); + + return 0; +} + + +static void stream_seek(MovieState *movState, double incr) +{ + if(!movState->seek_req) + { + double newtime = get_master_clock(movState)+incr; + if(newtime <= 0.0) movState->seek_pos = 0; + else movState->seek_pos = (int64_t)(newtime * AV_TIME_BASE); + movState->seek_req = true; + } +} + +int main(int argc, char *argv[]) +{ + SDL_Event event; + MovieState *movState; + bool first_update = true; + SDL_Window *screen; + SDL_Renderer *renderer; + ALCdevice *device; + ALCcontext *context; + + if(argc < 2) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + /* Register all formats and codecs */ + av_register_all(); + /* Initialize networking protocols */ + avformat_network_init(); + + if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) + { + fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError()); + return 1; + } + + /* Make a window to put our video */ + screen = SDL_CreateWindow("alffplay", 0, 0, 640, 480, SDL_WINDOW_RESIZABLE); + if(!screen) + { + fprintf(stderr, "SDL: could not set video mode - exiting\n"); + return 1; + } + /* Make a renderer to handle the texture image surface and rendering. */ + renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED); + if(renderer) + { + SDL_RendererInfo rinf; + bool ok = false; + + /* Make sure the renderer supports YV12 textures. If not, fallback to a + * software renderer. */ + if(SDL_GetRendererInfo(renderer, &rinf) == 0) + { + for(Uint32 i = 0;!ok && i < rinf.num_texture_formats;i++) + ok = (rinf.texture_formats[i] == SDL_PIXELFORMAT_YV12); + } + if(!ok) + { + fprintf(stderr, "YV12 pixelformat textures not supported on renderer %s\n", rinf.name); + SDL_DestroyRenderer(renderer); + renderer = NULL; + } + } + if(!renderer) + renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_SOFTWARE); + if(!renderer) + { + fprintf(stderr, "SDL: could not create renderer - exiting\n"); + return 1; + } + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderFillRect(renderer, NULL); + SDL_RenderPresent(renderer); + + /* Open an audio device */ + device = alcOpenDevice(NULL); + if(!device) + { + fprintf(stderr, "OpenAL: could not open device - exiting\n"); + return 1; + } + context = alcCreateContext(device, NULL); + if(!context) + { + fprintf(stderr, "OpenAL: could not create context - exiting\n"); + return 1; + } + if(alcMakeContextCurrent(context) == ALC_FALSE) + { + fprintf(stderr, "OpenAL: could not make context current - exiting\n"); + return 1; + } + + if(!alIsExtensionPresent("AL_SOFT_source_length")) + { + fprintf(stderr, "Required AL_SOFT_source_length not supported - exiting\n"); + return 1; + } + + if(!alIsExtensionPresent("AL_SOFT_source_latency")) + fprintf(stderr, "AL_SOFT_source_latency not supported, audio may be a bit laggy.\n"); + else + { + alGetSourcedvSOFT = alGetProcAddress("alGetSourcedvSOFT"); + has_latency_check = true; + } + + + movState = av_mallocz(sizeof(MovieState)); + + av_strlcpy(movState->filename, argv[1], sizeof(movState->filename)); + + packet_queue_init(&movState->audio.q); + packet_queue_init(&movState->video.q); + + almtx_init(&movState->video.pictq_mutex, almtx_plain); + alcnd_init(&movState->video.pictq_cond); + almtx_init(&movState->audio.src_mutex, almtx_recursive); + + movState->av_sync_type = DEFAULT_AV_SYNC_TYPE; + + movState->pFormatCtx = avformat_alloc_context(); + movState->pFormatCtx->interrupt_callback = (AVIOInterruptCB){.callback=decode_interrupt_cb, .opaque=movState}; + + if(avio_open2(&movState->pFormatCtx->pb, movState->filename, AVIO_FLAG_READ, + &movState->pFormatCtx->interrupt_callback, NULL)) + { + fprintf(stderr, "Failed to open %s\n", movState->filename); + return 1; + } + + /* Open movie file */ + if(avformat_open_input(&movState->pFormatCtx, movState->filename, NULL, NULL) != 0) + { + fprintf(stderr, "Failed to open %s\n", movState->filename); + return 1; + } + + /* Retrieve stream information */ + if(avformat_find_stream_info(movState->pFormatCtx, NULL) < 0) + { + fprintf(stderr, "%s: failed to find stream info\n", movState->filename); + return 1; + } + + schedule_refresh(movState, 40); + + + if(althrd_create(&movState->parse_thread, decode_thread, movState) != althrd_success) + { + fprintf(stderr, "Failed to create parse thread!\n"); + return 1; + } + while(SDL_WaitEvent(&event) == 1) + { + switch(event.type) + { + case SDL_KEYDOWN: + switch(event.key.keysym.sym) + { + case SDLK_ESCAPE: + movState->quit = true; + break; + + case SDLK_LEFT: + stream_seek(movState, -10.0); + break; + case SDLK_RIGHT: + stream_seek(movState, 10.0); + break; + case SDLK_UP: + stream_seek(movState, 30.0); + break; + case SDLK_DOWN: + stream_seek(movState, -30.0); + break; + + default: + break; + } + break; + + case SDL_WINDOWEVENT: + switch(event.window.event) + { + case SDL_WINDOWEVENT_RESIZED: + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderFillRect(renderer, NULL); + break; + + default: + break; + } + break; + + case SDL_QUIT: + movState->quit = true; + break; + + case FF_UPDATE_EVENT: + update_picture(event.user.data1, &first_update, screen, renderer); + break; + + case FF_REFRESH_EVENT: + video_refresh_timer(event.user.data1, screen, renderer); + break; + + case FF_QUIT_EVENT: + althrd_join(movState->parse_thread, NULL); + + avformat_close_input(&movState->pFormatCtx); + + almtx_destroy(&movState->audio.src_mutex); + almtx_destroy(&movState->video.pictq_mutex); + alcnd_destroy(&movState->video.pictq_cond); + packet_queue_deinit(&movState->video.q); + packet_queue_deinit(&movState->audio.q); + + alcMakeContextCurrent(NULL); + alcDestroyContext(context); + alcCloseDevice(device); + + SDL_Quit(); + exit(0); + + default: + break; + } + } + + fprintf(stderr, "SDL_WaitEvent error - %s\n", SDL_GetError()); + return 1; +} diff --git a/libs/openal-soft/examples/allatency.c b/libs/openal-soft/examples/allatency.c new file mode 100644 index 00000000..deb13d3b --- /dev/null +++ b/libs/openal-soft/examples/allatency.c @@ -0,0 +1,204 @@ +/* + * OpenAL Source Latency Example + * + * Copyright (c) 2012 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. + */ + +/* This file contains an example for checking the latency of a sound. */ + +#include +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include "common/alhelpers.h" +#include "common/sdl_sound.h" + + +static LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples; +static LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT; + +static LPALSOURCEDSOFT alSourcedSOFT; +static LPALSOURCE3DSOFT alSource3dSOFT; +static LPALSOURCEDVSOFT alSourcedvSOFT; +static LPALGETSOURCEDSOFT alGetSourcedSOFT; +static LPALGETSOURCE3DSOFT alGetSource3dSOFT; +static LPALGETSOURCEDVSOFT alGetSourcedvSOFT; +static LPALSOURCEI64SOFT alSourcei64SOFT; +static LPALSOURCE3I64SOFT alSource3i64SOFT; +static LPALSOURCEI64VSOFT alSourcei64vSOFT; +static LPALGETSOURCEI64SOFT alGetSourcei64SOFT; +static LPALGETSOURCE3I64SOFT alGetSource3i64SOFT; +static LPALGETSOURCEI64VSOFT alGetSourcei64vSOFT; + +/* LoadBuffer loads the named audio file into an OpenAL buffer object, and + * returns the new buffer ID. */ +static ALuint LoadSound(const char *filename) +{ + ALenum err, format, type, channels; + ALuint rate, buffer; + size_t datalen; + void *data; + FilePtr sound; + + /* Open the audio file */ + sound = openAudioFile(filename, 1000); + if(!sound) + { + fprintf(stderr, "Could not open audio in %s\n", filename); + closeAudioFile(sound); + return 0; + } + + /* Get the sound format, and figure out the OpenAL format */ + if(getAudioInfo(sound, &rate, &channels, &type) != 0) + { + fprintf(stderr, "Error getting audio info for %s\n", filename); + closeAudioFile(sound); + return 0; + } + + format = GetFormat(channels, type, alIsBufferFormatSupportedSOFT); + if(format == AL_NONE) + { + fprintf(stderr, "Unsupported format (%s, %s) for %s\n", + ChannelsName(channels), TypeName(type), filename); + closeAudioFile(sound); + return 0; + } + + /* Decode the whole audio stream to a buffer. */ + data = decodeAudioStream(sound, &datalen); + if(!data) + { + fprintf(stderr, "Failed to read audio from %s\n", filename); + closeAudioFile(sound); + return 0; + } + + /* Buffer the audio data into a new buffer object, then free the data and + * close the file. */ + buffer = 0; + alGenBuffers(1, &buffer); + alBufferSamplesSOFT(buffer, rate, format, BytesToFrames(datalen, channels, type), + channels, type, data); + free(data); + closeAudioFile(sound); + + /* Check if an error occured, and clean up if so. */ + err = alGetError(); + if(err != AL_NO_ERROR) + { + fprintf(stderr, "OpenAL Error: %s\n", alGetString(err)); + if(alIsBuffer(buffer)) + alDeleteBuffers(1, &buffer); + return 0; + } + + return buffer; +} + + +int main(int argc, char **argv) +{ + ALuint source, buffer; + ALdouble offsets[2]; + ALenum state; + + /* Print out usage if no file was specified */ + if(argc < 2) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + /* Initialize OpenAL with the default device, and check for EFX support. */ + if(InitAL() != 0) + return 1; + + if(!alIsExtensionPresent("AL_SOFT_source_latency")) + { + fprintf(stderr, "Error: AL_SOFT_source_latency not supported\n"); + CloseAL(); + return 1; + } + + /* Define a macro to help load the function pointers. */ +#define LOAD_PROC(x) ((x) = alGetProcAddress(#x)) + LOAD_PROC(alSourcedSOFT); + LOAD_PROC(alSource3dSOFT); + LOAD_PROC(alSourcedvSOFT); + LOAD_PROC(alGetSourcedSOFT); + LOAD_PROC(alGetSource3dSOFT); + LOAD_PROC(alGetSourcedvSOFT); + LOAD_PROC(alSourcei64SOFT); + LOAD_PROC(alSource3i64SOFT); + LOAD_PROC(alSourcei64vSOFT); + LOAD_PROC(alGetSourcei64SOFT); + LOAD_PROC(alGetSource3i64SOFT); + LOAD_PROC(alGetSourcei64vSOFT); + + if(alIsExtensionPresent("AL_SOFT_buffer_samples")) + { + LOAD_PROC(alBufferSamplesSOFT); + LOAD_PROC(alIsBufferFormatSupportedSOFT); + } +#undef LOAD_PROC + + /* Load the sound into a buffer. */ + buffer = LoadSound(argv[1]); + if(!buffer) + { + CloseAL(); + return 1; + } + + /* Create the source to play the sound with. */ + source = 0; + alGenSources(1, &source); + alSourcei(source, AL_BUFFER, buffer); + assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source"); + + /* Play the sound until it finishes. */ + alSourcePlay(source); + do { + Sleep(10); + alGetSourcei(source, AL_SOURCE_STATE, &state); + + /* Get the source offset and latency. AL_SEC_OFFSET_LATENCY_SOFT will + * place the offset (in seconds) in offsets[0], and the time until that + * offset will be heard (in seconds) in offsets[1]. */ + alGetSourcedvSOFT(source, AL_SEC_OFFSET_LATENCY_SOFT, offsets); + printf("\rOffset: %f - Latency:%3u ms ", offsets[0], (ALuint)(offsets[1]*1000)); + fflush(stdout); + } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); + printf("\n"); + + /* All done. Delete resources, and close OpenAL. */ + alDeleteSources(1, &source); + alDeleteBuffers(1, &buffer); + + CloseAL(); + + return 0; +} diff --git a/libs/openal-soft/examples/alloopback.c b/libs/openal-soft/examples/alloopback.c new file mode 100644 index 00000000..2361ada1 --- /dev/null +++ b/libs/openal-soft/examples/alloopback.c @@ -0,0 +1,247 @@ +/* + * OpenAL Loopback Example + * + * Copyright (c) 2013 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. + */ + +/* This file contains an example for using the loopback device for custom + * output handling. + */ + +#include +#include +#include + +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include "common/alhelpers.h" + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +typedef struct { + ALCdevice *Device; + ALCcontext *Context; + + ALCsizei FrameSize; +} PlaybackInfo; + +static LPALCLOOPBACKOPENDEVICESOFT alcLoopbackOpenDeviceSOFT; +static LPALCISRENDERFORMATSUPPORTEDSOFT alcIsRenderFormatSupportedSOFT; +static LPALCRENDERSAMPLESSOFT alcRenderSamplesSOFT; + + +void SDLCALL RenderSDLSamples(void *userdata, Uint8 *stream, int len) +{ + PlaybackInfo *playback = (PlaybackInfo*)userdata; + alcRenderSamplesSOFT(playback->Device, stream, len/playback->FrameSize); +} + + +/* Creates a one second buffer containing a sine wave, and returns the new + * buffer ID. */ +static ALuint CreateSineWave(void) +{ + ALshort data[44100]; + ALuint buffer; + ALenum err; + ALuint i; + + for(i = 0;i < 44100;i++) + data[i] = (ALshort)(sin(i * 441.0 / 44100.0 * 2.0*M_PI)*32767.0); + + /* Buffer the audio data into a new buffer object. */ + buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, AL_FORMAT_MONO16, data, sizeof(data), 44100); + + /* Check if an error occured, and clean up if so. */ + err = alGetError(); + if(err != AL_NO_ERROR) + { + fprintf(stderr, "OpenAL Error: %s\n", alGetString(err)); + if(alIsBuffer(buffer)) + alDeleteBuffers(1, &buffer); + return 0; + } + + return buffer; +} + + +int main(int argc, char *argv[]) +{ + PlaybackInfo playback = { NULL, NULL, 0 }; + SDL_AudioSpec desired, obtained; + ALuint source, buffer; + ALCint attrs[16]; + ALenum state; + (void)argc; + (void)argv; + + /* Print out error if extension is missing. */ + if(!alcIsExtensionPresent(NULL, "ALC_SOFT_loopback")) + { + fprintf(stderr, "Error: ALC_SOFT_loopback not supported!\n"); + return 1; + } + + /* Define a macro to help load the function pointers. */ +#define LOAD_PROC(x) ((x) = alcGetProcAddress(NULL, #x)) + LOAD_PROC(alcLoopbackOpenDeviceSOFT); + LOAD_PROC(alcIsRenderFormatSupportedSOFT); + LOAD_PROC(alcRenderSamplesSOFT); +#undef LOAD_PROC + + if(SDL_Init(SDL_INIT_AUDIO) == -1) + { + fprintf(stderr, "Failed to init SDL audio: %s\n", SDL_GetError()); + return 1; + } + + /* Set up SDL audio with our requested format and callback. */ + desired.channels = 2; + desired.format = AUDIO_S16SYS; + desired.freq = 44100; + desired.padding = 0; + desired.samples = 4096; + desired.callback = RenderSDLSamples; + desired.userdata = &playback; + if(SDL_OpenAudio(&desired, &obtained) != 0) + { + SDL_Quit(); + fprintf(stderr, "Failed to open SDL audio: %s\n", SDL_GetError()); + return 1; + } + + /* Set up our OpenAL attributes based on what we got from SDL. */ + attrs[0] = ALC_FORMAT_CHANNELS_SOFT; + if(obtained.channels == 1) + attrs[1] = ALC_MONO_SOFT; + else if(obtained.channels == 2) + attrs[1] = ALC_STEREO_SOFT; + else + { + fprintf(stderr, "Unhandled SDL channel count: %d\n", obtained.channels); + goto error; + } + + attrs[2] = ALC_FORMAT_TYPE_SOFT; + if(obtained.format == AUDIO_U8) + attrs[3] = ALC_UNSIGNED_BYTE_SOFT; + else if(obtained.format == AUDIO_S8) + attrs[3] = ALC_BYTE_SOFT; + else if(obtained.format == AUDIO_U16SYS) + attrs[3] = ALC_UNSIGNED_SHORT_SOFT; + else if(obtained.format == AUDIO_S16SYS) + attrs[3] = ALC_SHORT_SOFT; + else + { + fprintf(stderr, "Unhandled SDL format: 0x%04x\n", obtained.format); + goto error; + } + + attrs[4] = ALC_FREQUENCY; + attrs[5] = obtained.freq; + + attrs[6] = 0; /* end of list */ + + playback.FrameSize = FramesToBytes(1, attrs[1], attrs[3]); + + /* Initialize OpenAL loopback device, using our format attributes. */ + playback.Device = alcLoopbackOpenDeviceSOFT(NULL); + if(!playback.Device) + { + fprintf(stderr, "Failed to open loopback device!\n"); + goto error; + } + /* Make sure the format is supported before setting them on the device. */ + if(alcIsRenderFormatSupportedSOFT(playback.Device, attrs[5], attrs[1], attrs[3]) == ALC_FALSE) + { + fprintf(stderr, "Render format not supported: %s, %s, %dhz\n", + ChannelsName(attrs[1]), TypeName(attrs[3]), attrs[5]); + goto error; + } + playback.Context = alcCreateContext(playback.Device, attrs); + if(!playback.Context || alcMakeContextCurrent(playback.Context) == ALC_FALSE) + { + fprintf(stderr, "Failed to set an OpenAL audio context\n"); + goto error; + } + + /* Start SDL playing. Our callback (thus alcRenderSamplesSOFT) will now + * start being called regularly to update the AL playback state. */ + SDL_PauseAudio(0); + + /* Load the sound into a buffer. */ + buffer = CreateSineWave(); + if(!buffer) + { + SDL_CloseAudio(); + alcDestroyContext(playback.Context); + alcCloseDevice(playback.Device); + SDL_Quit(); + return 1; + } + + /* Create the source to play the sound with. */ + source = 0; + alGenSources(1, &source); + alSourcei(source, AL_BUFFER, buffer); + assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source"); + + /* Play the sound until it finishes. */ + alSourcePlay(source); + do { + Sleep(10); + alGetSourcei(source, AL_SOURCE_STATE, &state); + } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); + + /* All done. Delete resources, and close OpenAL. */ + alDeleteSources(1, &source); + alDeleteBuffers(1, &buffer); + + /* Stop SDL playing. */ + SDL_PauseAudio(1); + + /* Close up OpenAL and SDL. */ + SDL_CloseAudio(); + alcDestroyContext(playback.Context); + alcCloseDevice(playback.Device); + SDL_Quit(); + + return 0; + +error: + SDL_CloseAudio(); + if(playback.Context) + alcDestroyContext(playback.Context); + if(playback.Device) + alcCloseDevice(playback.Device); + SDL_Quit(); + + return 1; +} diff --git a/libs/openal-soft/examples/alreverb.c b/libs/openal-soft/examples/alreverb.c new file mode 100644 index 00000000..420b1c55 --- /dev/null +++ b/libs/openal-soft/examples/alreverb.c @@ -0,0 +1,327 @@ +/* + * OpenAL Reverb Example + * + * Copyright (c) 2012 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. + */ + +/* This file contains an example for applying reverb to a sound. */ + +#include +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" +#include "AL/efx-presets.h" + +#include "common/alhelpers.h" +#include "common/sdl_sound.h" + + +static LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples; +static LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT; + +/* Effect object functions */ +static LPALGENEFFECTS alGenEffects; +static LPALDELETEEFFECTS alDeleteEffects; +static LPALISEFFECT alIsEffect; +static LPALEFFECTI alEffecti; +static LPALEFFECTIV alEffectiv; +static LPALEFFECTF alEffectf; +static LPALEFFECTFV alEffectfv; +static LPALGETEFFECTI alGetEffecti; +static LPALGETEFFECTIV alGetEffectiv; +static LPALGETEFFECTF alGetEffectf; +static LPALGETEFFECTFV alGetEffectfv; + +/* Auxiliary Effect Slot object functions */ +static LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; +static LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; +static LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; +static LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; +static LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; +static LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; +static LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; +static LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; +static LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; +static LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; +static LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; + + +/* LoadEffect loads the given reverb properties into a new OpenAL effect + * object, and returns the new effect ID. */ +static ALuint LoadEffect(const EFXEAXREVERBPROPERTIES *reverb) +{ + ALuint effect = 0; + ALenum err; + + /* Create the effect object and check if we can do EAX reverb. */ + alGenEffects(1, &effect); + if(alGetEnumValue("AL_EFFECT_EAXREVERB") != 0) + { + printf("Using EAX Reverb\n"); + + /* EAX Reverb is available. Set the EAX effect type then load the + * reverb properties. */ + alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); + + alEffectf(effect, AL_EAXREVERB_DENSITY, reverb->flDensity); + alEffectf(effect, AL_EAXREVERB_DIFFUSION, reverb->flDiffusion); + alEffectf(effect, AL_EAXREVERB_GAIN, reverb->flGain); + alEffectf(effect, AL_EAXREVERB_GAINHF, reverb->flGainHF); + alEffectf(effect, AL_EAXREVERB_GAINLF, reverb->flGainLF); + alEffectf(effect, AL_EAXREVERB_DECAY_TIME, reverb->flDecayTime); + alEffectf(effect, AL_EAXREVERB_DECAY_HFRATIO, reverb->flDecayHFRatio); + alEffectf(effect, AL_EAXREVERB_DECAY_LFRATIO, reverb->flDecayLFRatio); + alEffectf(effect, AL_EAXREVERB_REFLECTIONS_GAIN, reverb->flReflectionsGain); + alEffectf(effect, AL_EAXREVERB_REFLECTIONS_DELAY, reverb->flReflectionsDelay); + alEffectfv(effect, AL_EAXREVERB_REFLECTIONS_PAN, reverb->flReflectionsPan); + alEffectf(effect, AL_EAXREVERB_LATE_REVERB_GAIN, reverb->flLateReverbGain); + alEffectf(effect, AL_EAXREVERB_LATE_REVERB_DELAY, reverb->flLateReverbDelay); + alEffectfv(effect, AL_EAXREVERB_LATE_REVERB_PAN, reverb->flLateReverbPan); + alEffectf(effect, AL_EAXREVERB_ECHO_TIME, reverb->flEchoTime); + alEffectf(effect, AL_EAXREVERB_ECHO_DEPTH, reverb->flEchoDepth); + alEffectf(effect, AL_EAXREVERB_MODULATION_TIME, reverb->flModulationTime); + alEffectf(effect, AL_EAXREVERB_MODULATION_DEPTH, reverb->flModulationDepth); + alEffectf(effect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, reverb->flAirAbsorptionGainHF); + alEffectf(effect, AL_EAXREVERB_HFREFERENCE, reverb->flHFReference); + alEffectf(effect, AL_EAXREVERB_LFREFERENCE, reverb->flLFReference); + alEffectf(effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, reverb->flRoomRolloffFactor); + alEffecti(effect, AL_EAXREVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit); + } + else + { + printf("Using Standard Reverb\n"); + + /* No EAX Reverb. Set the standard reverb effect type then load the + * available reverb properties. */ + alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB); + + alEffectf(effect, AL_REVERB_DENSITY, reverb->flDensity); + alEffectf(effect, AL_REVERB_DIFFUSION, reverb->flDiffusion); + alEffectf(effect, AL_REVERB_GAIN, reverb->flGain); + alEffectf(effect, AL_REVERB_GAINHF, reverb->flGainHF); + alEffectf(effect, AL_REVERB_DECAY_TIME, reverb->flDecayTime); + alEffectf(effect, AL_REVERB_DECAY_HFRATIO, reverb->flDecayHFRatio); + alEffectf(effect, AL_REVERB_REFLECTIONS_GAIN, reverb->flReflectionsGain); + alEffectf(effect, AL_REVERB_REFLECTIONS_DELAY, reverb->flReflectionsDelay); + alEffectf(effect, AL_REVERB_LATE_REVERB_GAIN, reverb->flLateReverbGain); + alEffectf(effect, AL_REVERB_LATE_REVERB_DELAY, reverb->flLateReverbDelay); + alEffectf(effect, AL_REVERB_AIR_ABSORPTION_GAINHF, reverb->flAirAbsorptionGainHF); + alEffectf(effect, AL_REVERB_ROOM_ROLLOFF_FACTOR, reverb->flRoomRolloffFactor); + alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit); + } + + /* Check if an error occured, and clean up if so. */ + err = alGetError(); + if(err != AL_NO_ERROR) + { + fprintf(stderr, "OpenAL error: %s\n", alGetString(err)); + if(alIsEffect(effect)) + alDeleteEffects(1, &effect); + return 0; + } + + return effect; +} + + +/* LoadBuffer loads the named audio file into an OpenAL buffer object, and + * returns the new buffer ID. */ +static ALuint LoadSound(const char *filename) +{ + ALenum err, format, type, channels; + ALuint rate, buffer; + size_t datalen; + void *data; + FilePtr sound; + + /* Open the file and get the first stream from it */ + sound = openAudioFile(filename, 1000); + if(!sound) + { + fprintf(stderr, "Could not open audio in %s\n", filename); + return 0; + } + + /* Get the sound format, and figure out the OpenAL format */ + if(getAudioInfo(sound, &rate, &channels, &type) != 0) + { + fprintf(stderr, "Error getting audio info for %s\n", filename); + closeAudioFile(sound); + return 0; + } + + format = GetFormat(channels, type, alIsBufferFormatSupportedSOFT); + if(format == AL_NONE) + { + fprintf(stderr, "Unsupported format (%s, %s) for %s\n", + ChannelsName(channels), TypeName(type), filename); + closeAudioFile(sound); + return 0; + } + + /* Decode the whole audio stream to a buffer. */ + data = decodeAudioStream(sound, &datalen); + if(!data) + { + fprintf(stderr, "Failed to read audio from %s\n", filename); + closeAudioFile(sound); + return 0; + } + + /* Buffer the audio data into a new buffer object, then free the data and + * close the file. */ + buffer = 0; + alGenBuffers(1, &buffer); + alBufferSamplesSOFT(buffer, rate, format, BytesToFrames(datalen, channels, type), + channels, type, data); + free(data); + closeAudioFile(sound); + + /* Check if an error occured, and clean up if so. */ + err = alGetError(); + if(err != AL_NO_ERROR) + { + fprintf(stderr, "OpenAL Error: %s\n", alGetString(err)); + if(alIsBuffer(buffer)) + alDeleteBuffers(1, &buffer); + return 0; + } + + return buffer; +} + + +int main(int argc, char **argv) +{ + EFXEAXREVERBPROPERTIES reverb = EFX_REVERB_PRESET_GENERIC; + ALuint source, buffer, effect, slot; + ALenum state; + + /* Print out usage if no file was specified */ + if(argc < 2) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + /* Initialize OpenAL with the default device, and check for EFX support. */ + if(InitAL() != 0) + return 1; + + if(!alcIsExtensionPresent(alcGetContextsDevice(alcGetCurrentContext()), "ALC_EXT_EFX")) + { + fprintf(stderr, "Error: EFX not supported\n"); + CloseAL(); + return 1; + } + + /* Define a macro to help load the function pointers. */ +#define LOAD_PROC(x) ((x) = alGetProcAddress(#x)) + LOAD_PROC(alGenEffects); + LOAD_PROC(alDeleteEffects); + LOAD_PROC(alIsEffect); + LOAD_PROC(alEffecti); + LOAD_PROC(alEffectiv); + LOAD_PROC(alEffectf); + LOAD_PROC(alEffectfv); + LOAD_PROC(alGetEffecti); + LOAD_PROC(alGetEffectiv); + LOAD_PROC(alGetEffectf); + LOAD_PROC(alGetEffectfv); + + LOAD_PROC(alGenAuxiliaryEffectSlots); + LOAD_PROC(alDeleteAuxiliaryEffectSlots); + LOAD_PROC(alIsAuxiliaryEffectSlot); + LOAD_PROC(alAuxiliaryEffectSloti); + LOAD_PROC(alAuxiliaryEffectSlotiv); + LOAD_PROC(alAuxiliaryEffectSlotf); + LOAD_PROC(alAuxiliaryEffectSlotfv); + LOAD_PROC(alGetAuxiliaryEffectSloti); + LOAD_PROC(alGetAuxiliaryEffectSlotiv); + LOAD_PROC(alGetAuxiliaryEffectSlotf); + LOAD_PROC(alGetAuxiliaryEffectSlotfv); + + if(alIsExtensionPresent("AL_SOFT_buffer_samples")) + { + LOAD_PROC(alBufferSamplesSOFT); + LOAD_PROC(alIsBufferFormatSupportedSOFT); + } +#undef LOAD_PROC + + /* Load the sound into a buffer. */ + buffer = LoadSound(argv[1]); + if(!buffer) + { + CloseAL(); + return 1; + } + + /* Load the reverb into an effect. */ + effect = LoadEffect(&reverb); + if(!effect) + { + alDeleteBuffers(1, &buffer); + CloseAL(); + return 1; + } + + /* Create the effect slot object. This is what "plays" an effect on sources + * that connect to it. */ + slot = 0; + alGenAuxiliaryEffectSlots(1, &slot); + + /* Tell the effect slot to use the loaded effect object. Note that the this + * effectively copies the effect properties. You can modify or delete the + * effect object afterward without affecting the effect slot. + */ + alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, effect); + assert(alGetError()==AL_NO_ERROR && "Failed to set effect slot"); + + /* Create the source to play the sound with. */ + source = 0; + alGenSources(1, &source); + alSourcei(source, AL_BUFFER, buffer); + + /* Connect the source to the effect slot. This tells the source to use the + * effect slot 'slot', on send #0 with the AL_FILTER_NULL filter object. + */ + alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot, 0, AL_FILTER_NULL); + assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source"); + + /* Play the sound until it finishes. */ + alSourcePlay(source); + do { + Sleep(10); + alGetSourcei(source, AL_SOURCE_STATE, &state); + } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); + + /* All done. Delete resources, and close OpenAL. */ + alDeleteSources(1, &source); + alDeleteAuxiliaryEffectSlots(1, &slot); + alDeleteEffects(1, &effect); + alDeleteBuffers(1, &buffer); + + CloseAL(); + + return 0; +} diff --git a/libs/openal-soft/examples/alstream.c b/libs/openal-soft/examples/alstream.c new file mode 100644 index 00000000..2972d375 --- /dev/null +++ b/libs/openal-soft/examples/alstream.c @@ -0,0 +1,335 @@ +/* + * OpenAL Audio Stream Example + * + * Copyright (c) 2011 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. + */ + +/* This file contains a relatively simple streaming audio player. */ + +#include +#include +#include +#include +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include "common/alhelpers.h" +#include "common/sdl_sound.h" + + +static LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples; +static LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT; + + +/* Define the number of buffers and buffer size (in milliseconds) to use. 4 + * buffers with 200ms each gives a nice per-chunk size, and lets the queue last + * for almost one second. */ +#define NUM_BUFFERS 4 +#define BUFFER_TIME_MS 200 + +typedef struct StreamPlayer { + /* These are the buffers and source to play out through OpenAL with */ + ALuint buffers[NUM_BUFFERS]; + ALuint source; + + /* Handle for the audio file */ + FilePtr file; + + /* The format of the output stream */ + ALenum format; + ALenum channels; + ALenum type; + ALuint rate; +} StreamPlayer; + +static StreamPlayer *NewPlayer(void); +static void DeletePlayer(StreamPlayer *player); +static int OpenPlayerFile(StreamPlayer *player, const char *filename); +static void ClosePlayerFile(StreamPlayer *player); +static int StartPlayer(StreamPlayer *player); +static int UpdatePlayer(StreamPlayer *player); + +/* Creates a new player object, and allocates the needed OpenAL source and + * buffer objects. Error checking is simplified for the purposes of this + * example, and will cause an abort if needed. */ +static StreamPlayer *NewPlayer(void) +{ + StreamPlayer *player; + + player = malloc(sizeof(*player)); + assert(player != NULL); + + memset(player, 0, sizeof(*player)); + + /* Generate the buffers and source */ + alGenBuffers(NUM_BUFFERS, player->buffers); + assert(alGetError() == AL_NO_ERROR && "Could not create buffers"); + + alGenSources(1, &player->source); + assert(alGetError() == AL_NO_ERROR && "Could not create source"); + + /* Set parameters so mono sources play out the front-center speaker and + * won't distance attenuate. */ + alSource3i(player->source, AL_POSITION, 0, 0, -1); + alSourcei(player->source, AL_SOURCE_RELATIVE, AL_TRUE); + alSourcei(player->source, AL_ROLLOFF_FACTOR, 0); + assert(alGetError() == AL_NO_ERROR && "Could not set source parameters"); + + return player; +} + +/* Destroys a player object, deleting the source and buffers. No error handling + * since these calls shouldn't fail with a properly-made player object. */ +static void DeletePlayer(StreamPlayer *player) +{ + ClosePlayerFile(player); + + alDeleteSources(1, &player->source); + alDeleteBuffers(NUM_BUFFERS, player->buffers); + if(alGetError() != AL_NO_ERROR) + fprintf(stderr, "Failed to delete object IDs\n"); + + memset(player, 0, sizeof(*player)); + free(player); +} + + +/* Opens the first audio stream of the named file. If a file is already open, + * it will be closed first. */ +static int OpenPlayerFile(StreamPlayer *player, const char *filename) +{ + ClosePlayerFile(player); + + /* Open the file and get the first stream from it */ + player->file = openAudioFile(filename, BUFFER_TIME_MS); + if(!player->file) + { + fprintf(stderr, "Could not open audio in %s\n", filename); + goto error; + } + + /* Get the stream format, and figure out the OpenAL format */ + if(getAudioInfo(player->file, &player->rate, &player->channels, &player->type) != 0) + { + fprintf(stderr, "Error getting audio info for %s\n", filename); + goto error; + } + + player->format = GetFormat(player->channels, player->type, alIsBufferFormatSupportedSOFT); + if(player->format == 0) + { + fprintf(stderr, "Unsupported format (%s, %s) for %s\n", + ChannelsName(player->channels), TypeName(player->type), + filename); + goto error; + } + + return 1; + +error: + closeAudioFile(player->file); + player->file = NULL; + + return 0; +} + +/* Closes the audio file stream */ +static void ClosePlayerFile(StreamPlayer *player) +{ + closeAudioFile(player->file); + player->file = NULL; +} + + +/* Prebuffers some audio from the file, and starts playing the source */ +static int StartPlayer(StreamPlayer *player) +{ + size_t i; + + /* Rewind the source position and clear the buffer queue */ + alSourceRewind(player->source); + alSourcei(player->source, AL_BUFFER, 0); + + /* Fill the buffer queue */ + for(i = 0;i < NUM_BUFFERS;i++) + { + uint8_t *data; + size_t got; + + /* Get some data to give it to the buffer */ + data = getAudioData(player->file, &got); + if(!data) break; + + alBufferSamplesSOFT(player->buffers[i], player->rate, player->format, + BytesToFrames(got, player->channels, player->type), + player->channels, player->type, data); + } + if(alGetError() != AL_NO_ERROR) + { + fprintf(stderr, "Error buffering for playback\n"); + return 0; + } + + /* Now queue and start playback! */ + alSourceQueueBuffers(player->source, i, player->buffers); + alSourcePlay(player->source); + if(alGetError() != AL_NO_ERROR) + { + fprintf(stderr, "Error starting playback\n"); + return 0; + } + + return 1; +} + +static int UpdatePlayer(StreamPlayer *player) +{ + ALint processed, state; + + /* Get relevant source info */ + alGetSourcei(player->source, AL_SOURCE_STATE, &state); + alGetSourcei(player->source, AL_BUFFERS_PROCESSED, &processed); + if(alGetError() != AL_NO_ERROR) + { + fprintf(stderr, "Error checking source state\n"); + return 0; + } + + /* Unqueue and handle each processed buffer */ + while(processed > 0) + { + ALuint bufid; + uint8_t *data; + size_t got; + + alSourceUnqueueBuffers(player->source, 1, &bufid); + processed--; + + /* Read the next chunk of data, refill the buffer, and queue it + * back on the source */ + data = getAudioData(player->file, &got); + if(data != NULL) + { + alBufferSamplesSOFT(bufid, player->rate, player->format, + BytesToFrames(got, player->channels, player->type), + player->channels, player->type, data); + alSourceQueueBuffers(player->source, 1, &bufid); + } + if(alGetError() != AL_NO_ERROR) + { + fprintf(stderr, "Error buffering data\n"); + return 0; + } + } + + /* Make sure the source hasn't underrun */ + if(state != AL_PLAYING && state != AL_PAUSED) + { + ALint queued; + + /* If no buffers are queued, playback is finished */ + alGetSourcei(player->source, AL_BUFFERS_QUEUED, &queued); + if(queued == 0) + return 0; + + alSourcePlay(player->source); + if(alGetError() != AL_NO_ERROR) + { + fprintf(stderr, "Error restarting playback\n"); + return 0; + } + } + + return 1; +} + + +int main(int argc, char **argv) +{ + StreamPlayer *player; + int i; + + /* Print out usage if no file was specified */ + if(argc < 2) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + if(InitAL() != 0) + return 1; + + if(alIsExtensionPresent("AL_SOFT_buffer_samples")) + { + printf("AL_SOFT_buffer_samples supported!\n"); + alBufferSamplesSOFT = alGetProcAddress("alBufferSamplesSOFT"); + alIsBufferFormatSupportedSOFT = alGetProcAddress("alIsBufferFormatSupportedSOFT"); + } + else + printf("AL_SOFT_buffer_samples not supported\n"); + + player = NewPlayer(); + + /* Play each file listed on the command line */ + for(i = 1;i < argc;i++) + { + const char *namepart; + + if(!OpenPlayerFile(player, argv[i])) + continue; + + /* Get the name portion, without the path, for display. */ + namepart = strrchr(argv[i], '/'); + if(namepart || (namepart=strrchr(argv[i], '\\'))) + namepart++; + else + namepart = argv[i]; + + printf("Playing: %s (%s, %s, %dhz)\n", namepart, + TypeName(player->type), ChannelsName(player->channels), + player->rate); + fflush(stdout); + + if(!StartPlayer(player)) + { + ClosePlayerFile(player); + continue; + } + + while(UpdatePlayer(player)) + Sleep(10); + + /* All done with this file. Close it and go to the next */ + ClosePlayerFile(player); + } + printf("Done.\n"); + + /* All files done. Delete the player, and close OpenAL */ + DeletePlayer(player); + player = NULL; + + CloseAL(); + + return 0; +} diff --git a/libs/openal-soft/examples/common/alhelpers.c b/libs/openal-soft/examples/common/alhelpers.c new file mode 100644 index 00000000..4582321c --- /dev/null +++ b/libs/openal-soft/examples/common/alhelpers.c @@ -0,0 +1,327 @@ +/* + * OpenAL Helpers + * + * Copyright (c) 2011 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. + */ + +/* This file contains routines to help with some menial OpenAL-related tasks, + * such as opening a device and setting up a context, closing the device and + * destroying its context, converting between frame counts and byte lengths, + * finding an appropriate buffer format, and getting readable strings for + * channel configs and sample types. */ + +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include "alhelpers.h" + + +/* InitAL opens the default device and sets up a context using default + * attributes, making the program ready to call OpenAL functions. */ +int InitAL(void) +{ + ALCdevice *device; + ALCcontext *ctx; + + /* Open and initialize a device with default settings */ + device = alcOpenDevice(NULL); + if(!device) + { + fprintf(stderr, "Could not open a device!\n"); + return 1; + } + + ctx = alcCreateContext(device, NULL); + if(ctx == NULL || alcMakeContextCurrent(ctx) == ALC_FALSE) + { + if(ctx != NULL) + alcDestroyContext(ctx); + alcCloseDevice(device); + fprintf(stderr, "Could not set a context!\n"); + return 1; + } + + printf("Opened \"%s\"\n", alcGetString(device, ALC_DEVICE_SPECIFIER)); + return 0; +} + +/* CloseAL closes the device belonging to the current context, and destroys the + * context. */ +void CloseAL(void) +{ + ALCdevice *device; + ALCcontext *ctx; + + ctx = alcGetCurrentContext(); + if(ctx == NULL) + return; + + device = alcGetContextsDevice(ctx); + + alcMakeContextCurrent(NULL); + alcDestroyContext(ctx); + alcCloseDevice(device); +} + + +/* GetFormat retrieves a compatible buffer format given the channel config and + * sample type. If an alIsBufferFormatSupportedSOFT-compatible function is + * provided, it will be called to find the closest-matching format from + * AL_SOFT_buffer_samples. Returns AL_NONE (0) if no supported format can be + * found. */ +ALenum GetFormat(ALenum channels, ALenum type, LPALISBUFFERFORMATSUPPORTEDSOFT palIsBufferFormatSupportedSOFT) +{ + ALenum format = AL_NONE; + + /* If using AL_SOFT_buffer_samples, try looking through its formats */ + if(palIsBufferFormatSupportedSOFT) + { + /* AL_SOFT_buffer_samples is more lenient with matching formats. The + * specified sample type does not need to match the returned format, + * but it is nice to try to get something close. */ + if(type == AL_UNSIGNED_BYTE_SOFT || type == AL_BYTE_SOFT) + { + if(channels == AL_MONO_SOFT) format = AL_MONO8_SOFT; + else if(channels == AL_STEREO_SOFT) format = AL_STEREO8_SOFT; + else if(channels == AL_QUAD_SOFT) format = AL_QUAD8_SOFT; + else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_8_SOFT; + else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_8_SOFT; + else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_8_SOFT; + } + else if(type == AL_UNSIGNED_SHORT_SOFT || type == AL_SHORT_SOFT) + { + if(channels == AL_MONO_SOFT) format = AL_MONO16_SOFT; + else if(channels == AL_STEREO_SOFT) format = AL_STEREO16_SOFT; + else if(channels == AL_QUAD_SOFT) format = AL_QUAD16_SOFT; + else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_16_SOFT; + else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_16_SOFT; + else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_16_SOFT; + } + else if(type == AL_UNSIGNED_BYTE3_SOFT || type == AL_BYTE3_SOFT || + type == AL_UNSIGNED_INT_SOFT || type == AL_INT_SOFT || + type == AL_FLOAT_SOFT || type == AL_DOUBLE_SOFT) + { + if(channels == AL_MONO_SOFT) format = AL_MONO32F_SOFT; + else if(channels == AL_STEREO_SOFT) format = AL_STEREO32F_SOFT; + else if(channels == AL_QUAD_SOFT) format = AL_QUAD32F_SOFT; + else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_32F_SOFT; + else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_32F_SOFT; + else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_32F_SOFT; + } + + if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format)) + format = AL_NONE; + + /* A matching format was not found or supported. Try 32-bit float. */ + if(format == AL_NONE) + { + if(channels == AL_MONO_SOFT) format = AL_MONO32F_SOFT; + else if(channels == AL_STEREO_SOFT) format = AL_STEREO32F_SOFT; + else if(channels == AL_QUAD_SOFT) format = AL_QUAD32F_SOFT; + else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_32F_SOFT; + else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_32F_SOFT; + else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_32F_SOFT; + + if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format)) + format = AL_NONE; + } + /* 32-bit float not supported. Try 16-bit int. */ + if(format == AL_NONE) + { + if(channels == AL_MONO_SOFT) format = AL_MONO16_SOFT; + else if(channels == AL_STEREO_SOFT) format = AL_STEREO16_SOFT; + else if(channels == AL_QUAD_SOFT) format = AL_QUAD16_SOFT; + else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_16_SOFT; + else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_16_SOFT; + else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_16_SOFT; + + if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format)) + format = AL_NONE; + } + /* 16-bit int not supported. Try 8-bit int. */ + if(format == AL_NONE) + { + if(channels == AL_MONO_SOFT) format = AL_MONO8_SOFT; + else if(channels == AL_STEREO_SOFT) format = AL_STEREO8_SOFT; + else if(channels == AL_QUAD_SOFT) format = AL_QUAD8_SOFT; + else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_8_SOFT; + else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_8_SOFT; + else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_8_SOFT; + + if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format)) + format = AL_NONE; + } + + return format; + } + + /* We use the AL_EXT_MCFORMATS extension to provide output of Quad, 5.1, + * and 7.1 channel configs, AL_EXT_FLOAT32 for 32-bit float samples, and + * AL_EXT_DOUBLE for 64-bit float samples. */ + if(type == AL_UNSIGNED_BYTE_SOFT) + { + if(channels == AL_MONO_SOFT) + format = AL_FORMAT_MONO8; + else if(channels == AL_STEREO_SOFT) + format = AL_FORMAT_STEREO8; + else if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(channels == AL_QUAD_SOFT) + format = alGetEnumValue("AL_FORMAT_QUAD8"); + else if(channels == AL_5POINT1_SOFT) + format = alGetEnumValue("AL_FORMAT_51CHN8"); + else if(channels == AL_6POINT1_SOFT) + format = alGetEnumValue("AL_FORMAT_61CHN8"); + else if(channels == AL_7POINT1_SOFT) + format = alGetEnumValue("AL_FORMAT_71CHN8"); + } + } + else if(type == AL_SHORT_SOFT) + { + if(channels == AL_MONO_SOFT) + format = AL_FORMAT_MONO16; + else if(channels == AL_STEREO_SOFT) + format = AL_FORMAT_STEREO16; + else if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(channels == AL_QUAD_SOFT) + format = alGetEnumValue("AL_FORMAT_QUAD16"); + else if(channels == AL_5POINT1_SOFT) + format = alGetEnumValue("AL_FORMAT_51CHN16"); + else if(channels == AL_6POINT1_SOFT) + format = alGetEnumValue("AL_FORMAT_61CHN16"); + else if(channels == AL_7POINT1_SOFT) + format = alGetEnumValue("AL_FORMAT_71CHN16"); + } + } + else if(type == AL_FLOAT_SOFT && alIsExtensionPresent("AL_EXT_FLOAT32")) + { + if(channels == AL_MONO_SOFT) + format = alGetEnumValue("AL_FORMAT_MONO_FLOAT32"); + else if(channels == AL_STEREO_SOFT) + format = alGetEnumValue("AL_FORMAT_STEREO_FLOAT32"); + else if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(channels == AL_QUAD_SOFT) + format = alGetEnumValue("AL_FORMAT_QUAD32"); + else if(channels == AL_5POINT1_SOFT) + format = alGetEnumValue("AL_FORMAT_51CHN32"); + else if(channels == AL_6POINT1_SOFT) + format = alGetEnumValue("AL_FORMAT_61CHN32"); + else if(channels == AL_7POINT1_SOFT) + format = alGetEnumValue("AL_FORMAT_71CHN32"); + } + } + else if(type == AL_DOUBLE_SOFT && alIsExtensionPresent("AL_EXT_DOUBLE")) + { + if(channels == AL_MONO_SOFT) + format = alGetEnumValue("AL_FORMAT_MONO_DOUBLE"); + else if(channels == AL_STEREO_SOFT) + format = alGetEnumValue("AL_FORMAT_STEREO_DOUBLE"); + } + + /* NOTE: It seems OSX returns -1 from alGetEnumValue for unknown enums, as + * opposed to 0. Correct it. */ + if(format == -1) + format = 0; + + return format; +} + + +void AL_APIENTRY wrap_BufferSamples(ALuint buffer, ALuint samplerate, + ALenum internalformat, ALsizei samples, + ALenum channels, ALenum type, + const ALvoid *data) +{ + alBufferData(buffer, internalformat, data, + FramesToBytes(samples, channels, type), + samplerate); +} + + +const char *ChannelsName(ALenum chans) +{ + switch(chans) + { + case AL_MONO_SOFT: return "Mono"; + case AL_STEREO_SOFT: return "Stereo"; + case AL_REAR_SOFT: return "Rear"; + case AL_QUAD_SOFT: return "Quadraphonic"; + case AL_5POINT1_SOFT: return "5.1 Surround"; + case AL_6POINT1_SOFT: return "6.1 Surround"; + case AL_7POINT1_SOFT: return "7.1 Surround"; + } + return "Unknown Channels"; +} + +const char *TypeName(ALenum type) +{ + switch(type) + { + case AL_BYTE_SOFT: return "S8"; + case AL_UNSIGNED_BYTE_SOFT: return "U8"; + case AL_SHORT_SOFT: return "S16"; + case AL_UNSIGNED_SHORT_SOFT: return "U16"; + case AL_INT_SOFT: return "S32"; + case AL_UNSIGNED_INT_SOFT: return "U32"; + case AL_FLOAT_SOFT: return "Float32"; + case AL_DOUBLE_SOFT: return "Float64"; + } + return "Unknown Type"; +} + + +ALsizei FramesToBytes(ALsizei size, ALenum channels, ALenum type) +{ + switch(channels) + { + case AL_MONO_SOFT: size *= 1; break; + case AL_STEREO_SOFT: size *= 2; break; + case AL_REAR_SOFT: size *= 2; break; + case AL_QUAD_SOFT: size *= 4; break; + case AL_5POINT1_SOFT: size *= 6; break; + case AL_6POINT1_SOFT: size *= 7; break; + case AL_7POINT1_SOFT: size *= 8; break; + } + + switch(type) + { + case AL_BYTE_SOFT: size *= sizeof(ALbyte); break; + case AL_UNSIGNED_BYTE_SOFT: size *= sizeof(ALubyte); break; + case AL_SHORT_SOFT: size *= sizeof(ALshort); break; + case AL_UNSIGNED_SHORT_SOFT: size *= sizeof(ALushort); break; + case AL_INT_SOFT: size *= sizeof(ALint); break; + case AL_UNSIGNED_INT_SOFT: size *= sizeof(ALuint); break; + case AL_FLOAT_SOFT: size *= sizeof(ALfloat); break; + case AL_DOUBLE_SOFT: size *= sizeof(ALdouble); break; + } + + return size; +} + +ALsizei BytesToFrames(ALsizei size, ALenum channels, ALenum type) +{ + return size / FramesToBytes(1, channels, type); +} diff --git a/libs/openal-soft/examples/common/alhelpers.h b/libs/openal-soft/examples/common/alhelpers.h new file mode 100644 index 00000000..62ed5be2 --- /dev/null +++ b/libs/openal-soft/examples/common/alhelpers.h @@ -0,0 +1,51 @@ +#ifndef ALHELPERS_H +#define ALHELPERS_H + +#ifndef _WIN32 +#include +#define Sleep(x) usleep((x)*1000) +#else +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include "AL/alc.h" +#include "AL/al.h" +#include "AL/alext.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Some helper functions to get the name from the channel and type enums. */ +const char *ChannelsName(ALenum chans); +const char *TypeName(ALenum type); + +/* Helpers to convert frame counts and byte lengths. */ +ALsizei FramesToBytes(ALsizei size, ALenum channels, ALenum type); +ALsizei BytesToFrames(ALsizei size, ALenum channels, ALenum type); + +/* Retrieves a compatible buffer format given the channel configuration and + * sample type. If an alIsBufferFormatSupportedSOFT-compatible function is + * provided, it will be called to find the closest-matching format from + * AL_SOFT_buffer_samples. Returns AL_NONE (0) if no supported format can be + * found. */ +ALenum GetFormat(ALenum channels, ALenum type, LPALISBUFFERFORMATSUPPORTEDSOFT palIsBufferFormatSupportedSOFT); + +/* Loads samples into a buffer using the standard alBufferData call, but with a + * LPALBUFFERSAMPLESSOFT-compatible prototype. Assumes internalformat is valid + * for alBufferData, and that channels and type match it. */ +void AL_APIENTRY wrap_BufferSamples(ALuint buffer, ALuint samplerate, + ALenum internalformat, ALsizei samples, + ALenum channels, ALenum type, + const ALvoid *data); + +/* Easy device init/deinit functions. InitAL returns 0 on success. */ +int InitAL(void); +void CloseAL(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ALHELPERS_H */ diff --git a/libs/openal-soft/examples/common/sdl_sound.c b/libs/openal-soft/examples/common/sdl_sound.c new file mode 100644 index 00000000..79a5bf32 --- /dev/null +++ b/libs/openal-soft/examples/common/sdl_sound.c @@ -0,0 +1,164 @@ +/* + * SDL_sound Decoder Helpers + * + * Copyright (c) 2013 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. + */ + +/* This file contains routines for helping to decode audio using SDL_sound. + * There's very little OpenAL-specific code here. + */ +#include "sdl_sound.h" + +#include +#include +#include +#include +#include + +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include "alhelpers.h" + + +static int done_init = 0; + +FilePtr openAudioFile(const char *fname, size_t buftime_ms) +{ + FilePtr file; + ALuint rate; + Uint32 bufsize; + ALenum chans, type; + + /* We need to make sure SDL_sound is initialized. */ + if(!done_init) + { + Sound_Init(); + done_init = 1; + } + + file = Sound_NewSampleFromFile(fname, NULL, 0); + if(!file) + { + fprintf(stderr, "Failed to open %s: %s\n", fname, Sound_GetError()); + return NULL; + } + + if(getAudioInfo(file, &rate, &chans, &type) != 0) + { + Sound_FreeSample(file); + return NULL; + } + + bufsize = FramesToBytes((ALsizei)(buftime_ms/1000.0*rate), chans, type); + if(Sound_SetBufferSize(file, bufsize) == 0) + { + fprintf(stderr, "Failed to set buffer size to %u bytes: %s\n", bufsize, Sound_GetError()); + Sound_FreeSample(file); + return NULL; + } + + return file; +} + +void closeAudioFile(FilePtr file) +{ + if(file) + Sound_FreeSample(file); +} + + +int getAudioInfo(FilePtr file, ALuint *rate, ALenum *channels, ALenum *type) +{ + if(file->actual.channels == 1) + *channels = AL_MONO_SOFT; + else if(file->actual.channels == 2) + *channels = AL_STEREO_SOFT; + else + { + fprintf(stderr, "Unsupported channel count: %d\n", file->actual.channels); + return 1; + } + + if(file->actual.format == AUDIO_U8) + *type = AL_UNSIGNED_BYTE_SOFT; + else if(file->actual.format == AUDIO_S8) + *type = AL_BYTE_SOFT; + else if(file->actual.format == AUDIO_U16LSB || file->actual.format == AUDIO_U16MSB) + *type = AL_UNSIGNED_SHORT_SOFT; + else if(file->actual.format == AUDIO_S16LSB || file->actual.format == AUDIO_S16MSB) + *type = AL_SHORT_SOFT; + else + { + fprintf(stderr, "Unsupported sample format: 0x%04x\n", file->actual.format); + return 1; + } + + *rate = file->actual.rate; + + return 0; +} + + +uint8_t *getAudioData(FilePtr file, size_t *length) +{ + *length = Sound_Decode(file); + if(*length == 0) + return NULL; + if((file->actual.format == AUDIO_U16LSB && AUDIO_U16LSB != AUDIO_U16SYS) || + (file->actual.format == AUDIO_U16MSB && AUDIO_U16MSB != AUDIO_U16SYS) || + (file->actual.format == AUDIO_S16LSB && AUDIO_S16LSB != AUDIO_S16SYS) || + (file->actual.format == AUDIO_S16MSB && AUDIO_S16MSB != AUDIO_S16SYS)) + { + /* Swap bytes if the decoded endianness doesn't match the system. */ + char *buffer = file->buffer; + size_t i; + for(i = 0;i < *length;i+=2) + { + char b = buffer[i]; + buffer[i] = buffer[i+1]; + buffer[i+1] = b; + } + } + return file->buffer; +} + +void *decodeAudioStream(FilePtr file, size_t *length) +{ + Uint32 got; + char *mem; + + got = Sound_DecodeAll(file); + if(got == 0) + { + *length = 0; + return NULL; + } + + mem = malloc(got); + memcpy(mem, file->buffer, got); + + *length = got; + return mem; +} diff --git a/libs/openal-soft/examples/common/sdl_sound.h b/libs/openal-soft/examples/common/sdl_sound.h new file mode 100644 index 00000000..e93ab92b --- /dev/null +++ b/libs/openal-soft/examples/common/sdl_sound.h @@ -0,0 +1,43 @@ +#ifndef EXAMPLES_SDL_SOUND_H +#define EXAMPLES_SDL_SOUND_H + +#include "AL/al.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Opaque handles to files and streams. Apps don't need to concern themselves + * with the internals */ +typedef Sound_Sample *FilePtr; + +/* Opens a file with SDL_sound, and specifies the size of the sample buffer in + * milliseconds. */ +FilePtr openAudioFile(const char *fname, size_t buftime_ms); + +/* Closes/frees an opened file */ +void closeAudioFile(FilePtr file); + +/* Returns information about the given audio stream. Returns 0 on success. */ +int getAudioInfo(FilePtr file, ALuint *rate, ALenum *channels, ALenum *type); + +/* Returns a pointer to the next available chunk of decoded audio. The size (in + * bytes) of the returned data buffer is stored in 'length', and the returned + * pointer is only valid until the next call to getAudioData. */ +uint8_t *getAudioData(FilePtr file, size_t *length); + +/* Decodes all remaining data from the stream and returns a buffer containing + * the audio data, with the size stored in 'length'. The returned pointer must + * be freed with a call to free(). Note that since this decodes the whole + * stream, using it on lengthy streams (eg, music) will use a lot of memory. + * Such streams are better handled using getAudioData to keep smaller chunks in + * memory at any given time. */ +void *decodeAudioStream(FilePtr, size_t *length); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* EXAMPLES_SDL_SOUND_H */ diff --git a/libs/openal-soft/hrtf.txt b/libs/openal-soft/hrtf.txt new file mode 100644 index 00000000..e19fb0b8 --- /dev/null +++ b/libs/openal-soft/hrtf.txt @@ -0,0 +1,74 @@ +HRTF Support +============ + +Starting with OpenAL Soft 1.14, HRTFs can be used to enable enhanced +spatialization for both 3D (mono) and multi-channel sources, when used with +headphones/stereo output. This can be enabled using the 'hrtf' config option. + +For multi-channel sources this creates a virtual speaker effect, making it +sound as if speakers provide a discrete position for each channel around the +listener. For mono sources this provides much more versatility in the perceived +placement of sounds, making it seem as though they are coming from all around, +including above and below the listener, instead of just to the front, back, and +sides. + +The default data set is based on the KEMAR HRTF data provided by MIT, which can +be found at . It's only +available when using 44100hz playback. + + +Custom HRTF Data Sets +===================== + +OpenAL Soft also provides an option to use user-specified data sets, in +addition to or in place of the default set. This allows users to provide their +own data sets, which could be better suited for their heads, or to work with +stereo speakers instead of headphones, or to support more playback sample +rates, for example. + +The file format is specified below. It uses little-endian byte order. + +== +ALchar magic[8] = "MinPHR01"; +ALuint sampleRate; + +ALubyte hrirSize; /* Can be 8 to 128 in steps of 8. */ +ALubyte evCount; /* Can be 5 to 128. */ + +ALubyte azCount[evCount]; /* Each can be 1 to 128. */ + +/* NOTE: hrirCount is the sum of all azCounts */ +ALshort coefficients[hrirCount][hrirSize]; +ALubyte delays[hrirCount]; /* Each can be 0 to 63. */ +== + +The data is described as thus: + +The file first starts with the 8-byte marker, "MinPHR01", to identify it as an +HRTF data set. This is followed by an unsigned 32-bit integer, specifying the +sample rate the data set is designed for (OpenAL Soft will not use it if the +output device's playback rate doesn't match). + +Afterward, an unsigned 8-bit integer specifies how many sample points (or +finite impulse response filter coefficients) make up each HRIR. + +The following unsigned 8-bit integer specifies the number of elevations used +by the data set. The elevations start at the bottom (-90 degrees), and +increment upwards. Following this is an array of unsigned 8-bit integers, one +for each elevation which specifies the number of azimuths (and thus HRIRs) that +make up each elevation. Azimuths start clockwise from the front, constructing +a full circle for the left ear only. The right ear uses the same HRIRs but in +reverse (ie, left = angle, right = 360-angle). + +The actual coefficients follow. Each coefficient is a signed 16-bit sample, +with each HRIR being a consecutive number of sample points. The HRIRs must be +minimum-phase. This allows the use of a smaller filter length, reducing +computation. For reference, the built-in data set uses a 32-point filter while +even the smallest data set provided by MIT used a 128-sample filter (a 4x +reduction by applying minimum-phase reconstruction). Theoretically, one could +further reduce the minimum-phase version down to a 16-point filter with only a +small reduction in quality. + +After the coefficients is an array of unsigned 8-bit delay values, one for +each HRIR. This is the propagation delay (in samples) a signal must wait before +being convolved with the corresponding minimum-phase HRIR filter. diff --git a/libs/openal-soft/hrtf/default-44100.mhr b/libs/openal-soft/hrtf/default-44100.mhr new file mode 100644 index 00000000..e0d0bf4b Binary files /dev/null and b/libs/openal-soft/hrtf/default-44100.mhr differ diff --git a/libs/openal-soft/hrtf/default-48000.mhr b/libs/openal-soft/hrtf/default-48000.mhr new file mode 100644 index 00000000..0ad547ad Binary files /dev/null and b/libs/openal-soft/hrtf/default-48000.mhr differ diff --git a/libs/openal-soft/include/AL/al.h b/libs/openal-soft/include/AL/al.h new file mode 100644 index 00000000..413b3833 --- /dev/null +++ b/libs/openal-soft/include/AL/al.h @@ -0,0 +1,656 @@ +#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 + + +/** Deprecated macro. */ +#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 + +/** Supported AL version. */ +#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 + + +/** + * Relative source. + * Type: ALboolean + * Range: [AL_TRUE, AL_FALSE] + * Default: AL_FALSE + * + * Specifies if the Source has relative coordinates. + */ +#define AL_SOURCE_RELATIVE 0x202 + + +/** + * Inner cone angle, in degrees. + * Type: ALint, ALfloat + * Range: [0 - 360] + * Default: 360 + * + * The angle covered by the inner cone, where the source will not attenuate. + */ +#define AL_CONE_INNER_ANGLE 0x1001 + +/** + * Outer cone angle, in degrees. + * Range: [0 - 360] + * Default: 360 + * + * The angle covered by the outer cone, where the source will be fully + * attenuated. + */ +#define AL_CONE_OUTER_ANGLE 0x1002 + +/** + * Source pitch. + * Type: ALfloat + * Range: [0.5 - 2.0] + * Default: 1.0 + * + * A multiplier for the frequency (sample rate) of the source's buffer. + */ +#define AL_PITCH 0x1003 + +/** + * Source or listener position. + * Type: ALfloat[3], ALint[3] + * Default: {0, 0, 0} + * + * The source or listener 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. + */ +#define AL_POSITION 0x1004 + +/** + * Source direction. + * Type: ALfloat[3], ALint[3] + * Default: {0, 0, 0} + * + * Specifies the current direction in local space. + * A zero-length vector specifies an omni-directional source (cone is ignored). + */ +#define AL_DIRECTION 0x1005 + +/** + * Source or listener velocity. + * Type: ALfloat[3], ALint[3] + * Default: {0, 0, 0} + * + * Specifies the current velocity in local space. + */ +#define AL_VELOCITY 0x1006 + +/** + * Source looping. + * Type: ALboolean + * Range: [AL_TRUE, AL_FALSE] + * Default: AL_FALSE + * + * Specifies whether source is looping. + */ +#define AL_LOOPING 0x1007 + +/** + * Source buffer. + * Type: ALuint + * Range: any valid Buffer. + * + * Specifies the buffer to provide sound samples. + */ +#define AL_BUFFER 0x1009 + +/** + * Source or listener gain. + * Type: ALfloat + * Range: [0.0 - ] + * + * A value of 1.0 means unattenuated. Each division by 2 equals an attenuation + * of about -6dB. Each multiplicaton by 2 equals an amplification of about + * +6dB. + * + * A value of 0.0 is meaningless with respect to a logarithmic scale; it is + * silent. + */ +#define AL_GAIN 0x100A + +/** + * Minimum source gain. + * Type: ALfloat + * Range: [0.0 - 1.0] + * + * The minimum gain allowed for a source, after distance and cone attenation is + * applied (if applicable). + */ +#define AL_MIN_GAIN 0x100D + +/** + * Maximum source gain. + * Type: ALfloat + * Range: [0.0 - 1.0] + * + * The maximum gain allowed for a source, after distance and cone attenation is + * applied (if applicable). + */ +#define AL_MAX_GAIN 0x100E + +/** + * Listener orientation. + * Type: ALfloat[6] + * Default: {0.0, 0.0, -1.0, 0.0, 1.0, 0.0} + * + * Effectively two three dimensional vectors. The first vector is the front (or + * "at") and the second is the top (or "up"). + * + * Both vectors are in local space. + */ +#define AL_ORIENTATION 0x100F + +/** + * Source state (query only). + * Type: ALint + * Range: [AL_INITIAL, AL_PLAYING, AL_PAUSED, AL_STOPPED] + */ +#define AL_SOURCE_STATE 0x1010 + +/** Source state value. */ +#define AL_INITIAL 0x1011 +#define AL_PLAYING 0x1012 +#define AL_PAUSED 0x1013 +#define AL_STOPPED 0x1014 + +/** + * Source Buffer Queue size (query only). + * Type: ALint + * + * The number of buffers queued using alSourceQueueBuffers, minus the buffers + * removed with alSourceUnqueueBuffers. + */ +#define AL_BUFFERS_QUEUED 0x1015 + +/** + * Source Buffer Queue processed count (query only). + * Type: ALint + * + * The number of queued buffers that have been fully processed, and can be + * removed with alSourceUnqueueBuffers. + * + * Looping sources will never fully process buffers because they will be set to + * play again for when the source loops. + */ +#define AL_BUFFERS_PROCESSED 0x1016 + +/** + * Source reference distance. + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + * + * The distance in units that no attenuation occurs. + * + * At 0.0, no distance attenuation ever occurs on non-linear attenuation models. + */ +#define AL_REFERENCE_DISTANCE 0x1020 + +/** + * Source rolloff factor. + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + * + * Multiplier to exaggerate or diminish distance attenuation. + * + * At 0.0, no distance attenuation ever occurs. + */ +#define AL_ROLLOFF_FACTOR 0x1021 + +/** + * Outer cone gain. + * Type: ALfloat + * Range: [0.0 - 1.0] + * Default: 0.0 + * + * The gain attenuation applied when the listener is outside of the source's + * outer cone. + */ +#define AL_CONE_OUTER_GAIN 0x1022 + +/** + * Source maximum distance. + * Type: ALfloat + * Range: [0.0 - ] + * Default: +inf + * + * The distance above which the source is not attenuated any further with a + * clamped distance model, or where attenuation reaches 0.0 gain for linear + * distance models with a default rolloff factor. + */ +#define AL_MAX_DISTANCE 0x1023 + +/** Source buffer position, in seconds */ +#define AL_SEC_OFFSET 0x1024 +/** Source buffer position, in sample frames */ +#define AL_SAMPLE_OFFSET 0x1025 +/** Source buffer position, in bytes */ +#define AL_BYTE_OFFSET 0x1026 + +/** + * Source type (query only). + * Type: ALint + * Range: [AL_STATIC, AL_STREAMING, AL_UNDETERMINED] + * + * A Source is Static if a Buffer has been attached using AL_BUFFER. + * + * A Source is Streaming if one or more Buffers have been attached using + * alSourceQueueBuffers. + * + * A Source is Undetermined when it has the NULL buffer attached using + * AL_BUFFER. + */ +#define AL_SOURCE_TYPE 0x1027 + +/** Source type value. */ +#define AL_STATIC 0x1028 +#define AL_STREAMING 0x1029 +#define AL_UNDETERMINED 0x1030 + +/** Buffer format specifier. */ +#define AL_FORMAT_MONO8 0x1100 +#define AL_FORMAT_MONO16 0x1101 +#define AL_FORMAT_STEREO8 0x1102 +#define AL_FORMAT_STEREO16 0x1103 + +/** Buffer frequency (query only). */ +#define AL_FREQUENCY 0x2001 +/** Buffer bits per sample (query only). */ +#define AL_BITS 0x2002 +/** Buffer channel count (query only). */ +#define AL_CHANNELS 0x2003 +/** Buffer data size (query only). */ +#define AL_SIZE 0x2004 + +/** + * Buffer state. + * + * Not for public use. + */ +#define AL_UNUSED 0x2010 +#define AL_PENDING 0x2011 +#define AL_PROCESSED 0x2012 + + +/** No error. */ +#define AL_NO_ERROR 0 + +/** Invalid name paramater passed to AL call. */ +#define AL_INVALID_NAME 0xA001 + +/** Invalid enum parameter passed to AL call. */ +#define AL_INVALID_ENUM 0xA002 + +/** Invalid value parameter passed to AL call. */ +#define AL_INVALID_VALUE 0xA003 + +/** Illegal AL call. */ +#define AL_INVALID_OPERATION 0xA004 + +/** Not enough memory. */ +#define AL_OUT_OF_MEMORY 0xA005 + + +/** Context string: Vendor ID. */ +#define AL_VENDOR 0xB001 +/** Context string: Version. */ +#define AL_VERSION 0xB002 +/** Context string: Renderer ID. */ +#define AL_RENDERER 0xB003 +/** Context string: Space-separated extension list. */ +#define AL_EXTENSIONS 0xB004 + + +/** + * Doppler scale. + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + * + * Scale for source and listener velocities. + */ +#define AL_DOPPLER_FACTOR 0xC000 +AL_API void AL_APIENTRY alDopplerFactor(ALfloat value); + +/** + * Doppler velocity (deprecated). + * + * A multiplier applied to the Speed of Sound. + */ +#define AL_DOPPLER_VELOCITY 0xC001 +AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value); + +/** + * Speed of Sound, in units per second. + * Type: ALfloat + * Range: [0.0001 - ] + * Default: 343.3 + * + * The speed at which sound waves are assumed to travel, when calculating the + * doppler effect. + */ +#define AL_SPEED_OF_SOUND 0xC003 +AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value); + +/** + * Distance attenuation model. + * Type: ALint + * Range: [AL_NONE, AL_INVERSE_DISTANCE, AL_INVERSE_DISTANCE_CLAMPED, + * AL_LINEAR_DISTANCE, AL_LINEAR_DISTANCE_CLAMPED, + * AL_EXPONENT_DISTANCE, AL_EXPONENT_DISTANCE_CLAMPED] + * Default: AL_INVERSE_DISTANCE_CLAMPED + * + * The model by which sources attenuate with distance. + * + * None - No distance attenuation. + * Inverse - Doubling the distance halves the source gain. + * Linear - Linear gain scaling between the reference and max distances. + * Exponent - Exponential gain dropoff. + * + * Clamped variations work like the non-clamped counterparts, except the + * distance calculated is clamped between the reference and max distances. + */ +#define AL_DISTANCE_MODEL 0xD000 +AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel); + +/** Distance model value. */ +#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 *values); +AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values); +AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values); +AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values); +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 retrieval. + * + * Obtain the first error generated in the AL context since the last check. + */ +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); + + +/** 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); + + +/** 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 source); + +/** Set Source parameters. */ +AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value); +AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); +AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values); +AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value); +AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); +AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values); + +/** Get Source parameters. */ +AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value); +AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); +AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values); +AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value); +AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); +AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values); + + +/** Play, replay, or resume (if paused) a list of Sources */ +AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources); +/** Stop a list of Sources */ +AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources); +/** Rewind a list of Sources */ +AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources); +/** Pause a list of Sources */ +AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources); + +/** Play, replay, or resume a Source */ +AL_API void AL_APIENTRY alSourcePlay(ALuint source); +/** Stop a Source */ +AL_API void AL_APIENTRY alSourceStop(ALuint source); +/** Rewind a Source (set playback postiton to beginning) */ +AL_API void AL_APIENTRY alSourceRewind(ALuint source); +/** Pause a Source */ +AL_API void AL_APIENTRY alSourcePause(ALuint source); + +/** Queue buffers onto a source */ +AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers); +/** Unqueue processed buffers from a source */ +AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers); + + +/** 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 buffer); + +/** Specifies the data to be copied into a buffer */ +AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq); + +/** Set Buffer parameters, */ +AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value); +AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); +AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values); +AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value); +AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); +AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values); + +/** Get Buffer parameters. */ +AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value); +AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); +AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values); +AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value); +AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); +AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values); + +/** Pointer-to-function type, 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 *values); +typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values); +typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values); +typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values); +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 source); +typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value); +typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); +typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values); +typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value); +typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); +typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values); +typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value); +typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); +typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values); +typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value); +typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); +typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values); +typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources); +typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources); +typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources); +typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources); +typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source); +typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source); +typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source); +typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source); +typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers); +typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers); +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 buffer); +typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq); +typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value); +typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); +typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values); +typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value); +typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); +typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values); +typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value); +typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); +typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values); +typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value); +typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); +typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, 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(__cplusplus) +} /* extern "C" */ +#endif + +#endif /* AL_AL_H */ diff --git a/libs/openal-soft/include/AL/alc.h b/libs/openal-soft/include/AL/alc.h new file mode 100644 index 00000000..294e8b33 --- /dev/null +++ b/libs/openal-soft/include/AL/alc.h @@ -0,0 +1,237 @@ +#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 + + +/** Deprecated macro. */ +#define ALCAPI ALC_API +#define ALCAPIENTRY ALC_APIENTRY +#define ALC_INVALID 0 + +/** Supported ALC version? */ +#define ALC_VERSION_0_1 1 + +/** Opaque device handle */ +typedef struct ALCdevice_struct ALCdevice; +/** Opaque context handle */ +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 + +/** Context attribute: Hz. */ +#define ALC_FREQUENCY 0x1007 + +/** Context attribute: Hz. */ +#define ALC_REFRESH 0x1008 + +/** Context attribute: AL_TRUE or AL_FALSE. */ +#define ALC_SYNC 0x1009 + +/** Context attribute: requested Mono (3D) Sources. */ +#define ALC_MONO_SOURCES 0x1010 + +/** Context attribute: requested Stereo Sources. */ +#define ALC_STEREO_SOURCES 0x1011 + +/** No error. */ +#define ALC_NO_ERROR 0 + +/** Invalid device handle. */ +#define ALC_INVALID_DEVICE 0xA001 + +/** Invalid context handle. */ +#define ALC_INVALID_CONTEXT 0xA002 + +/** Invalid enum parameter passed to an ALC call. */ +#define ALC_INVALID_ENUM 0xA003 + +/** Invalid value parameter passed to an ALC call. */ +#define ALC_INVALID_VALUE 0xA004 + +/** Out of memory. */ +#define ALC_OUT_OF_MEMORY 0xA005 + + +/** Runtime ALC version. */ +#define ALC_MAJOR_VERSION 0x1000 +#define ALC_MINOR_VERSION 0x1001 + +/** Context attribute list properties. */ +#define ALC_ATTRIBUTES_SIZE 0x1002 +#define ALC_ALL_ATTRIBUTES 0x1003 + +/** String for the default device specifier. */ +#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 +/** + * String for the given device's specifier. + * + * If device handle is NULL, it is instead a null-char separated list of + * strings of known device specifiers (list ends with an empty string). + */ +#define ALC_DEVICE_SPECIFIER 0x1005 +/** String for space-separated list of ALC extensions. */ +#define ALC_EXTENSIONS 0x1006 + + +/** Capture extension */ +#define ALC_EXT_CAPTURE 1 +/** + * String for the given capture device's specifier. + * + * If device handle is NULL, it is instead a null-char separated list of + * strings of known capture device specifiers (list ends with an empty string). + */ +#define ALC_CAPTURE_DEVICE_SPECIFIER 0x310 +/** String for the default capture device specifier. */ +#define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311 +/** Number of sample frames available for capture. */ +#define ALC_CAPTURE_SAMPLES 0x312 + + +/** Enumerate All extension */ +#define ALC_ENUMERATE_ALL_EXT 1 +/** String for the default extended device specifier. */ +#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012 +/** + * String for the given extended device's specifier. + * + * If device handle is NULL, it is instead a null-char separated list of + * strings of known extended device specifiers (list ends with an empty string). + */ +#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 Device 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 function. */ +ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param); +ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); + +/** Capture function. */ +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 type, 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 *values); +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(__cplusplus) +} +#endif + +#endif /* AL_ALC_H */ diff --git a/libs/openal-soft/include/AL/alext.h b/libs/openal-soft/include/AL/alext.h new file mode 100644 index 00000000..7d2a9527 --- /dev/null +++ b/libs/openal-soft/include/AL/alext.h @@ -0,0 +1,400 @@ +/** + * 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 +/* Define int64_t and uint64_t types */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +#elif defined(_WIN32) && defined(__GNUC__) +#include +#elif defined(_WIN32) +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +/* Fallback if nothing above works */ +#include +#endif + +#include "alc.h" +#include "al.h" + +#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 AL_EXT_MULAW +#define AL_EXT_MULAW 1 +#define AL_FORMAT_MONO_MULAW_EXT 0x10014 +#define AL_FORMAT_STEREO_MULAW_EXT 0x10015 +#endif + +#ifndef AL_EXT_ALAW +#define AL_EXT_ALAW 1 +#define AL_FORMAT_MONO_ALAW_EXT 0x10016 +#define AL_FORMAT_STEREO_ALAW_EXT 0x10017 +#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 + +#ifndef AL_SOFT_buffer_samples +#define AL_SOFT_buffer_samples 1 +/* Channel configurations */ +#define AL_MONO_SOFT 0x1500 +#define AL_STEREO_SOFT 0x1501 +#define AL_REAR_SOFT 0x1502 +#define AL_QUAD_SOFT 0x1503 +#define AL_5POINT1_SOFT 0x1504 +#define AL_6POINT1_SOFT 0x1505 +#define AL_7POINT1_SOFT 0x1506 + +/* Sample types */ +#define AL_BYTE_SOFT 0x1400 +#define AL_UNSIGNED_BYTE_SOFT 0x1401 +#define AL_SHORT_SOFT 0x1402 +#define AL_UNSIGNED_SHORT_SOFT 0x1403 +#define AL_INT_SOFT 0x1404 +#define AL_UNSIGNED_INT_SOFT 0x1405 +#define AL_FLOAT_SOFT 0x1406 +#define AL_DOUBLE_SOFT 0x1407 +#define AL_BYTE3_SOFT 0x1408 +#define AL_UNSIGNED_BYTE3_SOFT 0x1409 + +/* Storage formats */ +#define AL_MONO8_SOFT 0x1100 +#define AL_MONO16_SOFT 0x1101 +#define AL_MONO32F_SOFT 0x10010 +#define AL_STEREO8_SOFT 0x1102 +#define AL_STEREO16_SOFT 0x1103 +#define AL_STEREO32F_SOFT 0x10011 +#define AL_QUAD8_SOFT 0x1204 +#define AL_QUAD16_SOFT 0x1205 +#define AL_QUAD32F_SOFT 0x1206 +#define AL_REAR8_SOFT 0x1207 +#define AL_REAR16_SOFT 0x1208 +#define AL_REAR32F_SOFT 0x1209 +#define AL_5POINT1_8_SOFT 0x120A +#define AL_5POINT1_16_SOFT 0x120B +#define AL_5POINT1_32F_SOFT 0x120C +#define AL_6POINT1_8_SOFT 0x120D +#define AL_6POINT1_16_SOFT 0x120E +#define AL_6POINT1_32F_SOFT 0x120F +#define AL_7POINT1_8_SOFT 0x1210 +#define AL_7POINT1_16_SOFT 0x1211 +#define AL_7POINT1_32F_SOFT 0x1212 + +/* Buffer attributes */ +#define AL_INTERNAL_FORMAT_SOFT 0x2008 +#define AL_BYTE_LENGTH_SOFT 0x2009 +#define AL_SAMPLE_LENGTH_SOFT 0x200A +#define AL_SEC_LENGTH_SOFT 0x200B + +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 samples, ALenum channels, ALenum type, const ALvoid *data); +AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); +AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data); +AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format); +#endif +#endif + +#ifndef AL_SOFT_direct_channels +#define AL_SOFT_direct_channels 1 +#define AL_DIRECT_CHANNELS_SOFT 0x1033 +#endif + +#ifndef ALC_SOFT_loopback +#define ALC_SOFT_loopback 1 +#define ALC_FORMAT_CHANNELS_SOFT 0x1990 +#define ALC_FORMAT_TYPE_SOFT 0x1991 + +/* Sample types */ +#define ALC_BYTE_SOFT 0x1400 +#define ALC_UNSIGNED_BYTE_SOFT 0x1401 +#define ALC_SHORT_SOFT 0x1402 +#define ALC_UNSIGNED_SHORT_SOFT 0x1403 +#define ALC_INT_SOFT 0x1404 +#define ALC_UNSIGNED_INT_SOFT 0x1405 +#define ALC_FLOAT_SOFT 0x1406 + +/* Channel configurations */ +#define ALC_MONO_SOFT 0x1500 +#define ALC_STEREO_SOFT 0x1501 +#define ALC_QUAD_SOFT 0x1503 +#define ALC_5POINT1_SOFT 0x1504 +#define ALC_6POINT1_SOFT 0x1505 +#define ALC_7POINT1_SOFT 0x1506 + +typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*); +typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum); +typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei); +#ifdef AL_ALEXT_PROTOTYPES +ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName); +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_EXT_STEREO_ANGLES +#define AL_EXT_STEREO_ANGLES 1 +#define AL_STEREO_ANGLES 0x1030 +#endif + +#ifndef AL_EXT_SOURCE_RADIUS +#define AL_EXT_SOURCE_RADIUS 1 +#define AL_SOURCE_RADIUS 0x1031 +#endif + +#ifndef AL_SOFT_source_latency +#define AL_SOFT_source_latency 1 +#define AL_SAMPLE_OFFSET_LATENCY_SOFT 0x1200 +#define AL_SEC_OFFSET_LATENCY_SOFT 0x1201 +typedef int64_t ALint64SOFT; +typedef uint64_t ALuint64SOFT; +typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble); +typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble); +typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*); +typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*); +typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*); +typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*); +typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT); +typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT); +typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*); +typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*); +typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*); +typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*); +#ifdef AL_ALEXT_PROTOTYPES +AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value); +AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3); +AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values); +AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value); +AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3); +AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values); +AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value); +AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3); +AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values); +AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value); +AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3); +AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values); +#endif +#endif + +#ifndef ALC_EXT_DEFAULT_FILTER_ORDER +#define ALC_EXT_DEFAULT_FILTER_ORDER 1 +#define ALC_DEFAULT_FILTER_ORDER 0x1100 +#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 + +#ifndef AL_SOFT_block_alignment +#define AL_SOFT_block_alignment 1 +#define AL_UNPACK_BLOCK_ALIGNMENT_SOFT 0x200C +#define AL_PACK_BLOCK_ALIGNMENT_SOFT 0x200D +#endif + +#ifndef AL_SOFT_MSADPCM +#define AL_SOFT_MSADPCM 1 +#define AL_FORMAT_MONO_MSADPCM_SOFT 0x1302 +#define AL_FORMAT_STEREO_MSADPCM_SOFT 0x1303 +#endif + +#ifndef AL_SOFT_source_length +#define AL_SOFT_source_length 1 +/*#define AL_BYTE_LENGTH_SOFT 0x2009*/ +/*#define AL_SAMPLE_LENGTH_SOFT 0x200A*/ +/*#define AL_SEC_LENGTH_SOFT 0x200B*/ +#endif + +#ifndef ALC_SOFT_pause_device +#define ALC_SOFT_pause_device 1 +typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device); +typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device); +#ifdef AL_ALEXT_PROTOTYPES +ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device); +ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device); +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/openal-soft/include/AL/efx-creative.h b/libs/openal-soft/include/AL/efx-creative.h new file mode 100644 index 00000000..0a04c982 --- /dev/null +++ b/libs/openal-soft/include/AL/efx-creative.h @@ -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 --git a/libs/openal-soft/include/AL/efx-presets.h b/libs/openal-soft/include/AL/efx-presets.h new file mode 100644 index 00000000..86dcbda2 --- /dev/null +++ b/libs/openal-soft/include/AL/efx-presets.h @@ -0,0 +1,402 @@ +/* Reverb presets for EFX */ + +#ifndef EFX_PRESETS_H +#define EFX_PRESETS_H + +#ifndef EFXEAXREVERBPROPERTIES_DEFINED +#define EFXEAXREVERBPROPERTIES_DEFINED +typedef struct { + float flDensity; + float flDiffusion; + float flGain; + float flGainHF; + float flGainLF; + float flDecayTime; + float flDecayHFRatio; + float flDecayLFRatio; + float flReflectionsGain; + float flReflectionsDelay; + float flReflectionsPan[3]; + float flLateReverbGain; + float flLateReverbDelay; + float flLateReverbPan[3]; + float flEchoTime; + float flEchoDepth; + float flModulationTime; + float flModulationDepth; + float flAirAbsorptionGainHF; + float flHFReference; + float flLFReference; + float flRoomRolloffFactor; + int iDecayHFLimit; +} EFXEAXREVERBPROPERTIES, *LPEFXEAXREVERBPROPERTIES; +#endif + +/* Default Presets */ + +#define EFX_REVERB_PRESET_GENERIC \ + { 1.0000f, 1.0000f, 0.3162f, 0.8913f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PADDEDCELL \ + { 0.1715f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.1700f, 0.1000f, 1.0000f, 0.2500f, 0.0010f, { 0.0000f, 0.0000f, 0.0000f }, 1.2691f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ROOM \ + { 0.4287f, 1.0000f, 0.3162f, 0.5929f, 1.0000f, 0.4000f, 0.8300f, 1.0000f, 0.1503f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.0629f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_BATHROOM \ + { 0.1715f, 1.0000f, 0.3162f, 0.2512f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.6531f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 3.2734f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_LIVINGROOM \ + { 0.9766f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.5000f, 0.1000f, 1.0000f, 0.2051f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2805f, 0.0040f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_STONEROOM \ + { 1.0000f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 2.3100f, 0.6400f, 1.0000f, 0.4411f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1003f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_AUDITORIUM \ + { 1.0000f, 1.0000f, 0.3162f, 0.5781f, 1.0000f, 4.3200f, 0.5900f, 1.0000f, 0.4032f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7170f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CONCERTHALL \ + { 1.0000f, 1.0000f, 0.3162f, 0.5623f, 1.0000f, 3.9200f, 0.7000f, 1.0000f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.9977f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CAVE \ + { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 2.9100f, 1.3000f, 1.0000f, 0.5000f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.7063f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_ARENA \ + { 1.0000f, 1.0000f, 0.3162f, 0.4477f, 1.0000f, 7.2400f, 0.3300f, 1.0000f, 0.2612f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.0186f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_HANGAR \ + { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 10.0500f, 0.2300f, 1.0000f, 0.5000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2560f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CARPETEDHALLWAY \ + { 0.4287f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 0.3000f, 0.1000f, 1.0000f, 0.1215f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.1531f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_HALLWAY \ + { 0.3645f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 1.4900f, 0.5900f, 1.0000f, 0.2458f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.6615f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_STONECORRIDOR \ + { 1.0000f, 1.0000f, 0.3162f, 0.7612f, 1.0000f, 2.7000f, 0.7900f, 1.0000f, 0.2472f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 1.5758f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ALLEY \ + { 1.0000f, 0.3000f, 0.3162f, 0.7328f, 1.0000f, 1.4900f, 0.8600f, 1.0000f, 0.2500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.9954f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.9500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FOREST \ + { 1.0000f, 0.3000f, 0.3162f, 0.0224f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.0525f, 0.1620f, { 0.0000f, 0.0000f, 0.0000f }, 0.7682f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CITY \ + { 1.0000f, 0.5000f, 0.3162f, 0.3981f, 1.0000f, 1.4900f, 0.6700f, 1.0000f, 0.0730f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1427f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_MOUNTAINS \ + { 1.0000f, 0.2700f, 0.3162f, 0.0562f, 1.0000f, 1.4900f, 0.2100f, 1.0000f, 0.0407f, 0.3000f, { 0.0000f, 0.0000f, 0.0000f }, 0.1919f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_QUARRY \ + { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0000f, 0.0610f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.7000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PLAIN \ + { 1.0000f, 0.2100f, 0.3162f, 0.1000f, 1.0000f, 1.4900f, 0.5000f, 1.0000f, 0.0585f, 0.1790f, { 0.0000f, 0.0000f, 0.0000f }, 0.1089f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PARKINGLOT \ + { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 1.6500f, 1.5000f, 1.0000f, 0.2082f, 0.0080f, { 0.0000f, 0.0000f, 0.0000f }, 0.2652f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_SEWERPIPE \ + { 0.3071f, 0.8000f, 0.3162f, 0.3162f, 1.0000f, 2.8100f, 0.1400f, 1.0000f, 1.6387f, 0.0140f, { 0.0000f, 0.0000f, 0.0000f }, 3.2471f, 0.0210f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_UNDERWATER \ + { 0.3645f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 1.4900f, 0.1000f, 1.0000f, 0.5963f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 7.0795f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 1.1800f, 0.3480f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_DRUGGED \ + { 0.4287f, 0.5000f, 0.3162f, 1.0000f, 1.0000f, 8.3900f, 1.3900f, 1.0000f, 0.8760f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 3.1081f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_DIZZY \ + { 0.3645f, 0.6000f, 0.3162f, 0.6310f, 1.0000f, 17.2300f, 0.5600f, 1.0000f, 0.1392f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4937f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.8100f, 0.3100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_PSYCHOTIC \ + { 0.0625f, 0.5000f, 0.3162f, 0.8404f, 1.0000f, 7.5600f, 0.9100f, 1.0000f, 0.4864f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 2.4378f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 4.0000f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +/* Castle Presets */ + +#define EFX_REVERB_PRESET_CASTLE_SMALLROOM \ + { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 1.2200f, 0.8300f, 0.3100f, 0.8913f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_SHORTPASSAGE \ + { 1.0000f, 0.8900f, 0.3162f, 0.3162f, 0.1000f, 2.3200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_MEDIUMROOM \ + { 1.0000f, 0.9300f, 0.3162f, 0.2818f, 0.1000f, 2.0400f, 0.8300f, 0.4600f, 0.6310f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1550f, 0.0300f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_LARGEROOM \ + { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.1259f, 2.5300f, 0.8300f, 0.5000f, 0.4467f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1850f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_LONGPASSAGE \ + { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 3.4200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_HALL \ + { 1.0000f, 0.8100f, 0.3162f, 0.2818f, 0.1778f, 3.1400f, 0.7900f, 0.6200f, 0.1778f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_CUPBOARD \ + { 1.0000f, 0.8900f, 0.3162f, 0.2818f, 0.1000f, 0.6700f, 0.8700f, 0.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 3.5481f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_COURTYARD \ + { 1.0000f, 0.4200f, 0.3162f, 0.4467f, 0.1995f, 2.1300f, 0.6100f, 0.2300f, 0.2239f, 0.1600f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3700f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_CASTLE_ALCOVE \ + { 1.0000f, 0.8900f, 0.3162f, 0.5012f, 0.1000f, 1.6400f, 0.8700f, 0.3100f, 1.0000f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +/* Factory Presets */ + +#define EFX_REVERB_PRESET_FACTORY_SMALLROOM \ + { 0.3645f, 0.8200f, 0.3162f, 0.7943f, 0.5012f, 1.7200f, 0.6500f, 1.3100f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.1190f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_SHORTPASSAGE \ + { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 2.5300f, 0.6500f, 1.3100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_MEDIUMROOM \ + { 0.4287f, 0.8200f, 0.2512f, 0.7943f, 0.5012f, 2.7600f, 0.6500f, 1.3100f, 0.2818f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1740f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_LARGEROOM \ + { 0.4287f, 0.7500f, 0.2512f, 0.7079f, 0.6310f, 4.2400f, 0.5100f, 1.3100f, 0.1778f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2310f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_LONGPASSAGE \ + { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 4.0600f, 0.6500f, 1.3100f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_HALL \ + { 0.4287f, 0.7500f, 0.3162f, 0.7079f, 0.6310f, 7.4300f, 0.5100f, 1.3100f, 0.0631f, 0.0730f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_CUPBOARD \ + { 0.3071f, 0.6300f, 0.2512f, 0.7943f, 0.5012f, 0.4900f, 0.6500f, 1.3100f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.1070f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_COURTYARD \ + { 0.3071f, 0.5700f, 0.3162f, 0.3162f, 0.6310f, 2.3200f, 0.2900f, 0.5600f, 0.2239f, 0.1400f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2900f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_ALCOVE \ + { 0.3645f, 0.5900f, 0.2512f, 0.7943f, 0.5012f, 3.1400f, 0.6500f, 1.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1140f, 0.1000f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +/* Ice Palace Presets */ + +#define EFX_REVERB_PRESET_ICEPALACE_SMALLROOM \ + { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 1.5100f, 1.5300f, 0.2700f, 0.8913f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1640f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_SHORTPASSAGE \ + { 1.0000f, 0.7500f, 0.3162f, 0.5623f, 0.2818f, 1.7900f, 1.4600f, 0.2800f, 0.5012f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_MEDIUMROOM \ + { 1.0000f, 0.8700f, 0.3162f, 0.5623f, 0.4467f, 2.2200f, 1.5300f, 0.3200f, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_LARGEROOM \ + { 1.0000f, 0.8100f, 0.3162f, 0.5623f, 0.4467f, 3.1400f, 1.5300f, 0.3200f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_LONGPASSAGE \ + { 1.0000f, 0.7700f, 0.3162f, 0.5623f, 0.3981f, 3.0100f, 1.4600f, 0.2800f, 0.7943f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.0400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_HALL \ + { 1.0000f, 0.7600f, 0.3162f, 0.4467f, 0.5623f, 5.4900f, 1.5300f, 0.3800f, 0.1122f, 0.0540f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0520f, { 0.0000f, 0.0000f, 0.0000f }, 0.2260f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_CUPBOARD \ + { 1.0000f, 0.8300f, 0.3162f, 0.5012f, 0.2239f, 0.7600f, 1.5300f, 0.2600f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1430f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_COURTYARD \ + { 1.0000f, 0.5900f, 0.3162f, 0.2818f, 0.3162f, 2.0400f, 1.2000f, 0.3800f, 0.3162f, 0.1730f, { 0.0000f, 0.0000f, 0.0000f }, 0.3162f, 0.0430f, { 0.0000f, 0.0000f, 0.0000f }, 0.2350f, 0.4800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_ALCOVE \ + { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 2.7600f, 1.4600f, 0.2800f, 1.1220f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1610f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +/* Space Station Presets */ + +#define EFX_REVERB_PRESET_SPACESTATION_SMALLROOM \ + { 0.2109f, 0.7000f, 0.3162f, 0.7079f, 0.8913f, 1.7200f, 0.8200f, 0.5500f, 0.7943f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 0.1880f, 0.2600f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_SHORTPASSAGE \ + { 0.2109f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 3.5700f, 0.5000f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1720f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_MEDIUMROOM \ + { 0.2109f, 0.7500f, 0.3162f, 0.6310f, 0.8913f, 3.0100f, 0.5000f, 0.5500f, 0.3981f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2090f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_LARGEROOM \ + { 0.3645f, 0.8100f, 0.3162f, 0.6310f, 0.8913f, 3.8900f, 0.3800f, 0.6100f, 0.3162f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2330f, 0.2800f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_LONGPASSAGE \ + { 0.4287f, 0.8200f, 0.3162f, 0.6310f, 0.8913f, 4.6200f, 0.6200f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_HALL \ + { 0.4287f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 7.1100f, 0.3800f, 0.6100f, 0.1778f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2500f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_CUPBOARD \ + { 0.1715f, 0.5600f, 0.3162f, 0.7079f, 0.8913f, 0.7900f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1810f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_ALCOVE \ + { 0.2109f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.1600f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1920f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +/* Wooden Galleon Presets */ + +#define EFX_REVERB_PRESET_WOODEN_SMALLROOM \ + { 1.0000f, 1.0000f, 0.3162f, 0.1122f, 0.3162f, 0.7900f, 0.3200f, 0.8700f, 1.0000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_SHORTPASSAGE \ + { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.7500f, 0.5000f, 0.8700f, 0.8913f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_MEDIUMROOM \ + { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.2818f, 1.4700f, 0.4200f, 0.8200f, 0.8913f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_LARGEROOM \ + { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.2818f, 2.6500f, 0.3300f, 0.8200f, 0.8913f, 0.0660f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_LONGPASSAGE \ + { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.3162f, 1.9900f, 0.4000f, 0.7900f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4467f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_HALL \ + { 1.0000f, 1.0000f, 0.3162f, 0.0794f, 0.2818f, 3.4500f, 0.3000f, 0.8200f, 0.8913f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_CUPBOARD \ + { 1.0000f, 1.0000f, 0.3162f, 0.1413f, 0.3162f, 0.5600f, 0.4600f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_COURTYARD \ + { 1.0000f, 0.6500f, 0.3162f, 0.0794f, 0.3162f, 1.7900f, 0.3500f, 0.7900f, 0.5623f, 0.1230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_ALCOVE \ + { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.2200f, 0.6200f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +/* Sports Presets */ + +#define EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM \ + { 1.0000f, 1.0000f, 0.3162f, 0.4467f, 0.7943f, 6.2600f, 0.5100f, 1.1000f, 0.0631f, 0.1830f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPORT_SQUASHCOURT \ + { 1.0000f, 0.7500f, 0.3162f, 0.3162f, 0.7943f, 2.2200f, 0.9100f, 1.1600f, 0.4467f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1260f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPORT_SMALLSWIMMINGPOOL \ + { 1.0000f, 0.7000f, 0.3162f, 0.7943f, 0.8913f, 2.7600f, 1.2500f, 1.1400f, 0.6310f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_SPORT_LARGESWIMMINGPOOL \ + { 1.0000f, 0.8200f, 0.3162f, 0.7943f, 1.0000f, 5.4900f, 1.3100f, 1.1400f, 0.4467f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2220f, 0.5500f, 1.1590f, 0.2100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_SPORT_GYMNASIUM \ + { 1.0000f, 0.8100f, 0.3162f, 0.4467f, 0.8913f, 3.1400f, 1.0600f, 1.3500f, 0.3981f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0450f, { 0.0000f, 0.0000f, 0.0000f }, 0.1460f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPORT_FULLSTADIUM \ + { 1.0000f, 1.0000f, 0.3162f, 0.0708f, 0.7943f, 5.2500f, 0.1700f, 0.8000f, 0.1000f, 0.1880f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPORT_STADIUMTANNOY \ + { 1.0000f, 0.7800f, 0.3162f, 0.5623f, 0.5012f, 2.5300f, 0.8800f, 0.6800f, 0.2818f, 0.2300f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +/* Prefab Presets */ + +#define EFX_REVERB_PRESET_PREFAB_WORKSHOP \ + { 0.4287f, 1.0000f, 0.3162f, 0.1413f, 0.3981f, 0.7600f, 1.0000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_PREFAB_SCHOOLROOM \ + { 0.4022f, 0.6900f, 0.3162f, 0.6310f, 0.5012f, 0.9800f, 0.4500f, 0.1800f, 1.4125f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PREFAB_PRACTISEROOM \ + { 0.4022f, 0.8700f, 0.3162f, 0.3981f, 0.5012f, 1.1200f, 0.5600f, 0.1800f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PREFAB_OUTHOUSE \ + { 1.0000f, 0.8200f, 0.3162f, 0.1122f, 0.1585f, 1.3800f, 0.3800f, 0.3500f, 0.8913f, 0.0240f, { 0.0000f, 0.0000f, -0.0000f }, 0.6310f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.1210f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_PREFAB_CARAVAN \ + { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.1259f, 0.4300f, 1.5000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +/* Dome and Pipe Presets */ + +#define EFX_REVERB_PRESET_DOME_TOMB \ + { 1.0000f, 0.7900f, 0.3162f, 0.3548f, 0.2239f, 4.1800f, 0.2100f, 0.1000f, 0.3868f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 1.6788f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_PIPE_SMALL \ + { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 5.0400f, 0.1000f, 0.1000f, 0.5012f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 2.5119f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_DOME_SAINTPAULS \ + { 1.0000f, 0.8700f, 0.3162f, 0.3548f, 0.2239f, 10.4800f, 0.1900f, 0.1000f, 0.1778f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0420f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PIPE_LONGTHIN \ + { 0.2560f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 9.2100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_PIPE_LARGE \ + { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 8.4500f, 0.1000f, 0.1000f, 0.3981f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PIPE_RESONANT \ + { 0.1373f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 6.8100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 } + +/* Outdoors Presets */ + +#define EFX_REVERB_PRESET_OUTDOORS_BACKYARD \ + { 1.0000f, 0.4500f, 0.3162f, 0.2512f, 0.5012f, 1.1200f, 0.3400f, 0.4600f, 0.4467f, 0.0690f, { 0.0000f, 0.0000f, -0.0000f }, 0.7079f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_OUTDOORS_ROLLINGPLAINS \ + { 1.0000f, 0.0000f, 0.3162f, 0.0112f, 0.6310f, 2.1300f, 0.2100f, 0.4600f, 0.1778f, 0.3000f, { 0.0000f, 0.0000f, -0.0000f }, 0.4467f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_OUTDOORS_DEEPCANYON \ + { 1.0000f, 0.7400f, 0.3162f, 0.1778f, 0.6310f, 3.8900f, 0.2100f, 0.4600f, 0.3162f, 0.2230f, { 0.0000f, 0.0000f, -0.0000f }, 0.3548f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_OUTDOORS_CREEK \ + { 1.0000f, 0.3500f, 0.3162f, 0.1778f, 0.5012f, 2.1300f, 0.2100f, 0.4600f, 0.3981f, 0.1150f, { 0.0000f, 0.0000f, -0.0000f }, 0.1995f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_OUTDOORS_VALLEY \ + { 1.0000f, 0.2800f, 0.3162f, 0.0282f, 0.1585f, 2.8800f, 0.2600f, 0.3500f, 0.1413f, 0.2630f, { 0.0000f, 0.0000f, -0.0000f }, 0.3981f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } + +/* Mood Presets */ + +#define EFX_REVERB_PRESET_MOOD_HEAVEN \ + { 1.0000f, 0.9400f, 0.3162f, 0.7943f, 0.4467f, 5.0400f, 1.1200f, 0.5600f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0800f, 2.7420f, 0.0500f, 0.9977f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_MOOD_HELL \ + { 1.0000f, 0.5700f, 0.3162f, 0.3548f, 0.4467f, 3.5700f, 0.4900f, 2.0000f, 0.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1100f, 0.0400f, 2.1090f, 0.5200f, 0.9943f, 5000.0000f, 139.5000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_MOOD_MEMORY \ + { 1.0000f, 0.8500f, 0.3162f, 0.6310f, 0.3548f, 4.0600f, 0.8200f, 0.5600f, 0.0398f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.4740f, 0.4500f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +/* Driving Presets */ + +#define EFX_REVERB_PRESET_DRIVING_COMMENTATOR \ + { 1.0000f, 0.0000f, 3.1623f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_DRIVING_PITGARAGE \ + { 0.4287f, 0.5900f, 0.3162f, 0.7079f, 0.5623f, 1.7200f, 0.9300f, 0.8700f, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_DRIVING_INCAR_RACER \ + { 0.0832f, 0.8000f, 0.3162f, 1.0000f, 0.7943f, 0.1700f, 2.0000f, 0.4100f, 1.7783f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_DRIVING_INCAR_SPORTS \ + { 0.0832f, 0.8000f, 0.3162f, 0.6310f, 1.0000f, 0.1700f, 0.7500f, 0.4100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_DRIVING_INCAR_LUXURY \ + { 0.2560f, 1.0000f, 0.3162f, 0.1000f, 0.5012f, 0.1300f, 0.4100f, 0.4600f, 0.7943f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_DRIVING_FULLGRANDSTAND \ + { 1.0000f, 1.0000f, 0.3162f, 0.2818f, 0.6310f, 3.0100f, 1.3700f, 1.2800f, 0.3548f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.1778f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_DRIVING_EMPTYGRANDSTAND \ + { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 0.7943f, 4.6200f, 1.7500f, 1.4000f, 0.2082f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_DRIVING_TUNNEL \ + { 1.0000f, 0.8100f, 0.3162f, 0.3981f, 0.8913f, 3.4200f, 0.9400f, 1.3100f, 0.7079f, 0.0510f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.0500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 155.3000f, 0.0000f, 0x1 } + +/* City Presets */ + +#define EFX_REVERB_PRESET_CITY_STREETS \ + { 1.0000f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.7900f, 1.1200f, 0.9100f, 0.2818f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 0.1995f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CITY_SUBWAY \ + { 1.0000f, 0.7400f, 0.3162f, 0.7079f, 0.8913f, 3.0100f, 1.2300f, 0.9100f, 0.7079f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CITY_MUSEUM \ + { 1.0000f, 0.8200f, 0.3162f, 0.1778f, 0.1778f, 3.2800f, 1.4000f, 0.5700f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_CITY_LIBRARY \ + { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.0891f, 2.7600f, 0.8900f, 0.4100f, 0.3548f, 0.0290f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_CITY_UNDERPASS \ + { 1.0000f, 0.8200f, 0.3162f, 0.4467f, 0.8913f, 3.5700f, 1.1200f, 0.9100f, 0.3981f, 0.0590f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1400f, 0.2500f, 0.0000f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CITY_ABANDONED \ + { 1.0000f, 0.6900f, 0.3162f, 0.7943f, 0.8913f, 3.2800f, 1.1700f, 0.9100f, 0.4467f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9966f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +/* Misc. Presets */ + +#define EFX_REVERB_PRESET_DUSTYROOM \ + { 0.3645f, 0.5600f, 0.3162f, 0.7943f, 0.7079f, 1.7900f, 0.3800f, 0.2100f, 0.5012f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0060f, { 0.0000f, 0.0000f, 0.0000f }, 0.2020f, 0.0500f, 0.2500f, 0.0000f, 0.9886f, 13046.0000f, 163.3000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CHAPEL \ + { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 1.0000f, 4.6200f, 0.6400f, 1.2300f, 0.4467f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.1100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SMALLWATERROOM \ + { 1.0000f, 0.7000f, 0.3162f, 0.4477f, 1.0000f, 1.5100f, 1.2500f, 1.1400f, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#endif /* EFX_PRESETS_H */ diff --git a/libs/openal-soft/include/AL/efx.h b/libs/openal-soft/include/AL/efx.h new file mode 100644 index 00000000..57766983 --- /dev/null +++ b/libs/openal-soft/include/AL/efx.h @@ -0,0 +1,761 @@ +#ifndef AL_EFX_H +#define AL_EFX_H + + +#include "alc.h" +#include "al.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, const ALuint*); +typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint); +typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*); +typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat); +typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const 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, const ALuint*); +typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint); +typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*); +typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat); +typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const 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, const ALuint*); +typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const 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, const 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, const ALint *piValues); +AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue); +AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const 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, const 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, const ALint *piValues); +AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue); +AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const 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, const 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, const ALint *piValues); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const 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 --git a/libs/openal-soft/include/align.h b/libs/openal-soft/include/align.h new file mode 100644 index 00000000..babd4106 --- /dev/null +++ b/libs/openal-soft/include/align.h @@ -0,0 +1,21 @@ +#ifndef AL_ALIGN_H +#define AL_ALIGN_H + +#ifdef HAVE_STDALIGN_H +#include +#endif + +#ifndef alignas +#ifdef HAVE_C11_ALIGNAS +#define alignas _Alignas +#elif defined(IN_IDE_PARSER) +/* KDevelop has problems with our align macro, so just use nothing for parsing. */ +#define alignas(x) +#else +/* NOTE: Our custom ALIGN macro can't take a type name like alignas can. For + * maximum compatibility, only provide constant integer values to alignas. */ +#define alignas(_x) ALIGN(_x) +#endif +#endif + +#endif /* AL_ALIGN_H */ diff --git a/libs/openal-soft/include/atomic.h b/libs/openal-soft/include/atomic.h new file mode 100644 index 00000000..d761890e --- /dev/null +++ b/libs/openal-soft/include/atomic.h @@ -0,0 +1,313 @@ +#ifndef AL_ATOMIC_H +#define AL_ATOMIC_H + +#include "static_assert.h" +#include "bool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *volatile XchgPtr; + +/* Atomics using C11 */ +#ifdef HAVE_C11_ATOMIC + +#include + +inline int ExchangeInt(volatile int *ptr, int newval) +{ return atomic_exchange(ptr, newval); } +inline void *ExchangePtr(XchgPtr *ptr, void *newval) +{ return atomic_exchange(ptr, newval); } + + +#define ATOMIC(T) struct { T _Atomic value; } + +#define ATOMIC_INIT(_val, _newval) atomic_init(&(_val)->value, (_newval)) +#define ATOMIC_INIT_STATIC(_newval) {ATOMIC_VAR_INIT(_newval)} + +#define ATOMIC_LOAD(_val) atomic_load(&(_val)->value) +#define ATOMIC_STORE(_val, _newval) atomic_store(&(_val)->value, (_newval)) + +#define ATOMIC_ADD(T, _val, _incr) atomic_fetch_add(&(_val)->value, (_incr)) +#define ATOMIC_SUB(T, _val, _decr) atomic_fetch_sub(&(_val)->value, (_decr)) + +#define ATOMIC_EXCHANGE(T, _val, _newval) atomic_exchange(&(_val)->value, (_newval)) +#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval) \ + atomic_compare_exchange_strong(&(_val)->value, (_oldval), (_newval)) +#define ATOMIC_COMPARE_EXCHANGE_WEAK(T, _val, _oldval, _newval) \ + atomic_compare_exchange_weak(&(_val)->value, (_oldval), (_newval)) + +/* Atomics using GCC intrinsics */ +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && !defined(__QNXNTO__) + +inline int ExchangeInt(volatile int *ptr, int newval) +{ return __sync_lock_test_and_set(ptr, newval); } +inline void *ExchangePtr(XchgPtr *ptr, void *newval) +{ return __sync_lock_test_and_set(ptr, newval); } + + +#define ATOMIC(T) struct { T volatile value; } + +#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0) +#define ATOMIC_INIT_STATIC(_newval) {(_newval)} + +#define ATOMIC_LOAD(_val) __extension__({ \ + __typeof((_val)->value) _r = (_val)->value; \ + __asm__ __volatile__("" ::: "memory"); \ + _r; \ +}) +#define ATOMIC_STORE(_val, _newval) do { \ + __asm__ __volatile__("" ::: "memory"); \ + (_val)->value = (_newval); \ +} while(0) + +#define ATOMIC_ADD(T, _val, _incr) __extension__({ \ + static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ + __sync_fetch_and_add(&(_val)->value, (_incr)); \ +}) +#define ATOMIC_SUB(T, _val, _decr) __extension__({ \ + static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ + __sync_fetch_and_sub(&(_val)->value, (_decr)); \ +}) + +#define ATOMIC_EXCHANGE(T, _val, _newval) __extension__({ \ + static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ + __sync_lock_test_and_set(&(_val)->value, (_newval)); \ +}) +#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval) __extension__({ \ + static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ + T _o = *(_oldval); \ + *(_oldval) = __sync_val_compare_and_swap(&(_val)->value, _o, (_newval)); \ + *(_oldval) == _o; \ +}) + +/* Atomics using x86/x86-64 GCC inline assembly */ +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +#define WRAP_ADD(ret, dest, incr) __asm__ __volatile__( \ + "lock; xaddl %0,(%1)" \ + : "=r" (ret) \ + : "r" (dest), "0" (incr) \ + : "memory" \ +) +#define WRAP_SUB(ret, dest, decr) __asm__ __volatile__( \ + "lock; xaddl %0,(%1)" \ + : "=r" (ret) \ + : "r" (dest), "0" (-(decr)) \ + : "memory" \ +) + +#define WRAP_XCHG(S, ret, dest, newval) __asm__ __volatile__( \ + "lock; xchg"S" %0,(%1)" \ + : "=r" (ret) \ + : "r" (dest), "0" (newval) \ + : "memory" \ +) +#define WRAP_CMPXCHG(S, ret, dest, oldval, newval) __asm__ __volatile__( \ + "lock; cmpxchg"S" %2,(%1)" \ + : "=a" (ret) \ + : "r" (dest), "r" (newval), "0" (oldval) \ + : "memory" \ +) + + +inline int ExchangeInt(volatile int *dest, int newval) +{ int ret; WRAP_XCHG("l", ret, dest, newval); return ret; } + +#ifdef __i386__ +inline void *ExchangePtr(XchgPtr *dest, void *newval) +{ void *ret; WRAP_XCHG("l", ret, dest, newval); return ret; } +#else +inline void *ExchangePtr(XchgPtr *dest, void *newval) +{ void *ret; WRAP_XCHG("q", ret, dest, newval); return ret; } +#endif + + +#define ATOMIC(T) struct { T volatile value; } + +#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0) +#define ATOMIC_INIT_STATIC(_newval) {(_newval)} + +#define ATOMIC_LOAD(_val) __extension__({ \ + __typeof((_val)->value) _r = (_val)->value; \ + __asm__ __volatile__("" ::: "memory"); \ + _r; \ +}) +#define ATOMIC_STORE(_val, _newval) do { \ + __asm__ __volatile__("" ::: "memory"); \ + (_val)->value = (_newval); \ +} while(0) + +#define ATOMIC_ADD(T, _val, _incr) __extension__({ \ + static_assert(sizeof(T)==4, "Type "#T" has incorrect size!"); \ + static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ + T _r; \ + WRAP_ADD(_r, &(_val)->value, (T)(_incr)); \ + _r; \ +}) +#define ATOMIC_SUB(T, _val, _decr) __extension__({ \ + static_assert(sizeof(T)==4, "Type "#T" has incorrect size!"); \ + static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ + T _r; \ + WRAP_SUB(_r, &(_val)->value, (T)(_decr)); \ + _r; \ +}) + +#define ATOMIC_EXCHANGE(T, _val, _newval) __extension__({ \ + static_assert(sizeof(T)==4 || sizeof(T)==8, "Type "#T" has incorrect size!"); \ + static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ + T _r; \ + if(sizeof(T) == 4) WRAP_XCHG("l", _r, &(_val)->value, (T)(_newval)); \ + else if(sizeof(T) == 8) WRAP_XCHG("q", _r, &(_val)->value, (T)(_newval)); \ + _r; \ +}) +#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval) __extension__({ \ + static_assert(sizeof(T)==4 || sizeof(T)==8, "Type "#T" has incorrect size!"); \ + static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ + T _old = *(_oldval); \ + if(sizeof(T) == 4) WRAP_CMPXCHG("l", *(_oldval), &(_val)->value, _old, (T)(_newval)); \ + else if(sizeof(T) == 8) WRAP_CMPXCHG("q", *(_oldval), &(_val)->value, _old, (T)(_newval)); \ + *(_oldval) == _old; \ +}) + +/* Atomics using Windows methods */ +#elif defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN +#include + +/* NOTE: This mess is *extremely* noisy, at least on GCC. It works by wrapping + * Windows' 32-bit and 64-bit atomic methods, which are then casted to use the + * given type based on its size (e.g. int and float use 32-bit atomics). This + * is fine for the swap and compare-and-swap methods, although the add and + * subtract methods only work properly for integer types. + * + * Despite how noisy it is, it's unfortunately the only way that doesn't rely + * on C99 (damn MSVC). + */ + +inline LONG AtomicAdd32(volatile LONG *dest, LONG incr) +{ + return InterlockedExchangeAdd(dest, incr); +} +inline LONG AtomicSub32(volatile LONG *dest, LONG decr) +{ + return InterlockedExchangeAdd(dest, -decr); +} + +inline LONG AtomicSwap32(volatile LONG *dest, LONG newval) +{ + return InterlockedExchange(dest, newval); +} +inline LONGLONG AtomicSwap64(volatile LONGLONG *dest, LONGLONG newval) +{ + return InterlockedExchange64(dest, newval); +} + +inline bool CompareAndSwap32(volatile LONG *dest, LONG newval, LONG *oldval) +{ + LONG old = *oldval; + *oldval = InterlockedCompareExchange(dest, newval, *oldval); + return old == *oldval; +} +inline bool CompareAndSwap64(volatile LONGLONG *dest, LONGLONG newval, LONGLONG *oldval) +{ + LONGLONG old = *oldval; + *oldval = InterlockedCompareExchange64(dest, newval, *oldval); + return old == *oldval; +} + +#define WRAP_ADDSUB(T, _func, _ptr, _amnt) ((T(*)(T volatile*,T))_func)((_ptr), (_amnt)) +#define WRAP_XCHG(T, _func, _ptr, _newval) ((T(*)(T volatile*,T))_func)((_ptr), (_newval)) +#define WRAP_CMPXCHG(T, _func, _ptr, _newval, _oldval) ((bool(*)(T volatile*,T,T*))_func)((_ptr), (_newval), (_oldval)) + +inline int ExchangeInt(volatile int *ptr, int newval) +{ return WRAP_XCHG(int,AtomicSwap32,ptr,newval); } + +#ifdef _WIN64 +inline void *ExchangePtr(XchgPtr *ptr, void *newval) +{ return WRAP_XCHG(void*,AtomicSwap64,ptr,newval); } +#else +inline void *ExchangePtr(XchgPtr *ptr, void *newval) +{ return WRAP_XCHG(void*,AtomicSwap32,ptr,newval); } +#endif + + +#define ATOMIC(T) struct { T volatile value; } + +#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0) +#define ATOMIC_INIT_STATIC(_newval) {(_newval)} + +#define ATOMIC_LOAD(_val) ((_val)->value) +#define ATOMIC_STORE(_val, _newval) do { \ + (_val)->value = (_newval); \ +} while(0) + +int _al_invalid_atomic_size(); /* not defined */ + +#define ATOMIC_ADD(T, _val, _incr) \ + ((sizeof(T)==4) ? WRAP_ADDSUB(T, AtomicAdd32, &(_val)->value, (_incr)) : \ + (T)_al_invalid_atomic_size()) +#define ATOMIC_SUB(T, _val, _decr) \ + ((sizeof(T)==4) ? WRAP_ADDSUB(T, AtomicSub32, &(_val)->value, (_decr)) : \ + (T)_al_invalid_atomic_size()) + +#define ATOMIC_EXCHANGE(T, _val, _newval) \ + ((sizeof(T)==4) ? WRAP_XCHG(T, AtomicSwap32, &(_val)->value, (_newval)) : \ + (sizeof(T)==8) ? WRAP_XCHG(T, AtomicSwap64, &(_val)->value, (_newval)) : \ + (T)_al_invalid_atomic_size()) +#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval) \ + ((sizeof(T)==4) ? WRAP_CMPXCHG(T, CompareAndSwap32, &(_val)->value, (_newval), (_oldval)) : \ + (sizeof(T)==8) ? WRAP_CMPXCHG(T, CompareAndSwap64, &(_val)->value, (_newval), (_oldval)) : \ + (bool)_al_invalid_atomic_size()) + +#else + +#error "No atomic functions available on this platform!" + +#define ATOMIC(T) T + +#define ATOMIC_INIT_STATIC(_newval) (0) + +#define ATOMIC_LOAD_UNSAFE(_val) (0) +#define ATOMIC_STORE_UNSAFE(_val, _newval) ((void)0) + +#define ATOMIC_LOAD(_val) (0) +#define ATOMIC_STORE(_val, _newval) ((void)0) + +#define ATOMIC_ADD(T, _val, _incr) (0) +#define ATOMIC_SUB(T, _val, _decr) (0) + +#define ATOMIC_EXCHANGE(T, _val, _newval) (0) +#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval) (0) +#endif + +/* If no weak cmpxchg is provided (not all systems will have one), substitute a + * strong cmpxchg. */ +#ifndef ATOMIC_COMPARE_EXCHANGE_WEAK +#define ATOMIC_COMPARE_EXCHANGE_WEAK(a, b, c, d) ATOMIC_COMPARE_EXCHANGE_STRONG(a, b, c, d) +#endif + +/* This is *NOT* atomic, but is a handy utility macro to compare-and-swap non- + * atomic variables. */ +#define COMPARE_EXCHANGE(_val, _oldval, _newval) ((*(_val) == *(_oldval)) ? ((*(_val)=(_newval)),true) : ((*(_oldval)=*(_val)),false)) + + +typedef unsigned int uint; +typedef ATOMIC(uint) RefCount; + +inline void InitRef(RefCount *ptr, uint value) +{ ATOMIC_INIT(ptr, value); } +inline uint ReadRef(RefCount *ptr) +{ return ATOMIC_LOAD(ptr); } +inline uint IncrementRef(RefCount *ptr) +{ return ATOMIC_ADD(uint, ptr, 1)+1; } +inline uint DecrementRef(RefCount *ptr) +{ return ATOMIC_SUB(uint, ptr, 1)-1; } + +#ifdef __cplusplus +} +#endif + +#endif /* AL_ATOMIC_H */ diff --git a/libs/openal-soft/include/bool.h b/libs/openal-soft/include/bool.h new file mode 100644 index 00000000..6f714d09 --- /dev/null +++ b/libs/openal-soft/include/bool.h @@ -0,0 +1,18 @@ +#ifndef AL_BOOL_H +#define AL_BOOL_H + +#ifdef HAVE_STDBOOL_H +#include +#endif + +#ifndef bool +#ifdef HAVE_C99_BOOL +#define bool _Bool +#else +#define bool int +#endif +#define false 0 +#define true 1 +#endif + +#endif /* AL_BOOL_H */ diff --git a/libs/openal-soft/include/rwlock.h b/libs/openal-soft/include/rwlock.h new file mode 100644 index 00000000..158a0670 --- /dev/null +++ b/libs/openal-soft/include/rwlock.h @@ -0,0 +1,32 @@ +#ifndef AL_RWLOCK_H +#define AL_RWLOCK_H + +#include "bool.h" +#include "atomic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + RefCount read_count; + RefCount write_count; + ATOMIC(int) read_lock; + ATOMIC(int) read_entry_lock; + ATOMIC(int) write_lock; +} RWLock; +#define RWLOCK_STATIC_INITIALIZE { ATOMIC_INIT_STATIC(0), ATOMIC_INIT_STATIC(0), \ + ATOMIC_INIT_STATIC(false), ATOMIC_INIT_STATIC(false), \ + ATOMIC_INIT_STATIC(false) } + +void RWLockInit(RWLock *lock); +void ReadLock(RWLock *lock); +void ReadUnlock(RWLock *lock); +void WriteLock(RWLock *lock); +void WriteUnlock(RWLock *lock); + +#ifdef __cplusplus +} +#endif + +#endif /* AL_RWLOCK_H */ diff --git a/libs/openal-soft/include/static_assert.h b/libs/openal-soft/include/static_assert.h new file mode 100644 index 00000000..65283cf0 --- /dev/null +++ b/libs/openal-soft/include/static_assert.h @@ -0,0 +1,17 @@ +#ifndef AL_STATIC_ASSERT_H +#define AL_STATIC_ASSERT_H + +#include + + +#ifndef static_assert +#ifdef HAVE_C11_STATIC_ASSERT +#define static_assert _Static_assert +#else +#define CTASTR2(_pre,_post) _pre##_post +#define CTASTR(_pre,_post) CTASTR2(_pre,_post) +#define static_assert(_cond, _msg) typedef struct { int CTASTR(static_assert_failed_at_line_,__LINE__) : !!(_cond); } CTASTR(static_assertion_,__COUNTER__) +#endif +#endif + +#endif /* AL_STATIC_ASSERT_H */ diff --git a/libs/openal-soft/include/threads.h b/libs/openal-soft/include/threads.h new file mode 100644 index 00000000..25a75625 --- /dev/null +++ b/libs/openal-soft/include/threads.h @@ -0,0 +1,243 @@ +#ifndef AL_THREADS_H +#define AL_THREADS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + althrd_success = 0, + althrd_error, + althrd_nomem, + althrd_timedout, + althrd_busy +}; + +enum { + almtx_plain = 0, + almtx_recursive = 1, + almtx_timed = 2 +}; + +typedef int (*althrd_start_t)(void*); +typedef void (*altss_dtor_t)(void*); + + +#define AL_TIME_UTC 1 + + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include + + +#ifndef _TIMESPEC_DEFINED +#define _TIMESPEC_DEFINED +struct timespec { + time_t tv_sec; + long tv_nsec; +}; + +struct itimerspec { + struct timespec it_interval; + struct timespec it_value; +}; +#endif + +typedef DWORD althrd_t; +typedef CRITICAL_SECTION almtx_t; +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 +typedef CONDITION_VARIABLE alcnd_t; +#else +typedef struct { void *Ptr; } alcnd_t; +#endif +typedef DWORD altss_t; +typedef LONG alonce_flag; + +#define AL_ONCE_FLAG_INIT 0 + +int althrd_sleep(const struct timespec *ts, struct timespec *rem); +void alcall_once(alonce_flag *once, void (*callback)(void)); + + +inline althrd_t althrd_current(void) +{ + return GetCurrentThreadId(); +} + +inline int althrd_equal(althrd_t thr0, althrd_t thr1) +{ + return thr0 == thr1; +} + +inline void althrd_exit(int res) +{ + ExitThread(res); +} + +inline void althrd_yield(void) +{ + SwitchToThread(); +} + + +inline int almtx_lock(almtx_t *mtx) +{ + if(!mtx) return althrd_error; + EnterCriticalSection(mtx); + return althrd_success; +} + +inline int almtx_unlock(almtx_t *mtx) +{ + if(!mtx) return althrd_error; + LeaveCriticalSection(mtx); + return althrd_success; +} + +inline int almtx_trylock(almtx_t *mtx) +{ + if(!mtx) return althrd_error; + if(!TryEnterCriticalSection(mtx)) + return althrd_busy; + return althrd_success; +} + + +inline void *altss_get(altss_t tss_id) +{ + return TlsGetValue(tss_id); +} + +inline int altss_set(altss_t tss_id, void *val) +{ + if(TlsSetValue(tss_id, val) == 0) + return althrd_error; + return althrd_success; +} + +#else + +#include +#include +#include + + +typedef pthread_t althrd_t; +typedef pthread_mutex_t almtx_t; +typedef pthread_cond_t alcnd_t; +typedef pthread_key_t altss_t; +typedef pthread_once_t alonce_flag; + +#define AL_ONCE_FLAG_INIT PTHREAD_ONCE_INIT + + +inline althrd_t althrd_current(void) +{ + return pthread_self(); +} + +inline int althrd_equal(althrd_t thr0, althrd_t thr1) +{ + return pthread_equal(thr0, thr1); +} + +inline void althrd_exit(int res) +{ + pthread_exit((void*)(intptr_t)res); +} + +inline void althrd_yield(void) +{ + sched_yield(); +} + +inline int althrd_sleep(const struct timespec *ts, struct timespec *rem) +{ + int ret = nanosleep(ts, rem); + if(ret != 0) + { + ret = ((errno==EINTR) ? -1 : -2); + errno = 0; + } + return ret; +} + + +inline int almtx_lock(almtx_t *mtx) +{ + if(pthread_mutex_lock(mtx) != 0) + return althrd_error; + return althrd_success; +} + +inline int almtx_unlock(almtx_t *mtx) +{ + if(pthread_mutex_unlock(mtx) != 0) + return althrd_error; + return althrd_success; +} + +inline int almtx_trylock(almtx_t *mtx) +{ + int ret = pthread_mutex_trylock(mtx); + switch(ret) + { + case 0: return althrd_success; + case EBUSY: return althrd_busy; + } + return althrd_error; +} + + +inline void *altss_get(altss_t tss_id) +{ + return pthread_getspecific(tss_id); +} + +inline int altss_set(altss_t tss_id, void *val) +{ + if(pthread_setspecific(tss_id, val) != 0) + return althrd_error; + return althrd_success; +} + + +inline void alcall_once(alonce_flag *once, void (*callback)(void)) +{ + pthread_once(once, callback); +} + +#endif + + +int althrd_create(althrd_t *thr, althrd_start_t func, void *arg); +int althrd_detach(althrd_t thr); +int althrd_join(althrd_t thr, int *res); +void althrd_setname(althrd_t thr, const char *name); + +int almtx_init(almtx_t *mtx, int type); +void almtx_destroy(almtx_t *mtx); +int almtx_timedlock(almtx_t *mtx, const struct timespec *ts); + +int alcnd_init(alcnd_t *cond); +int alcnd_signal(alcnd_t *cond); +int alcnd_broadcast(alcnd_t *cond); +int alcnd_wait(alcnd_t *cond, almtx_t *mtx); +int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point); +void alcnd_destroy(alcnd_t *cond); + +int altss_create(altss_t *tss_id, altss_dtor_t callback); +void altss_delete(altss_t tss_id); + +int altimespec_get(struct timespec *ts, int base); + +void al_nssleep(time_t sec, long nsec); + +#ifdef __cplusplus +} +#endif + +#endif /* AL_THREADS_H */ diff --git a/libs/openal-soft/include/uintmap.h b/libs/openal-soft/include/uintmap.h new file mode 100644 index 00000000..2c4c5e7a --- /dev/null +++ b/libs/openal-soft/include/uintmap.h @@ -0,0 +1,43 @@ +#ifndef AL_UINTMAP_H +#define AL_UINTMAP_H + +#include "AL/al.h" +#include "rwlock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct UIntMap { + struct { + ALuint key; + ALvoid *value; + } *array; + ALsizei size; + ALsizei maxsize; + ALsizei limit; + RWLock lock; +} UIntMap; +#define UINTMAP_STATIC_INITIALIZE_N(_n) { NULL, 0, 0, (_n), RWLOCK_STATIC_INITIALIZE } +#define UINTMAP_STATIC_INITIALIZE UINTMAP_STATIC_INITIALIZE_N(~0) + +void InitUIntMap(UIntMap *map, ALsizei limit); +void ResetUIntMap(UIntMap *map); +ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value); +ALvoid *RemoveUIntMapKey(UIntMap *map, ALuint key); +ALvoid *LookupUIntMapKey(UIntMap *map, ALuint key); + +inline void LockUIntMapRead(UIntMap *map) +{ ReadLock(&map->lock); } +inline void UnlockUIntMapRead(UIntMap *map) +{ ReadUnlock(&map->lock); } +inline void LockUIntMapWrite(UIntMap *map) +{ WriteLock(&map->lock); } +inline void UnlockUIntMapWrite(UIntMap *map) +{ WriteUnlock(&map->lock); } + +#ifdef __cplusplus +} +#endif + +#endif /* AL_UINTMAP_H */ diff --git a/libs/openal-soft/openal.pc.in b/libs/openal-soft/openal.pc.in new file mode 100644 index 00000000..8bdd4f3b --- /dev/null +++ b/libs/openal-soft/openal.pc.in @@ -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} -I${includedir}/AL @PKG_CONFIG_CFLAGS@ diff --git a/libs/openal-soft/utils/CIAIR.def b/libs/openal-soft/utils/CIAIR.def new file mode 100644 index 00000000..9589a694 --- /dev/null +++ b/libs/openal-soft/utils/CIAIR.def @@ -0,0 +1,2010 @@ +# This is a makehrtf HRIR definition file. It is used to define the layout +# and source data to be processed into an OpenAL Soft compatible HRTF. +# +# This definition is used to transform the left ear HRIRs from a data set +# used in several papers and articles by Fumitada Itakura, Kazuya Takeda, +# Mikio Ikeda, Shoji Kajita, and Takanori Nishino. +# +# The data (data02.tgz) can be obtained from The Database of Head Related +# Transfer Functions hosted by the Takeda Laboratory at Nagoya University: +# +# http://www.sp.m.is.nagoya-u.ac.jp/HRTF/database.html +# +# It is copyright 1999 by Itakura Laboratory and the Center for Integrated +# Acoustic Information Research (CIAIR) of Nagoya University and provided +# free of charge with no restrictions on use so long as the authors (above) +# are cited. + +rate = 44100 + +points = 512 + +# The CIAIR set is composed of a uniform number of azimuths for all but the +# poles (-90 and 90 degree elevation). +azimuths = 1, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 1 + +# No head radius was provided. Just use the average radius of 9 cm. +radius = 0.09 + +# The distance between the source and the listener is not known for this set, +# so 1.5 m is used. +distance = 1.5 + +# The CIAIR source azimuth is counter-clockwise, so it needs to be flipped. +# The extension of the source data may be misleading, they're ASCII text +# lists of floating point values (one per line). + +[ 9, 0 ] = ascii (fp) : "./hrtfs/elev-45/L-45e000a.dat" +[ 9, 1 ] = ascii (fp) : "./hrtfs/elev-45/L-45e355a.dat" +[ 9, 2 ] = ascii (fp) : "./hrtfs/elev-45/L-45e350a.dat" +[ 9, 3 ] = ascii (fp) : "./hrtfs/elev-45/L-45e345a.dat" +[ 9, 4 ] = ascii (fp) : "./hrtfs/elev-45/L-45e340a.dat" +[ 9, 5 ] = ascii (fp) : "./hrtfs/elev-45/L-45e335a.dat" +[ 9, 6 ] = ascii (fp) : "./hrtfs/elev-45/L-45e330a.dat" +[ 9, 7 ] = ascii (fp) : "./hrtfs/elev-45/L-45e325a.dat" +[ 9, 8 ] = ascii (fp) : "./hrtfs/elev-45/L-45e320a.dat" +[ 9, 9 ] = ascii (fp) : "./hrtfs/elev-45/L-45e315a.dat" +[ 9, 10 ] = ascii (fp) : "./hrtfs/elev-45/L-45e310a.dat" +[ 9, 11 ] = ascii (fp) : "./hrtfs/elev-45/L-45e305a.dat" +[ 9, 12 ] = ascii (fp) : "./hrtfs/elev-45/L-45e300a.dat" +[ 9, 13 ] = ascii (fp) : "./hrtfs/elev-45/L-45e295a.dat" +[ 9, 14 ] = ascii (fp) : "./hrtfs/elev-45/L-45e290a.dat" +[ 9, 15 ] = ascii (fp) : "./hrtfs/elev-45/L-45e285a.dat" +[ 9, 16 ] = ascii (fp) : "./hrtfs/elev-45/L-45e280a.dat" +[ 9, 17 ] = ascii (fp) : "./hrtfs/elev-45/L-45e275a.dat" +[ 9, 18 ] = ascii (fp) : "./hrtfs/elev-45/L-45e270a.dat" +[ 9, 19 ] = ascii (fp) : "./hrtfs/elev-45/L-45e265a.dat" +[ 9, 20 ] = ascii (fp) : "./hrtfs/elev-45/L-45e260a.dat" +[ 9, 21 ] = ascii (fp) : "./hrtfs/elev-45/L-45e255a.dat" +[ 9, 22 ] = ascii (fp) : "./hrtfs/elev-45/L-45e250a.dat" +[ 9, 23 ] = ascii (fp) : "./hrtfs/elev-45/L-45e245a.dat" +[ 9, 24 ] = ascii (fp) : "./hrtfs/elev-45/L-45e240a.dat" +[ 9, 25 ] = ascii (fp) : "./hrtfs/elev-45/L-45e235a.dat" +[ 9, 26 ] = ascii (fp) : "./hrtfs/elev-45/L-45e230a.dat" +[ 9, 27 ] = ascii (fp) : "./hrtfs/elev-45/L-45e225a.dat" +[ 9, 28 ] = ascii (fp) : "./hrtfs/elev-45/L-45e220a.dat" +[ 9, 29 ] = ascii (fp) : "./hrtfs/elev-45/L-45e215a.dat" +[ 9, 30 ] = ascii (fp) : "./hrtfs/elev-45/L-45e210a.dat" +[ 9, 31 ] = ascii (fp) : "./hrtfs/elev-45/L-45e205a.dat" +[ 9, 32 ] = ascii (fp) : "./hrtfs/elev-45/L-45e200a.dat" +[ 9, 33 ] = ascii (fp) : "./hrtfs/elev-45/L-45e195a.dat" +[ 9, 34 ] = ascii (fp) : "./hrtfs/elev-45/L-45e190a.dat" +[ 9, 35 ] = ascii (fp) : "./hrtfs/elev-45/L-45e185a.dat" +[ 9, 36 ] = ascii (fp) : "./hrtfs/elev-45/L-45e180a.dat" +[ 9, 37 ] = ascii (fp) : "./hrtfs/elev-45/L-45e175a.dat" +[ 9, 38 ] = ascii (fp) : "./hrtfs/elev-45/L-45e170a.dat" +[ 9, 39 ] = ascii (fp) : "./hrtfs/elev-45/L-45e165a.dat" +[ 9, 40 ] = ascii (fp) : "./hrtfs/elev-45/L-45e160a.dat" +[ 9, 41 ] = ascii (fp) : "./hrtfs/elev-45/L-45e155a.dat" +[ 9, 42 ] = ascii (fp) : "./hrtfs/elev-45/L-45e150a.dat" +[ 9, 43 ] = ascii (fp) : "./hrtfs/elev-45/L-45e145a.dat" +[ 9, 44 ] = ascii (fp) : "./hrtfs/elev-45/L-45e140a.dat" +[ 9, 45 ] = ascii (fp) : "./hrtfs/elev-45/L-45e135a.dat" +[ 9, 46 ] = ascii (fp) : "./hrtfs/elev-45/L-45e130a.dat" +[ 9, 47 ] = ascii (fp) : "./hrtfs/elev-45/L-45e125a.dat" +[ 9, 48 ] = ascii (fp) : "./hrtfs/elev-45/L-45e120a.dat" +[ 9, 49 ] = ascii (fp) : "./hrtfs/elev-45/L-45e115a.dat" +[ 9, 50 ] = ascii (fp) : "./hrtfs/elev-45/L-45e110a.dat" +[ 9, 51 ] = ascii (fp) : "./hrtfs/elev-45/L-45e105a.dat" +[ 9, 52 ] = ascii (fp) : "./hrtfs/elev-45/L-45e100a.dat" +[ 9, 53 ] = ascii (fp) : "./hrtfs/elev-45/L-45e095a.dat" +[ 9, 54 ] = ascii (fp) : "./hrtfs/elev-45/L-45e090a.dat" +[ 9, 55 ] = ascii (fp) : "./hrtfs/elev-45/L-45e085a.dat" +[ 9, 56 ] = ascii (fp) : "./hrtfs/elev-45/L-45e080a.dat" +[ 9, 57 ] = ascii (fp) : "./hrtfs/elev-45/L-45e075a.dat" +[ 9, 58 ] = ascii (fp) : "./hrtfs/elev-45/L-45e070a.dat" +[ 9, 59 ] = ascii (fp) : "./hrtfs/elev-45/L-45e065a.dat" +[ 9, 60 ] = ascii (fp) : "./hrtfs/elev-45/L-45e060a.dat" +[ 9, 61 ] = ascii (fp) : "./hrtfs/elev-45/L-45e055a.dat" +[ 9, 62 ] = ascii (fp) : "./hrtfs/elev-45/L-45e050a.dat" +[ 9, 63 ] = ascii (fp) : "./hrtfs/elev-45/L-45e045a.dat" +[ 9, 64 ] = ascii (fp) : "./hrtfs/elev-45/L-45e040a.dat" +[ 9, 65 ] = ascii (fp) : "./hrtfs/elev-45/L-45e035a.dat" +[ 9, 66 ] = ascii (fp) : "./hrtfs/elev-45/L-45e030a.dat" +[ 9, 67 ] = ascii (fp) : "./hrtfs/elev-45/L-45e025a.dat" +[ 9, 68 ] = ascii (fp) : "./hrtfs/elev-45/L-45e020a.dat" +[ 9, 69 ] = ascii (fp) : "./hrtfs/elev-45/L-45e015a.dat" +[ 9, 70 ] = ascii (fp) : "./hrtfs/elev-45/L-45e010a.dat" +[ 9, 71 ] = ascii (fp) : "./hrtfs/elev-45/L-45e005a.dat" + +[ 10, 0 ] = ascii (fp) : "./hrtfs/elev-40/L-40e000a.dat" +[ 10, 1 ] = ascii (fp) : "./hrtfs/elev-40/L-40e355a.dat" +[ 10, 2 ] = ascii (fp) : "./hrtfs/elev-40/L-40e350a.dat" +[ 10, 3 ] = ascii (fp) : "./hrtfs/elev-40/L-40e345a.dat" +[ 10, 4 ] = ascii (fp) : "./hrtfs/elev-40/L-40e340a.dat" +[ 10, 5 ] = ascii (fp) : "./hrtfs/elev-40/L-40e335a.dat" +[ 10, 6 ] = ascii (fp) : "./hrtfs/elev-40/L-40e330a.dat" +[ 10, 7 ] = ascii (fp) : "./hrtfs/elev-40/L-40e325a.dat" +[ 10, 8 ] = ascii (fp) : "./hrtfs/elev-40/L-40e320a.dat" +[ 10, 9 ] = ascii (fp) : "./hrtfs/elev-40/L-40e315a.dat" +[ 10, 10 ] = ascii (fp) : "./hrtfs/elev-40/L-40e310a.dat" +[ 10, 11 ] = ascii (fp) : "./hrtfs/elev-40/L-40e305a.dat" +[ 10, 12 ] = ascii (fp) : "./hrtfs/elev-40/L-40e300a.dat" +[ 10, 13 ] = ascii (fp) : "./hrtfs/elev-40/L-40e295a.dat" +[ 10, 14 ] = ascii (fp) : "./hrtfs/elev-40/L-40e290a.dat" +[ 10, 15 ] = ascii (fp) : "./hrtfs/elev-40/L-40e285a.dat" +[ 10, 16 ] = ascii (fp) : "./hrtfs/elev-40/L-40e280a.dat" +[ 10, 17 ] = ascii (fp) : "./hrtfs/elev-40/L-40e275a.dat" +[ 10, 18 ] = ascii (fp) : "./hrtfs/elev-40/L-40e270a.dat" +[ 10, 19 ] = ascii (fp) : "./hrtfs/elev-40/L-40e265a.dat" +[ 10, 20 ] = ascii (fp) : "./hrtfs/elev-40/L-40e260a.dat" +[ 10, 21 ] = ascii (fp) : "./hrtfs/elev-40/L-40e255a.dat" +[ 10, 22 ] = ascii (fp) : "./hrtfs/elev-40/L-40e250a.dat" +[ 10, 23 ] = ascii (fp) : "./hrtfs/elev-40/L-40e245a.dat" +[ 10, 24 ] = ascii (fp) : "./hrtfs/elev-40/L-40e240a.dat" +[ 10, 25 ] = ascii (fp) : "./hrtfs/elev-40/L-40e235a.dat" +[ 10, 26 ] = ascii (fp) : "./hrtfs/elev-40/L-40e230a.dat" +[ 10, 27 ] = ascii (fp) : "./hrtfs/elev-40/L-40e225a.dat" +[ 10, 28 ] = ascii (fp) : "./hrtfs/elev-40/L-40e220a.dat" +[ 10, 29 ] = ascii (fp) : "./hrtfs/elev-40/L-40e215a.dat" +[ 10, 30 ] = ascii (fp) : "./hrtfs/elev-40/L-40e210a.dat" +[ 10, 31 ] = ascii (fp) : "./hrtfs/elev-40/L-40e205a.dat" +[ 10, 32 ] = ascii (fp) : "./hrtfs/elev-40/L-40e200a.dat" +[ 10, 33 ] = ascii (fp) : "./hrtfs/elev-40/L-40e195a.dat" +[ 10, 34 ] = ascii (fp) : "./hrtfs/elev-40/L-40e190a.dat" +[ 10, 35 ] = ascii (fp) : "./hrtfs/elev-40/L-40e185a.dat" +[ 10, 36 ] = ascii (fp) : "./hrtfs/elev-40/L-40e180a.dat" +[ 10, 37 ] = ascii (fp) : "./hrtfs/elev-40/L-40e175a.dat" +[ 10, 38 ] = ascii (fp) : "./hrtfs/elev-40/L-40e170a.dat" +[ 10, 39 ] = ascii (fp) : "./hrtfs/elev-40/L-40e165a.dat" +[ 10, 40 ] = ascii (fp) : "./hrtfs/elev-40/L-40e160a.dat" +[ 10, 41 ] = ascii (fp) : "./hrtfs/elev-40/L-40e155a.dat" +[ 10, 42 ] = ascii (fp) : "./hrtfs/elev-40/L-40e150a.dat" +[ 10, 43 ] = ascii (fp) : "./hrtfs/elev-40/L-40e145a.dat" +[ 10, 44 ] = ascii (fp) : "./hrtfs/elev-40/L-40e140a.dat" +[ 10, 45 ] = ascii (fp) : "./hrtfs/elev-40/L-40e135a.dat" +[ 10, 46 ] = ascii (fp) : "./hrtfs/elev-40/L-40e130a.dat" +[ 10, 47 ] = ascii (fp) : "./hrtfs/elev-40/L-40e125a.dat" +[ 10, 48 ] = ascii (fp) : "./hrtfs/elev-40/L-40e120a.dat" +[ 10, 49 ] = ascii (fp) : "./hrtfs/elev-40/L-40e115a.dat" +[ 10, 50 ] = ascii (fp) : "./hrtfs/elev-40/L-40e110a.dat" +[ 10, 51 ] = ascii (fp) : "./hrtfs/elev-40/L-40e105a.dat" +[ 10, 52 ] = ascii (fp) : "./hrtfs/elev-40/L-40e100a.dat" +[ 10, 53 ] = ascii (fp) : "./hrtfs/elev-40/L-40e095a.dat" +[ 10, 54 ] = ascii (fp) : "./hrtfs/elev-40/L-40e090a.dat" +[ 10, 55 ] = ascii (fp) : "./hrtfs/elev-40/L-40e085a.dat" +[ 10, 56 ] = ascii (fp) : "./hrtfs/elev-40/L-40e080a.dat" +[ 10, 57 ] = ascii (fp) : "./hrtfs/elev-40/L-40e075a.dat" +[ 10, 58 ] = ascii (fp) : "./hrtfs/elev-40/L-40e070a.dat" +[ 10, 59 ] = ascii (fp) : "./hrtfs/elev-40/L-40e065a.dat" +[ 10, 60 ] = ascii (fp) : "./hrtfs/elev-40/L-40e060a.dat" +[ 10, 61 ] = ascii (fp) : "./hrtfs/elev-40/L-40e055a.dat" +[ 10, 62 ] = ascii (fp) : "./hrtfs/elev-40/L-40e050a.dat" +[ 10, 63 ] = ascii (fp) : "./hrtfs/elev-40/L-40e045a.dat" +[ 10, 64 ] = ascii (fp) : "./hrtfs/elev-40/L-40e040a.dat" +[ 10, 65 ] = ascii (fp) : "./hrtfs/elev-40/L-40e035a.dat" +[ 10, 66 ] = ascii (fp) : "./hrtfs/elev-40/L-40e030a.dat" +[ 10, 67 ] = ascii (fp) : "./hrtfs/elev-40/L-40e025a.dat" +[ 10, 68 ] = ascii (fp) : "./hrtfs/elev-40/L-40e020a.dat" +[ 10, 69 ] = ascii (fp) : "./hrtfs/elev-40/L-40e015a.dat" +[ 10, 70 ] = ascii (fp) : "./hrtfs/elev-40/L-40e010a.dat" +[ 10, 71 ] = ascii (fp) : "./hrtfs/elev-40/L-40e005a.dat" + +[ 11, 0 ] = ascii (fp) : "./hrtfs/elev-35/L-35e000a.dat" +[ 11, 1 ] = ascii (fp) : "./hrtfs/elev-35/L-35e355a.dat" +[ 11, 2 ] = ascii (fp) : "./hrtfs/elev-35/L-35e350a.dat" +[ 11, 3 ] = ascii (fp) : "./hrtfs/elev-35/L-35e345a.dat" +[ 11, 4 ] = ascii (fp) : "./hrtfs/elev-35/L-35e340a.dat" +[ 11, 5 ] = ascii (fp) : "./hrtfs/elev-35/L-35e335a.dat" +[ 11, 6 ] = ascii (fp) : "./hrtfs/elev-35/L-35e330a.dat" +[ 11, 7 ] = ascii (fp) : "./hrtfs/elev-35/L-35e325a.dat" +[ 11, 8 ] = ascii (fp) : "./hrtfs/elev-35/L-35e320a.dat" +[ 11, 9 ] = ascii (fp) : "./hrtfs/elev-35/L-35e315a.dat" +[ 11, 10 ] = ascii (fp) : "./hrtfs/elev-35/L-35e310a.dat" +[ 11, 11 ] = ascii (fp) : "./hrtfs/elev-35/L-35e305a.dat" +[ 11, 12 ] = ascii (fp) : "./hrtfs/elev-35/L-35e300a.dat" +[ 11, 13 ] = ascii (fp) : "./hrtfs/elev-35/L-35e295a.dat" +[ 11, 14 ] = ascii (fp) : "./hrtfs/elev-35/L-35e290a.dat" +[ 11, 15 ] = ascii (fp) : "./hrtfs/elev-35/L-35e285a.dat" +[ 11, 16 ] = ascii (fp) : "./hrtfs/elev-35/L-35e280a.dat" +[ 11, 17 ] = ascii (fp) : "./hrtfs/elev-35/L-35e275a.dat" +[ 11, 18 ] = ascii (fp) : "./hrtfs/elev-35/L-35e270a.dat" +[ 11, 19 ] = ascii (fp) : "./hrtfs/elev-35/L-35e265a.dat" +[ 11, 20 ] = ascii (fp) : "./hrtfs/elev-35/L-35e260a.dat" +[ 11, 21 ] = ascii (fp) : "./hrtfs/elev-35/L-35e255a.dat" +[ 11, 22 ] = ascii (fp) : "./hrtfs/elev-35/L-35e250a.dat" +[ 11, 23 ] = ascii (fp) : "./hrtfs/elev-35/L-35e245a.dat" +[ 11, 24 ] = ascii (fp) : "./hrtfs/elev-35/L-35e240a.dat" +[ 11, 25 ] = ascii (fp) : "./hrtfs/elev-35/L-35e235a.dat" +[ 11, 26 ] = ascii (fp) : "./hrtfs/elev-35/L-35e230a.dat" +[ 11, 27 ] = ascii (fp) : "./hrtfs/elev-35/L-35e225a.dat" +[ 11, 28 ] = ascii (fp) : "./hrtfs/elev-35/L-35e220a.dat" +[ 11, 29 ] = ascii (fp) : "./hrtfs/elev-35/L-35e215a.dat" +[ 11, 30 ] = ascii (fp) : "./hrtfs/elev-35/L-35e210a.dat" +[ 11, 31 ] = ascii (fp) : "./hrtfs/elev-35/L-35e205a.dat" +[ 11, 32 ] = ascii (fp) : "./hrtfs/elev-35/L-35e200a.dat" +[ 11, 33 ] = ascii (fp) : "./hrtfs/elev-35/L-35e195a.dat" +[ 11, 34 ] = ascii (fp) : "./hrtfs/elev-35/L-35e190a.dat" +[ 11, 35 ] = ascii (fp) : "./hrtfs/elev-35/L-35e185a.dat" +[ 11, 36 ] = ascii (fp) : "./hrtfs/elev-35/L-35e180a.dat" +[ 11, 37 ] = ascii (fp) : "./hrtfs/elev-35/L-35e175a.dat" +[ 11, 38 ] = ascii (fp) : "./hrtfs/elev-35/L-35e170a.dat" +[ 11, 39 ] = ascii (fp) : "./hrtfs/elev-35/L-35e165a.dat" +[ 11, 40 ] = ascii (fp) : "./hrtfs/elev-35/L-35e160a.dat" +[ 11, 41 ] = ascii (fp) : "./hrtfs/elev-35/L-35e155a.dat" +[ 11, 42 ] = ascii (fp) : "./hrtfs/elev-35/L-35e150a.dat" +[ 11, 43 ] = ascii (fp) : "./hrtfs/elev-35/L-35e145a.dat" +[ 11, 44 ] = ascii (fp) : "./hrtfs/elev-35/L-35e140a.dat" +[ 11, 45 ] = ascii (fp) : "./hrtfs/elev-35/L-35e135a.dat" +[ 11, 46 ] = ascii (fp) : "./hrtfs/elev-35/L-35e130a.dat" +[ 11, 47 ] = ascii (fp) : "./hrtfs/elev-35/L-35e125a.dat" +[ 11, 48 ] = ascii (fp) : "./hrtfs/elev-35/L-35e120a.dat" +[ 11, 49 ] = ascii (fp) : "./hrtfs/elev-35/L-35e115a.dat" +[ 11, 50 ] = ascii (fp) : "./hrtfs/elev-35/L-35e110a.dat" +[ 11, 51 ] = ascii (fp) : "./hrtfs/elev-35/L-35e105a.dat" +[ 11, 52 ] = ascii (fp) : "./hrtfs/elev-35/L-35e100a.dat" +[ 11, 53 ] = ascii (fp) : "./hrtfs/elev-35/L-35e095a.dat" +[ 11, 54 ] = ascii (fp) : "./hrtfs/elev-35/L-35e090a.dat" +[ 11, 55 ] = ascii (fp) : "./hrtfs/elev-35/L-35e085a.dat" +[ 11, 56 ] = ascii (fp) : "./hrtfs/elev-35/L-35e080a.dat" +[ 11, 57 ] = ascii (fp) : "./hrtfs/elev-35/L-35e075a.dat" +[ 11, 58 ] = ascii (fp) : "./hrtfs/elev-35/L-35e070a.dat" +[ 11, 59 ] = ascii (fp) : "./hrtfs/elev-35/L-35e065a.dat" +[ 11, 60 ] = ascii (fp) : "./hrtfs/elev-35/L-35e060a.dat" +[ 11, 61 ] = ascii (fp) : "./hrtfs/elev-35/L-35e055a.dat" +[ 11, 62 ] = ascii (fp) : "./hrtfs/elev-35/L-35e050a.dat" +[ 11, 63 ] = ascii (fp) : "./hrtfs/elev-35/L-35e045a.dat" +[ 11, 64 ] = ascii (fp) : "./hrtfs/elev-35/L-35e040a.dat" +[ 11, 65 ] = ascii (fp) : "./hrtfs/elev-35/L-35e035a.dat" +[ 11, 66 ] = ascii (fp) : "./hrtfs/elev-35/L-35e030a.dat" +[ 11, 67 ] = ascii (fp) : "./hrtfs/elev-35/L-35e025a.dat" +[ 11, 68 ] = ascii (fp) : "./hrtfs/elev-35/L-35e020a.dat" +[ 11, 69 ] = ascii (fp) : "./hrtfs/elev-35/L-35e015a.dat" +[ 11, 70 ] = ascii (fp) : "./hrtfs/elev-35/L-35e010a.dat" +[ 11, 71 ] = ascii (fp) : "./hrtfs/elev-35/L-35e005a.dat" + +[ 12, 0 ] = ascii (fp) : "./hrtfs/elev-30/L-30e000a.dat" +[ 12, 1 ] = ascii (fp) : "./hrtfs/elev-30/L-30e355a.dat" +[ 12, 2 ] = ascii (fp) : "./hrtfs/elev-30/L-30e350a.dat" +[ 12, 3 ] = ascii (fp) : "./hrtfs/elev-30/L-30e345a.dat" +[ 12, 4 ] = ascii (fp) : "./hrtfs/elev-30/L-30e340a.dat" +[ 12, 5 ] = ascii (fp) : "./hrtfs/elev-30/L-30e335a.dat" +[ 12, 6 ] = ascii (fp) : "./hrtfs/elev-30/L-30e330a.dat" +[ 12, 7 ] = ascii (fp) : "./hrtfs/elev-30/L-30e325a.dat" +[ 12, 8 ] = ascii (fp) : "./hrtfs/elev-30/L-30e320a.dat" +[ 12, 9 ] = ascii (fp) : "./hrtfs/elev-30/L-30e315a.dat" +[ 12, 10 ] = ascii (fp) : "./hrtfs/elev-30/L-30e310a.dat" +[ 12, 11 ] = ascii (fp) : "./hrtfs/elev-30/L-30e305a.dat" +[ 12, 12 ] = ascii (fp) : "./hrtfs/elev-30/L-30e300a.dat" +[ 12, 13 ] = ascii (fp) : "./hrtfs/elev-30/L-30e295a.dat" +[ 12, 14 ] = ascii (fp) : "./hrtfs/elev-30/L-30e290a.dat" +[ 12, 15 ] = ascii (fp) : "./hrtfs/elev-30/L-30e285a.dat" +[ 12, 16 ] = ascii (fp) : "./hrtfs/elev-30/L-30e280a.dat" +[ 12, 17 ] = ascii (fp) : "./hrtfs/elev-30/L-30e275a.dat" +[ 12, 18 ] = ascii (fp) : "./hrtfs/elev-30/L-30e270a.dat" +[ 12, 19 ] = ascii (fp) : "./hrtfs/elev-30/L-30e265a.dat" +[ 12, 20 ] = ascii (fp) : "./hrtfs/elev-30/L-30e260a.dat" +[ 12, 21 ] = ascii (fp) : "./hrtfs/elev-30/L-30e255a.dat" +[ 12, 22 ] = ascii (fp) : "./hrtfs/elev-30/L-30e250a.dat" +[ 12, 23 ] = ascii (fp) : "./hrtfs/elev-30/L-30e245a.dat" +[ 12, 24 ] = ascii (fp) : "./hrtfs/elev-30/L-30e240a.dat" +[ 12, 25 ] = ascii (fp) : "./hrtfs/elev-30/L-30e235a.dat" +[ 12, 26 ] = ascii (fp) : "./hrtfs/elev-30/L-30e230a.dat" +[ 12, 27 ] = ascii (fp) : "./hrtfs/elev-30/L-30e225a.dat" +[ 12, 28 ] = ascii (fp) : "./hrtfs/elev-30/L-30e220a.dat" +[ 12, 29 ] = ascii (fp) : "./hrtfs/elev-30/L-30e215a.dat" +[ 12, 30 ] = ascii (fp) : "./hrtfs/elev-30/L-30e210a.dat" +[ 12, 31 ] = ascii (fp) : "./hrtfs/elev-30/L-30e205a.dat" +[ 12, 32 ] = ascii (fp) : "./hrtfs/elev-30/L-30e200a.dat" +[ 12, 33 ] = ascii (fp) : "./hrtfs/elev-30/L-30e195a.dat" +[ 12, 34 ] = ascii (fp) : "./hrtfs/elev-30/L-30e190a.dat" +[ 12, 35 ] = ascii (fp) : "./hrtfs/elev-30/L-30e185a.dat" +[ 12, 36 ] = ascii (fp) : "./hrtfs/elev-30/L-30e180a.dat" +[ 12, 37 ] = ascii (fp) : "./hrtfs/elev-30/L-30e175a.dat" +[ 12, 38 ] = ascii (fp) : "./hrtfs/elev-30/L-30e170a.dat" +[ 12, 39 ] = ascii (fp) : "./hrtfs/elev-30/L-30e165a.dat" +[ 12, 40 ] = ascii (fp) : "./hrtfs/elev-30/L-30e160a.dat" +[ 12, 41 ] = ascii (fp) : "./hrtfs/elev-30/L-30e155a.dat" +[ 12, 42 ] = ascii (fp) : "./hrtfs/elev-30/L-30e150a.dat" +[ 12, 43 ] = ascii (fp) : "./hrtfs/elev-30/L-30e145a.dat" +[ 12, 44 ] = ascii (fp) : "./hrtfs/elev-30/L-30e140a.dat" +[ 12, 45 ] = ascii (fp) : "./hrtfs/elev-30/L-30e135a.dat" +[ 12, 46 ] = ascii (fp) : "./hrtfs/elev-30/L-30e130a.dat" +[ 12, 47 ] = ascii (fp) : "./hrtfs/elev-30/L-30e125a.dat" +[ 12, 48 ] = ascii (fp) : "./hrtfs/elev-30/L-30e120a.dat" +[ 12, 49 ] = ascii (fp) : "./hrtfs/elev-30/L-30e115a.dat" +[ 12, 50 ] = ascii (fp) : "./hrtfs/elev-30/L-30e110a.dat" +[ 12, 51 ] = ascii (fp) : "./hrtfs/elev-30/L-30e105a.dat" +[ 12, 52 ] = ascii (fp) : "./hrtfs/elev-30/L-30e100a.dat" +[ 12, 53 ] = ascii (fp) : "./hrtfs/elev-30/L-30e095a.dat" +[ 12, 54 ] = ascii (fp) : "./hrtfs/elev-30/L-30e090a.dat" +[ 12, 55 ] = ascii (fp) : "./hrtfs/elev-30/L-30e085a.dat" +[ 12, 56 ] = ascii (fp) : "./hrtfs/elev-30/L-30e080a.dat" +[ 12, 57 ] = ascii (fp) : "./hrtfs/elev-30/L-30e075a.dat" +[ 12, 58 ] = ascii (fp) : "./hrtfs/elev-30/L-30e070a.dat" +[ 12, 59 ] = ascii (fp) : "./hrtfs/elev-30/L-30e065a.dat" +[ 12, 60 ] = ascii (fp) : "./hrtfs/elev-30/L-30e060a.dat" +[ 12, 61 ] = ascii (fp) : "./hrtfs/elev-30/L-30e055a.dat" +[ 12, 62 ] = ascii (fp) : "./hrtfs/elev-30/L-30e050a.dat" +[ 12, 63 ] = ascii (fp) : "./hrtfs/elev-30/L-30e045a.dat" +[ 12, 64 ] = ascii (fp) : "./hrtfs/elev-30/L-30e040a.dat" +[ 12, 65 ] = ascii (fp) : "./hrtfs/elev-30/L-30e035a.dat" +[ 12, 66 ] = ascii (fp) : "./hrtfs/elev-30/L-30e030a.dat" +[ 12, 67 ] = ascii (fp) : "./hrtfs/elev-30/L-30e025a.dat" +[ 12, 68 ] = ascii (fp) : "./hrtfs/elev-30/L-30e020a.dat" +[ 12, 69 ] = ascii (fp) : "./hrtfs/elev-30/L-30e015a.dat" +[ 12, 70 ] = ascii (fp) : "./hrtfs/elev-30/L-30e010a.dat" +[ 12, 71 ] = ascii (fp) : "./hrtfs/elev-30/L-30e005a.dat" + +[ 13, 0 ] = ascii (fp) : "./hrtfs/elev-25/L-25e000a.dat" +[ 13, 1 ] = ascii (fp) : "./hrtfs/elev-25/L-25e355a.dat" +[ 13, 2 ] = ascii (fp) : "./hrtfs/elev-25/L-25e350a.dat" +[ 13, 3 ] = ascii (fp) : "./hrtfs/elev-25/L-25e345a.dat" +[ 13, 4 ] = ascii (fp) : "./hrtfs/elev-25/L-25e340a.dat" +[ 13, 5 ] = ascii (fp) : "./hrtfs/elev-25/L-25e335a.dat" +[ 13, 6 ] = ascii (fp) : "./hrtfs/elev-25/L-25e330a.dat" +[ 13, 7 ] = ascii (fp) : "./hrtfs/elev-25/L-25e325a.dat" +[ 13, 8 ] = ascii (fp) : "./hrtfs/elev-25/L-25e320a.dat" +[ 13, 9 ] = ascii (fp) : "./hrtfs/elev-25/L-25e315a.dat" +[ 13, 10 ] = ascii (fp) : "./hrtfs/elev-25/L-25e310a.dat" +[ 13, 11 ] = ascii (fp) : "./hrtfs/elev-25/L-25e305a.dat" +[ 13, 12 ] = ascii (fp) : "./hrtfs/elev-25/L-25e300a.dat" +[ 13, 13 ] = ascii (fp) : "./hrtfs/elev-25/L-25e295a.dat" +[ 13, 14 ] = ascii (fp) : "./hrtfs/elev-25/L-25e290a.dat" +[ 13, 15 ] = ascii (fp) : "./hrtfs/elev-25/L-25e285a.dat" +[ 13, 16 ] = ascii (fp) : "./hrtfs/elev-25/L-25e280a.dat" +[ 13, 17 ] = ascii (fp) : "./hrtfs/elev-25/L-25e275a.dat" +[ 13, 18 ] = ascii (fp) : "./hrtfs/elev-25/L-25e270a.dat" +[ 13, 19 ] = ascii (fp) : "./hrtfs/elev-25/L-25e265a.dat" +[ 13, 20 ] = ascii (fp) : "./hrtfs/elev-25/L-25e260a.dat" +[ 13, 21 ] = ascii (fp) : "./hrtfs/elev-25/L-25e255a.dat" +[ 13, 22 ] = ascii (fp) : "./hrtfs/elev-25/L-25e250a.dat" +[ 13, 23 ] = ascii (fp) : "./hrtfs/elev-25/L-25e245a.dat" +[ 13, 24 ] = ascii (fp) : "./hrtfs/elev-25/L-25e240a.dat" +[ 13, 25 ] = ascii (fp) : "./hrtfs/elev-25/L-25e235a.dat" +[ 13, 26 ] = ascii (fp) : "./hrtfs/elev-25/L-25e230a.dat" +[ 13, 27 ] = ascii (fp) : "./hrtfs/elev-25/L-25e225a.dat" +[ 13, 28 ] = ascii (fp) : "./hrtfs/elev-25/L-25e220a.dat" +[ 13, 29 ] = ascii (fp) : "./hrtfs/elev-25/L-25e215a.dat" +[ 13, 30 ] = ascii (fp) : "./hrtfs/elev-25/L-25e210a.dat" +[ 13, 31 ] = ascii (fp) : "./hrtfs/elev-25/L-25e205a.dat" +[ 13, 32 ] = ascii (fp) : "./hrtfs/elev-25/L-25e200a.dat" +[ 13, 33 ] = ascii (fp) : "./hrtfs/elev-25/L-25e195a.dat" +[ 13, 34 ] = ascii (fp) : "./hrtfs/elev-25/L-25e190a.dat" +[ 13, 35 ] = ascii (fp) : "./hrtfs/elev-25/L-25e185a.dat" +[ 13, 36 ] = ascii (fp) : "./hrtfs/elev-25/L-25e180a.dat" +[ 13, 37 ] = ascii (fp) : "./hrtfs/elev-25/L-25e175a.dat" +[ 13, 38 ] = ascii (fp) : "./hrtfs/elev-25/L-25e170a.dat" +[ 13, 39 ] = ascii (fp) : "./hrtfs/elev-25/L-25e165a.dat" +[ 13, 40 ] = ascii (fp) : "./hrtfs/elev-25/L-25e160a.dat" +[ 13, 41 ] = ascii (fp) : "./hrtfs/elev-25/L-25e155a.dat" +[ 13, 42 ] = ascii (fp) : "./hrtfs/elev-25/L-25e150a.dat" +[ 13, 43 ] = ascii (fp) : "./hrtfs/elev-25/L-25e145a.dat" +[ 13, 44 ] = ascii (fp) : "./hrtfs/elev-25/L-25e140a.dat" +[ 13, 45 ] = ascii (fp) : "./hrtfs/elev-25/L-25e135a.dat" +[ 13, 46 ] = ascii (fp) : "./hrtfs/elev-25/L-25e130a.dat" +[ 13, 47 ] = ascii (fp) : "./hrtfs/elev-25/L-25e125a.dat" +[ 13, 48 ] = ascii (fp) : "./hrtfs/elev-25/L-25e120a.dat" +[ 13, 49 ] = ascii (fp) : "./hrtfs/elev-25/L-25e115a.dat" +[ 13, 50 ] = ascii (fp) : "./hrtfs/elev-25/L-25e110a.dat" +[ 13, 51 ] = ascii (fp) : "./hrtfs/elev-25/L-25e105a.dat" +[ 13, 52 ] = ascii (fp) : "./hrtfs/elev-25/L-25e100a.dat" +[ 13, 53 ] = ascii (fp) : "./hrtfs/elev-25/L-25e095a.dat" +[ 13, 54 ] = ascii (fp) : "./hrtfs/elev-25/L-25e090a.dat" +[ 13, 55 ] = ascii (fp) : "./hrtfs/elev-25/L-25e085a.dat" +[ 13, 56 ] = ascii (fp) : "./hrtfs/elev-25/L-25e080a.dat" +[ 13, 57 ] = ascii (fp) : "./hrtfs/elev-25/L-25e075a.dat" +[ 13, 58 ] = ascii (fp) : "./hrtfs/elev-25/L-25e070a.dat" +[ 13, 59 ] = ascii (fp) : "./hrtfs/elev-25/L-25e065a.dat" +[ 13, 60 ] = ascii (fp) : "./hrtfs/elev-25/L-25e060a.dat" +[ 13, 61 ] = ascii (fp) : "./hrtfs/elev-25/L-25e055a.dat" +[ 13, 62 ] = ascii (fp) : "./hrtfs/elev-25/L-25e050a.dat" +[ 13, 63 ] = ascii (fp) : "./hrtfs/elev-25/L-25e045a.dat" +[ 13, 64 ] = ascii (fp) : "./hrtfs/elev-25/L-25e040a.dat" +[ 13, 65 ] = ascii (fp) : "./hrtfs/elev-25/L-25e035a.dat" +[ 13, 66 ] = ascii (fp) : "./hrtfs/elev-25/L-25e030a.dat" +[ 13, 67 ] = ascii (fp) : "./hrtfs/elev-25/L-25e025a.dat" +[ 13, 68 ] = ascii (fp) : "./hrtfs/elev-25/L-25e020a.dat" +[ 13, 69 ] = ascii (fp) : "./hrtfs/elev-25/L-25e015a.dat" +[ 13, 70 ] = ascii (fp) : "./hrtfs/elev-25/L-25e010a.dat" +[ 13, 71 ] = ascii (fp) : "./hrtfs/elev-25/L-25e005a.dat" + +[ 14, 0 ] = ascii (fp) : "./hrtfs/elev-20/L-20e000a.dat" +[ 14, 1 ] = ascii (fp) : "./hrtfs/elev-20/L-20e355a.dat" +[ 14, 2 ] = ascii (fp) : "./hrtfs/elev-20/L-20e350a.dat" +[ 14, 3 ] = ascii (fp) : "./hrtfs/elev-20/L-20e345a.dat" +[ 14, 4 ] = ascii (fp) : "./hrtfs/elev-20/L-20e340a.dat" +[ 14, 5 ] = ascii (fp) : "./hrtfs/elev-20/L-20e335a.dat" +[ 14, 6 ] = ascii (fp) : "./hrtfs/elev-20/L-20e330a.dat" +[ 14, 7 ] = ascii (fp) : "./hrtfs/elev-20/L-20e325a.dat" +[ 14, 8 ] = ascii (fp) : "./hrtfs/elev-20/L-20e320a.dat" +[ 14, 9 ] = ascii (fp) : "./hrtfs/elev-20/L-20e315a.dat" +[ 14, 10 ] = ascii (fp) : "./hrtfs/elev-20/L-20e310a.dat" +[ 14, 11 ] = ascii (fp) : "./hrtfs/elev-20/L-20e305a.dat" +[ 14, 12 ] = ascii (fp) : "./hrtfs/elev-20/L-20e300a.dat" +[ 14, 13 ] = ascii (fp) : "./hrtfs/elev-20/L-20e295a.dat" +[ 14, 14 ] = ascii (fp) : "./hrtfs/elev-20/L-20e290a.dat" +[ 14, 15 ] = ascii (fp) : "./hrtfs/elev-20/L-20e285a.dat" +[ 14, 16 ] = ascii (fp) : "./hrtfs/elev-20/L-20e280a.dat" +[ 14, 17 ] = ascii (fp) : "./hrtfs/elev-20/L-20e275a.dat" +[ 14, 18 ] = ascii (fp) : "./hrtfs/elev-20/L-20e270a.dat" +[ 14, 19 ] = ascii (fp) : "./hrtfs/elev-20/L-20e265a.dat" +[ 14, 20 ] = ascii (fp) : "./hrtfs/elev-20/L-20e260a.dat" +[ 14, 21 ] = ascii (fp) : "./hrtfs/elev-20/L-20e255a.dat" +[ 14, 22 ] = ascii (fp) : "./hrtfs/elev-20/L-20e250a.dat" +[ 14, 23 ] = ascii (fp) : "./hrtfs/elev-20/L-20e245a.dat" +[ 14, 24 ] = ascii (fp) : "./hrtfs/elev-20/L-20e240a.dat" +[ 14, 25 ] = ascii (fp) : "./hrtfs/elev-20/L-20e235a.dat" +[ 14, 26 ] = ascii (fp) : "./hrtfs/elev-20/L-20e230a.dat" +[ 14, 27 ] = ascii (fp) : "./hrtfs/elev-20/L-20e225a.dat" +[ 14, 28 ] = ascii (fp) : "./hrtfs/elev-20/L-20e220a.dat" +[ 14, 29 ] = ascii (fp) : "./hrtfs/elev-20/L-20e215a.dat" +[ 14, 30 ] = ascii (fp) : "./hrtfs/elev-20/L-20e210a.dat" +[ 14, 31 ] = ascii (fp) : "./hrtfs/elev-20/L-20e205a.dat" +[ 14, 32 ] = ascii (fp) : "./hrtfs/elev-20/L-20e200a.dat" +[ 14, 33 ] = ascii (fp) : "./hrtfs/elev-20/L-20e195a.dat" +[ 14, 34 ] = ascii (fp) : "./hrtfs/elev-20/L-20e190a.dat" +[ 14, 35 ] = ascii (fp) : "./hrtfs/elev-20/L-20e185a.dat" +[ 14, 36 ] = ascii (fp) : "./hrtfs/elev-20/L-20e180a.dat" +[ 14, 37 ] = ascii (fp) : "./hrtfs/elev-20/L-20e175a.dat" +[ 14, 38 ] = ascii (fp) : "./hrtfs/elev-20/L-20e170a.dat" +[ 14, 39 ] = ascii (fp) : "./hrtfs/elev-20/L-20e165a.dat" +[ 14, 40 ] = ascii (fp) : "./hrtfs/elev-20/L-20e160a.dat" +[ 14, 41 ] = ascii (fp) : "./hrtfs/elev-20/L-20e155a.dat" +[ 14, 42 ] = ascii (fp) : "./hrtfs/elev-20/L-20e150a.dat" +[ 14, 43 ] = ascii (fp) : "./hrtfs/elev-20/L-20e145a.dat" +[ 14, 44 ] = ascii (fp) : "./hrtfs/elev-20/L-20e140a.dat" +[ 14, 45 ] = ascii (fp) : "./hrtfs/elev-20/L-20e135a.dat" +[ 14, 46 ] = ascii (fp) : "./hrtfs/elev-20/L-20e130a.dat" +[ 14, 47 ] = ascii (fp) : "./hrtfs/elev-20/L-20e125a.dat" +[ 14, 48 ] = ascii (fp) : "./hrtfs/elev-20/L-20e120a.dat" +[ 14, 49 ] = ascii (fp) : "./hrtfs/elev-20/L-20e115a.dat" +[ 14, 50 ] = ascii (fp) : "./hrtfs/elev-20/L-20e110a.dat" +[ 14, 51 ] = ascii (fp) : "./hrtfs/elev-20/L-20e105a.dat" +[ 14, 52 ] = ascii (fp) : "./hrtfs/elev-20/L-20e100a.dat" +[ 14, 53 ] = ascii (fp) : "./hrtfs/elev-20/L-20e095a.dat" +[ 14, 54 ] = ascii (fp) : "./hrtfs/elev-20/L-20e090a.dat" +[ 14, 55 ] = ascii (fp) : "./hrtfs/elev-20/L-20e085a.dat" +[ 14, 56 ] = ascii (fp) : "./hrtfs/elev-20/L-20e080a.dat" +[ 14, 57 ] = ascii (fp) : "./hrtfs/elev-20/L-20e075a.dat" +[ 14, 58 ] = ascii (fp) : "./hrtfs/elev-20/L-20e070a.dat" +[ 14, 59 ] = ascii (fp) : "./hrtfs/elev-20/L-20e065a.dat" +[ 14, 60 ] = ascii (fp) : "./hrtfs/elev-20/L-20e060a.dat" +[ 14, 61 ] = ascii (fp) : "./hrtfs/elev-20/L-20e055a.dat" +[ 14, 62 ] = ascii (fp) : "./hrtfs/elev-20/L-20e050a.dat" +[ 14, 63 ] = ascii (fp) : "./hrtfs/elev-20/L-20e045a.dat" +[ 14, 64 ] = ascii (fp) : "./hrtfs/elev-20/L-20e040a.dat" +[ 14, 65 ] = ascii (fp) : "./hrtfs/elev-20/L-20e035a.dat" +[ 14, 66 ] = ascii (fp) : "./hrtfs/elev-20/L-20e030a.dat" +[ 14, 67 ] = ascii (fp) : "./hrtfs/elev-20/L-20e025a.dat" +[ 14, 68 ] = ascii (fp) : "./hrtfs/elev-20/L-20e020a.dat" +[ 14, 69 ] = ascii (fp) : "./hrtfs/elev-20/L-20e015a.dat" +[ 14, 70 ] = ascii (fp) : "./hrtfs/elev-20/L-20e010a.dat" +[ 14, 71 ] = ascii (fp) : "./hrtfs/elev-20/L-20e005a.dat" + +[ 15, 0 ] = ascii (fp) : "./hrtfs/elev-15/L-15e000a.dat" +[ 15, 1 ] = ascii (fp) : "./hrtfs/elev-15/L-15e355a.dat" +[ 15, 2 ] = ascii (fp) : "./hrtfs/elev-15/L-15e350a.dat" +[ 15, 3 ] = ascii (fp) : "./hrtfs/elev-15/L-15e345a.dat" +[ 15, 4 ] = ascii (fp) : "./hrtfs/elev-15/L-15e340a.dat" +[ 15, 5 ] = ascii (fp) : "./hrtfs/elev-15/L-15e335a.dat" +[ 15, 6 ] = ascii (fp) : "./hrtfs/elev-15/L-15e330a.dat" +[ 15, 7 ] = ascii (fp) : "./hrtfs/elev-15/L-15e325a.dat" +[ 15, 8 ] = ascii (fp) : "./hrtfs/elev-15/L-15e320a.dat" +[ 15, 9 ] = ascii (fp) : "./hrtfs/elev-15/L-15e315a.dat" +[ 15, 10 ] = ascii (fp) : "./hrtfs/elev-15/L-15e310a.dat" +[ 15, 11 ] = ascii (fp) : "./hrtfs/elev-15/L-15e305a.dat" +[ 15, 12 ] = ascii (fp) : "./hrtfs/elev-15/L-15e300a.dat" +[ 15, 13 ] = ascii (fp) : "./hrtfs/elev-15/L-15e295a.dat" +[ 15, 14 ] = ascii (fp) : "./hrtfs/elev-15/L-15e290a.dat" +[ 15, 15 ] = ascii (fp) : "./hrtfs/elev-15/L-15e285a.dat" +[ 15, 16 ] = ascii (fp) : "./hrtfs/elev-15/L-15e280a.dat" +[ 15, 17 ] = ascii (fp) : "./hrtfs/elev-15/L-15e275a.dat" +[ 15, 18 ] = ascii (fp) : "./hrtfs/elev-15/L-15e270a.dat" +[ 15, 19 ] = ascii (fp) : "./hrtfs/elev-15/L-15e265a.dat" +[ 15, 20 ] = ascii (fp) : "./hrtfs/elev-15/L-15e260a.dat" +[ 15, 21 ] = ascii (fp) : "./hrtfs/elev-15/L-15e255a.dat" +[ 15, 22 ] = ascii (fp) : "./hrtfs/elev-15/L-15e250a.dat" +[ 15, 23 ] = ascii (fp) : "./hrtfs/elev-15/L-15e245a.dat" +[ 15, 24 ] = ascii (fp) : "./hrtfs/elev-15/L-15e240a.dat" +[ 15, 25 ] = ascii (fp) : "./hrtfs/elev-15/L-15e235a.dat" +[ 15, 26 ] = ascii (fp) : "./hrtfs/elev-15/L-15e230a.dat" +[ 15, 27 ] = ascii (fp) : "./hrtfs/elev-15/L-15e225a.dat" +[ 15, 28 ] = ascii (fp) : "./hrtfs/elev-15/L-15e220a.dat" +[ 15, 29 ] = ascii (fp) : "./hrtfs/elev-15/L-15e215a.dat" +[ 15, 30 ] = ascii (fp) : "./hrtfs/elev-15/L-15e210a.dat" +[ 15, 31 ] = ascii (fp) : "./hrtfs/elev-15/L-15e205a.dat" +[ 15, 32 ] = ascii (fp) : "./hrtfs/elev-15/L-15e200a.dat" +[ 15, 33 ] = ascii (fp) : "./hrtfs/elev-15/L-15e195a.dat" +[ 15, 34 ] = ascii (fp) : "./hrtfs/elev-15/L-15e190a.dat" +[ 15, 35 ] = ascii (fp) : "./hrtfs/elev-15/L-15e185a.dat" +[ 15, 36 ] = ascii (fp) : "./hrtfs/elev-15/L-15e180a.dat" +[ 15, 37 ] = ascii (fp) : "./hrtfs/elev-15/L-15e175a.dat" +[ 15, 38 ] = ascii (fp) : "./hrtfs/elev-15/L-15e170a.dat" +[ 15, 39 ] = ascii (fp) : "./hrtfs/elev-15/L-15e165a.dat" +[ 15, 40 ] = ascii (fp) : "./hrtfs/elev-15/L-15e160a.dat" +[ 15, 41 ] = ascii (fp) : "./hrtfs/elev-15/L-15e155a.dat" +[ 15, 42 ] = ascii (fp) : "./hrtfs/elev-15/L-15e150a.dat" +[ 15, 43 ] = ascii (fp) : "./hrtfs/elev-15/L-15e145a.dat" +[ 15, 44 ] = ascii (fp) : "./hrtfs/elev-15/L-15e140a.dat" +[ 15, 45 ] = ascii (fp) : "./hrtfs/elev-15/L-15e135a.dat" +[ 15, 46 ] = ascii (fp) : "./hrtfs/elev-15/L-15e130a.dat" +[ 15, 47 ] = ascii (fp) : "./hrtfs/elev-15/L-15e125a.dat" +[ 15, 48 ] = ascii (fp) : "./hrtfs/elev-15/L-15e120a.dat" +[ 15, 49 ] = ascii (fp) : "./hrtfs/elev-15/L-15e115a.dat" +[ 15, 50 ] = ascii (fp) : "./hrtfs/elev-15/L-15e110a.dat" +[ 15, 51 ] = ascii (fp) : "./hrtfs/elev-15/L-15e105a.dat" +[ 15, 52 ] = ascii (fp) : "./hrtfs/elev-15/L-15e100a.dat" +[ 15, 53 ] = ascii (fp) : "./hrtfs/elev-15/L-15e095a.dat" +[ 15, 54 ] = ascii (fp) : "./hrtfs/elev-15/L-15e090a.dat" +[ 15, 55 ] = ascii (fp) : "./hrtfs/elev-15/L-15e085a.dat" +[ 15, 56 ] = ascii (fp) : "./hrtfs/elev-15/L-15e080a.dat" +[ 15, 57 ] = ascii (fp) : "./hrtfs/elev-15/L-15e075a.dat" +[ 15, 58 ] = ascii (fp) : "./hrtfs/elev-15/L-15e070a.dat" +[ 15, 59 ] = ascii (fp) : "./hrtfs/elev-15/L-15e065a.dat" +[ 15, 60 ] = ascii (fp) : "./hrtfs/elev-15/L-15e060a.dat" +[ 15, 61 ] = ascii (fp) : "./hrtfs/elev-15/L-15e055a.dat" +[ 15, 62 ] = ascii (fp) : "./hrtfs/elev-15/L-15e050a.dat" +[ 15, 63 ] = ascii (fp) : "./hrtfs/elev-15/L-15e045a.dat" +[ 15, 64 ] = ascii (fp) : "./hrtfs/elev-15/L-15e040a.dat" +[ 15, 65 ] = ascii (fp) : "./hrtfs/elev-15/L-15e035a.dat" +[ 15, 66 ] = ascii (fp) : "./hrtfs/elev-15/L-15e030a.dat" +[ 15, 67 ] = ascii (fp) : "./hrtfs/elev-15/L-15e025a.dat" +[ 15, 68 ] = ascii (fp) : "./hrtfs/elev-15/L-15e020a.dat" +[ 15, 69 ] = ascii (fp) : "./hrtfs/elev-15/L-15e015a.dat" +[ 15, 70 ] = ascii (fp) : "./hrtfs/elev-15/L-15e010a.dat" +[ 15, 71 ] = ascii (fp) : "./hrtfs/elev-15/L-15e005a.dat" + +[ 16, 0 ] = ascii (fp) : "./hrtfs/elev-10/L-10e000a.dat" +[ 16, 1 ] = ascii (fp) : "./hrtfs/elev-10/L-10e355a.dat" +[ 16, 2 ] = ascii (fp) : "./hrtfs/elev-10/L-10e350a.dat" +[ 16, 3 ] = ascii (fp) : "./hrtfs/elev-10/L-10e345a.dat" +[ 16, 4 ] = ascii (fp) : "./hrtfs/elev-10/L-10e340a.dat" +[ 16, 5 ] = ascii (fp) : "./hrtfs/elev-10/L-10e335a.dat" +[ 16, 6 ] = ascii (fp) : "./hrtfs/elev-10/L-10e330a.dat" +[ 16, 7 ] = ascii (fp) : "./hrtfs/elev-10/L-10e325a.dat" +[ 16, 8 ] = ascii (fp) : "./hrtfs/elev-10/L-10e320a.dat" +[ 16, 9 ] = ascii (fp) : "./hrtfs/elev-10/L-10e315a.dat" +[ 16, 10 ] = ascii (fp) : "./hrtfs/elev-10/L-10e310a.dat" +[ 16, 11 ] = ascii (fp) : "./hrtfs/elev-10/L-10e305a.dat" +[ 16, 12 ] = ascii (fp) : "./hrtfs/elev-10/L-10e300a.dat" +[ 16, 13 ] = ascii (fp) : "./hrtfs/elev-10/L-10e295a.dat" +[ 16, 14 ] = ascii (fp) : "./hrtfs/elev-10/L-10e290a.dat" +[ 16, 15 ] = ascii (fp) : "./hrtfs/elev-10/L-10e285a.dat" +[ 16, 16 ] = ascii (fp) : "./hrtfs/elev-10/L-10e280a.dat" +[ 16, 17 ] = ascii (fp) : "./hrtfs/elev-10/L-10e275a.dat" +[ 16, 18 ] = ascii (fp) : "./hrtfs/elev-10/L-10e270a.dat" +[ 16, 19 ] = ascii (fp) : "./hrtfs/elev-10/L-10e265a.dat" +[ 16, 20 ] = ascii (fp) : "./hrtfs/elev-10/L-10e260a.dat" +[ 16, 21 ] = ascii (fp) : "./hrtfs/elev-10/L-10e255a.dat" +[ 16, 22 ] = ascii (fp) : "./hrtfs/elev-10/L-10e250a.dat" +[ 16, 23 ] = ascii (fp) : "./hrtfs/elev-10/L-10e245a.dat" +[ 16, 24 ] = ascii (fp) : "./hrtfs/elev-10/L-10e240a.dat" +[ 16, 25 ] = ascii (fp) : "./hrtfs/elev-10/L-10e235a.dat" +[ 16, 26 ] = ascii (fp) : "./hrtfs/elev-10/L-10e230a.dat" +[ 16, 27 ] = ascii (fp) : "./hrtfs/elev-10/L-10e225a.dat" +[ 16, 28 ] = ascii (fp) : "./hrtfs/elev-10/L-10e220a.dat" +[ 16, 29 ] = ascii (fp) : "./hrtfs/elev-10/L-10e215a.dat" +[ 16, 30 ] = ascii (fp) : "./hrtfs/elev-10/L-10e210a.dat" +[ 16, 31 ] = ascii (fp) : "./hrtfs/elev-10/L-10e205a.dat" +[ 16, 32 ] = ascii (fp) : "./hrtfs/elev-10/L-10e200a.dat" +[ 16, 33 ] = ascii (fp) : "./hrtfs/elev-10/L-10e195a.dat" +[ 16, 34 ] = ascii (fp) : "./hrtfs/elev-10/L-10e190a.dat" +[ 16, 35 ] = ascii (fp) : "./hrtfs/elev-10/L-10e185a.dat" +[ 16, 36 ] = ascii (fp) : "./hrtfs/elev-10/L-10e180a.dat" +[ 16, 37 ] = ascii (fp) : "./hrtfs/elev-10/L-10e175a.dat" +[ 16, 38 ] = ascii (fp) : "./hrtfs/elev-10/L-10e170a.dat" +[ 16, 39 ] = ascii (fp) : "./hrtfs/elev-10/L-10e165a.dat" +[ 16, 40 ] = ascii (fp) : "./hrtfs/elev-10/L-10e160a.dat" +[ 16, 41 ] = ascii (fp) : "./hrtfs/elev-10/L-10e155a.dat" +[ 16, 42 ] = ascii (fp) : "./hrtfs/elev-10/L-10e150a.dat" +[ 16, 43 ] = ascii (fp) : "./hrtfs/elev-10/L-10e145a.dat" +[ 16, 44 ] = ascii (fp) : "./hrtfs/elev-10/L-10e140a.dat" +[ 16, 45 ] = ascii (fp) : "./hrtfs/elev-10/L-10e135a.dat" +[ 16, 46 ] = ascii (fp) : "./hrtfs/elev-10/L-10e130a.dat" +[ 16, 47 ] = ascii (fp) : "./hrtfs/elev-10/L-10e125a.dat" +[ 16, 48 ] = ascii (fp) : "./hrtfs/elev-10/L-10e120a.dat" +[ 16, 49 ] = ascii (fp) : "./hrtfs/elev-10/L-10e115a.dat" +[ 16, 50 ] = ascii (fp) : "./hrtfs/elev-10/L-10e110a.dat" +[ 16, 51 ] = ascii (fp) : "./hrtfs/elev-10/L-10e105a.dat" +[ 16, 52 ] = ascii (fp) : "./hrtfs/elev-10/L-10e100a.dat" +[ 16, 53 ] = ascii (fp) : "./hrtfs/elev-10/L-10e095a.dat" +[ 16, 54 ] = ascii (fp) : "./hrtfs/elev-10/L-10e090a.dat" +[ 16, 55 ] = ascii (fp) : "./hrtfs/elev-10/L-10e085a.dat" +[ 16, 56 ] = ascii (fp) : "./hrtfs/elev-10/L-10e080a.dat" +[ 16, 57 ] = ascii (fp) : "./hrtfs/elev-10/L-10e075a.dat" +[ 16, 58 ] = ascii (fp) : "./hrtfs/elev-10/L-10e070a.dat" +[ 16, 59 ] = ascii (fp) : "./hrtfs/elev-10/L-10e065a.dat" +[ 16, 60 ] = ascii (fp) : "./hrtfs/elev-10/L-10e060a.dat" +[ 16, 61 ] = ascii (fp) : "./hrtfs/elev-10/L-10e055a.dat" +[ 16, 62 ] = ascii (fp) : "./hrtfs/elev-10/L-10e050a.dat" +[ 16, 63 ] = ascii (fp) : "./hrtfs/elev-10/L-10e045a.dat" +[ 16, 64 ] = ascii (fp) : "./hrtfs/elev-10/L-10e040a.dat" +[ 16, 65 ] = ascii (fp) : "./hrtfs/elev-10/L-10e035a.dat" +[ 16, 66 ] = ascii (fp) : "./hrtfs/elev-10/L-10e030a.dat" +[ 16, 67 ] = ascii (fp) : "./hrtfs/elev-10/L-10e025a.dat" +[ 16, 68 ] = ascii (fp) : "./hrtfs/elev-10/L-10e020a.dat" +[ 16, 69 ] = ascii (fp) : "./hrtfs/elev-10/L-10e015a.dat" +[ 16, 70 ] = ascii (fp) : "./hrtfs/elev-10/L-10e010a.dat" +[ 16, 71 ] = ascii (fp) : "./hrtfs/elev-10/L-10e005a.dat" + +[ 17, 0 ] = ascii (fp) : "./hrtfs/elev-5/L-5e000a.dat" +[ 17, 1 ] = ascii (fp) : "./hrtfs/elev-5/L-5e355a.dat" +[ 17, 2 ] = ascii (fp) : "./hrtfs/elev-5/L-5e350a.dat" +[ 17, 3 ] = ascii (fp) : "./hrtfs/elev-5/L-5e345a.dat" +[ 17, 4 ] = ascii (fp) : "./hrtfs/elev-5/L-5e340a.dat" +[ 17, 5 ] = ascii (fp) : "./hrtfs/elev-5/L-5e335a.dat" +[ 17, 6 ] = ascii (fp) : "./hrtfs/elev-5/L-5e330a.dat" +[ 17, 7 ] = ascii (fp) : "./hrtfs/elev-5/L-5e325a.dat" +[ 17, 8 ] = ascii (fp) : "./hrtfs/elev-5/L-5e320a.dat" +[ 17, 9 ] = ascii (fp) : "./hrtfs/elev-5/L-5e315a.dat" +[ 17, 10 ] = ascii (fp) : "./hrtfs/elev-5/L-5e310a.dat" +[ 17, 11 ] = ascii (fp) : "./hrtfs/elev-5/L-5e305a.dat" +[ 17, 12 ] = ascii (fp) : "./hrtfs/elev-5/L-5e300a.dat" +[ 17, 13 ] = ascii (fp) : "./hrtfs/elev-5/L-5e295a.dat" +[ 17, 14 ] = ascii (fp) : "./hrtfs/elev-5/L-5e290a.dat" +[ 17, 15 ] = ascii (fp) : "./hrtfs/elev-5/L-5e285a.dat" +[ 17, 16 ] = ascii (fp) : "./hrtfs/elev-5/L-5e280a.dat" +[ 17, 17 ] = ascii (fp) : "./hrtfs/elev-5/L-5e275a.dat" +[ 17, 18 ] = ascii (fp) : "./hrtfs/elev-5/L-5e270a.dat" +[ 17, 19 ] = ascii (fp) : "./hrtfs/elev-5/L-5e265a.dat" +[ 17, 20 ] = ascii (fp) : "./hrtfs/elev-5/L-5e260a.dat" +[ 17, 21 ] = ascii (fp) : "./hrtfs/elev-5/L-5e255a.dat" +[ 17, 22 ] = ascii (fp) : "./hrtfs/elev-5/L-5e250a.dat" +[ 17, 23 ] = ascii (fp) : "./hrtfs/elev-5/L-5e245a.dat" +[ 17, 24 ] = ascii (fp) : "./hrtfs/elev-5/L-5e240a.dat" +[ 17, 25 ] = ascii (fp) : "./hrtfs/elev-5/L-5e235a.dat" +[ 17, 26 ] = ascii (fp) : "./hrtfs/elev-5/L-5e230a.dat" +[ 17, 27 ] = ascii (fp) : "./hrtfs/elev-5/L-5e225a.dat" +[ 17, 28 ] = ascii (fp) : "./hrtfs/elev-5/L-5e220a.dat" +[ 17, 29 ] = ascii (fp) : "./hrtfs/elev-5/L-5e215a.dat" +[ 17, 30 ] = ascii (fp) : "./hrtfs/elev-5/L-5e210a.dat" +[ 17, 31 ] = ascii (fp) : "./hrtfs/elev-5/L-5e205a.dat" +[ 17, 32 ] = ascii (fp) : "./hrtfs/elev-5/L-5e200a.dat" +[ 17, 33 ] = ascii (fp) : "./hrtfs/elev-5/L-5e195a.dat" +[ 17, 34 ] = ascii (fp) : "./hrtfs/elev-5/L-5e190a.dat" +[ 17, 35 ] = ascii (fp) : "./hrtfs/elev-5/L-5e185a.dat" +[ 17, 36 ] = ascii (fp) : "./hrtfs/elev-5/L-5e180a.dat" +[ 17, 37 ] = ascii (fp) : "./hrtfs/elev-5/L-5e175a.dat" +[ 17, 38 ] = ascii (fp) : "./hrtfs/elev-5/L-5e170a.dat" +[ 17, 39 ] = ascii (fp) : "./hrtfs/elev-5/L-5e165a.dat" +[ 17, 40 ] = ascii (fp) : "./hrtfs/elev-5/L-5e160a.dat" +[ 17, 41 ] = ascii (fp) : "./hrtfs/elev-5/L-5e155a.dat" +[ 17, 42 ] = ascii (fp) : "./hrtfs/elev-5/L-5e150a.dat" +[ 17, 43 ] = ascii (fp) : "./hrtfs/elev-5/L-5e145a.dat" +[ 17, 44 ] = ascii (fp) : "./hrtfs/elev-5/L-5e140a.dat" +[ 17, 45 ] = ascii (fp) : "./hrtfs/elev-5/L-5e135a.dat" +[ 17, 46 ] = ascii (fp) : "./hrtfs/elev-5/L-5e130a.dat" +[ 17, 47 ] = ascii (fp) : "./hrtfs/elev-5/L-5e125a.dat" +[ 17, 48 ] = ascii (fp) : "./hrtfs/elev-5/L-5e120a.dat" +[ 17, 49 ] = ascii (fp) : "./hrtfs/elev-5/L-5e115a.dat" +[ 17, 50 ] = ascii (fp) : "./hrtfs/elev-5/L-5e110a.dat" +[ 17, 51 ] = ascii (fp) : "./hrtfs/elev-5/L-5e105a.dat" +[ 17, 52 ] = ascii (fp) : "./hrtfs/elev-5/L-5e100a.dat" +[ 17, 53 ] = ascii (fp) : "./hrtfs/elev-5/L-5e095a.dat" +[ 17, 54 ] = ascii (fp) : "./hrtfs/elev-5/L-5e090a.dat" +[ 17, 55 ] = ascii (fp) : "./hrtfs/elev-5/L-5e085a.dat" +[ 17, 56 ] = ascii (fp) : "./hrtfs/elev-5/L-5e080a.dat" +[ 17, 57 ] = ascii (fp) : "./hrtfs/elev-5/L-5e075a.dat" +[ 17, 58 ] = ascii (fp) : "./hrtfs/elev-5/L-5e070a.dat" +[ 17, 59 ] = ascii (fp) : "./hrtfs/elev-5/L-5e065a.dat" +[ 17, 60 ] = ascii (fp) : "./hrtfs/elev-5/L-5e060a.dat" +[ 17, 61 ] = ascii (fp) : "./hrtfs/elev-5/L-5e055a.dat" +[ 17, 62 ] = ascii (fp) : "./hrtfs/elev-5/L-5e050a.dat" +[ 17, 63 ] = ascii (fp) : "./hrtfs/elev-5/L-5e045a.dat" +[ 17, 64 ] = ascii (fp) : "./hrtfs/elev-5/L-5e040a.dat" +[ 17, 65 ] = ascii (fp) : "./hrtfs/elev-5/L-5e035a.dat" +[ 17, 66 ] = ascii (fp) : "./hrtfs/elev-5/L-5e030a.dat" +[ 17, 67 ] = ascii (fp) : "./hrtfs/elev-5/L-5e025a.dat" +[ 17, 68 ] = ascii (fp) : "./hrtfs/elev-5/L-5e020a.dat" +[ 17, 69 ] = ascii (fp) : "./hrtfs/elev-5/L-5e015a.dat" +[ 17, 70 ] = ascii (fp) : "./hrtfs/elev-5/L-5e010a.dat" +[ 17, 71 ] = ascii (fp) : "./hrtfs/elev-5/L-5e005a.dat" + +[ 18, 0 ] = ascii (fp) : "./hrtfs/elev0/L0e000a.dat" +[ 18, 1 ] = ascii (fp) : "./hrtfs/elev0/L0e355a.dat" +[ 18, 2 ] = ascii (fp) : "./hrtfs/elev0/L0e350a.dat" +[ 18, 3 ] = ascii (fp) : "./hrtfs/elev0/L0e345a.dat" +[ 18, 4 ] = ascii (fp) : "./hrtfs/elev0/L0e340a.dat" +[ 18, 5 ] = ascii (fp) : "./hrtfs/elev0/L0e335a.dat" +[ 18, 6 ] = ascii (fp) : "./hrtfs/elev0/L0e330a.dat" +[ 18, 7 ] = ascii (fp) : "./hrtfs/elev0/L0e325a.dat" +[ 18, 8 ] = ascii (fp) : "./hrtfs/elev0/L0e320a.dat" +[ 18, 9 ] = ascii (fp) : "./hrtfs/elev0/L0e315a.dat" +[ 18, 10 ] = ascii (fp) : "./hrtfs/elev0/L0e310a.dat" +[ 18, 11 ] = ascii (fp) : "./hrtfs/elev0/L0e305a.dat" +[ 18, 12 ] = ascii (fp) : "./hrtfs/elev0/L0e300a.dat" +[ 18, 13 ] = ascii (fp) : "./hrtfs/elev0/L0e295a.dat" +[ 18, 14 ] = ascii (fp) : "./hrtfs/elev0/L0e290a.dat" +[ 18, 15 ] = ascii (fp) : "./hrtfs/elev0/L0e285a.dat" +[ 18, 16 ] = ascii (fp) : "./hrtfs/elev0/L0e280a.dat" +[ 18, 17 ] = ascii (fp) : "./hrtfs/elev0/L0e275a.dat" +[ 18, 18 ] = ascii (fp) : "./hrtfs/elev0/L0e270a.dat" +[ 18, 19 ] = ascii (fp) : "./hrtfs/elev0/L0e265a.dat" +[ 18, 20 ] = ascii (fp) : "./hrtfs/elev0/L0e260a.dat" +[ 18, 21 ] = ascii (fp) : "./hrtfs/elev0/L0e255a.dat" +[ 18, 22 ] = ascii (fp) : "./hrtfs/elev0/L0e250a.dat" +[ 18, 23 ] = ascii (fp) : "./hrtfs/elev0/L0e245a.dat" +[ 18, 24 ] = ascii (fp) : "./hrtfs/elev0/L0e240a.dat" +[ 18, 25 ] = ascii (fp) : "./hrtfs/elev0/L0e235a.dat" +[ 18, 26 ] = ascii (fp) : "./hrtfs/elev0/L0e230a.dat" +[ 18, 27 ] = ascii (fp) : "./hrtfs/elev0/L0e225a.dat" +[ 18, 28 ] = ascii (fp) : "./hrtfs/elev0/L0e220a.dat" +[ 18, 29 ] = ascii (fp) : "./hrtfs/elev0/L0e215a.dat" +[ 18, 30 ] = ascii (fp) : "./hrtfs/elev0/L0e210a.dat" +[ 18, 31 ] = ascii (fp) : "./hrtfs/elev0/L0e205a.dat" +[ 18, 32 ] = ascii (fp) : "./hrtfs/elev0/L0e200a.dat" +[ 18, 33 ] = ascii (fp) : "./hrtfs/elev0/L0e195a.dat" +[ 18, 34 ] = ascii (fp) : "./hrtfs/elev0/L0e190a.dat" +[ 18, 35 ] = ascii (fp) : "./hrtfs/elev0/L0e185a.dat" +[ 18, 36 ] = ascii (fp) : "./hrtfs/elev0/L0e180a.dat" +[ 18, 37 ] = ascii (fp) : "./hrtfs/elev0/L0e175a.dat" +[ 18, 38 ] = ascii (fp) : "./hrtfs/elev0/L0e170a.dat" +[ 18, 39 ] = ascii (fp) : "./hrtfs/elev0/L0e165a.dat" +[ 18, 40 ] = ascii (fp) : "./hrtfs/elev0/L0e160a.dat" +[ 18, 41 ] = ascii (fp) : "./hrtfs/elev0/L0e155a.dat" +[ 18, 42 ] = ascii (fp) : "./hrtfs/elev0/L0e150a.dat" +[ 18, 43 ] = ascii (fp) : "./hrtfs/elev0/L0e145a.dat" +[ 18, 44 ] = ascii (fp) : "./hrtfs/elev0/L0e140a.dat" +[ 18, 45 ] = ascii (fp) : "./hrtfs/elev0/L0e135a.dat" +[ 18, 46 ] = ascii (fp) : "./hrtfs/elev0/L0e130a.dat" +[ 18, 47 ] = ascii (fp) : "./hrtfs/elev0/L0e125a.dat" +[ 18, 48 ] = ascii (fp) : "./hrtfs/elev0/L0e120a.dat" +[ 18, 49 ] = ascii (fp) : "./hrtfs/elev0/L0e115a.dat" +[ 18, 50 ] = ascii (fp) : "./hrtfs/elev0/L0e110a.dat" +[ 18, 51 ] = ascii (fp) : "./hrtfs/elev0/L0e105a.dat" +[ 18, 52 ] = ascii (fp) : "./hrtfs/elev0/L0e100a.dat" +[ 18, 53 ] = ascii (fp) : "./hrtfs/elev0/L0e095a.dat" +[ 18, 54 ] = ascii (fp) : "./hrtfs/elev0/L0e090a.dat" +[ 18, 55 ] = ascii (fp) : "./hrtfs/elev0/L0e085a.dat" +[ 18, 56 ] = ascii (fp) : "./hrtfs/elev0/L0e080a.dat" +[ 18, 57 ] = ascii (fp) : "./hrtfs/elev0/L0e075a.dat" +[ 18, 58 ] = ascii (fp) : "./hrtfs/elev0/L0e070a.dat" +[ 18, 59 ] = ascii (fp) : "./hrtfs/elev0/L0e065a.dat" +[ 18, 60 ] = ascii (fp) : "./hrtfs/elev0/L0e060a.dat" +[ 18, 61 ] = ascii (fp) : "./hrtfs/elev0/L0e055a.dat" +[ 18, 62 ] = ascii (fp) : "./hrtfs/elev0/L0e050a.dat" +[ 18, 63 ] = ascii (fp) : "./hrtfs/elev0/L0e045a.dat" +[ 18, 64 ] = ascii (fp) : "./hrtfs/elev0/L0e040a.dat" +[ 18, 65 ] = ascii (fp) : "./hrtfs/elev0/L0e035a.dat" +[ 18, 66 ] = ascii (fp) : "./hrtfs/elev0/L0e030a.dat" +[ 18, 67 ] = ascii (fp) : "./hrtfs/elev0/L0e025a.dat" +[ 18, 68 ] = ascii (fp) : "./hrtfs/elev0/L0e020a.dat" +[ 18, 69 ] = ascii (fp) : "./hrtfs/elev0/L0e015a.dat" +[ 18, 70 ] = ascii (fp) : "./hrtfs/elev0/L0e010a.dat" +[ 18, 71 ] = ascii (fp) : "./hrtfs/elev0/L0e005a.dat" + +[ 19, 0 ] = ascii (fp) : "./hrtfs/elev5/L5e000a.dat" +[ 19, 1 ] = ascii (fp) : "./hrtfs/elev5/L5e355a.dat" +[ 19, 2 ] = ascii (fp) : "./hrtfs/elev5/L5e350a.dat" +[ 19, 3 ] = ascii (fp) : "./hrtfs/elev5/L5e345a.dat" +[ 19, 4 ] = ascii (fp) : "./hrtfs/elev5/L5e340a.dat" +[ 19, 5 ] = ascii (fp) : "./hrtfs/elev5/L5e335a.dat" +[ 19, 6 ] = ascii (fp) : "./hrtfs/elev5/L5e330a.dat" +[ 19, 7 ] = ascii (fp) : "./hrtfs/elev5/L5e325a.dat" +[ 19, 8 ] = ascii (fp) : "./hrtfs/elev5/L5e320a.dat" +[ 19, 9 ] = ascii (fp) : "./hrtfs/elev5/L5e315a.dat" +[ 19, 10 ] = ascii (fp) : "./hrtfs/elev5/L5e310a.dat" +[ 19, 11 ] = ascii (fp) : "./hrtfs/elev5/L5e305a.dat" +[ 19, 12 ] = ascii (fp) : "./hrtfs/elev5/L5e300a.dat" +[ 19, 13 ] = ascii (fp) : "./hrtfs/elev5/L5e295a.dat" +[ 19, 14 ] = ascii (fp) : "./hrtfs/elev5/L5e290a.dat" +[ 19, 15 ] = ascii (fp) : "./hrtfs/elev5/L5e285a.dat" +[ 19, 16 ] = ascii (fp) : "./hrtfs/elev5/L5e280a.dat" +[ 19, 17 ] = ascii (fp) : "./hrtfs/elev5/L5e275a.dat" +[ 19, 18 ] = ascii (fp) : "./hrtfs/elev5/L5e270a.dat" +[ 19, 19 ] = ascii (fp) : "./hrtfs/elev5/L5e265a.dat" +[ 19, 20 ] = ascii (fp) : "./hrtfs/elev5/L5e260a.dat" +[ 19, 21 ] = ascii (fp) : "./hrtfs/elev5/L5e255a.dat" +[ 19, 22 ] = ascii (fp) : "./hrtfs/elev5/L5e250a.dat" +[ 19, 23 ] = ascii (fp) : "./hrtfs/elev5/L5e245a.dat" +[ 19, 24 ] = ascii (fp) : "./hrtfs/elev5/L5e240a.dat" +[ 19, 25 ] = ascii (fp) : "./hrtfs/elev5/L5e235a.dat" +[ 19, 26 ] = ascii (fp) : "./hrtfs/elev5/L5e230a.dat" +[ 19, 27 ] = ascii (fp) : "./hrtfs/elev5/L5e225a.dat" +[ 19, 28 ] = ascii (fp) : "./hrtfs/elev5/L5e220a.dat" +[ 19, 29 ] = ascii (fp) : "./hrtfs/elev5/L5e215a.dat" +[ 19, 30 ] = ascii (fp) : "./hrtfs/elev5/L5e210a.dat" +[ 19, 31 ] = ascii (fp) : "./hrtfs/elev5/L5e205a.dat" +[ 19, 32 ] = ascii (fp) : "./hrtfs/elev5/L5e200a.dat" +[ 19, 33 ] = ascii (fp) : "./hrtfs/elev5/L5e195a.dat" +[ 19, 34 ] = ascii (fp) : "./hrtfs/elev5/L5e190a.dat" +[ 19, 35 ] = ascii (fp) : "./hrtfs/elev5/L5e185a.dat" +[ 19, 36 ] = ascii (fp) : "./hrtfs/elev5/L5e180a.dat" +[ 19, 37 ] = ascii (fp) : "./hrtfs/elev5/L5e175a.dat" +[ 19, 38 ] = ascii (fp) : "./hrtfs/elev5/L5e170a.dat" +[ 19, 39 ] = ascii (fp) : "./hrtfs/elev5/L5e165a.dat" +[ 19, 40 ] = ascii (fp) : "./hrtfs/elev5/L5e160a.dat" +[ 19, 41 ] = ascii (fp) : "./hrtfs/elev5/L5e155a.dat" +[ 19, 42 ] = ascii (fp) : "./hrtfs/elev5/L5e150a.dat" +[ 19, 43 ] = ascii (fp) : "./hrtfs/elev5/L5e145a.dat" +[ 19, 44 ] = ascii (fp) : "./hrtfs/elev5/L5e140a.dat" +[ 19, 45 ] = ascii (fp) : "./hrtfs/elev5/L5e135a.dat" +[ 19, 46 ] = ascii (fp) : "./hrtfs/elev5/L5e130a.dat" +[ 19, 47 ] = ascii (fp) : "./hrtfs/elev5/L5e125a.dat" +[ 19, 48 ] = ascii (fp) : "./hrtfs/elev5/L5e120a.dat" +[ 19, 49 ] = ascii (fp) : "./hrtfs/elev5/L5e115a.dat" +[ 19, 50 ] = ascii (fp) : "./hrtfs/elev5/L5e110a.dat" +[ 19, 51 ] = ascii (fp) : "./hrtfs/elev5/L5e105a.dat" +[ 19, 52 ] = ascii (fp) : "./hrtfs/elev5/L5e100a.dat" +[ 19, 53 ] = ascii (fp) : "./hrtfs/elev5/L5e095a.dat" +[ 19, 54 ] = ascii (fp) : "./hrtfs/elev5/L5e090a.dat" +[ 19, 55 ] = ascii (fp) : "./hrtfs/elev5/L5e085a.dat" +[ 19, 56 ] = ascii (fp) : "./hrtfs/elev5/L5e080a.dat" +[ 19, 57 ] = ascii (fp) : "./hrtfs/elev5/L5e075a.dat" +[ 19, 58 ] = ascii (fp) : "./hrtfs/elev5/L5e070a.dat" +[ 19, 59 ] = ascii (fp) : "./hrtfs/elev5/L5e065a.dat" +[ 19, 60 ] = ascii (fp) : "./hrtfs/elev5/L5e060a.dat" +[ 19, 61 ] = ascii (fp) : "./hrtfs/elev5/L5e055a.dat" +[ 19, 62 ] = ascii (fp) : "./hrtfs/elev5/L5e050a.dat" +[ 19, 63 ] = ascii (fp) : "./hrtfs/elev5/L5e045a.dat" +[ 19, 64 ] = ascii (fp) : "./hrtfs/elev5/L5e040a.dat" +[ 19, 65 ] = ascii (fp) : "./hrtfs/elev5/L5e035a.dat" +[ 19, 66 ] = ascii (fp) : "./hrtfs/elev5/L5e030a.dat" +[ 19, 67 ] = ascii (fp) : "./hrtfs/elev5/L5e025a.dat" +[ 19, 68 ] = ascii (fp) : "./hrtfs/elev5/L5e020a.dat" +[ 19, 69 ] = ascii (fp) : "./hrtfs/elev5/L5e015a.dat" +[ 19, 70 ] = ascii (fp) : "./hrtfs/elev5/L5e010a.dat" +[ 19, 71 ] = ascii (fp) : "./hrtfs/elev5/L5e005a.dat" + +[ 20, 0 ] = ascii (fp) : "./hrtfs/elev10/L10e000a.dat" +[ 20, 1 ] = ascii (fp) : "./hrtfs/elev10/L10e355a.dat" +[ 20, 2 ] = ascii (fp) : "./hrtfs/elev10/L10e350a.dat" +[ 20, 3 ] = ascii (fp) : "./hrtfs/elev10/L10e345a.dat" +[ 20, 4 ] = ascii (fp) : "./hrtfs/elev10/L10e340a.dat" +[ 20, 5 ] = ascii (fp) : "./hrtfs/elev10/L10e335a.dat" +[ 20, 6 ] = ascii (fp) : "./hrtfs/elev10/L10e330a.dat" +[ 20, 7 ] = ascii (fp) : "./hrtfs/elev10/L10e325a.dat" +[ 20, 8 ] = ascii (fp) : "./hrtfs/elev10/L10e320a.dat" +[ 20, 9 ] = ascii (fp) : "./hrtfs/elev10/L10e315a.dat" +[ 20, 10 ] = ascii (fp) : "./hrtfs/elev10/L10e310a.dat" +[ 20, 11 ] = ascii (fp) : "./hrtfs/elev10/L10e305a.dat" +[ 20, 12 ] = ascii (fp) : "./hrtfs/elev10/L10e300a.dat" +[ 20, 13 ] = ascii (fp) : "./hrtfs/elev10/L10e295a.dat" +[ 20, 14 ] = ascii (fp) : "./hrtfs/elev10/L10e290a.dat" +[ 20, 15 ] = ascii (fp) : "./hrtfs/elev10/L10e285a.dat" +[ 20, 16 ] = ascii (fp) : "./hrtfs/elev10/L10e280a.dat" +[ 20, 17 ] = ascii (fp) : "./hrtfs/elev10/L10e275a.dat" +[ 20, 18 ] = ascii (fp) : "./hrtfs/elev10/L10e270a.dat" +[ 20, 19 ] = ascii (fp) : "./hrtfs/elev10/L10e265a.dat" +[ 20, 20 ] = ascii (fp) : "./hrtfs/elev10/L10e260a.dat" +[ 20, 21 ] = ascii (fp) : "./hrtfs/elev10/L10e255a.dat" +[ 20, 22 ] = ascii (fp) : "./hrtfs/elev10/L10e250a.dat" +[ 20, 23 ] = ascii (fp) : "./hrtfs/elev10/L10e245a.dat" +[ 20, 24 ] = ascii (fp) : "./hrtfs/elev10/L10e240a.dat" +[ 20, 25 ] = ascii (fp) : "./hrtfs/elev10/L10e235a.dat" +[ 20, 26 ] = ascii (fp) : "./hrtfs/elev10/L10e230a.dat" +[ 20, 27 ] = ascii (fp) : "./hrtfs/elev10/L10e225a.dat" +[ 20, 28 ] = ascii (fp) : "./hrtfs/elev10/L10e220a.dat" +[ 20, 29 ] = ascii (fp) : "./hrtfs/elev10/L10e215a.dat" +[ 20, 30 ] = ascii (fp) : "./hrtfs/elev10/L10e210a.dat" +[ 20, 31 ] = ascii (fp) : "./hrtfs/elev10/L10e205a.dat" +[ 20, 32 ] = ascii (fp) : "./hrtfs/elev10/L10e200a.dat" +[ 20, 33 ] = ascii (fp) : "./hrtfs/elev10/L10e195a.dat" +[ 20, 34 ] = ascii (fp) : "./hrtfs/elev10/L10e190a.dat" +[ 20, 35 ] = ascii (fp) : "./hrtfs/elev10/L10e185a.dat" +[ 20, 36 ] = ascii (fp) : "./hrtfs/elev10/L10e180a.dat" +[ 20, 37 ] = ascii (fp) : "./hrtfs/elev10/L10e175a.dat" +[ 20, 38 ] = ascii (fp) : "./hrtfs/elev10/L10e170a.dat" +[ 20, 39 ] = ascii (fp) : "./hrtfs/elev10/L10e165a.dat" +[ 20, 40 ] = ascii (fp) : "./hrtfs/elev10/L10e160a.dat" +[ 20, 41 ] = ascii (fp) : "./hrtfs/elev10/L10e155a.dat" +[ 20, 42 ] = ascii (fp) : "./hrtfs/elev10/L10e150a.dat" +[ 20, 43 ] = ascii (fp) : "./hrtfs/elev10/L10e145a.dat" +[ 20, 44 ] = ascii (fp) : "./hrtfs/elev10/L10e140a.dat" +[ 20, 45 ] = ascii (fp) : "./hrtfs/elev10/L10e135a.dat" +[ 20, 46 ] = ascii (fp) : "./hrtfs/elev10/L10e130a.dat" +[ 20, 47 ] = ascii (fp) : "./hrtfs/elev10/L10e125a.dat" +[ 20, 48 ] = ascii (fp) : "./hrtfs/elev10/L10e120a.dat" +[ 20, 49 ] = ascii (fp) : "./hrtfs/elev10/L10e115a.dat" +[ 20, 50 ] = ascii (fp) : "./hrtfs/elev10/L10e110a.dat" +[ 20, 51 ] = ascii (fp) : "./hrtfs/elev10/L10e105a.dat" +[ 20, 52 ] = ascii (fp) : "./hrtfs/elev10/L10e100a.dat" +[ 20, 53 ] = ascii (fp) : "./hrtfs/elev10/L10e095a.dat" +[ 20, 54 ] = ascii (fp) : "./hrtfs/elev10/L10e090a.dat" +[ 20, 55 ] = ascii (fp) : "./hrtfs/elev10/L10e085a.dat" +[ 20, 56 ] = ascii (fp) : "./hrtfs/elev10/L10e080a.dat" +[ 20, 57 ] = ascii (fp) : "./hrtfs/elev10/L10e075a.dat" +[ 20, 58 ] = ascii (fp) : "./hrtfs/elev10/L10e070a.dat" +[ 20, 59 ] = ascii (fp) : "./hrtfs/elev10/L10e065a.dat" +[ 20, 60 ] = ascii (fp) : "./hrtfs/elev10/L10e060a.dat" +[ 20, 61 ] = ascii (fp) : "./hrtfs/elev10/L10e055a.dat" +[ 20, 62 ] = ascii (fp) : "./hrtfs/elev10/L10e050a.dat" +[ 20, 63 ] = ascii (fp) : "./hrtfs/elev10/L10e045a.dat" +[ 20, 64 ] = ascii (fp) : "./hrtfs/elev10/L10e040a.dat" +[ 20, 65 ] = ascii (fp) : "./hrtfs/elev10/L10e035a.dat" +[ 20, 66 ] = ascii (fp) : "./hrtfs/elev10/L10e030a.dat" +[ 20, 67 ] = ascii (fp) : "./hrtfs/elev10/L10e025a.dat" +[ 20, 68 ] = ascii (fp) : "./hrtfs/elev10/L10e020a.dat" +[ 20, 69 ] = ascii (fp) : "./hrtfs/elev10/L10e015a.dat" +[ 20, 70 ] = ascii (fp) : "./hrtfs/elev10/L10e010a.dat" +[ 20, 71 ] = ascii (fp) : "./hrtfs/elev10/L10e005a.dat" + +[ 21, 0 ] = ascii (fp) : "./hrtfs/elev15/L15e000a.dat" +[ 21, 1 ] = ascii (fp) : "./hrtfs/elev15/L15e355a.dat" +[ 21, 2 ] = ascii (fp) : "./hrtfs/elev15/L15e350a.dat" +[ 21, 3 ] = ascii (fp) : "./hrtfs/elev15/L15e345a.dat" +[ 21, 4 ] = ascii (fp) : "./hrtfs/elev15/L15e340a.dat" +[ 21, 5 ] = ascii (fp) : "./hrtfs/elev15/L15e335a.dat" +[ 21, 6 ] = ascii (fp) : "./hrtfs/elev15/L15e330a.dat" +[ 21, 7 ] = ascii (fp) : "./hrtfs/elev15/L15e325a.dat" +[ 21, 8 ] = ascii (fp) : "./hrtfs/elev15/L15e320a.dat" +[ 21, 9 ] = ascii (fp) : "./hrtfs/elev15/L15e315a.dat" +[ 21, 10 ] = ascii (fp) : "./hrtfs/elev15/L15e310a.dat" +[ 21, 11 ] = ascii (fp) : "./hrtfs/elev15/L15e305a.dat" +[ 21, 12 ] = ascii (fp) : "./hrtfs/elev15/L15e300a.dat" +[ 21, 13 ] = ascii (fp) : "./hrtfs/elev15/L15e295a.dat" +[ 21, 14 ] = ascii (fp) : "./hrtfs/elev15/L15e290a.dat" +[ 21, 15 ] = ascii (fp) : "./hrtfs/elev15/L15e285a.dat" +[ 21, 16 ] = ascii (fp) : "./hrtfs/elev15/L15e280a.dat" +[ 21, 17 ] = ascii (fp) : "./hrtfs/elev15/L15e275a.dat" +[ 21, 18 ] = ascii (fp) : "./hrtfs/elev15/L15e270a.dat" +[ 21, 19 ] = ascii (fp) : "./hrtfs/elev15/L15e265a.dat" +[ 21, 20 ] = ascii (fp) : "./hrtfs/elev15/L15e260a.dat" +[ 21, 21 ] = ascii (fp) : "./hrtfs/elev15/L15e255a.dat" +[ 21, 22 ] = ascii (fp) : "./hrtfs/elev15/L15e250a.dat" +[ 21, 23 ] = ascii (fp) : "./hrtfs/elev15/L15e245a.dat" +[ 21, 24 ] = ascii (fp) : "./hrtfs/elev15/L15e240a.dat" +[ 21, 25 ] = ascii (fp) : "./hrtfs/elev15/L15e235a.dat" +[ 21, 26 ] = ascii (fp) : "./hrtfs/elev15/L15e230a.dat" +[ 21, 27 ] = ascii (fp) : "./hrtfs/elev15/L15e225a.dat" +[ 21, 28 ] = ascii (fp) : "./hrtfs/elev15/L15e220a.dat" +[ 21, 29 ] = ascii (fp) : "./hrtfs/elev15/L15e215a.dat" +[ 21, 30 ] = ascii (fp) : "./hrtfs/elev15/L15e210a.dat" +[ 21, 31 ] = ascii (fp) : "./hrtfs/elev15/L15e205a.dat" +[ 21, 32 ] = ascii (fp) : "./hrtfs/elev15/L15e200a.dat" +[ 21, 33 ] = ascii (fp) : "./hrtfs/elev15/L15e195a.dat" +[ 21, 34 ] = ascii (fp) : "./hrtfs/elev15/L15e190a.dat" +[ 21, 35 ] = ascii (fp) : "./hrtfs/elev15/L15e185a.dat" +[ 21, 36 ] = ascii (fp) : "./hrtfs/elev15/L15e180a.dat" +[ 21, 37 ] = ascii (fp) : "./hrtfs/elev15/L15e175a.dat" +[ 21, 38 ] = ascii (fp) : "./hrtfs/elev15/L15e170a.dat" +[ 21, 39 ] = ascii (fp) : "./hrtfs/elev15/L15e165a.dat" +[ 21, 40 ] = ascii (fp) : "./hrtfs/elev15/L15e160a.dat" +[ 21, 41 ] = ascii (fp) : "./hrtfs/elev15/L15e155a.dat" +[ 21, 42 ] = ascii (fp) : "./hrtfs/elev15/L15e150a.dat" +[ 21, 43 ] = ascii (fp) : "./hrtfs/elev15/L15e145a.dat" +[ 21, 44 ] = ascii (fp) : "./hrtfs/elev15/L15e140a.dat" +[ 21, 45 ] = ascii (fp) : "./hrtfs/elev15/L15e135a.dat" +[ 21, 46 ] = ascii (fp) : "./hrtfs/elev15/L15e130a.dat" +[ 21, 47 ] = ascii (fp) : "./hrtfs/elev15/L15e125a.dat" +[ 21, 48 ] = ascii (fp) : "./hrtfs/elev15/L15e120a.dat" +[ 21, 49 ] = ascii (fp) : "./hrtfs/elev15/L15e115a.dat" +[ 21, 50 ] = ascii (fp) : "./hrtfs/elev15/L15e110a.dat" +[ 21, 51 ] = ascii (fp) : "./hrtfs/elev15/L15e105a.dat" +[ 21, 52 ] = ascii (fp) : "./hrtfs/elev15/L15e100a.dat" +[ 21, 53 ] = ascii (fp) : "./hrtfs/elev15/L15e095a.dat" +[ 21, 54 ] = ascii (fp) : "./hrtfs/elev15/L15e090a.dat" +[ 21, 55 ] = ascii (fp) : "./hrtfs/elev15/L15e085a.dat" +[ 21, 56 ] = ascii (fp) : "./hrtfs/elev15/L15e080a.dat" +[ 21, 57 ] = ascii (fp) : "./hrtfs/elev15/L15e075a.dat" +[ 21, 58 ] = ascii (fp) : "./hrtfs/elev15/L15e070a.dat" +[ 21, 59 ] = ascii (fp) : "./hrtfs/elev15/L15e065a.dat" +[ 21, 60 ] = ascii (fp) : "./hrtfs/elev15/L15e060a.dat" +[ 21, 61 ] = ascii (fp) : "./hrtfs/elev15/L15e055a.dat" +[ 21, 62 ] = ascii (fp) : "./hrtfs/elev15/L15e050a.dat" +[ 21, 63 ] = ascii (fp) : "./hrtfs/elev15/L15e045a.dat" +[ 21, 64 ] = ascii (fp) : "./hrtfs/elev15/L15e040a.dat" +[ 21, 65 ] = ascii (fp) : "./hrtfs/elev15/L15e035a.dat" +[ 21, 66 ] = ascii (fp) : "./hrtfs/elev15/L15e030a.dat" +[ 21, 67 ] = ascii (fp) : "./hrtfs/elev15/L15e025a.dat" +[ 21, 68 ] = ascii (fp) : "./hrtfs/elev15/L15e020a.dat" +[ 21, 69 ] = ascii (fp) : "./hrtfs/elev15/L15e015a.dat" +[ 21, 70 ] = ascii (fp) : "./hrtfs/elev15/L15e010a.dat" +[ 21, 71 ] = ascii (fp) : "./hrtfs/elev15/L15e005a.dat" + +[ 22, 0 ] = ascii (fp) : "./hrtfs/elev20/L20e000a.dat" +[ 22, 1 ] = ascii (fp) : "./hrtfs/elev20/L20e355a.dat" +[ 22, 2 ] = ascii (fp) : "./hrtfs/elev20/L20e350a.dat" +[ 22, 3 ] = ascii (fp) : "./hrtfs/elev20/L20e345a.dat" +[ 22, 4 ] = ascii (fp) : "./hrtfs/elev20/L20e340a.dat" +[ 22, 5 ] = ascii (fp) : "./hrtfs/elev20/L20e335a.dat" +[ 22, 6 ] = ascii (fp) : "./hrtfs/elev20/L20e330a.dat" +[ 22, 7 ] = ascii (fp) : "./hrtfs/elev20/L20e325a.dat" +[ 22, 8 ] = ascii (fp) : "./hrtfs/elev20/L20e320a.dat" +[ 22, 9 ] = ascii (fp) : "./hrtfs/elev20/L20e315a.dat" +[ 22, 10 ] = ascii (fp) : "./hrtfs/elev20/L20e310a.dat" +[ 22, 11 ] = ascii (fp) : "./hrtfs/elev20/L20e305a.dat" +[ 22, 12 ] = ascii (fp) : "./hrtfs/elev20/L20e300a.dat" +[ 22, 13 ] = ascii (fp) : "./hrtfs/elev20/L20e295a.dat" +[ 22, 14 ] = ascii (fp) : "./hrtfs/elev20/L20e290a.dat" +[ 22, 15 ] = ascii (fp) : "./hrtfs/elev20/L20e285a.dat" +[ 22, 16 ] = ascii (fp) : "./hrtfs/elev20/L20e280a.dat" +[ 22, 17 ] = ascii (fp) : "./hrtfs/elev20/L20e275a.dat" +[ 22, 18 ] = ascii (fp) : "./hrtfs/elev20/L20e270a.dat" +[ 22, 19 ] = ascii (fp) : "./hrtfs/elev20/L20e265a.dat" +[ 22, 20 ] = ascii (fp) : "./hrtfs/elev20/L20e260a.dat" +[ 22, 21 ] = ascii (fp) : "./hrtfs/elev20/L20e255a.dat" +[ 22, 22 ] = ascii (fp) : "./hrtfs/elev20/L20e250a.dat" +[ 22, 23 ] = ascii (fp) : "./hrtfs/elev20/L20e245a.dat" +[ 22, 24 ] = ascii (fp) : "./hrtfs/elev20/L20e240a.dat" +[ 22, 25 ] = ascii (fp) : "./hrtfs/elev20/L20e235a.dat" +[ 22, 26 ] = ascii (fp) : "./hrtfs/elev20/L20e230a.dat" +[ 22, 27 ] = ascii (fp) : "./hrtfs/elev20/L20e225a.dat" +[ 22, 28 ] = ascii (fp) : "./hrtfs/elev20/L20e220a.dat" +[ 22, 29 ] = ascii (fp) : "./hrtfs/elev20/L20e215a.dat" +[ 22, 30 ] = ascii (fp) : "./hrtfs/elev20/L20e210a.dat" +[ 22, 31 ] = ascii (fp) : "./hrtfs/elev20/L20e205a.dat" +[ 22, 32 ] = ascii (fp) : "./hrtfs/elev20/L20e200a.dat" +[ 22, 33 ] = ascii (fp) : "./hrtfs/elev20/L20e195a.dat" +[ 22, 34 ] = ascii (fp) : "./hrtfs/elev20/L20e190a.dat" +[ 22, 35 ] = ascii (fp) : "./hrtfs/elev20/L20e185a.dat" +[ 22, 36 ] = ascii (fp) : "./hrtfs/elev20/L20e180a.dat" +[ 22, 37 ] = ascii (fp) : "./hrtfs/elev20/L20e175a.dat" +[ 22, 38 ] = ascii (fp) : "./hrtfs/elev20/L20e170a.dat" +[ 22, 39 ] = ascii (fp) : "./hrtfs/elev20/L20e165a.dat" +[ 22, 40 ] = ascii (fp) : "./hrtfs/elev20/L20e160a.dat" +[ 22, 41 ] = ascii (fp) : "./hrtfs/elev20/L20e155a.dat" +[ 22, 42 ] = ascii (fp) : "./hrtfs/elev20/L20e150a.dat" +[ 22, 43 ] = ascii (fp) : "./hrtfs/elev20/L20e145a.dat" +[ 22, 44 ] = ascii (fp) : "./hrtfs/elev20/L20e140a.dat" +[ 22, 45 ] = ascii (fp) : "./hrtfs/elev20/L20e135a.dat" +[ 22, 46 ] = ascii (fp) : "./hrtfs/elev20/L20e130a.dat" +[ 22, 47 ] = ascii (fp) : "./hrtfs/elev20/L20e125a.dat" +[ 22, 48 ] = ascii (fp) : "./hrtfs/elev20/L20e120a.dat" +[ 22, 49 ] = ascii (fp) : "./hrtfs/elev20/L20e115a.dat" +[ 22, 50 ] = ascii (fp) : "./hrtfs/elev20/L20e110a.dat" +[ 22, 51 ] = ascii (fp) : "./hrtfs/elev20/L20e105a.dat" +[ 22, 52 ] = ascii (fp) : "./hrtfs/elev20/L20e100a.dat" +[ 22, 53 ] = ascii (fp) : "./hrtfs/elev20/L20e095a.dat" +[ 22, 54 ] = ascii (fp) : "./hrtfs/elev20/L20e090a.dat" +[ 22, 55 ] = ascii (fp) : "./hrtfs/elev20/L20e085a.dat" +[ 22, 56 ] = ascii (fp) : "./hrtfs/elev20/L20e080a.dat" +[ 22, 57 ] = ascii (fp) : "./hrtfs/elev20/L20e075a.dat" +[ 22, 58 ] = ascii (fp) : "./hrtfs/elev20/L20e070a.dat" +[ 22, 59 ] = ascii (fp) : "./hrtfs/elev20/L20e065a.dat" +[ 22, 60 ] = ascii (fp) : "./hrtfs/elev20/L20e060a.dat" +[ 22, 61 ] = ascii (fp) : "./hrtfs/elev20/L20e055a.dat" +[ 22, 62 ] = ascii (fp) : "./hrtfs/elev20/L20e050a.dat" +[ 22, 63 ] = ascii (fp) : "./hrtfs/elev20/L20e045a.dat" +[ 22, 64 ] = ascii (fp) : "./hrtfs/elev20/L20e040a.dat" +[ 22, 65 ] = ascii (fp) : "./hrtfs/elev20/L20e035a.dat" +[ 22, 66 ] = ascii (fp) : "./hrtfs/elev20/L20e030a.dat" +[ 22, 67 ] = ascii (fp) : "./hrtfs/elev20/L20e025a.dat" +[ 22, 68 ] = ascii (fp) : "./hrtfs/elev20/L20e020a.dat" +[ 22, 69 ] = ascii (fp) : "./hrtfs/elev20/L20e015a.dat" +[ 22, 70 ] = ascii (fp) : "./hrtfs/elev20/L20e010a.dat" +[ 22, 71 ] = ascii (fp) : "./hrtfs/elev20/L20e005a.dat" + +[ 23, 0 ] = ascii (fp) : "./hrtfs/elev25/L25e000a.dat" +[ 23, 1 ] = ascii (fp) : "./hrtfs/elev25/L25e355a.dat" +[ 23, 2 ] = ascii (fp) : "./hrtfs/elev25/L25e350a.dat" +[ 23, 3 ] = ascii (fp) : "./hrtfs/elev25/L25e345a.dat" +[ 23, 4 ] = ascii (fp) : "./hrtfs/elev25/L25e340a.dat" +[ 23, 5 ] = ascii (fp) : "./hrtfs/elev25/L25e335a.dat" +[ 23, 6 ] = ascii (fp) : "./hrtfs/elev25/L25e330a.dat" +[ 23, 7 ] = ascii (fp) : "./hrtfs/elev25/L25e325a.dat" +[ 23, 8 ] = ascii (fp) : "./hrtfs/elev25/L25e320a.dat" +[ 23, 9 ] = ascii (fp) : "./hrtfs/elev25/L25e315a.dat" +[ 23, 10 ] = ascii (fp) : "./hrtfs/elev25/L25e310a.dat" +[ 23, 11 ] = ascii (fp) : "./hrtfs/elev25/L25e305a.dat" +[ 23, 12 ] = ascii (fp) : "./hrtfs/elev25/L25e300a.dat" +[ 23, 13 ] = ascii (fp) : "./hrtfs/elev25/L25e295a.dat" +[ 23, 14 ] = ascii (fp) : "./hrtfs/elev25/L25e290a.dat" +[ 23, 15 ] = ascii (fp) : "./hrtfs/elev25/L25e285a.dat" +[ 23, 16 ] = ascii (fp) : "./hrtfs/elev25/L25e280a.dat" +[ 23, 17 ] = ascii (fp) : "./hrtfs/elev25/L25e275a.dat" +[ 23, 18 ] = ascii (fp) : "./hrtfs/elev25/L25e270a.dat" +[ 23, 19 ] = ascii (fp) : "./hrtfs/elev25/L25e265a.dat" +[ 23, 20 ] = ascii (fp) : "./hrtfs/elev25/L25e260a.dat" +[ 23, 21 ] = ascii (fp) : "./hrtfs/elev25/L25e255a.dat" +[ 23, 22 ] = ascii (fp) : "./hrtfs/elev25/L25e250a.dat" +[ 23, 23 ] = ascii (fp) : "./hrtfs/elev25/L25e245a.dat" +[ 23, 24 ] = ascii (fp) : "./hrtfs/elev25/L25e240a.dat" +[ 23, 25 ] = ascii (fp) : "./hrtfs/elev25/L25e235a.dat" +[ 23, 26 ] = ascii (fp) : "./hrtfs/elev25/L25e230a.dat" +[ 23, 27 ] = ascii (fp) : "./hrtfs/elev25/L25e225a.dat" +[ 23, 28 ] = ascii (fp) : "./hrtfs/elev25/L25e220a.dat" +[ 23, 29 ] = ascii (fp) : "./hrtfs/elev25/L25e215a.dat" +[ 23, 30 ] = ascii (fp) : "./hrtfs/elev25/L25e210a.dat" +[ 23, 31 ] = ascii (fp) : "./hrtfs/elev25/L25e205a.dat" +[ 23, 32 ] = ascii (fp) : "./hrtfs/elev25/L25e200a.dat" +[ 23, 33 ] = ascii (fp) : "./hrtfs/elev25/L25e195a.dat" +[ 23, 34 ] = ascii (fp) : "./hrtfs/elev25/L25e190a.dat" +[ 23, 35 ] = ascii (fp) : "./hrtfs/elev25/L25e185a.dat" +[ 23, 36 ] = ascii (fp) : "./hrtfs/elev25/L25e180a.dat" +[ 23, 37 ] = ascii (fp) : "./hrtfs/elev25/L25e175a.dat" +[ 23, 38 ] = ascii (fp) : "./hrtfs/elev25/L25e170a.dat" +[ 23, 39 ] = ascii (fp) : "./hrtfs/elev25/L25e165a.dat" +[ 23, 40 ] = ascii (fp) : "./hrtfs/elev25/L25e160a.dat" +[ 23, 41 ] = ascii (fp) : "./hrtfs/elev25/L25e155a.dat" +[ 23, 42 ] = ascii (fp) : "./hrtfs/elev25/L25e150a.dat" +[ 23, 43 ] = ascii (fp) : "./hrtfs/elev25/L25e145a.dat" +[ 23, 44 ] = ascii (fp) : "./hrtfs/elev25/L25e140a.dat" +[ 23, 45 ] = ascii (fp) : "./hrtfs/elev25/L25e135a.dat" +[ 23, 46 ] = ascii (fp) : "./hrtfs/elev25/L25e130a.dat" +[ 23, 47 ] = ascii (fp) : "./hrtfs/elev25/L25e125a.dat" +[ 23, 48 ] = ascii (fp) : "./hrtfs/elev25/L25e120a.dat" +[ 23, 49 ] = ascii (fp) : "./hrtfs/elev25/L25e115a.dat" +[ 23, 50 ] = ascii (fp) : "./hrtfs/elev25/L25e110a.dat" +[ 23, 51 ] = ascii (fp) : "./hrtfs/elev25/L25e105a.dat" +[ 23, 52 ] = ascii (fp) : "./hrtfs/elev25/L25e100a.dat" +[ 23, 53 ] = ascii (fp) : "./hrtfs/elev25/L25e095a.dat" +[ 23, 54 ] = ascii (fp) : "./hrtfs/elev25/L25e090a.dat" +[ 23, 55 ] = ascii (fp) : "./hrtfs/elev25/L25e085a.dat" +[ 23, 56 ] = ascii (fp) : "./hrtfs/elev25/L25e080a.dat" +[ 23, 57 ] = ascii (fp) : "./hrtfs/elev25/L25e075a.dat" +[ 23, 58 ] = ascii (fp) : "./hrtfs/elev25/L25e070a.dat" +[ 23, 59 ] = ascii (fp) : "./hrtfs/elev25/L25e065a.dat" +[ 23, 60 ] = ascii (fp) : "./hrtfs/elev25/L25e060a.dat" +[ 23, 61 ] = ascii (fp) : "./hrtfs/elev25/L25e055a.dat" +[ 23, 62 ] = ascii (fp) : "./hrtfs/elev25/L25e050a.dat" +[ 23, 63 ] = ascii (fp) : "./hrtfs/elev25/L25e045a.dat" +[ 23, 64 ] = ascii (fp) : "./hrtfs/elev25/L25e040a.dat" +[ 23, 65 ] = ascii (fp) : "./hrtfs/elev25/L25e035a.dat" +[ 23, 66 ] = ascii (fp) : "./hrtfs/elev25/L25e030a.dat" +[ 23, 67 ] = ascii (fp) : "./hrtfs/elev25/L25e025a.dat" +[ 23, 68 ] = ascii (fp) : "./hrtfs/elev25/L25e020a.dat" +[ 23, 69 ] = ascii (fp) : "./hrtfs/elev25/L25e015a.dat" +[ 23, 70 ] = ascii (fp) : "./hrtfs/elev25/L25e010a.dat" +[ 23, 71 ] = ascii (fp) : "./hrtfs/elev25/L25e005a.dat" + +[ 24, 0 ] = ascii (fp) : "./hrtfs/elev30/L30e000a.dat" +[ 24, 1 ] = ascii (fp) : "./hrtfs/elev30/L30e355a.dat" +[ 24, 2 ] = ascii (fp) : "./hrtfs/elev30/L30e350a.dat" +[ 24, 3 ] = ascii (fp) : "./hrtfs/elev30/L30e345a.dat" +[ 24, 4 ] = ascii (fp) : "./hrtfs/elev30/L30e340a.dat" +[ 24, 5 ] = ascii (fp) : "./hrtfs/elev30/L30e335a.dat" +[ 24, 6 ] = ascii (fp) : "./hrtfs/elev30/L30e330a.dat" +[ 24, 7 ] = ascii (fp) : "./hrtfs/elev30/L30e325a.dat" +[ 24, 8 ] = ascii (fp) : "./hrtfs/elev30/L30e320a.dat" +[ 24, 9 ] = ascii (fp) : "./hrtfs/elev30/L30e315a.dat" +[ 24, 10 ] = ascii (fp) : "./hrtfs/elev30/L30e310a.dat" +[ 24, 11 ] = ascii (fp) : "./hrtfs/elev30/L30e305a.dat" +[ 24, 12 ] = ascii (fp) : "./hrtfs/elev30/L30e300a.dat" +[ 24, 13 ] = ascii (fp) : "./hrtfs/elev30/L30e295a.dat" +[ 24, 14 ] = ascii (fp) : "./hrtfs/elev30/L30e290a.dat" +[ 24, 15 ] = ascii (fp) : "./hrtfs/elev30/L30e285a.dat" +[ 24, 16 ] = ascii (fp) : "./hrtfs/elev30/L30e280a.dat" +[ 24, 17 ] = ascii (fp) : "./hrtfs/elev30/L30e275a.dat" +[ 24, 18 ] = ascii (fp) : "./hrtfs/elev30/L30e270a.dat" +[ 24, 19 ] = ascii (fp) : "./hrtfs/elev30/L30e265a.dat" +[ 24, 20 ] = ascii (fp) : "./hrtfs/elev30/L30e260a.dat" +[ 24, 21 ] = ascii (fp) : "./hrtfs/elev30/L30e255a.dat" +[ 24, 22 ] = ascii (fp) : "./hrtfs/elev30/L30e250a.dat" +[ 24, 23 ] = ascii (fp) : "./hrtfs/elev30/L30e245a.dat" +[ 24, 24 ] = ascii (fp) : "./hrtfs/elev30/L30e240a.dat" +[ 24, 25 ] = ascii (fp) : "./hrtfs/elev30/L30e235a.dat" +[ 24, 26 ] = ascii (fp) : "./hrtfs/elev30/L30e230a.dat" +[ 24, 27 ] = ascii (fp) : "./hrtfs/elev30/L30e225a.dat" +[ 24, 28 ] = ascii (fp) : "./hrtfs/elev30/L30e220a.dat" +[ 24, 29 ] = ascii (fp) : "./hrtfs/elev30/L30e215a.dat" +[ 24, 30 ] = ascii (fp) : "./hrtfs/elev30/L30e210a.dat" +[ 24, 31 ] = ascii (fp) : "./hrtfs/elev30/L30e205a.dat" +[ 24, 32 ] = ascii (fp) : "./hrtfs/elev30/L30e200a.dat" +[ 24, 33 ] = ascii (fp) : "./hrtfs/elev30/L30e195a.dat" +[ 24, 34 ] = ascii (fp) : "./hrtfs/elev30/L30e190a.dat" +[ 24, 35 ] = ascii (fp) : "./hrtfs/elev30/L30e185a.dat" +[ 24, 36 ] = ascii (fp) : "./hrtfs/elev30/L30e180a.dat" +[ 24, 37 ] = ascii (fp) : "./hrtfs/elev30/L30e175a.dat" +[ 24, 38 ] = ascii (fp) : "./hrtfs/elev30/L30e170a.dat" +[ 24, 39 ] = ascii (fp) : "./hrtfs/elev30/L30e165a.dat" +[ 24, 40 ] = ascii (fp) : "./hrtfs/elev30/L30e160a.dat" +[ 24, 41 ] = ascii (fp) : "./hrtfs/elev30/L30e155a.dat" +[ 24, 42 ] = ascii (fp) : "./hrtfs/elev30/L30e150a.dat" +[ 24, 43 ] = ascii (fp) : "./hrtfs/elev30/L30e145a.dat" +[ 24, 44 ] = ascii (fp) : "./hrtfs/elev30/L30e140a.dat" +[ 24, 45 ] = ascii (fp) : "./hrtfs/elev30/L30e135a.dat" +[ 24, 46 ] = ascii (fp) : "./hrtfs/elev30/L30e130a.dat" +[ 24, 47 ] = ascii (fp) : "./hrtfs/elev30/L30e125a.dat" +[ 24, 48 ] = ascii (fp) : "./hrtfs/elev30/L30e120a.dat" +[ 24, 49 ] = ascii (fp) : "./hrtfs/elev30/L30e115a.dat" +[ 24, 50 ] = ascii (fp) : "./hrtfs/elev30/L30e110a.dat" +[ 24, 51 ] = ascii (fp) : "./hrtfs/elev30/L30e105a.dat" +[ 24, 52 ] = ascii (fp) : "./hrtfs/elev30/L30e100a.dat" +[ 24, 53 ] = ascii (fp) : "./hrtfs/elev30/L30e095a.dat" +[ 24, 54 ] = ascii (fp) : "./hrtfs/elev30/L30e090a.dat" +[ 24, 55 ] = ascii (fp) : "./hrtfs/elev30/L30e085a.dat" +[ 24, 56 ] = ascii (fp) : "./hrtfs/elev30/L30e080a.dat" +[ 24, 57 ] = ascii (fp) : "./hrtfs/elev30/L30e075a.dat" +[ 24, 58 ] = ascii (fp) : "./hrtfs/elev30/L30e070a.dat" +[ 24, 59 ] = ascii (fp) : "./hrtfs/elev30/L30e065a.dat" +[ 24, 60 ] = ascii (fp) : "./hrtfs/elev30/L30e060a.dat" +[ 24, 61 ] = ascii (fp) : "./hrtfs/elev30/L30e055a.dat" +[ 24, 62 ] = ascii (fp) : "./hrtfs/elev30/L30e050a.dat" +[ 24, 63 ] = ascii (fp) : "./hrtfs/elev30/L30e045a.dat" +[ 24, 64 ] = ascii (fp) : "./hrtfs/elev30/L30e040a.dat" +[ 24, 65 ] = ascii (fp) : "./hrtfs/elev30/L30e035a.dat" +[ 24, 66 ] = ascii (fp) : "./hrtfs/elev30/L30e030a.dat" +[ 24, 67 ] = ascii (fp) : "./hrtfs/elev30/L30e025a.dat" +[ 24, 68 ] = ascii (fp) : "./hrtfs/elev30/L30e020a.dat" +[ 24, 69 ] = ascii (fp) : "./hrtfs/elev30/L30e015a.dat" +[ 24, 70 ] = ascii (fp) : "./hrtfs/elev30/L30e010a.dat" +[ 24, 71 ] = ascii (fp) : "./hrtfs/elev30/L30e005a.dat" + +[ 25, 0 ] = ascii (fp) : "./hrtfs/elev35/L35e000a.dat" +[ 25, 1 ] = ascii (fp) : "./hrtfs/elev35/L35e355a.dat" +[ 25, 2 ] = ascii (fp) : "./hrtfs/elev35/L35e350a.dat" +[ 25, 3 ] = ascii (fp) : "./hrtfs/elev35/L35e345a.dat" +[ 25, 4 ] = ascii (fp) : "./hrtfs/elev35/L35e340a.dat" +[ 25, 5 ] = ascii (fp) : "./hrtfs/elev35/L35e335a.dat" +[ 25, 6 ] = ascii (fp) : "./hrtfs/elev35/L35e330a.dat" +[ 25, 7 ] = ascii (fp) : "./hrtfs/elev35/L35e325a.dat" +[ 25, 8 ] = ascii (fp) : "./hrtfs/elev35/L35e320a.dat" +[ 25, 9 ] = ascii (fp) : "./hrtfs/elev35/L35e315a.dat" +[ 25, 10 ] = ascii (fp) : "./hrtfs/elev35/L35e310a.dat" +[ 25, 11 ] = ascii (fp) : "./hrtfs/elev35/L35e305a.dat" +[ 25, 12 ] = ascii (fp) : "./hrtfs/elev35/L35e300a.dat" +[ 25, 13 ] = ascii (fp) : "./hrtfs/elev35/L35e295a.dat" +[ 25, 14 ] = ascii (fp) : "./hrtfs/elev35/L35e290a.dat" +[ 25, 15 ] = ascii (fp) : "./hrtfs/elev35/L35e285a.dat" +[ 25, 16 ] = ascii (fp) : "./hrtfs/elev35/L35e280a.dat" +[ 25, 17 ] = ascii (fp) : "./hrtfs/elev35/L35e275a.dat" +[ 25, 18 ] = ascii (fp) : "./hrtfs/elev35/L35e270a.dat" +[ 25, 19 ] = ascii (fp) : "./hrtfs/elev35/L35e265a.dat" +[ 25, 20 ] = ascii (fp) : "./hrtfs/elev35/L35e260a.dat" +[ 25, 21 ] = ascii (fp) : "./hrtfs/elev35/L35e255a.dat" +[ 25, 22 ] = ascii (fp) : "./hrtfs/elev35/L35e250a.dat" +[ 25, 23 ] = ascii (fp) : "./hrtfs/elev35/L35e245a.dat" +[ 25, 24 ] = ascii (fp) : "./hrtfs/elev35/L35e240a.dat" +[ 25, 25 ] = ascii (fp) : "./hrtfs/elev35/L35e235a.dat" +[ 25, 26 ] = ascii (fp) : "./hrtfs/elev35/L35e230a.dat" +[ 25, 27 ] = ascii (fp) : "./hrtfs/elev35/L35e225a.dat" +[ 25, 28 ] = ascii (fp) : "./hrtfs/elev35/L35e220a.dat" +[ 25, 29 ] = ascii (fp) : "./hrtfs/elev35/L35e215a.dat" +[ 25, 30 ] = ascii (fp) : "./hrtfs/elev35/L35e210a.dat" +[ 25, 31 ] = ascii (fp) : "./hrtfs/elev35/L35e205a.dat" +[ 25, 32 ] = ascii (fp) : "./hrtfs/elev35/L35e200a.dat" +[ 25, 33 ] = ascii (fp) : "./hrtfs/elev35/L35e195a.dat" +[ 25, 34 ] = ascii (fp) : "./hrtfs/elev35/L35e190a.dat" +[ 25, 35 ] = ascii (fp) : "./hrtfs/elev35/L35e185a.dat" +[ 25, 36 ] = ascii (fp) : "./hrtfs/elev35/L35e180a.dat" +[ 25, 37 ] = ascii (fp) : "./hrtfs/elev35/L35e175a.dat" +[ 25, 38 ] = ascii (fp) : "./hrtfs/elev35/L35e170a.dat" +[ 25, 39 ] = ascii (fp) : "./hrtfs/elev35/L35e165a.dat" +[ 25, 40 ] = ascii (fp) : "./hrtfs/elev35/L35e160a.dat" +[ 25, 41 ] = ascii (fp) : "./hrtfs/elev35/L35e155a.dat" +[ 25, 42 ] = ascii (fp) : "./hrtfs/elev35/L35e150a.dat" +[ 25, 43 ] = ascii (fp) : "./hrtfs/elev35/L35e145a.dat" +[ 25, 44 ] = ascii (fp) : "./hrtfs/elev35/L35e140a.dat" +[ 25, 45 ] = ascii (fp) : "./hrtfs/elev35/L35e135a.dat" +[ 25, 46 ] = ascii (fp) : "./hrtfs/elev35/L35e130a.dat" +[ 25, 47 ] = ascii (fp) : "./hrtfs/elev35/L35e125a.dat" +[ 25, 48 ] = ascii (fp) : "./hrtfs/elev35/L35e120a.dat" +[ 25, 49 ] = ascii (fp) : "./hrtfs/elev35/L35e115a.dat" +[ 25, 50 ] = ascii (fp) : "./hrtfs/elev35/L35e110a.dat" +[ 25, 51 ] = ascii (fp) : "./hrtfs/elev35/L35e105a.dat" +[ 25, 52 ] = ascii (fp) : "./hrtfs/elev35/L35e100a.dat" +[ 25, 53 ] = ascii (fp) : "./hrtfs/elev35/L35e095a.dat" +[ 25, 54 ] = ascii (fp) : "./hrtfs/elev35/L35e090a.dat" +[ 25, 55 ] = ascii (fp) : "./hrtfs/elev35/L35e085a.dat" +[ 25, 56 ] = ascii (fp) : "./hrtfs/elev35/L35e080a.dat" +[ 25, 57 ] = ascii (fp) : "./hrtfs/elev35/L35e075a.dat" +[ 25, 58 ] = ascii (fp) : "./hrtfs/elev35/L35e070a.dat" +[ 25, 59 ] = ascii (fp) : "./hrtfs/elev35/L35e065a.dat" +[ 25, 60 ] = ascii (fp) : "./hrtfs/elev35/L35e060a.dat" +[ 25, 61 ] = ascii (fp) : "./hrtfs/elev35/L35e055a.dat" +[ 25, 62 ] = ascii (fp) : "./hrtfs/elev35/L35e050a.dat" +[ 25, 63 ] = ascii (fp) : "./hrtfs/elev35/L35e045a.dat" +[ 25, 64 ] = ascii (fp) : "./hrtfs/elev35/L35e040a.dat" +[ 25, 65 ] = ascii (fp) : "./hrtfs/elev35/L35e035a.dat" +[ 25, 66 ] = ascii (fp) : "./hrtfs/elev35/L35e030a.dat" +[ 25, 67 ] = ascii (fp) : "./hrtfs/elev35/L35e025a.dat" +[ 25, 68 ] = ascii (fp) : "./hrtfs/elev35/L35e020a.dat" +[ 25, 69 ] = ascii (fp) : "./hrtfs/elev35/L35e015a.dat" +[ 25, 70 ] = ascii (fp) : "./hrtfs/elev35/L35e010a.dat" +[ 25, 71 ] = ascii (fp) : "./hrtfs/elev35/L35e005a.dat" + +[ 26, 0 ] = ascii (fp) : "./hrtfs/elev40/L40e000a.dat" +[ 26, 1 ] = ascii (fp) : "./hrtfs/elev40/L40e355a.dat" +[ 26, 2 ] = ascii (fp) : "./hrtfs/elev40/L40e350a.dat" +[ 26, 3 ] = ascii (fp) : "./hrtfs/elev40/L40e345a.dat" +[ 26, 4 ] = ascii (fp) : "./hrtfs/elev40/L40e340a.dat" +[ 26, 5 ] = ascii (fp) : "./hrtfs/elev40/L40e335a.dat" +[ 26, 6 ] = ascii (fp) : "./hrtfs/elev40/L40e330a.dat" +[ 26, 7 ] = ascii (fp) : "./hrtfs/elev40/L40e325a.dat" +[ 26, 8 ] = ascii (fp) : "./hrtfs/elev40/L40e320a.dat" +[ 26, 9 ] = ascii (fp) : "./hrtfs/elev40/L40e315a.dat" +[ 26, 10 ] = ascii (fp) : "./hrtfs/elev40/L40e310a.dat" +[ 26, 11 ] = ascii (fp) : "./hrtfs/elev40/L40e305a.dat" +[ 26, 12 ] = ascii (fp) : "./hrtfs/elev40/L40e300a.dat" +[ 26, 13 ] = ascii (fp) : "./hrtfs/elev40/L40e295a.dat" +[ 26, 14 ] = ascii (fp) : "./hrtfs/elev40/L40e290a.dat" +[ 26, 15 ] = ascii (fp) : "./hrtfs/elev40/L40e285a.dat" +[ 26, 16 ] = ascii (fp) : "./hrtfs/elev40/L40e280a.dat" +[ 26, 17 ] = ascii (fp) : "./hrtfs/elev40/L40e275a.dat" +[ 26, 18 ] = ascii (fp) : "./hrtfs/elev40/L40e270a.dat" +[ 26, 19 ] = ascii (fp) : "./hrtfs/elev40/L40e265a.dat" +[ 26, 20 ] = ascii (fp) : "./hrtfs/elev40/L40e260a.dat" +[ 26, 21 ] = ascii (fp) : "./hrtfs/elev40/L40e255a.dat" +[ 26, 22 ] = ascii (fp) : "./hrtfs/elev40/L40e250a.dat" +[ 26, 23 ] = ascii (fp) : "./hrtfs/elev40/L40e245a.dat" +[ 26, 24 ] = ascii (fp) : "./hrtfs/elev40/L40e240a.dat" +[ 26, 25 ] = ascii (fp) : "./hrtfs/elev40/L40e235a.dat" +[ 26, 26 ] = ascii (fp) : "./hrtfs/elev40/L40e230a.dat" +[ 26, 27 ] = ascii (fp) : "./hrtfs/elev40/L40e225a.dat" +[ 26, 28 ] = ascii (fp) : "./hrtfs/elev40/L40e220a.dat" +[ 26, 29 ] = ascii (fp) : "./hrtfs/elev40/L40e215a.dat" +[ 26, 30 ] = ascii (fp) : "./hrtfs/elev40/L40e210a.dat" +[ 26, 31 ] = ascii (fp) : "./hrtfs/elev40/L40e205a.dat" +[ 26, 32 ] = ascii (fp) : "./hrtfs/elev40/L40e200a.dat" +[ 26, 33 ] = ascii (fp) : "./hrtfs/elev40/L40e195a.dat" +[ 26, 34 ] = ascii (fp) : "./hrtfs/elev40/L40e190a.dat" +[ 26, 35 ] = ascii (fp) : "./hrtfs/elev40/L40e185a.dat" +[ 26, 36 ] = ascii (fp) : "./hrtfs/elev40/L40e180a.dat" +[ 26, 37 ] = ascii (fp) : "./hrtfs/elev40/L40e175a.dat" +[ 26, 38 ] = ascii (fp) : "./hrtfs/elev40/L40e170a.dat" +[ 26, 39 ] = ascii (fp) : "./hrtfs/elev40/L40e165a.dat" +[ 26, 40 ] = ascii (fp) : "./hrtfs/elev40/L40e160a.dat" +[ 26, 41 ] = ascii (fp) : "./hrtfs/elev40/L40e155a.dat" +[ 26, 42 ] = ascii (fp) : "./hrtfs/elev40/L40e150a.dat" +[ 26, 43 ] = ascii (fp) : "./hrtfs/elev40/L40e145a.dat" +[ 26, 44 ] = ascii (fp) : "./hrtfs/elev40/L40e140a.dat" +[ 26, 45 ] = ascii (fp) : "./hrtfs/elev40/L40e135a.dat" +[ 26, 46 ] = ascii (fp) : "./hrtfs/elev40/L40e130a.dat" +[ 26, 47 ] = ascii (fp) : "./hrtfs/elev40/L40e125a.dat" +[ 26, 48 ] = ascii (fp) : "./hrtfs/elev40/L40e120a.dat" +[ 26, 49 ] = ascii (fp) : "./hrtfs/elev40/L40e115a.dat" +[ 26, 50 ] = ascii (fp) : "./hrtfs/elev40/L40e110a.dat" +[ 26, 51 ] = ascii (fp) : "./hrtfs/elev40/L40e105a.dat" +[ 26, 52 ] = ascii (fp) : "./hrtfs/elev40/L40e100a.dat" +[ 26, 53 ] = ascii (fp) : "./hrtfs/elev40/L40e095a.dat" +[ 26, 54 ] = ascii (fp) : "./hrtfs/elev40/L40e090a.dat" +[ 26, 55 ] = ascii (fp) : "./hrtfs/elev40/L40e085a.dat" +[ 26, 56 ] = ascii (fp) : "./hrtfs/elev40/L40e080a.dat" +[ 26, 57 ] = ascii (fp) : "./hrtfs/elev40/L40e075a.dat" +[ 26, 58 ] = ascii (fp) : "./hrtfs/elev40/L40e070a.dat" +[ 26, 59 ] = ascii (fp) : "./hrtfs/elev40/L40e065a.dat" +[ 26, 60 ] = ascii (fp) : "./hrtfs/elev40/L40e060a.dat" +[ 26, 61 ] = ascii (fp) : "./hrtfs/elev40/L40e055a.dat" +[ 26, 62 ] = ascii (fp) : "./hrtfs/elev40/L40e050a.dat" +[ 26, 63 ] = ascii (fp) : "./hrtfs/elev40/L40e045a.dat" +[ 26, 64 ] = ascii (fp) : "./hrtfs/elev40/L40e040a.dat" +[ 26, 65 ] = ascii (fp) : "./hrtfs/elev40/L40e035a.dat" +[ 26, 66 ] = ascii (fp) : "./hrtfs/elev40/L40e030a.dat" +[ 26, 67 ] = ascii (fp) : "./hrtfs/elev40/L40e025a.dat" +[ 26, 68 ] = ascii (fp) : "./hrtfs/elev40/L40e020a.dat" +[ 26, 69 ] = ascii (fp) : "./hrtfs/elev40/L40e015a.dat" +[ 26, 70 ] = ascii (fp) : "./hrtfs/elev40/L40e010a.dat" +[ 26, 71 ] = ascii (fp) : "./hrtfs/elev40/L40e005a.dat" + +[ 27, 0 ] = ascii (fp) : "./hrtfs/elev45/L45e000a.dat" +[ 27, 1 ] = ascii (fp) : "./hrtfs/elev45/L45e355a.dat" +[ 27, 2 ] = ascii (fp) : "./hrtfs/elev45/L45e350a.dat" +[ 27, 3 ] = ascii (fp) : "./hrtfs/elev45/L45e345a.dat" +[ 27, 4 ] = ascii (fp) : "./hrtfs/elev45/L45e340a.dat" +[ 27, 5 ] = ascii (fp) : "./hrtfs/elev45/L45e335a.dat" +[ 27, 6 ] = ascii (fp) : "./hrtfs/elev45/L45e330a.dat" +[ 27, 7 ] = ascii (fp) : "./hrtfs/elev45/L45e325a.dat" +[ 27, 8 ] = ascii (fp) : "./hrtfs/elev45/L45e320a.dat" +[ 27, 9 ] = ascii (fp) : "./hrtfs/elev45/L45e315a.dat" +[ 27, 10 ] = ascii (fp) : "./hrtfs/elev45/L45e310a.dat" +[ 27, 11 ] = ascii (fp) : "./hrtfs/elev45/L45e305a.dat" +[ 27, 12 ] = ascii (fp) : "./hrtfs/elev45/L45e300a.dat" +[ 27, 13 ] = ascii (fp) : "./hrtfs/elev45/L45e295a.dat" +[ 27, 14 ] = ascii (fp) : "./hrtfs/elev45/L45e290a.dat" +[ 27, 15 ] = ascii (fp) : "./hrtfs/elev45/L45e285a.dat" +[ 27, 16 ] = ascii (fp) : "./hrtfs/elev45/L45e280a.dat" +[ 27, 17 ] = ascii (fp) : "./hrtfs/elev45/L45e275a.dat" +[ 27, 18 ] = ascii (fp) : "./hrtfs/elev45/L45e270a.dat" +[ 27, 19 ] = ascii (fp) : "./hrtfs/elev45/L45e265a.dat" +[ 27, 20 ] = ascii (fp) : "./hrtfs/elev45/L45e260a.dat" +[ 27, 21 ] = ascii (fp) : "./hrtfs/elev45/L45e255a.dat" +[ 27, 22 ] = ascii (fp) : "./hrtfs/elev45/L45e250a.dat" +[ 27, 23 ] = ascii (fp) : "./hrtfs/elev45/L45e245a.dat" +[ 27, 24 ] = ascii (fp) : "./hrtfs/elev45/L45e240a.dat" +[ 27, 25 ] = ascii (fp) : "./hrtfs/elev45/L45e235a.dat" +[ 27, 26 ] = ascii (fp) : "./hrtfs/elev45/L45e230a.dat" +[ 27, 27 ] = ascii (fp) : "./hrtfs/elev45/L45e225a.dat" +[ 27, 28 ] = ascii (fp) : "./hrtfs/elev45/L45e220a.dat" +[ 27, 29 ] = ascii (fp) : "./hrtfs/elev45/L45e215a.dat" +[ 27, 30 ] = ascii (fp) : "./hrtfs/elev45/L45e210a.dat" +[ 27, 31 ] = ascii (fp) : "./hrtfs/elev45/L45e205a.dat" +[ 27, 32 ] = ascii (fp) : "./hrtfs/elev45/L45e200a.dat" +[ 27, 33 ] = ascii (fp) : "./hrtfs/elev45/L45e195a.dat" +[ 27, 34 ] = ascii (fp) : "./hrtfs/elev45/L45e190a.dat" +[ 27, 35 ] = ascii (fp) : "./hrtfs/elev45/L45e185a.dat" +[ 27, 36 ] = ascii (fp) : "./hrtfs/elev45/L45e180a.dat" +[ 27, 37 ] = ascii (fp) : "./hrtfs/elev45/L45e175a.dat" +[ 27, 38 ] = ascii (fp) : "./hrtfs/elev45/L45e170a.dat" +[ 27, 39 ] = ascii (fp) : "./hrtfs/elev45/L45e165a.dat" +[ 27, 40 ] = ascii (fp) : "./hrtfs/elev45/L45e160a.dat" +[ 27, 41 ] = ascii (fp) : "./hrtfs/elev45/L45e155a.dat" +[ 27, 42 ] = ascii (fp) : "./hrtfs/elev45/L45e150a.dat" +[ 27, 43 ] = ascii (fp) : "./hrtfs/elev45/L45e145a.dat" +[ 27, 44 ] = ascii (fp) : "./hrtfs/elev45/L45e140a.dat" +[ 27, 45 ] = ascii (fp) : "./hrtfs/elev45/L45e135a.dat" +[ 27, 46 ] = ascii (fp) : "./hrtfs/elev45/L45e130a.dat" +[ 27, 47 ] = ascii (fp) : "./hrtfs/elev45/L45e125a.dat" +[ 27, 48 ] = ascii (fp) : "./hrtfs/elev45/L45e120a.dat" +[ 27, 49 ] = ascii (fp) : "./hrtfs/elev45/L45e115a.dat" +[ 27, 50 ] = ascii (fp) : "./hrtfs/elev45/L45e110a.dat" +[ 27, 51 ] = ascii (fp) : "./hrtfs/elev45/L45e105a.dat" +[ 27, 52 ] = ascii (fp) : "./hrtfs/elev45/L45e100a.dat" +[ 27, 53 ] = ascii (fp) : "./hrtfs/elev45/L45e095a.dat" +[ 27, 54 ] = ascii (fp) : "./hrtfs/elev45/L45e090a.dat" +[ 27, 55 ] = ascii (fp) : "./hrtfs/elev45/L45e085a.dat" +[ 27, 56 ] = ascii (fp) : "./hrtfs/elev45/L45e080a.dat" +[ 27, 57 ] = ascii (fp) : "./hrtfs/elev45/L45e075a.dat" +[ 27, 58 ] = ascii (fp) : "./hrtfs/elev45/L45e070a.dat" +[ 27, 59 ] = ascii (fp) : "./hrtfs/elev45/L45e065a.dat" +[ 27, 60 ] = ascii (fp) : "./hrtfs/elev45/L45e060a.dat" +[ 27, 61 ] = ascii (fp) : "./hrtfs/elev45/L45e055a.dat" +[ 27, 62 ] = ascii (fp) : "./hrtfs/elev45/L45e050a.dat" +[ 27, 63 ] = ascii (fp) : "./hrtfs/elev45/L45e045a.dat" +[ 27, 64 ] = ascii (fp) : "./hrtfs/elev45/L45e040a.dat" +[ 27, 65 ] = ascii (fp) : "./hrtfs/elev45/L45e035a.dat" +[ 27, 66 ] = ascii (fp) : "./hrtfs/elev45/L45e030a.dat" +[ 27, 67 ] = ascii (fp) : "./hrtfs/elev45/L45e025a.dat" +[ 27, 68 ] = ascii (fp) : "./hrtfs/elev45/L45e020a.dat" +[ 27, 69 ] = ascii (fp) : "./hrtfs/elev45/L45e015a.dat" +[ 27, 70 ] = ascii (fp) : "./hrtfs/elev45/L45e010a.dat" +[ 27, 71 ] = ascii (fp) : "./hrtfs/elev45/L45e005a.dat" + +[ 28, 0 ] = ascii (fp) : "./hrtfs/elev50/L50e000a.dat" +[ 28, 1 ] = ascii (fp) : "./hrtfs/elev50/L50e355a.dat" +[ 28, 2 ] = ascii (fp) : "./hrtfs/elev50/L50e350a.dat" +[ 28, 3 ] = ascii (fp) : "./hrtfs/elev50/L50e345a.dat" +[ 28, 4 ] = ascii (fp) : "./hrtfs/elev50/L50e340a.dat" +[ 28, 5 ] = ascii (fp) : "./hrtfs/elev50/L50e335a.dat" +[ 28, 6 ] = ascii (fp) : "./hrtfs/elev50/L50e330a.dat" +[ 28, 7 ] = ascii (fp) : "./hrtfs/elev50/L50e325a.dat" +[ 28, 8 ] = ascii (fp) : "./hrtfs/elev50/L50e320a.dat" +[ 28, 9 ] = ascii (fp) : "./hrtfs/elev50/L50e315a.dat" +[ 28, 10 ] = ascii (fp) : "./hrtfs/elev50/L50e310a.dat" +[ 28, 11 ] = ascii (fp) : "./hrtfs/elev50/L50e305a.dat" +[ 28, 12 ] = ascii (fp) : "./hrtfs/elev50/L50e300a.dat" +[ 28, 13 ] = ascii (fp) : "./hrtfs/elev50/L50e295a.dat" +[ 28, 14 ] = ascii (fp) : "./hrtfs/elev50/L50e290a.dat" +[ 28, 15 ] = ascii (fp) : "./hrtfs/elev50/L50e285a.dat" +[ 28, 16 ] = ascii (fp) : "./hrtfs/elev50/L50e280a.dat" +[ 28, 17 ] = ascii (fp) : "./hrtfs/elev50/L50e275a.dat" +[ 28, 18 ] = ascii (fp) : "./hrtfs/elev50/L50e270a.dat" +[ 28, 19 ] = ascii (fp) : "./hrtfs/elev50/L50e265a.dat" +[ 28, 20 ] = ascii (fp) : "./hrtfs/elev50/L50e260a.dat" +[ 28, 21 ] = ascii (fp) : "./hrtfs/elev50/L50e255a.dat" +[ 28, 22 ] = ascii (fp) : "./hrtfs/elev50/L50e250a.dat" +[ 28, 23 ] = ascii (fp) : "./hrtfs/elev50/L50e245a.dat" +[ 28, 24 ] = ascii (fp) : "./hrtfs/elev50/L50e240a.dat" +[ 28, 25 ] = ascii (fp) : "./hrtfs/elev50/L50e235a.dat" +[ 28, 26 ] = ascii (fp) : "./hrtfs/elev50/L50e230a.dat" +[ 28, 27 ] = ascii (fp) : "./hrtfs/elev50/L50e225a.dat" +[ 28, 28 ] = ascii (fp) : "./hrtfs/elev50/L50e220a.dat" +[ 28, 29 ] = ascii (fp) : "./hrtfs/elev50/L50e215a.dat" +[ 28, 30 ] = ascii (fp) : "./hrtfs/elev50/L50e210a.dat" +[ 28, 31 ] = ascii (fp) : "./hrtfs/elev50/L50e205a.dat" +[ 28, 32 ] = ascii (fp) : "./hrtfs/elev50/L50e200a.dat" +[ 28, 33 ] = ascii (fp) : "./hrtfs/elev50/L50e195a.dat" +[ 28, 34 ] = ascii (fp) : "./hrtfs/elev50/L50e190a.dat" +[ 28, 35 ] = ascii (fp) : "./hrtfs/elev50/L50e185a.dat" +[ 28, 36 ] = ascii (fp) : "./hrtfs/elev50/L50e180a.dat" +[ 28, 37 ] = ascii (fp) : "./hrtfs/elev50/L50e175a.dat" +[ 28, 38 ] = ascii (fp) : "./hrtfs/elev50/L50e170a.dat" +[ 28, 39 ] = ascii (fp) : "./hrtfs/elev50/L50e165a.dat" +[ 28, 40 ] = ascii (fp) : "./hrtfs/elev50/L50e160a.dat" +[ 28, 41 ] = ascii (fp) : "./hrtfs/elev50/L50e155a.dat" +[ 28, 42 ] = ascii (fp) : "./hrtfs/elev50/L50e150a.dat" +[ 28, 43 ] = ascii (fp) : "./hrtfs/elev50/L50e145a.dat" +[ 28, 44 ] = ascii (fp) : "./hrtfs/elev50/L50e140a.dat" +[ 28, 45 ] = ascii (fp) : "./hrtfs/elev50/L50e135a.dat" +[ 28, 46 ] = ascii (fp) : "./hrtfs/elev50/L50e130a.dat" +[ 28, 47 ] = ascii (fp) : "./hrtfs/elev50/L50e125a.dat" +[ 28, 48 ] = ascii (fp) : "./hrtfs/elev50/L50e120a.dat" +[ 28, 49 ] = ascii (fp) : "./hrtfs/elev50/L50e115a.dat" +[ 28, 50 ] = ascii (fp) : "./hrtfs/elev50/L50e110a.dat" +[ 28, 51 ] = ascii (fp) : "./hrtfs/elev50/L50e105a.dat" +[ 28, 52 ] = ascii (fp) : "./hrtfs/elev50/L50e100a.dat" +[ 28, 53 ] = ascii (fp) : "./hrtfs/elev50/L50e095a.dat" +[ 28, 54 ] = ascii (fp) : "./hrtfs/elev50/L50e090a.dat" +[ 28, 55 ] = ascii (fp) : "./hrtfs/elev50/L50e085a.dat" +[ 28, 56 ] = ascii (fp) : "./hrtfs/elev50/L50e080a.dat" +[ 28, 57 ] = ascii (fp) : "./hrtfs/elev50/L50e075a.dat" +[ 28, 58 ] = ascii (fp) : "./hrtfs/elev50/L50e070a.dat" +[ 28, 59 ] = ascii (fp) : "./hrtfs/elev50/L50e065a.dat" +[ 28, 60 ] = ascii (fp) : "./hrtfs/elev50/L50e060a.dat" +[ 28, 61 ] = ascii (fp) : "./hrtfs/elev50/L50e055a.dat" +[ 28, 62 ] = ascii (fp) : "./hrtfs/elev50/L50e050a.dat" +[ 28, 63 ] = ascii (fp) : "./hrtfs/elev50/L50e045a.dat" +[ 28, 64 ] = ascii (fp) : "./hrtfs/elev50/L50e040a.dat" +[ 28, 65 ] = ascii (fp) : "./hrtfs/elev50/L50e035a.dat" +[ 28, 66 ] = ascii (fp) : "./hrtfs/elev50/L50e030a.dat" +[ 28, 67 ] = ascii (fp) : "./hrtfs/elev50/L50e025a.dat" +[ 28, 68 ] = ascii (fp) : "./hrtfs/elev50/L50e020a.dat" +[ 28, 69 ] = ascii (fp) : "./hrtfs/elev50/L50e015a.dat" +[ 28, 70 ] = ascii (fp) : "./hrtfs/elev50/L50e010a.dat" +[ 28, 71 ] = ascii (fp) : "./hrtfs/elev50/L50e005a.dat" + +[ 29, 0 ] = ascii (fp) : "./hrtfs/elev55/L55e000a.dat" +[ 29, 1 ] = ascii (fp) : "./hrtfs/elev55/L55e355a.dat" +[ 29, 2 ] = ascii (fp) : "./hrtfs/elev55/L55e350a.dat" +[ 29, 3 ] = ascii (fp) : "./hrtfs/elev55/L55e345a.dat" +[ 29, 4 ] = ascii (fp) : "./hrtfs/elev55/L55e340a.dat" +[ 29, 5 ] = ascii (fp) : "./hrtfs/elev55/L55e335a.dat" +[ 29, 6 ] = ascii (fp) : "./hrtfs/elev55/L55e330a.dat" +[ 29, 7 ] = ascii (fp) : "./hrtfs/elev55/L55e325a.dat" +[ 29, 8 ] = ascii (fp) : "./hrtfs/elev55/L55e320a.dat" +[ 29, 9 ] = ascii (fp) : "./hrtfs/elev55/L55e315a.dat" +[ 29, 10 ] = ascii (fp) : "./hrtfs/elev55/L55e310a.dat" +[ 29, 11 ] = ascii (fp) : "./hrtfs/elev55/L55e305a.dat" +[ 29, 12 ] = ascii (fp) : "./hrtfs/elev55/L55e300a.dat" +[ 29, 13 ] = ascii (fp) : "./hrtfs/elev55/L55e295a.dat" +[ 29, 14 ] = ascii (fp) : "./hrtfs/elev55/L55e290a.dat" +[ 29, 15 ] = ascii (fp) : "./hrtfs/elev55/L55e285a.dat" +[ 29, 16 ] = ascii (fp) : "./hrtfs/elev55/L55e280a.dat" +[ 29, 17 ] = ascii (fp) : "./hrtfs/elev55/L55e275a.dat" +[ 29, 18 ] = ascii (fp) : "./hrtfs/elev55/L55e270a.dat" +[ 29, 19 ] = ascii (fp) : "./hrtfs/elev55/L55e265a.dat" +[ 29, 20 ] = ascii (fp) : "./hrtfs/elev55/L55e260a.dat" +[ 29, 21 ] = ascii (fp) : "./hrtfs/elev55/L55e255a.dat" +[ 29, 22 ] = ascii (fp) : "./hrtfs/elev55/L55e250a.dat" +[ 29, 23 ] = ascii (fp) : "./hrtfs/elev55/L55e245a.dat" +[ 29, 24 ] = ascii (fp) : "./hrtfs/elev55/L55e240a.dat" +[ 29, 25 ] = ascii (fp) : "./hrtfs/elev55/L55e235a.dat" +[ 29, 26 ] = ascii (fp) : "./hrtfs/elev55/L55e230a.dat" +[ 29, 27 ] = ascii (fp) : "./hrtfs/elev55/L55e225a.dat" +[ 29, 28 ] = ascii (fp) : "./hrtfs/elev55/L55e220a.dat" +[ 29, 29 ] = ascii (fp) : "./hrtfs/elev55/L55e215a.dat" +[ 29, 30 ] = ascii (fp) : "./hrtfs/elev55/L55e210a.dat" +[ 29, 31 ] = ascii (fp) : "./hrtfs/elev55/L55e205a.dat" +[ 29, 32 ] = ascii (fp) : "./hrtfs/elev55/L55e200a.dat" +[ 29, 33 ] = ascii (fp) : "./hrtfs/elev55/L55e195a.dat" +[ 29, 34 ] = ascii (fp) : "./hrtfs/elev55/L55e190a.dat" +[ 29, 35 ] = ascii (fp) : "./hrtfs/elev55/L55e185a.dat" +[ 29, 36 ] = ascii (fp) : "./hrtfs/elev55/L55e180a.dat" +[ 29, 37 ] = ascii (fp) : "./hrtfs/elev55/L55e175a.dat" +[ 29, 38 ] = ascii (fp) : "./hrtfs/elev55/L55e170a.dat" +[ 29, 39 ] = ascii (fp) : "./hrtfs/elev55/L55e165a.dat" +[ 29, 40 ] = ascii (fp) : "./hrtfs/elev55/L55e160a.dat" +[ 29, 41 ] = ascii (fp) : "./hrtfs/elev55/L55e155a.dat" +[ 29, 42 ] = ascii (fp) : "./hrtfs/elev55/L55e150a.dat" +[ 29, 43 ] = ascii (fp) : "./hrtfs/elev55/L55e145a.dat" +[ 29, 44 ] = ascii (fp) : "./hrtfs/elev55/L55e140a.dat" +[ 29, 45 ] = ascii (fp) : "./hrtfs/elev55/L55e135a.dat" +[ 29, 46 ] = ascii (fp) : "./hrtfs/elev55/L55e130a.dat" +[ 29, 47 ] = ascii (fp) : "./hrtfs/elev55/L55e125a.dat" +[ 29, 48 ] = ascii (fp) : "./hrtfs/elev55/L55e120a.dat" +[ 29, 49 ] = ascii (fp) : "./hrtfs/elev55/L55e115a.dat" +[ 29, 50 ] = ascii (fp) : "./hrtfs/elev55/L55e110a.dat" +[ 29, 51 ] = ascii (fp) : "./hrtfs/elev55/L55e105a.dat" +[ 29, 52 ] = ascii (fp) : "./hrtfs/elev55/L55e100a.dat" +[ 29, 53 ] = ascii (fp) : "./hrtfs/elev55/L55e095a.dat" +[ 29, 54 ] = ascii (fp) : "./hrtfs/elev55/L55e090a.dat" +[ 29, 55 ] = ascii (fp) : "./hrtfs/elev55/L55e085a.dat" +[ 29, 56 ] = ascii (fp) : "./hrtfs/elev55/L55e080a.dat" +[ 29, 57 ] = ascii (fp) : "./hrtfs/elev55/L55e075a.dat" +[ 29, 58 ] = ascii (fp) : "./hrtfs/elev55/L55e070a.dat" +[ 29, 59 ] = ascii (fp) : "./hrtfs/elev55/L55e065a.dat" +[ 29, 60 ] = ascii (fp) : "./hrtfs/elev55/L55e060a.dat" +[ 29, 61 ] = ascii (fp) : "./hrtfs/elev55/L55e055a.dat" +[ 29, 62 ] = ascii (fp) : "./hrtfs/elev55/L55e050a.dat" +[ 29, 63 ] = ascii (fp) : "./hrtfs/elev55/L55e045a.dat" +[ 29, 64 ] = ascii (fp) : "./hrtfs/elev55/L55e040a.dat" +[ 29, 65 ] = ascii (fp) : "./hrtfs/elev55/L55e035a.dat" +[ 29, 66 ] = ascii (fp) : "./hrtfs/elev55/L55e030a.dat" +[ 29, 67 ] = ascii (fp) : "./hrtfs/elev55/L55e025a.dat" +[ 29, 68 ] = ascii (fp) : "./hrtfs/elev55/L55e020a.dat" +[ 29, 69 ] = ascii (fp) : "./hrtfs/elev55/L55e015a.dat" +[ 29, 70 ] = ascii (fp) : "./hrtfs/elev55/L55e010a.dat" +[ 29, 71 ] = ascii (fp) : "./hrtfs/elev55/L55e005a.dat" + +[ 30, 0 ] = ascii (fp) : "./hrtfs/elev60/L60e000a.dat" +[ 30, 1 ] = ascii (fp) : "./hrtfs/elev60/L60e355a.dat" +[ 30, 2 ] = ascii (fp) : "./hrtfs/elev60/L60e350a.dat" +[ 30, 3 ] = ascii (fp) : "./hrtfs/elev60/L60e345a.dat" +[ 30, 4 ] = ascii (fp) : "./hrtfs/elev60/L60e340a.dat" +[ 30, 5 ] = ascii (fp) : "./hrtfs/elev60/L60e335a.dat" +[ 30, 6 ] = ascii (fp) : "./hrtfs/elev60/L60e330a.dat" +[ 30, 7 ] = ascii (fp) : "./hrtfs/elev60/L60e325a.dat" +[ 30, 8 ] = ascii (fp) : "./hrtfs/elev60/L60e320a.dat" +[ 30, 9 ] = ascii (fp) : "./hrtfs/elev60/L60e315a.dat" +[ 30, 10 ] = ascii (fp) : "./hrtfs/elev60/L60e310a.dat" +[ 30, 11 ] = ascii (fp) : "./hrtfs/elev60/L60e305a.dat" +[ 30, 12 ] = ascii (fp) : "./hrtfs/elev60/L60e300a.dat" +[ 30, 13 ] = ascii (fp) : "./hrtfs/elev60/L60e295a.dat" +[ 30, 14 ] = ascii (fp) : "./hrtfs/elev60/L60e290a.dat" +[ 30, 15 ] = ascii (fp) : "./hrtfs/elev60/L60e285a.dat" +[ 30, 16 ] = ascii (fp) : "./hrtfs/elev60/L60e280a.dat" +[ 30, 17 ] = ascii (fp) : "./hrtfs/elev60/L60e275a.dat" +[ 30, 18 ] = ascii (fp) : "./hrtfs/elev60/L60e270a.dat" +[ 30, 19 ] = ascii (fp) : "./hrtfs/elev60/L60e265a.dat" +[ 30, 20 ] = ascii (fp) : "./hrtfs/elev60/L60e260a.dat" +[ 30, 21 ] = ascii (fp) : "./hrtfs/elev60/L60e255a.dat" +[ 30, 22 ] = ascii (fp) : "./hrtfs/elev60/L60e250a.dat" +[ 30, 23 ] = ascii (fp) : "./hrtfs/elev60/L60e245a.dat" +[ 30, 24 ] = ascii (fp) : "./hrtfs/elev60/L60e240a.dat" +[ 30, 25 ] = ascii (fp) : "./hrtfs/elev60/L60e235a.dat" +[ 30, 26 ] = ascii (fp) : "./hrtfs/elev60/L60e230a.dat" +[ 30, 27 ] = ascii (fp) : "./hrtfs/elev60/L60e225a.dat" +[ 30, 28 ] = ascii (fp) : "./hrtfs/elev60/L60e220a.dat" +[ 30, 29 ] = ascii (fp) : "./hrtfs/elev60/L60e215a.dat" +[ 30, 30 ] = ascii (fp) : "./hrtfs/elev60/L60e210a.dat" +[ 30, 31 ] = ascii (fp) : "./hrtfs/elev60/L60e205a.dat" +[ 30, 32 ] = ascii (fp) : "./hrtfs/elev60/L60e200a.dat" +[ 30, 33 ] = ascii (fp) : "./hrtfs/elev60/L60e195a.dat" +[ 30, 34 ] = ascii (fp) : "./hrtfs/elev60/L60e190a.dat" +[ 30, 35 ] = ascii (fp) : "./hrtfs/elev60/L60e185a.dat" +[ 30, 36 ] = ascii (fp) : "./hrtfs/elev60/L60e180a.dat" +[ 30, 37 ] = ascii (fp) : "./hrtfs/elev60/L60e175a.dat" +[ 30, 38 ] = ascii (fp) : "./hrtfs/elev60/L60e170a.dat" +[ 30, 39 ] = ascii (fp) : "./hrtfs/elev60/L60e165a.dat" +[ 30, 40 ] = ascii (fp) : "./hrtfs/elev60/L60e160a.dat" +[ 30, 41 ] = ascii (fp) : "./hrtfs/elev60/L60e155a.dat" +[ 30, 42 ] = ascii (fp) : "./hrtfs/elev60/L60e150a.dat" +[ 30, 43 ] = ascii (fp) : "./hrtfs/elev60/L60e145a.dat" +[ 30, 44 ] = ascii (fp) : "./hrtfs/elev60/L60e140a.dat" +[ 30, 45 ] = ascii (fp) : "./hrtfs/elev60/L60e135a.dat" +[ 30, 46 ] = ascii (fp) : "./hrtfs/elev60/L60e130a.dat" +[ 30, 47 ] = ascii (fp) : "./hrtfs/elev60/L60e125a.dat" +[ 30, 48 ] = ascii (fp) : "./hrtfs/elev60/L60e120a.dat" +[ 30, 49 ] = ascii (fp) : "./hrtfs/elev60/L60e115a.dat" +[ 30, 50 ] = ascii (fp) : "./hrtfs/elev60/L60e110a.dat" +[ 30, 51 ] = ascii (fp) : "./hrtfs/elev60/L60e105a.dat" +[ 30, 52 ] = ascii (fp) : "./hrtfs/elev60/L60e100a.dat" +[ 30, 53 ] = ascii (fp) : "./hrtfs/elev60/L60e095a.dat" +[ 30, 54 ] = ascii (fp) : "./hrtfs/elev60/L60e090a.dat" +[ 30, 55 ] = ascii (fp) : "./hrtfs/elev60/L60e085a.dat" +[ 30, 56 ] = ascii (fp) : "./hrtfs/elev60/L60e080a.dat" +[ 30, 57 ] = ascii (fp) : "./hrtfs/elev60/L60e075a.dat" +[ 30, 58 ] = ascii (fp) : "./hrtfs/elev60/L60e070a.dat" +[ 30, 59 ] = ascii (fp) : "./hrtfs/elev60/L60e065a.dat" +[ 30, 60 ] = ascii (fp) : "./hrtfs/elev60/L60e060a.dat" +[ 30, 61 ] = ascii (fp) : "./hrtfs/elev60/L60e055a.dat" +[ 30, 62 ] = ascii (fp) : "./hrtfs/elev60/L60e050a.dat" +[ 30, 63 ] = ascii (fp) : "./hrtfs/elev60/L60e045a.dat" +[ 30, 64 ] = ascii (fp) : "./hrtfs/elev60/L60e040a.dat" +[ 30, 65 ] = ascii (fp) : "./hrtfs/elev60/L60e035a.dat" +[ 30, 66 ] = ascii (fp) : "./hrtfs/elev60/L60e030a.dat" +[ 30, 67 ] = ascii (fp) : "./hrtfs/elev60/L60e025a.dat" +[ 30, 68 ] = ascii (fp) : "./hrtfs/elev60/L60e020a.dat" +[ 30, 69 ] = ascii (fp) : "./hrtfs/elev60/L60e015a.dat" +[ 30, 70 ] = ascii (fp) : "./hrtfs/elev60/L60e010a.dat" +[ 30, 71 ] = ascii (fp) : "./hrtfs/elev60/L60e005a.dat" + +[ 31, 0 ] = ascii (fp) : "./hrtfs/elev65/L65e000a.dat" +[ 31, 1 ] = ascii (fp) : "./hrtfs/elev65/L65e355a.dat" +[ 31, 2 ] = ascii (fp) : "./hrtfs/elev65/L65e350a.dat" +[ 31, 3 ] = ascii (fp) : "./hrtfs/elev65/L65e345a.dat" +[ 31, 4 ] = ascii (fp) : "./hrtfs/elev65/L65e340a.dat" +[ 31, 5 ] = ascii (fp) : "./hrtfs/elev65/L65e335a.dat" +[ 31, 6 ] = ascii (fp) : "./hrtfs/elev65/L65e330a.dat" +[ 31, 7 ] = ascii (fp) : "./hrtfs/elev65/L65e325a.dat" +[ 31, 8 ] = ascii (fp) : "./hrtfs/elev65/L65e320a.dat" +[ 31, 9 ] = ascii (fp) : "./hrtfs/elev65/L65e315a.dat" +[ 31, 10 ] = ascii (fp) : "./hrtfs/elev65/L65e310a.dat" +[ 31, 11 ] = ascii (fp) : "./hrtfs/elev65/L65e305a.dat" +[ 31, 12 ] = ascii (fp) : "./hrtfs/elev65/L65e300a.dat" +[ 31, 13 ] = ascii (fp) : "./hrtfs/elev65/L65e295a.dat" +[ 31, 14 ] = ascii (fp) : "./hrtfs/elev65/L65e290a.dat" +[ 31, 15 ] = ascii (fp) : "./hrtfs/elev65/L65e285a.dat" +[ 31, 16 ] = ascii (fp) : "./hrtfs/elev65/L65e280a.dat" +[ 31, 17 ] = ascii (fp) : "./hrtfs/elev65/L65e275a.dat" +[ 31, 18 ] = ascii (fp) : "./hrtfs/elev65/L65e270a.dat" +[ 31, 19 ] = ascii (fp) : "./hrtfs/elev65/L65e265a.dat" +[ 31, 20 ] = ascii (fp) : "./hrtfs/elev65/L65e260a.dat" +[ 31, 21 ] = ascii (fp) : "./hrtfs/elev65/L65e255a.dat" +[ 31, 22 ] = ascii (fp) : "./hrtfs/elev65/L65e250a.dat" +[ 31, 23 ] = ascii (fp) : "./hrtfs/elev65/L65e245a.dat" +[ 31, 24 ] = ascii (fp) : "./hrtfs/elev65/L65e240a.dat" +[ 31, 25 ] = ascii (fp) : "./hrtfs/elev65/L65e235a.dat" +[ 31, 26 ] = ascii (fp) : "./hrtfs/elev65/L65e230a.dat" +[ 31, 27 ] = ascii (fp) : "./hrtfs/elev65/L65e225a.dat" +[ 31, 28 ] = ascii (fp) : "./hrtfs/elev65/L65e220a.dat" +[ 31, 29 ] = ascii (fp) : "./hrtfs/elev65/L65e215a.dat" +[ 31, 30 ] = ascii (fp) : "./hrtfs/elev65/L65e210a.dat" +[ 31, 31 ] = ascii (fp) : "./hrtfs/elev65/L65e205a.dat" +[ 31, 32 ] = ascii (fp) : "./hrtfs/elev65/L65e200a.dat" +[ 31, 33 ] = ascii (fp) : "./hrtfs/elev65/L65e195a.dat" +[ 31, 34 ] = ascii (fp) : "./hrtfs/elev65/L65e190a.dat" +[ 31, 35 ] = ascii (fp) : "./hrtfs/elev65/L65e185a.dat" +[ 31, 36 ] = ascii (fp) : "./hrtfs/elev65/L65e180a.dat" +[ 31, 37 ] = ascii (fp) : "./hrtfs/elev65/L65e175a.dat" +[ 31, 38 ] = ascii (fp) : "./hrtfs/elev65/L65e170a.dat" +[ 31, 39 ] = ascii (fp) : "./hrtfs/elev65/L65e165a.dat" +[ 31, 40 ] = ascii (fp) : "./hrtfs/elev65/L65e160a.dat" +[ 31, 41 ] = ascii (fp) : "./hrtfs/elev65/L65e155a.dat" +[ 31, 42 ] = ascii (fp) : "./hrtfs/elev65/L65e150a.dat" +[ 31, 43 ] = ascii (fp) : "./hrtfs/elev65/L65e145a.dat" +[ 31, 44 ] = ascii (fp) : "./hrtfs/elev65/L65e140a.dat" +[ 31, 45 ] = ascii (fp) : "./hrtfs/elev65/L65e135a.dat" +[ 31, 46 ] = ascii (fp) : "./hrtfs/elev65/L65e130a.dat" +[ 31, 47 ] = ascii (fp) : "./hrtfs/elev65/L65e125a.dat" +[ 31, 48 ] = ascii (fp) : "./hrtfs/elev65/L65e120a.dat" +[ 31, 49 ] = ascii (fp) : "./hrtfs/elev65/L65e115a.dat" +[ 31, 50 ] = ascii (fp) : "./hrtfs/elev65/L65e110a.dat" +[ 31, 51 ] = ascii (fp) : "./hrtfs/elev65/L65e105a.dat" +[ 31, 52 ] = ascii (fp) : "./hrtfs/elev65/L65e100a.dat" +[ 31, 53 ] = ascii (fp) : "./hrtfs/elev65/L65e095a.dat" +[ 31, 54 ] = ascii (fp) : "./hrtfs/elev65/L65e090a.dat" +[ 31, 55 ] = ascii (fp) : "./hrtfs/elev65/L65e085a.dat" +[ 31, 56 ] = ascii (fp) : "./hrtfs/elev65/L65e080a.dat" +[ 31, 57 ] = ascii (fp) : "./hrtfs/elev65/L65e075a.dat" +[ 31, 58 ] = ascii (fp) : "./hrtfs/elev65/L65e070a.dat" +[ 31, 59 ] = ascii (fp) : "./hrtfs/elev65/L65e065a.dat" +[ 31, 60 ] = ascii (fp) : "./hrtfs/elev65/L65e060a.dat" +[ 31, 61 ] = ascii (fp) : "./hrtfs/elev65/L65e055a.dat" +[ 31, 62 ] = ascii (fp) : "./hrtfs/elev65/L65e050a.dat" +[ 31, 63 ] = ascii (fp) : "./hrtfs/elev65/L65e045a.dat" +[ 31, 64 ] = ascii (fp) : "./hrtfs/elev65/L65e040a.dat" +[ 31, 65 ] = ascii (fp) : "./hrtfs/elev65/L65e035a.dat" +[ 31, 66 ] = ascii (fp) : "./hrtfs/elev65/L65e030a.dat" +[ 31, 67 ] = ascii (fp) : "./hrtfs/elev65/L65e025a.dat" +[ 31, 68 ] = ascii (fp) : "./hrtfs/elev65/L65e020a.dat" +[ 31, 69 ] = ascii (fp) : "./hrtfs/elev65/L65e015a.dat" +[ 31, 70 ] = ascii (fp) : "./hrtfs/elev65/L65e010a.dat" +[ 31, 71 ] = ascii (fp) : "./hrtfs/elev65/L65e005a.dat" + +[ 32, 0 ] = ascii (fp) : "./hrtfs/elev70/L70e000a.dat" +[ 32, 1 ] = ascii (fp) : "./hrtfs/elev70/L70e355a.dat" +[ 32, 2 ] = ascii (fp) : "./hrtfs/elev70/L70e350a.dat" +[ 32, 3 ] = ascii (fp) : "./hrtfs/elev70/L70e345a.dat" +[ 32, 4 ] = ascii (fp) : "./hrtfs/elev70/L70e340a.dat" +[ 32, 5 ] = ascii (fp) : "./hrtfs/elev70/L70e335a.dat" +[ 32, 6 ] = ascii (fp) : "./hrtfs/elev70/L70e330a.dat" +[ 32, 7 ] = ascii (fp) : "./hrtfs/elev70/L70e325a.dat" +[ 32, 8 ] = ascii (fp) : "./hrtfs/elev70/L70e320a.dat" +[ 32, 9 ] = ascii (fp) : "./hrtfs/elev70/L70e315a.dat" +[ 32, 10 ] = ascii (fp) : "./hrtfs/elev70/L70e310a.dat" +[ 32, 11 ] = ascii (fp) : "./hrtfs/elev70/L70e305a.dat" +[ 32, 12 ] = ascii (fp) : "./hrtfs/elev70/L70e300a.dat" +[ 32, 13 ] = ascii (fp) : "./hrtfs/elev70/L70e295a.dat" +[ 32, 14 ] = ascii (fp) : "./hrtfs/elev70/L70e290a.dat" +[ 32, 15 ] = ascii (fp) : "./hrtfs/elev70/L70e285a.dat" +[ 32, 16 ] = ascii (fp) : "./hrtfs/elev70/L70e280a.dat" +[ 32, 17 ] = ascii (fp) : "./hrtfs/elev70/L70e275a.dat" +[ 32, 18 ] = ascii (fp) : "./hrtfs/elev70/L70e270a.dat" +[ 32, 19 ] = ascii (fp) : "./hrtfs/elev70/L70e265a.dat" +[ 32, 20 ] = ascii (fp) : "./hrtfs/elev70/L70e260a.dat" +[ 32, 21 ] = ascii (fp) : "./hrtfs/elev70/L70e255a.dat" +[ 32, 22 ] = ascii (fp) : "./hrtfs/elev70/L70e250a.dat" +[ 32, 23 ] = ascii (fp) : "./hrtfs/elev70/L70e245a.dat" +[ 32, 24 ] = ascii (fp) : "./hrtfs/elev70/L70e240a.dat" +[ 32, 25 ] = ascii (fp) : "./hrtfs/elev70/L70e235a.dat" +[ 32, 26 ] = ascii (fp) : "./hrtfs/elev70/L70e230a.dat" +[ 32, 27 ] = ascii (fp) : "./hrtfs/elev70/L70e225a.dat" +[ 32, 28 ] = ascii (fp) : "./hrtfs/elev70/L70e220a.dat" +[ 32, 29 ] = ascii (fp) : "./hrtfs/elev70/L70e215a.dat" +[ 32, 30 ] = ascii (fp) : "./hrtfs/elev70/L70e210a.dat" +[ 32, 31 ] = ascii (fp) : "./hrtfs/elev70/L70e205a.dat" +[ 32, 32 ] = ascii (fp) : "./hrtfs/elev70/L70e200a.dat" +[ 32, 33 ] = ascii (fp) : "./hrtfs/elev70/L70e195a.dat" +[ 32, 34 ] = ascii (fp) : "./hrtfs/elev70/L70e190a.dat" +[ 32, 35 ] = ascii (fp) : "./hrtfs/elev70/L70e185a.dat" +[ 32, 36 ] = ascii (fp) : "./hrtfs/elev70/L70e180a.dat" +[ 32, 37 ] = ascii (fp) : "./hrtfs/elev70/L70e175a.dat" +[ 32, 38 ] = ascii (fp) : "./hrtfs/elev70/L70e170a.dat" +[ 32, 39 ] = ascii (fp) : "./hrtfs/elev70/L70e165a.dat" +[ 32, 40 ] = ascii (fp) : "./hrtfs/elev70/L70e160a.dat" +[ 32, 41 ] = ascii (fp) : "./hrtfs/elev70/L70e155a.dat" +[ 32, 42 ] = ascii (fp) : "./hrtfs/elev70/L70e150a.dat" +[ 32, 43 ] = ascii (fp) : "./hrtfs/elev70/L70e145a.dat" +[ 32, 44 ] = ascii (fp) : "./hrtfs/elev70/L70e140a.dat" +[ 32, 45 ] = ascii (fp) : "./hrtfs/elev70/L70e135a.dat" +[ 32, 46 ] = ascii (fp) : "./hrtfs/elev70/L70e130a.dat" +[ 32, 47 ] = ascii (fp) : "./hrtfs/elev70/L70e125a.dat" +[ 32, 48 ] = ascii (fp) : "./hrtfs/elev70/L70e120a.dat" +[ 32, 49 ] = ascii (fp) : "./hrtfs/elev70/L70e115a.dat" +[ 32, 50 ] = ascii (fp) : "./hrtfs/elev70/L70e110a.dat" +[ 32, 51 ] = ascii (fp) : "./hrtfs/elev70/L70e105a.dat" +[ 32, 52 ] = ascii (fp) : "./hrtfs/elev70/L70e100a.dat" +[ 32, 53 ] = ascii (fp) : "./hrtfs/elev70/L70e095a.dat" +[ 32, 54 ] = ascii (fp) : "./hrtfs/elev70/L70e090a.dat" +[ 32, 55 ] = ascii (fp) : "./hrtfs/elev70/L70e085a.dat" +[ 32, 56 ] = ascii (fp) : "./hrtfs/elev70/L70e080a.dat" +[ 32, 57 ] = ascii (fp) : "./hrtfs/elev70/L70e075a.dat" +[ 32, 58 ] = ascii (fp) : "./hrtfs/elev70/L70e070a.dat" +[ 32, 59 ] = ascii (fp) : "./hrtfs/elev70/L70e065a.dat" +[ 32, 60 ] = ascii (fp) : "./hrtfs/elev70/L70e060a.dat" +[ 32, 61 ] = ascii (fp) : "./hrtfs/elev70/L70e055a.dat" +[ 32, 62 ] = ascii (fp) : "./hrtfs/elev70/L70e050a.dat" +[ 32, 63 ] = ascii (fp) : "./hrtfs/elev70/L70e045a.dat" +[ 32, 64 ] = ascii (fp) : "./hrtfs/elev70/L70e040a.dat" +[ 32, 65 ] = ascii (fp) : "./hrtfs/elev70/L70e035a.dat" +[ 32, 66 ] = ascii (fp) : "./hrtfs/elev70/L70e030a.dat" +[ 32, 67 ] = ascii (fp) : "./hrtfs/elev70/L70e025a.dat" +[ 32, 68 ] = ascii (fp) : "./hrtfs/elev70/L70e020a.dat" +[ 32, 69 ] = ascii (fp) : "./hrtfs/elev70/L70e015a.dat" +[ 32, 70 ] = ascii (fp) : "./hrtfs/elev70/L70e010a.dat" +[ 32, 71 ] = ascii (fp) : "./hrtfs/elev70/L70e005a.dat" + +[ 33, 0 ] = ascii (fp) : "./hrtfs/elev75/L75e000a.dat" +[ 33, 1 ] = ascii (fp) : "./hrtfs/elev75/L75e355a.dat" +[ 33, 2 ] = ascii (fp) : "./hrtfs/elev75/L75e350a.dat" +[ 33, 3 ] = ascii (fp) : "./hrtfs/elev75/L75e345a.dat" +[ 33, 4 ] = ascii (fp) : "./hrtfs/elev75/L75e340a.dat" +[ 33, 5 ] = ascii (fp) : "./hrtfs/elev75/L75e335a.dat" +[ 33, 6 ] = ascii (fp) : "./hrtfs/elev75/L75e330a.dat" +[ 33, 7 ] = ascii (fp) : "./hrtfs/elev75/L75e325a.dat" +[ 33, 8 ] = ascii (fp) : "./hrtfs/elev75/L75e320a.dat" +[ 33, 9 ] = ascii (fp) : "./hrtfs/elev75/L75e315a.dat" +[ 33, 10 ] = ascii (fp) : "./hrtfs/elev75/L75e310a.dat" +[ 33, 11 ] = ascii (fp) : "./hrtfs/elev75/L75e305a.dat" +[ 33, 12 ] = ascii (fp) : "./hrtfs/elev75/L75e300a.dat" +[ 33, 13 ] = ascii (fp) : "./hrtfs/elev75/L75e295a.dat" +[ 33, 14 ] = ascii (fp) : "./hrtfs/elev75/L75e290a.dat" +[ 33, 15 ] = ascii (fp) : "./hrtfs/elev75/L75e285a.dat" +[ 33, 16 ] = ascii (fp) : "./hrtfs/elev75/L75e280a.dat" +[ 33, 17 ] = ascii (fp) : "./hrtfs/elev75/L75e275a.dat" +[ 33, 18 ] = ascii (fp) : "./hrtfs/elev75/L75e270a.dat" +[ 33, 19 ] = ascii (fp) : "./hrtfs/elev75/L75e265a.dat" +[ 33, 20 ] = ascii (fp) : "./hrtfs/elev75/L75e260a.dat" +[ 33, 21 ] = ascii (fp) : "./hrtfs/elev75/L75e255a.dat" +[ 33, 22 ] = ascii (fp) : "./hrtfs/elev75/L75e250a.dat" +[ 33, 23 ] = ascii (fp) : "./hrtfs/elev75/L75e245a.dat" +[ 33, 24 ] = ascii (fp) : "./hrtfs/elev75/L75e240a.dat" +[ 33, 25 ] = ascii (fp) : "./hrtfs/elev75/L75e235a.dat" +[ 33, 26 ] = ascii (fp) : "./hrtfs/elev75/L75e230a.dat" +[ 33, 27 ] = ascii (fp) : "./hrtfs/elev75/L75e225a.dat" +[ 33, 28 ] = ascii (fp) : "./hrtfs/elev75/L75e220a.dat" +[ 33, 29 ] = ascii (fp) : "./hrtfs/elev75/L75e215a.dat" +[ 33, 30 ] = ascii (fp) : "./hrtfs/elev75/L75e210a.dat" +[ 33, 31 ] = ascii (fp) : "./hrtfs/elev75/L75e205a.dat" +[ 33, 32 ] = ascii (fp) : "./hrtfs/elev75/L75e200a.dat" +[ 33, 33 ] = ascii (fp) : "./hrtfs/elev75/L75e195a.dat" +[ 33, 34 ] = ascii (fp) : "./hrtfs/elev75/L75e190a.dat" +[ 33, 35 ] = ascii (fp) : "./hrtfs/elev75/L75e185a.dat" +[ 33, 36 ] = ascii (fp) : "./hrtfs/elev75/L75e180a.dat" +[ 33, 37 ] = ascii (fp) : "./hrtfs/elev75/L75e175a.dat" +[ 33, 38 ] = ascii (fp) : "./hrtfs/elev75/L75e170a.dat" +[ 33, 39 ] = ascii (fp) : "./hrtfs/elev75/L75e165a.dat" +[ 33, 40 ] = ascii (fp) : "./hrtfs/elev75/L75e160a.dat" +[ 33, 41 ] = ascii (fp) : "./hrtfs/elev75/L75e155a.dat" +[ 33, 42 ] = ascii (fp) : "./hrtfs/elev75/L75e150a.dat" +[ 33, 43 ] = ascii (fp) : "./hrtfs/elev75/L75e145a.dat" +[ 33, 44 ] = ascii (fp) : "./hrtfs/elev75/L75e140a.dat" +[ 33, 45 ] = ascii (fp) : "./hrtfs/elev75/L75e135a.dat" +[ 33, 46 ] = ascii (fp) : "./hrtfs/elev75/L75e130a.dat" +[ 33, 47 ] = ascii (fp) : "./hrtfs/elev75/L75e125a.dat" +[ 33, 48 ] = ascii (fp) : "./hrtfs/elev75/L75e120a.dat" +[ 33, 49 ] = ascii (fp) : "./hrtfs/elev75/L75e115a.dat" +[ 33, 50 ] = ascii (fp) : "./hrtfs/elev75/L75e110a.dat" +[ 33, 51 ] = ascii (fp) : "./hrtfs/elev75/L75e105a.dat" +[ 33, 52 ] = ascii (fp) : "./hrtfs/elev75/L75e100a.dat" +[ 33, 53 ] = ascii (fp) : "./hrtfs/elev75/L75e095a.dat" +[ 33, 54 ] = ascii (fp) : "./hrtfs/elev75/L75e090a.dat" +[ 33, 55 ] = ascii (fp) : "./hrtfs/elev75/L75e085a.dat" +[ 33, 56 ] = ascii (fp) : "./hrtfs/elev75/L75e080a.dat" +[ 33, 57 ] = ascii (fp) : "./hrtfs/elev75/L75e075a.dat" +[ 33, 58 ] = ascii (fp) : "./hrtfs/elev75/L75e070a.dat" +[ 33, 59 ] = ascii (fp) : "./hrtfs/elev75/L75e065a.dat" +[ 33, 60 ] = ascii (fp) : "./hrtfs/elev75/L75e060a.dat" +[ 33, 61 ] = ascii (fp) : "./hrtfs/elev75/L75e055a.dat" +[ 33, 62 ] = ascii (fp) : "./hrtfs/elev75/L75e050a.dat" +[ 33, 63 ] = ascii (fp) : "./hrtfs/elev75/L75e045a.dat" +[ 33, 64 ] = ascii (fp) : "./hrtfs/elev75/L75e040a.dat" +[ 33, 65 ] = ascii (fp) : "./hrtfs/elev75/L75e035a.dat" +[ 33, 66 ] = ascii (fp) : "./hrtfs/elev75/L75e030a.dat" +[ 33, 67 ] = ascii (fp) : "./hrtfs/elev75/L75e025a.dat" +[ 33, 68 ] = ascii (fp) : "./hrtfs/elev75/L75e020a.dat" +[ 33, 69 ] = ascii (fp) : "./hrtfs/elev75/L75e015a.dat" +[ 33, 70 ] = ascii (fp) : "./hrtfs/elev75/L75e010a.dat" +[ 33, 71 ] = ascii (fp) : "./hrtfs/elev75/L75e005a.dat" + +[ 34, 0 ] = ascii (fp) : "./hrtfs/elev80/L80e000a.dat" +[ 34, 1 ] = ascii (fp) : "./hrtfs/elev80/L80e355a.dat" +[ 34, 2 ] = ascii (fp) : "./hrtfs/elev80/L80e350a.dat" +[ 34, 3 ] = ascii (fp) : "./hrtfs/elev80/L80e345a.dat" +[ 34, 4 ] = ascii (fp) : "./hrtfs/elev80/L80e340a.dat" +[ 34, 5 ] = ascii (fp) : "./hrtfs/elev80/L80e335a.dat" +[ 34, 6 ] = ascii (fp) : "./hrtfs/elev80/L80e330a.dat" +[ 34, 7 ] = ascii (fp) : "./hrtfs/elev80/L80e325a.dat" +[ 34, 8 ] = ascii (fp) : "./hrtfs/elev80/L80e320a.dat" +[ 34, 9 ] = ascii (fp) : "./hrtfs/elev80/L80e315a.dat" +[ 34, 10 ] = ascii (fp) : "./hrtfs/elev80/L80e310a.dat" +[ 34, 11 ] = ascii (fp) : "./hrtfs/elev80/L80e305a.dat" +[ 34, 12 ] = ascii (fp) : "./hrtfs/elev80/L80e300a.dat" +[ 34, 13 ] = ascii (fp) : "./hrtfs/elev80/L80e295a.dat" +[ 34, 14 ] = ascii (fp) : "./hrtfs/elev80/L80e290a.dat" +[ 34, 15 ] = ascii (fp) : "./hrtfs/elev80/L80e285a.dat" +[ 34, 16 ] = ascii (fp) : "./hrtfs/elev80/L80e280a.dat" +[ 34, 17 ] = ascii (fp) : "./hrtfs/elev80/L80e275a.dat" +[ 34, 18 ] = ascii (fp) : "./hrtfs/elev80/L80e270a.dat" +[ 34, 19 ] = ascii (fp) : "./hrtfs/elev80/L80e265a.dat" +[ 34, 20 ] = ascii (fp) : "./hrtfs/elev80/L80e260a.dat" +[ 34, 21 ] = ascii (fp) : "./hrtfs/elev80/L80e255a.dat" +[ 34, 22 ] = ascii (fp) : "./hrtfs/elev80/L80e250a.dat" +[ 34, 23 ] = ascii (fp) : "./hrtfs/elev80/L80e245a.dat" +[ 34, 24 ] = ascii (fp) : "./hrtfs/elev80/L80e240a.dat" +[ 34, 25 ] = ascii (fp) : "./hrtfs/elev80/L80e235a.dat" +[ 34, 26 ] = ascii (fp) : "./hrtfs/elev80/L80e230a.dat" +[ 34, 27 ] = ascii (fp) : "./hrtfs/elev80/L80e225a.dat" +[ 34, 28 ] = ascii (fp) : "./hrtfs/elev80/L80e220a.dat" +[ 34, 29 ] = ascii (fp) : "./hrtfs/elev80/L80e215a.dat" +[ 34, 30 ] = ascii (fp) : "./hrtfs/elev80/L80e210a.dat" +[ 34, 31 ] = ascii (fp) : "./hrtfs/elev80/L80e205a.dat" +[ 34, 32 ] = ascii (fp) : "./hrtfs/elev80/L80e200a.dat" +[ 34, 33 ] = ascii (fp) : "./hrtfs/elev80/L80e195a.dat" +[ 34, 34 ] = ascii (fp) : "./hrtfs/elev80/L80e190a.dat" +[ 34, 35 ] = ascii (fp) : "./hrtfs/elev80/L80e185a.dat" +[ 34, 36 ] = ascii (fp) : "./hrtfs/elev80/L80e180a.dat" +[ 34, 37 ] = ascii (fp) : "./hrtfs/elev80/L80e175a.dat" +[ 34, 38 ] = ascii (fp) : "./hrtfs/elev80/L80e170a.dat" +[ 34, 39 ] = ascii (fp) : "./hrtfs/elev80/L80e165a.dat" +[ 34, 40 ] = ascii (fp) : "./hrtfs/elev80/L80e160a.dat" +[ 34, 41 ] = ascii (fp) : "./hrtfs/elev80/L80e155a.dat" +[ 34, 42 ] = ascii (fp) : "./hrtfs/elev80/L80e150a.dat" +[ 34, 43 ] = ascii (fp) : "./hrtfs/elev80/L80e145a.dat" +[ 34, 44 ] = ascii (fp) : "./hrtfs/elev80/L80e140a.dat" +[ 34, 45 ] = ascii (fp) : "./hrtfs/elev80/L80e135a.dat" +[ 34, 46 ] = ascii (fp) : "./hrtfs/elev80/L80e130a.dat" +[ 34, 47 ] = ascii (fp) : "./hrtfs/elev80/L80e125a.dat" +[ 34, 48 ] = ascii (fp) : "./hrtfs/elev80/L80e120a.dat" +[ 34, 49 ] = ascii (fp) : "./hrtfs/elev80/L80e115a.dat" +[ 34, 50 ] = ascii (fp) : "./hrtfs/elev80/L80e110a.dat" +[ 34, 51 ] = ascii (fp) : "./hrtfs/elev80/L80e105a.dat" +[ 34, 52 ] = ascii (fp) : "./hrtfs/elev80/L80e100a.dat" +[ 34, 53 ] = ascii (fp) : "./hrtfs/elev80/L80e095a.dat" +[ 34, 54 ] = ascii (fp) : "./hrtfs/elev80/L80e090a.dat" +[ 34, 55 ] = ascii (fp) : "./hrtfs/elev80/L80e085a.dat" +[ 34, 56 ] = ascii (fp) : "./hrtfs/elev80/L80e080a.dat" +[ 34, 57 ] = ascii (fp) : "./hrtfs/elev80/L80e075a.dat" +[ 34, 58 ] = ascii (fp) : "./hrtfs/elev80/L80e070a.dat" +[ 34, 59 ] = ascii (fp) : "./hrtfs/elev80/L80e065a.dat" +[ 34, 60 ] = ascii (fp) : "./hrtfs/elev80/L80e060a.dat" +[ 34, 61 ] = ascii (fp) : "./hrtfs/elev80/L80e055a.dat" +[ 34, 62 ] = ascii (fp) : "./hrtfs/elev80/L80e050a.dat" +[ 34, 63 ] = ascii (fp) : "./hrtfs/elev80/L80e045a.dat" +[ 34, 64 ] = ascii (fp) : "./hrtfs/elev80/L80e040a.dat" +[ 34, 65 ] = ascii (fp) : "./hrtfs/elev80/L80e035a.dat" +[ 34, 66 ] = ascii (fp) : "./hrtfs/elev80/L80e030a.dat" +[ 34, 67 ] = ascii (fp) : "./hrtfs/elev80/L80e025a.dat" +[ 34, 68 ] = ascii (fp) : "./hrtfs/elev80/L80e020a.dat" +[ 34, 69 ] = ascii (fp) : "./hrtfs/elev80/L80e015a.dat" +[ 34, 70 ] = ascii (fp) : "./hrtfs/elev80/L80e010a.dat" +[ 34, 71 ] = ascii (fp) : "./hrtfs/elev80/L80e005a.dat" + +[ 35, 0 ] = ascii (fp) : "./hrtfs/elev85/L85e000a.dat" +[ 35, 1 ] = ascii (fp) : "./hrtfs/elev85/L85e355a.dat" +[ 35, 2 ] = ascii (fp) : "./hrtfs/elev85/L85e350a.dat" +[ 35, 3 ] = ascii (fp) : "./hrtfs/elev85/L85e345a.dat" +[ 35, 4 ] = ascii (fp) : "./hrtfs/elev85/L85e340a.dat" +[ 35, 5 ] = ascii (fp) : "./hrtfs/elev85/L85e335a.dat" +[ 35, 6 ] = ascii (fp) : "./hrtfs/elev85/L85e330a.dat" +[ 35, 7 ] = ascii (fp) : "./hrtfs/elev85/L85e325a.dat" +[ 35, 8 ] = ascii (fp) : "./hrtfs/elev85/L85e320a.dat" +[ 35, 9 ] = ascii (fp) : "./hrtfs/elev85/L85e315a.dat" +[ 35, 10 ] = ascii (fp) : "./hrtfs/elev85/L85e310a.dat" +[ 35, 11 ] = ascii (fp) : "./hrtfs/elev85/L85e305a.dat" +[ 35, 12 ] = ascii (fp) : "./hrtfs/elev85/L85e300a.dat" +[ 35, 13 ] = ascii (fp) : "./hrtfs/elev85/L85e295a.dat" +[ 35, 14 ] = ascii (fp) : "./hrtfs/elev85/L85e290a.dat" +[ 35, 15 ] = ascii (fp) : "./hrtfs/elev85/L85e285a.dat" +[ 35, 16 ] = ascii (fp) : "./hrtfs/elev85/L85e280a.dat" +[ 35, 17 ] = ascii (fp) : "./hrtfs/elev85/L85e275a.dat" +[ 35, 18 ] = ascii (fp) : "./hrtfs/elev85/L85e270a.dat" +[ 35, 19 ] = ascii (fp) : "./hrtfs/elev85/L85e265a.dat" +[ 35, 20 ] = ascii (fp) : "./hrtfs/elev85/L85e260a.dat" +[ 35, 21 ] = ascii (fp) : "./hrtfs/elev85/L85e255a.dat" +[ 35, 22 ] = ascii (fp) : "./hrtfs/elev85/L85e250a.dat" +[ 35, 23 ] = ascii (fp) : "./hrtfs/elev85/L85e245a.dat" +[ 35, 24 ] = ascii (fp) : "./hrtfs/elev85/L85e240a.dat" +[ 35, 25 ] = ascii (fp) : "./hrtfs/elev85/L85e235a.dat" +[ 35, 26 ] = ascii (fp) : "./hrtfs/elev85/L85e230a.dat" +[ 35, 27 ] = ascii (fp) : "./hrtfs/elev85/L85e225a.dat" +[ 35, 28 ] = ascii (fp) : "./hrtfs/elev85/L85e220a.dat" +[ 35, 29 ] = ascii (fp) : "./hrtfs/elev85/L85e215a.dat" +[ 35, 30 ] = ascii (fp) : "./hrtfs/elev85/L85e210a.dat" +[ 35, 31 ] = ascii (fp) : "./hrtfs/elev85/L85e205a.dat" +[ 35, 32 ] = ascii (fp) : "./hrtfs/elev85/L85e200a.dat" +[ 35, 33 ] = ascii (fp) : "./hrtfs/elev85/L85e195a.dat" +[ 35, 34 ] = ascii (fp) : "./hrtfs/elev85/L85e190a.dat" +[ 35, 35 ] = ascii (fp) : "./hrtfs/elev85/L85e185a.dat" +[ 35, 36 ] = ascii (fp) : "./hrtfs/elev85/L85e180a.dat" +[ 35, 37 ] = ascii (fp) : "./hrtfs/elev85/L85e175a.dat" +[ 35, 38 ] = ascii (fp) : "./hrtfs/elev85/L85e170a.dat" +[ 35, 39 ] = ascii (fp) : "./hrtfs/elev85/L85e165a.dat" +[ 35, 40 ] = ascii (fp) : "./hrtfs/elev85/L85e160a.dat" +[ 35, 41 ] = ascii (fp) : "./hrtfs/elev85/L85e155a.dat" +[ 35, 42 ] = ascii (fp) : "./hrtfs/elev85/L85e150a.dat" +[ 35, 43 ] = ascii (fp) : "./hrtfs/elev85/L85e145a.dat" +[ 35, 44 ] = ascii (fp) : "./hrtfs/elev85/L85e140a.dat" +[ 35, 45 ] = ascii (fp) : "./hrtfs/elev85/L85e135a.dat" +[ 35, 46 ] = ascii (fp) : "./hrtfs/elev85/L85e130a.dat" +[ 35, 47 ] = ascii (fp) : "./hrtfs/elev85/L85e125a.dat" +[ 35, 48 ] = ascii (fp) : "./hrtfs/elev85/L85e120a.dat" +[ 35, 49 ] = ascii (fp) : "./hrtfs/elev85/L85e115a.dat" +[ 35, 50 ] = ascii (fp) : "./hrtfs/elev85/L85e110a.dat" +[ 35, 51 ] = ascii (fp) : "./hrtfs/elev85/L85e105a.dat" +[ 35, 52 ] = ascii (fp) : "./hrtfs/elev85/L85e100a.dat" +[ 35, 53 ] = ascii (fp) : "./hrtfs/elev85/L85e095a.dat" +[ 35, 54 ] = ascii (fp) : "./hrtfs/elev85/L85e090a.dat" +[ 35, 55 ] = ascii (fp) : "./hrtfs/elev85/L85e085a.dat" +[ 35, 56 ] = ascii (fp) : "./hrtfs/elev85/L85e080a.dat" +[ 35, 57 ] = ascii (fp) : "./hrtfs/elev85/L85e075a.dat" +[ 35, 58 ] = ascii (fp) : "./hrtfs/elev85/L85e070a.dat" +[ 35, 59 ] = ascii (fp) : "./hrtfs/elev85/L85e065a.dat" +[ 35, 60 ] = ascii (fp) : "./hrtfs/elev85/L85e060a.dat" +[ 35, 61 ] = ascii (fp) : "./hrtfs/elev85/L85e055a.dat" +[ 35, 62 ] = ascii (fp) : "./hrtfs/elev85/L85e050a.dat" +[ 35, 63 ] = ascii (fp) : "./hrtfs/elev85/L85e045a.dat" +[ 35, 64 ] = ascii (fp) : "./hrtfs/elev85/L85e040a.dat" +[ 35, 65 ] = ascii (fp) : "./hrtfs/elev85/L85e035a.dat" +[ 35, 66 ] = ascii (fp) : "./hrtfs/elev85/L85e030a.dat" +[ 35, 67 ] = ascii (fp) : "./hrtfs/elev85/L85e025a.dat" +[ 35, 68 ] = ascii (fp) : "./hrtfs/elev85/L85e020a.dat" +[ 35, 69 ] = ascii (fp) : "./hrtfs/elev85/L85e015a.dat" +[ 35, 70 ] = ascii (fp) : "./hrtfs/elev85/L85e010a.dat" +[ 35, 71 ] = ascii (fp) : "./hrtfs/elev85/L85e005a.dat" + +[ 36, 0 ] = ascii (fp) : "./hrtfs/elev90/L90e000a.dat" + + diff --git a/libs/openal-soft/utils/IRC_1005.def b/libs/openal-soft/utils/IRC_1005.def new file mode 100644 index 00000000..f5a16934 --- /dev/null +++ b/libs/openal-soft/utils/IRC_1005.def @@ -0,0 +1,421 @@ +# This is a makehrtf HRIR definition file. It is used to define the layout +# and source data to be processed into an OpenAL Soft compatible HRTF. +# +# This definition is used to transform an average of the left and right ear +# HRIRs from any raw data set from the IRCAM/AKG Listen HRTF database. +# +# The data sets are available free of charge from: +# +# http://recherche.ircam.fr/equipes/salles/listen/index.html +# +# Contact for the Listen HRTF Database: +# +# Olivier Warusfel , +# Room Acoustics Team, IRCAM +# 1, place Igor Stravinsky +# 75004 PARIS, France + +rate = 44100 + +# The raw sets have up to 8192 samples, but 2048 seems large enough. +points = 2048 + +# The IRCAM sets are not as dense as the MIT set. +azimuths = 1, 6, 12, 24, 24, 24, 24, 24, 24, 24, 12, 6, 1 + +# No head radius was provided. Just use the average radius of 9 cm. +radius = 0.09 + +# The distance between the source and the listener (in meters). +distance = 1.95 + +# The IRCAM source azimuth is counter-clockwise, so it needs to be flipped. +# Left and right ear HRIRs (from the respective WAVE channels) are averaged. + +# Replace all occurrences of IRC_#### for the desired subject (1005 was used +# in this demonstration). + +[ 3, 0 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P315.wav" +[ 3, 1 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P315.wav" +[ 3, 2 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P315.wav" +[ 3, 3 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P315.wav" +[ 3, 4 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P315.wav" +[ 3, 5 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P315.wav" +[ 3, 6 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P315.wav" +[ 3, 7 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P315.wav" +[ 3, 8 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P315.wav" +[ 3, 9 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P315.wav" +[ 3, 10 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P315.wav" +[ 3, 11 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P315.wav" +[ 3, 12 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P315.wav" +[ 3, 13 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P315.wav" +[ 3, 14 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P315.wav" +[ 3, 15 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P315.wav" +[ 3, 16 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P315.wav" +[ 3, 17 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P315.wav" +[ 3, 18 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P315.wav" +[ 3, 19 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P315.wav" +[ 3, 20 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P315.wav" +[ 3, 21 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P315.wav" +[ 3, 22 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P315.wav" +[ 3, 23 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P315.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P315.wav" + +[ 4, 0 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P330.wav" +[ 4, 1 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P330.wav" +[ 4, 2 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P330.wav" +[ 4, 3 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P330.wav" +[ 4, 4 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P330.wav" +[ 4, 5 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P330.wav" +[ 4, 6 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P330.wav" +[ 4, 7 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P330.wav" +[ 4, 8 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P330.wav" +[ 4, 9 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P330.wav" +[ 4, 10 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P330.wav" +[ 4, 11 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P330.wav" +[ 4, 12 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P330.wav" +[ 4, 13 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P330.wav" +[ 4, 14 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P330.wav" +[ 4, 15 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P330.wav" +[ 4, 16 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P330.wav" +[ 4, 17 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P330.wav" +[ 4, 18 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P330.wav" +[ 4, 19 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P330.wav" +[ 4, 20 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P330.wav" +[ 4, 21 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P330.wav" +[ 4, 22 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P330.wav" +[ 4, 23 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P330.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P330.wav" + +[ 5, 0 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P345.wav" +[ 5, 1 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P345.wav" +[ 5, 2 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P345.wav" +[ 5, 3 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P345.wav" +[ 5, 4 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P345.wav" +[ 5, 5 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P345.wav" +[ 5, 6 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P345.wav" +[ 5, 7 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P345.wav" +[ 5, 8 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P345.wav" +[ 5, 9 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P345.wav" +[ 5, 10 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P345.wav" +[ 5, 11 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P345.wav" +[ 5, 12 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P345.wav" +[ 5, 13 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P345.wav" +[ 5, 14 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P345.wav" +[ 5, 15 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P345.wav" +[ 5, 16 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P345.wav" +[ 5, 17 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P345.wav" +[ 5, 18 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P345.wav" +[ 5, 19 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P345.wav" +[ 5, 20 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P345.wav" +[ 5, 21 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P345.wav" +[ 5, 22 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P345.wav" +[ 5, 23 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P345.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P345.wav" + +[ 6, 0 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P000.wav" +[ 6, 1 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P000.wav" +[ 6, 2 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P000.wav" +[ 6, 3 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P000.wav" +[ 6, 4 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P000.wav" +[ 6, 5 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P000.wav" +[ 6, 6 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P000.wav" +[ 6, 7 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P000.wav" +[ 6, 8 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P000.wav" +[ 6, 9 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P000.wav" +[ 6, 10 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P000.wav" +[ 6, 11 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P000.wav" +[ 6, 12 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P000.wav" +[ 6, 13 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P000.wav" +[ 6, 14 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P000.wav" +[ 6, 15 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P000.wav" +[ 6, 16 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P000.wav" +[ 6, 17 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P000.wav" +[ 6, 18 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P000.wav" +[ 6, 19 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P000.wav" +[ 6, 20 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P000.wav" +[ 6, 21 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P000.wav" +[ 6, 22 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P000.wav" +[ 6, 23 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P000.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P000.wav" + +[ 7, 0 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P015.wav" +[ 7, 1 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P015.wav" +[ 7, 2 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P015.wav" +[ 7, 3 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P015.wav" +[ 7, 4 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P015.wav" +[ 7, 5 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P015.wav" +[ 7, 6 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P015.wav" +[ 7, 7 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P015.wav" +[ 7, 8 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P015.wav" +[ 7, 9 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P015.wav" +[ 7, 10 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P015.wav" +[ 7, 11 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P015.wav" +[ 7, 12 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P015.wav" +[ 7, 13 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P015.wav" +[ 7, 14 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P015.wav" +[ 7, 15 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P015.wav" +[ 7, 16 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P015.wav" +[ 7, 17 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P015.wav" +[ 7, 18 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P015.wav" +[ 7, 19 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P015.wav" +[ 7, 20 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P015.wav" +[ 7, 21 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P015.wav" +[ 7, 22 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P015.wav" +[ 7, 23 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P015.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P015.wav" + +[ 8, 0 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P030.wav" +[ 8, 1 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P030.wav" +[ 8, 2 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P030.wav" +[ 8, 3 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P030.wav" +[ 8, 4 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P030.wav" +[ 8, 5 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P030.wav" +[ 8, 6 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P030.wav" +[ 8, 7 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P030.wav" +[ 8, 8 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P030.wav" +[ 8, 9 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P030.wav" +[ 8, 10 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P030.wav" +[ 8, 11 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P030.wav" +[ 8, 12 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P030.wav" +[ 8, 13 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P030.wav" +[ 8, 14 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P030.wav" +[ 8, 15 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P030.wav" +[ 8, 16 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P030.wav" +[ 8, 17 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P030.wav" +[ 8, 18 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P030.wav" +[ 8, 19 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P030.wav" +[ 8, 20 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P030.wav" +[ 8, 21 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P030.wav" +[ 8, 22 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P030.wav" +[ 8, 23 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P030.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P030.wav" + +[ 9, 0 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P045.wav" +[ 9, 1 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P045.wav" +[ 9, 2 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P045.wav" +[ 9, 3 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P045.wav" +[ 9, 4 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P045.wav" +[ 9, 5 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P045.wav" +[ 9, 6 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P045.wav" +[ 9, 7 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P045.wav" +[ 9, 8 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P045.wav" +[ 9, 9 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P045.wav" +[ 9, 10 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P045.wav" +[ 9, 11 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P045.wav" +[ 9, 12 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P045.wav" +[ 9, 13 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T165_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T195_P045.wav" +[ 9, 14 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P045.wav" +[ 9, 15 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T135_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T225_P045.wav" +[ 9, 16 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P045.wav" +[ 9, 17 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T105_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T255_P045.wav" +[ 9, 18 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P045.wav" +[ 9, 19 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T075_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T285_P045.wav" +[ 9, 20 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P045.wav" +[ 9, 21 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T045_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T315_P045.wav" +[ 9, 22 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P045.wav" +[ 9, 23 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T015_P045.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T345_P045.wav" + +[ 10, 0 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P060.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P060.wav" +[ 10, 1 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P060.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P060.wav" +[ 10, 2 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P060.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P060.wav" +[ 10, 3 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P060.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P060.wav" +[ 10, 4 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P060.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P060.wav" +[ 10, 5 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P060.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P060.wav" +[ 10, 6 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P060.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P060.wav" +[ 10, 7 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T150_P060.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T210_P060.wav" +[ 10, 8 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P060.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P060.wav" +[ 10, 9 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T090_P060.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T270_P060.wav" +[ 10, 10 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P060.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P060.wav" +[ 10, 11 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T030_P060.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T330_P060.wav" + +[ 11, 0 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P075.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P075.wav" +[ 11, 1 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P075.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P075.wav" +[ 11, 2 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P075.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P075.wav" +[ 11, 3 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P075.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T180_P075.wav" +[ 11, 4 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T120_P075.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T240_P075.wav" +[ 11, 5 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T060_P075.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T300_P075.wav" + +[ 12, 0 ] = wave (0) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P090.wav" + + wave (1) : "./IRC/RAW/WAV/IRC_1005_R/IRC_1005_R_R0195_T000_P090.wav" + diff --git a/libs/openal-soft/utils/MIT_KEMAR.def b/libs/openal-soft/utils/MIT_KEMAR.def new file mode 100644 index 00000000..1067e0b4 --- /dev/null +++ b/libs/openal-soft/utils/MIT_KEMAR.def @@ -0,0 +1,823 @@ +# This is a makehrtf HRIR definition file. It is used to define the layout +# and source data to be processed into an OpenAL Soft compatible HRTF. +# +# This definition is used to transform the left ear HRIRs from the full set +# of KEMAR HRIRs provided by Bill Gardner and Keith +# Martin of MIT Media Laboratory. +# +# The data (full.zip) is available from: +# +# http://sound.media.mit.edu/resources/KEMAR.html +# +# It is copyrighted 1994 by MIT Media Laboratory, and provided free of charge +# with no restrictions on use so long as the authors (above) are cited. +# +# This definition is used to generate the internal HRTF table used by OpenAL +# Soft. + +# The following are the data set metrics. They must always be specified at +# start of a definition file, but their order is not important. + +# Sampling rate of the HRIR data (in hertz). +rate = 44100 + +# The number of points to use from the HRIR data. This should be a +# sufficiently large value (to encompass the entire impulse response). It +# cannot be smaller than the truncation size (default is 32) specified on the +# command line. +points = 512 + +# A list of the number of azimuths measured for each elevation. There must +# be at least 5 elevations covering the 180 degrees for the data set to be +# viable. +azimuths = 1, 12, 24, 36, 45, 56, 60, 72, 72, 72, 72, 72, 60, 56, 45, 36, 24, 12, 1 + +# The radius of the listener's head (measured ear-to-ear in meters). The +# makehrtf utility uses this value to rescale measured propagation delays +# when a custom head radius is specified on the command line. It is also +# used as the default radius when the spherical model is used to calculate an +# approximate set of delays. This should match the data set as close as +# possible for accurate rescaling when using the measured delays (the +# default). At the moment, radius rescaling does not adjust HRIR coupling. +radius = 0.09 + +# The distance between the source and the listener (in meters). This does +# have to match the data set, but it's effect is minimal at the moment due to +# the coupled nature of OpenAL Soft's HRTF model. +distance = 1.4 + +# Following the metrics is the list of source HRIRs for each elevation and +# azimuth pair. They don't have to be specified in order, but the final +# composition must not be sparse. They can however begin above a number of +# elevations (as typical for HRIR measurements). +# +# The elevation and azimuth indices are used to determine the resulting polar +# coordinates following OpenAL Soft's convention (-90 degree elevation +# increasing counter-clockwise from the bottom; 0 degree azimuth increasing +# clockwise from the front). +# +# More than one HRIR can be used per source, in which case the average +# magnitude response of all references for that source is used. +# +# Source specification is of the form (~BNF): +# +# source = '[' ev_index ',' az_index ']' '=' source_ref [ '+' source_ref ]* +# +# ev_index = unsigned_integer +# az_index = unsigned_integer +# source_ref = ref_spec ':' filename +# +# ref_spec = ( wave_fmt '(' wave_parms ')' [ '@' start_sample ] ) | +# ( bin_fmt '(' bini_parms ')' [ '@' start_bytes ] ) | +# ( bin_fmt '(' binf_parms ')' [ '@' start_bytes ] ) | +# ( ascii_fmt '(' asci_parms ')' [ '@' start_elements ] ) | +# ( ascii_fmt '(' ascf_parms ')' [ '@' start_elements ] ) +# filename = double_quoted_string +# +# wave_fmt = 'wave' +# wave_parms = channel +# bin_fmt = 'bin_le' | 'bin_be' +# bini_parms = 'int' ',' byte_size [ ',' bin_sig_bits ] [ ';' skip_bytes ] +# binf_parms = 'fp' ',' byte_size [ ';' skip_bytes ] +# ascii_fmt = 'ascii' +# asci_parms = 'int' ',' sig_bits [ ';' skip_elements ] +# ascf_parms = 'fp' [ ';' skip_elements ] +# start_sample = unsigned_integer +# start_bytes = unsigned_integer +# start_elements = unsigned_integer +# +# channel = unsigned_integer +# byte_size = unsigned_integer +# bin_sig_bits = signed_integer +# skip_bytes = unsigned_integer +# sig_bits = unsigned_integer +# skip_elements = unsigned_integer +# +# For bin_sig_bits, positive values mean the significant bits start at the +# MSB (padding toward the LSB) while negative values mean they start at the +# LSB. + +[ 5, 0 ] = wave (0) : "./MITfull/elev-40/L-40e000a.wav" +[ 5, 1 ] = wave (0) : "./MITfull/elev-40/L-40e006a.wav" +[ 5, 2 ] = wave (0) : "./MITfull/elev-40/L-40e013a.wav" +[ 5, 3 ] = wave (0) : "./MITfull/elev-40/L-40e019a.wav" +[ 5, 4 ] = wave (0) : "./MITfull/elev-40/L-40e026a.wav" +[ 5, 5 ] = wave (0) : "./MITfull/elev-40/L-40e032a.wav" +[ 5, 6 ] = wave (0) : "./MITfull/elev-40/L-40e039a.wav" +[ 5, 7 ] = wave (0) : "./MITfull/elev-40/L-40e045a.wav" +[ 5, 8 ] = wave (0) : "./MITfull/elev-40/L-40e051a.wav" +[ 5, 9 ] = wave (0) : "./MITfull/elev-40/L-40e058a.wav" +[ 5, 10 ] = wave (0) : "./MITfull/elev-40/L-40e064a.wav" +[ 5, 11 ] = wave (0) : "./MITfull/elev-40/L-40e071a.wav" +[ 5, 12 ] = wave (0) : "./MITfull/elev-40/L-40e077a.wav" +[ 5, 13 ] = wave (0) : "./MITfull/elev-40/L-40e084a.wav" +[ 5, 14 ] = wave (0) : "./MITfull/elev-40/L-40e090a.wav" +[ 5, 15 ] = wave (0) : "./MITfull/elev-40/L-40e096a.wav" +[ 5, 16 ] = wave (0) : "./MITfull/elev-40/L-40e103a.wav" +[ 5, 17 ] = wave (0) : "./MITfull/elev-40/L-40e109a.wav" +[ 5, 18 ] = wave (0) : "./MITfull/elev-40/L-40e116a.wav" +[ 5, 19 ] = wave (0) : "./MITfull/elev-40/L-40e122a.wav" +[ 5, 20 ] = wave (0) : "./MITfull/elev-40/L-40e129a.wav" +[ 5, 21 ] = wave (0) : "./MITfull/elev-40/L-40e135a.wav" +[ 5, 22 ] = wave (0) : "./MITfull/elev-40/L-40e141a.wav" +[ 5, 23 ] = wave (0) : "./MITfull/elev-40/L-40e148a.wav" +[ 5, 24 ] = wave (0) : "./MITfull/elev-40/L-40e154a.wav" +[ 5, 25 ] = wave (0) : "./MITfull/elev-40/L-40e161a.wav" +[ 5, 26 ] = wave (0) : "./MITfull/elev-40/L-40e167a.wav" +[ 5, 27 ] = wave (0) : "./MITfull/elev-40/L-40e174a.wav" +[ 5, 28 ] = wave (0) : "./MITfull/elev-40/L-40e180a.wav" +[ 5, 29 ] = wave (0) : "./MITfull/elev-40/L-40e186a.wav" +[ 5, 30 ] = wave (0) : "./MITfull/elev-40/L-40e193a.wav" +[ 5, 31 ] = wave (0) : "./MITfull/elev-40/L-40e199a.wav" +[ 5, 32 ] = wave (0) : "./MITfull/elev-40/L-40e206a.wav" +[ 5, 33 ] = wave (0) : "./MITfull/elev-40/L-40e212a.wav" +[ 5, 34 ] = wave (0) : "./MITfull/elev-40/L-40e219a.wav" +[ 5, 35 ] = wave (0) : "./MITfull/elev-40/L-40e225a.wav" +[ 5, 36 ] = wave (0) : "./MITfull/elev-40/L-40e231a.wav" +[ 5, 37 ] = wave (0) : "./MITfull/elev-40/L-40e238a.wav" +[ 5, 38 ] = wave (0) : "./MITfull/elev-40/L-40e244a.wav" +[ 5, 39 ] = wave (0) : "./MITfull/elev-40/L-40e251a.wav" +[ 5, 40 ] = wave (0) : "./MITfull/elev-40/L-40e257a.wav" +[ 5, 41 ] = wave (0) : "./MITfull/elev-40/L-40e264a.wav" +[ 5, 42 ] = wave (0) : "./MITfull/elev-40/L-40e270a.wav" +[ 5, 43 ] = wave (0) : "./MITfull/elev-40/L-40e276a.wav" +[ 5, 44 ] = wave (0) : "./MITfull/elev-40/L-40e283a.wav" +[ 5, 45 ] = wave (0) : "./MITfull/elev-40/L-40e289a.wav" +[ 5, 46 ] = wave (0) : "./MITfull/elev-40/L-40e296a.wav" +[ 5, 47 ] = wave (0) : "./MITfull/elev-40/L-40e302a.wav" +[ 5, 48 ] = wave (0) : "./MITfull/elev-40/L-40e309a.wav" +[ 5, 49 ] = wave (0) : "./MITfull/elev-40/L-40e315a.wav" +[ 5, 50 ] = wave (0) : "./MITfull/elev-40/L-40e321a.wav" +[ 5, 51 ] = wave (0) : "./MITfull/elev-40/L-40e328a.wav" +[ 5, 52 ] = wave (0) : "./MITfull/elev-40/L-40e334a.wav" +[ 5, 53 ] = wave (0) : "./MITfull/elev-40/L-40e341a.wav" +[ 5, 54 ] = wave (0) : "./MITfull/elev-40/L-40e347a.wav" +[ 5, 55 ] = wave (0) : "./MITfull/elev-40/L-40e354a.wav" + +[ 6, 0 ] = wave (0) : "./MITfull/elev-30/L-30e000a.wav" +[ 6, 1 ] = wave (0) : "./MITfull/elev-30/L-30e006a.wav" +[ 6, 2 ] = wave (0) : "./MITfull/elev-30/L-30e012a.wav" +[ 6, 3 ] = wave (0) : "./MITfull/elev-30/L-30e018a.wav" +[ 6, 4 ] = wave (0) : "./MITfull/elev-30/L-30e024a.wav" +[ 6, 5 ] = wave (0) : "./MITfull/elev-30/L-30e030a.wav" +[ 6, 6 ] = wave (0) : "./MITfull/elev-30/L-30e036a.wav" +[ 6, 7 ] = wave (0) : "./MITfull/elev-30/L-30e042a.wav" +[ 6, 8 ] = wave (0) : "./MITfull/elev-30/L-30e048a.wav" +[ 6, 9 ] = wave (0) : "./MITfull/elev-30/L-30e054a.wav" +[ 6, 10 ] = wave (0) : "./MITfull/elev-30/L-30e060a.wav" +[ 6, 11 ] = wave (0) : "./MITfull/elev-30/L-30e066a.wav" +[ 6, 12 ] = wave (0) : "./MITfull/elev-30/L-30e072a.wav" +[ 6, 13 ] = wave (0) : "./MITfull/elev-30/L-30e078a.wav" +[ 6, 14 ] = wave (0) : "./MITfull/elev-30/L-30e084a.wav" +[ 6, 15 ] = wave (0) : "./MITfull/elev-30/L-30e090a.wav" +[ 6, 16 ] = wave (0) : "./MITfull/elev-30/L-30e096a.wav" +[ 6, 17 ] = wave (0) : "./MITfull/elev-30/L-30e102a.wav" +[ 6, 18 ] = wave (0) : "./MITfull/elev-30/L-30e108a.wav" +[ 6, 19 ] = wave (0) : "./MITfull/elev-30/L-30e114a.wav" +[ 6, 20 ] = wave (0) : "./MITfull/elev-30/L-30e120a.wav" +[ 6, 21 ] = wave (0) : "./MITfull/elev-30/L-30e126a.wav" +[ 6, 22 ] = wave (0) : "./MITfull/elev-30/L-30e132a.wav" +[ 6, 23 ] = wave (0) : "./MITfull/elev-30/L-30e138a.wav" +[ 6, 24 ] = wave (0) : "./MITfull/elev-30/L-30e144a.wav" +[ 6, 25 ] = wave (0) : "./MITfull/elev-30/L-30e150a.wav" +[ 6, 26 ] = wave (0) : "./MITfull/elev-30/L-30e156a.wav" +[ 6, 27 ] = wave (0) : "./MITfull/elev-30/L-30e162a.wav" +[ 6, 28 ] = wave (0) : "./MITfull/elev-30/L-30e168a.wav" +[ 6, 29 ] = wave (0) : "./MITfull/elev-30/L-30e174a.wav" +[ 6, 30 ] = wave (0) : "./MITfull/elev-30/L-30e180a.wav" +[ 6, 31 ] = wave (0) : "./MITfull/elev-30/L-30e186a.wav" +[ 6, 32 ] = wave (0) : "./MITfull/elev-30/L-30e192a.wav" +[ 6, 33 ] = wave (0) : "./MITfull/elev-30/L-30e198a.wav" +[ 6, 34 ] = wave (0) : "./MITfull/elev-30/L-30e204a.wav" +[ 6, 35 ] = wave (0) : "./MITfull/elev-30/L-30e210a.wav" +[ 6, 36 ] = wave (0) : "./MITfull/elev-30/L-30e216a.wav" +[ 6, 37 ] = wave (0) : "./MITfull/elev-30/L-30e222a.wav" +[ 6, 38 ] = wave (0) : "./MITfull/elev-30/L-30e228a.wav" +[ 6, 39 ] = wave (0) : "./MITfull/elev-30/L-30e234a.wav" +[ 6, 40 ] = wave (0) : "./MITfull/elev-30/L-30e240a.wav" +[ 6, 41 ] = wave (0) : "./MITfull/elev-30/L-30e246a.wav" +[ 6, 42 ] = wave (0) : "./MITfull/elev-30/L-30e252a.wav" +[ 6, 43 ] = wave (0) : "./MITfull/elev-30/L-30e258a.wav" +[ 6, 44 ] = wave (0) : "./MITfull/elev-30/L-30e264a.wav" +[ 6, 45 ] = wave (0) : "./MITfull/elev-30/L-30e270a.wav" +[ 6, 46 ] = wave (0) : "./MITfull/elev-30/L-30e276a.wav" +[ 6, 47 ] = wave (0) : "./MITfull/elev-30/L-30e282a.wav" +[ 6, 48 ] = wave (0) : "./MITfull/elev-30/L-30e288a.wav" +[ 6, 49 ] = wave (0) : "./MITfull/elev-30/L-30e294a.wav" +[ 6, 50 ] = wave (0) : "./MITfull/elev-30/L-30e300a.wav" +[ 6, 51 ] = wave (0) : "./MITfull/elev-30/L-30e306a.wav" +[ 6, 52 ] = wave (0) : "./MITfull/elev-30/L-30e312a.wav" +[ 6, 53 ] = wave (0) : "./MITfull/elev-30/L-30e318a.wav" +[ 6, 54 ] = wave (0) : "./MITfull/elev-30/L-30e324a.wav" +[ 6, 55 ] = wave (0) : "./MITfull/elev-30/L-30e330a.wav" +[ 6, 56 ] = wave (0) : "./MITfull/elev-30/L-30e336a.wav" +[ 6, 57 ] = wave (0) : "./MITfull/elev-30/L-30e342a.wav" +[ 6, 58 ] = wave (0) : "./MITfull/elev-30/L-30e348a.wav" +[ 6, 59 ] = wave (0) : "./MITfull/elev-30/L-30e354a.wav" + +[ 7, 0 ] = wave (0) : "./MITfull/elev-20/L-20e000a.wav" +[ 7, 1 ] = wave (0) : "./MITfull/elev-20/L-20e005a.wav" +[ 7, 2 ] = wave (0) : "./MITfull/elev-20/L-20e010a.wav" +[ 7, 3 ] = wave (0) : "./MITfull/elev-20/L-20e015a.wav" +[ 7, 4 ] = wave (0) : "./MITfull/elev-20/L-20e020a.wav" +[ 7, 5 ] = wave (0) : "./MITfull/elev-20/L-20e025a.wav" +[ 7, 6 ] = wave (0) : "./MITfull/elev-20/L-20e030a.wav" +[ 7, 7 ] = wave (0) : "./MITfull/elev-20/L-20e035a.wav" +[ 7, 8 ] = wave (0) : "./MITfull/elev-20/L-20e040a.wav" +[ 7, 9 ] = wave (0) : "./MITfull/elev-20/L-20e045a.wav" +[ 7, 10 ] = wave (0) : "./MITfull/elev-20/L-20e050a.wav" +[ 7, 11 ] = wave (0) : "./MITfull/elev-20/L-20e055a.wav" +[ 7, 12 ] = wave (0) : "./MITfull/elev-20/L-20e060a.wav" +[ 7, 13 ] = wave (0) : "./MITfull/elev-20/L-20e065a.wav" +[ 7, 14 ] = wave (0) : "./MITfull/elev-20/L-20e070a.wav" +[ 7, 15 ] = wave (0) : "./MITfull/elev-20/L-20e075a.wav" +[ 7, 16 ] = wave (0) : "./MITfull/elev-20/L-20e080a.wav" +[ 7, 17 ] = wave (0) : "./MITfull/elev-20/L-20e085a.wav" +[ 7, 18 ] = wave (0) : "./MITfull/elev-20/L-20e090a.wav" +[ 7, 19 ] = wave (0) : "./MITfull/elev-20/L-20e095a.wav" +[ 7, 20 ] = wave (0) : "./MITfull/elev-20/L-20e100a.wav" +[ 7, 21 ] = wave (0) : "./MITfull/elev-20/L-20e105a.wav" +[ 7, 22 ] = wave (0) : "./MITfull/elev-20/L-20e110a.wav" +[ 7, 23 ] = wave (0) : "./MITfull/elev-20/L-20e115a.wav" +[ 7, 24 ] = wave (0) : "./MITfull/elev-20/L-20e120a.wav" +[ 7, 25 ] = wave (0) : "./MITfull/elev-20/L-20e125a.wav" +[ 7, 26 ] = wave (0) : "./MITfull/elev-20/L-20e130a.wav" +[ 7, 27 ] = wave (0) : "./MITfull/elev-20/L-20e135a.wav" +[ 7, 28 ] = wave (0) : "./MITfull/elev-20/L-20e140a.wav" +[ 7, 29 ] = wave (0) : "./MITfull/elev-20/L-20e145a.wav" +[ 7, 30 ] = wave (0) : "./MITfull/elev-20/L-20e150a.wav" +[ 7, 31 ] = wave (0) : "./MITfull/elev-20/L-20e155a.wav" +[ 7, 32 ] = wave (0) : "./MITfull/elev-20/L-20e160a.wav" +[ 7, 33 ] = wave (0) : "./MITfull/elev-20/L-20e165a.wav" +[ 7, 34 ] = wave (0) : "./MITfull/elev-20/L-20e170a.wav" +[ 7, 35 ] = wave (0) : "./MITfull/elev-20/L-20e175a.wav" +[ 7, 36 ] = wave (0) : "./MITfull/elev-20/L-20e180a.wav" +[ 7, 37 ] = wave (0) : "./MITfull/elev-20/L-20e185a.wav" +[ 7, 38 ] = wave (0) : "./MITfull/elev-20/L-20e190a.wav" +[ 7, 39 ] = wave (0) : "./MITfull/elev-20/L-20e195a.wav" +[ 7, 40 ] = wave (0) : "./MITfull/elev-20/L-20e200a.wav" +[ 7, 41 ] = wave (0) : "./MITfull/elev-20/L-20e205a.wav" +[ 7, 42 ] = wave (0) : "./MITfull/elev-20/L-20e210a.wav" +[ 7, 43 ] = wave (0) : "./MITfull/elev-20/L-20e215a.wav" +[ 7, 44 ] = wave (0) : "./MITfull/elev-20/L-20e220a.wav" +[ 7, 45 ] = wave (0) : "./MITfull/elev-20/L-20e225a.wav" +[ 7, 46 ] = wave (0) : "./MITfull/elev-20/L-20e230a.wav" +[ 7, 47 ] = wave (0) : "./MITfull/elev-20/L-20e235a.wav" +[ 7, 48 ] = wave (0) : "./MITfull/elev-20/L-20e240a.wav" +[ 7, 49 ] = wave (0) : "./MITfull/elev-20/L-20e245a.wav" +[ 7, 50 ] = wave (0) : "./MITfull/elev-20/L-20e250a.wav" +[ 7, 51 ] = wave (0) : "./MITfull/elev-20/L-20e255a.wav" +[ 7, 52 ] = wave (0) : "./MITfull/elev-20/L-20e260a.wav" +[ 7, 53 ] = wave (0) : "./MITfull/elev-20/L-20e265a.wav" +[ 7, 54 ] = wave (0) : "./MITfull/elev-20/L-20e270a.wav" +[ 7, 55 ] = wave (0) : "./MITfull/elev-20/L-20e275a.wav" +[ 7, 56 ] = wave (0) : "./MITfull/elev-20/L-20e280a.wav" +[ 7, 57 ] = wave (0) : "./MITfull/elev-20/L-20e285a.wav" +[ 7, 58 ] = wave (0) : "./MITfull/elev-20/L-20e290a.wav" +[ 7, 59 ] = wave (0) : "./MITfull/elev-20/L-20e295a.wav" +[ 7, 60 ] = wave (0) : "./MITfull/elev-20/L-20e300a.wav" +[ 7, 61 ] = wave (0) : "./MITfull/elev-20/L-20e305a.wav" +[ 7, 62 ] = wave (0) : "./MITfull/elev-20/L-20e310a.wav" +[ 7, 63 ] = wave (0) : "./MITfull/elev-20/L-20e315a.wav" +[ 7, 64 ] = wave (0) : "./MITfull/elev-20/L-20e320a.wav" +[ 7, 65 ] = wave (0) : "./MITfull/elev-20/L-20e325a.wav" +[ 7, 66 ] = wave (0) : "./MITfull/elev-20/L-20e330a.wav" +[ 7, 67 ] = wave (0) : "./MITfull/elev-20/L-20e335a.wav" +[ 7, 68 ] = wave (0) : "./MITfull/elev-20/L-20e340a.wav" +[ 7, 69 ] = wave (0) : "./MITfull/elev-20/L-20e345a.wav" +[ 7, 70 ] = wave (0) : "./MITfull/elev-20/L-20e350a.wav" +[ 7, 71 ] = wave (0) : "./MITfull/elev-20/L-20e355a.wav" + +[ 8, 0 ] = wave (0) : "./MITfull/elev-10/L-10e000a.wav" +[ 8, 1 ] = wave (0) : "./MITfull/elev-10/L-10e005a.wav" +[ 8, 2 ] = wave (0) : "./MITfull/elev-10/L-10e010a.wav" +[ 8, 3 ] = wave (0) : "./MITfull/elev-10/L-10e015a.wav" +[ 8, 4 ] = wave (0) : "./MITfull/elev-10/L-10e020a.wav" +[ 8, 5 ] = wave (0) : "./MITfull/elev-10/L-10e025a.wav" +[ 8, 6 ] = wave (0) : "./MITfull/elev-10/L-10e030a.wav" +[ 8, 7 ] = wave (0) : "./MITfull/elev-10/L-10e035a.wav" +[ 8, 8 ] = wave (0) : "./MITfull/elev-10/L-10e040a.wav" +[ 8, 9 ] = wave (0) : "./MITfull/elev-10/L-10e045a.wav" +[ 8, 10 ] = wave (0) : "./MITfull/elev-10/L-10e050a.wav" +[ 8, 11 ] = wave (0) : "./MITfull/elev-10/L-10e055a.wav" +[ 8, 12 ] = wave (0) : "./MITfull/elev-10/L-10e060a.wav" +[ 8, 13 ] = wave (0) : "./MITfull/elev-10/L-10e065a.wav" +[ 8, 14 ] = wave (0) : "./MITfull/elev-10/L-10e070a.wav" +[ 8, 15 ] = wave (0) : "./MITfull/elev-10/L-10e075a.wav" +[ 8, 16 ] = wave (0) : "./MITfull/elev-10/L-10e080a.wav" +[ 8, 17 ] = wave (0) : "./MITfull/elev-10/L-10e085a.wav" +[ 8, 18 ] = wave (0) : "./MITfull/elev-10/L-10e090a.wav" +[ 8, 19 ] = wave (0) : "./MITfull/elev-10/L-10e095a.wav" +[ 8, 20 ] = wave (0) : "./MITfull/elev-10/L-10e100a.wav" +[ 8, 21 ] = wave (0) : "./MITfull/elev-10/L-10e105a.wav" +[ 8, 22 ] = wave (0) : "./MITfull/elev-10/L-10e110a.wav" +[ 8, 23 ] = wave (0) : "./MITfull/elev-10/L-10e115a.wav" +[ 8, 24 ] = wave (0) : "./MITfull/elev-10/L-10e120a.wav" +[ 8, 25 ] = wave (0) : "./MITfull/elev-10/L-10e125a.wav" +[ 8, 26 ] = wave (0) : "./MITfull/elev-10/L-10e130a.wav" +[ 8, 27 ] = wave (0) : "./MITfull/elev-10/L-10e135a.wav" +[ 8, 28 ] = wave (0) : "./MITfull/elev-10/L-10e140a.wav" +[ 8, 29 ] = wave (0) : "./MITfull/elev-10/L-10e145a.wav" +[ 8, 30 ] = wave (0) : "./MITfull/elev-10/L-10e150a.wav" +[ 8, 31 ] = wave (0) : "./MITfull/elev-10/L-10e155a.wav" +[ 8, 32 ] = wave (0) : "./MITfull/elev-10/L-10e160a.wav" +[ 8, 33 ] = wave (0) : "./MITfull/elev-10/L-10e165a.wav" +[ 8, 34 ] = wave (0) : "./MITfull/elev-10/L-10e170a.wav" +[ 8, 35 ] = wave (0) : "./MITfull/elev-10/L-10e175a.wav" +[ 8, 36 ] = wave (0) : "./MITfull/elev-10/L-10e180a.wav" +[ 8, 37 ] = wave (0) : "./MITfull/elev-10/L-10e185a.wav" +[ 8, 38 ] = wave (0) : "./MITfull/elev-10/L-10e190a.wav" +[ 8, 39 ] = wave (0) : "./MITfull/elev-10/L-10e195a.wav" +[ 8, 40 ] = wave (0) : "./MITfull/elev-10/L-10e200a.wav" +[ 8, 41 ] = wave (0) : "./MITfull/elev-10/L-10e205a.wav" +[ 8, 42 ] = wave (0) : "./MITfull/elev-10/L-10e210a.wav" +[ 8, 43 ] = wave (0) : "./MITfull/elev-10/L-10e215a.wav" +[ 8, 44 ] = wave (0) : "./MITfull/elev-10/L-10e220a.wav" +[ 8, 45 ] = wave (0) : "./MITfull/elev-10/L-10e225a.wav" +[ 8, 46 ] = wave (0) : "./MITfull/elev-10/L-10e230a.wav" +[ 8, 47 ] = wave (0) : "./MITfull/elev-10/L-10e235a.wav" +[ 8, 48 ] = wave (0) : "./MITfull/elev-10/L-10e240a.wav" +[ 8, 49 ] = wave (0) : "./MITfull/elev-10/L-10e245a.wav" +[ 8, 50 ] = wave (0) : "./MITfull/elev-10/L-10e250a.wav" +[ 8, 51 ] = wave (0) : "./MITfull/elev-10/L-10e255a.wav" +[ 8, 52 ] = wave (0) : "./MITfull/elev-10/L-10e260a.wav" +[ 8, 53 ] = wave (0) : "./MITfull/elev-10/L-10e265a.wav" +[ 8, 54 ] = wave (0) : "./MITfull/elev-10/L-10e270a.wav" +[ 8, 55 ] = wave (0) : "./MITfull/elev-10/L-10e275a.wav" +[ 8, 56 ] = wave (0) : "./MITfull/elev-10/L-10e280a.wav" +[ 8, 57 ] = wave (0) : "./MITfull/elev-10/L-10e285a.wav" +[ 8, 58 ] = wave (0) : "./MITfull/elev-10/L-10e290a.wav" +[ 8, 59 ] = wave (0) : "./MITfull/elev-10/L-10e295a.wav" +[ 8, 60 ] = wave (0) : "./MITfull/elev-10/L-10e300a.wav" +[ 8, 61 ] = wave (0) : "./MITfull/elev-10/L-10e305a.wav" +[ 8, 62 ] = wave (0) : "./MITfull/elev-10/L-10e310a.wav" +[ 8, 63 ] = wave (0) : "./MITfull/elev-10/L-10e315a.wav" +[ 8, 64 ] = wave (0) : "./MITfull/elev-10/L-10e320a.wav" +[ 8, 65 ] = wave (0) : "./MITfull/elev-10/L-10e325a.wav" +[ 8, 66 ] = wave (0) : "./MITfull/elev-10/L-10e330a.wav" +[ 8, 67 ] = wave (0) : "./MITfull/elev-10/L-10e335a.wav" +[ 8, 68 ] = wave (0) : "./MITfull/elev-10/L-10e340a.wav" +[ 8, 69 ] = wave (0) : "./MITfull/elev-10/L-10e345a.wav" +[ 8, 70 ] = wave (0) : "./MITfull/elev-10/L-10e350a.wav" +[ 8, 71 ] = wave (0) : "./MITfull/elev-10/L-10e355a.wav" + +[ 9, 0 ] = wave (0) : "./MITfull/elev0/L0e000a.wav" +[ 9, 1 ] = wave (0) : "./MITfull/elev0/L0e005a.wav" +[ 9, 2 ] = wave (0) : "./MITfull/elev0/L0e010a.wav" +[ 9, 3 ] = wave (0) : "./MITfull/elev0/L0e015a.wav" +[ 9, 4 ] = wave (0) : "./MITfull/elev0/L0e020a.wav" +[ 9, 5 ] = wave (0) : "./MITfull/elev0/L0e025a.wav" +[ 9, 6 ] = wave (0) : "./MITfull/elev0/L0e030a.wav" +[ 9, 7 ] = wave (0) : "./MITfull/elev0/L0e035a.wav" +[ 9, 8 ] = wave (0) : "./MITfull/elev0/L0e040a.wav" +[ 9, 9 ] = wave (0) : "./MITfull/elev0/L0e045a.wav" +[ 9, 10 ] = wave (0) : "./MITfull/elev0/L0e050a.wav" +[ 9, 11 ] = wave (0) : "./MITfull/elev0/L0e055a.wav" +[ 9, 12 ] = wave (0) : "./MITfull/elev0/L0e060a.wav" +[ 9, 13 ] = wave (0) : "./MITfull/elev0/L0e065a.wav" +[ 9, 14 ] = wave (0) : "./MITfull/elev0/L0e070a.wav" +[ 9, 15 ] = wave (0) : "./MITfull/elev0/L0e075a.wav" +[ 9, 16 ] = wave (0) : "./MITfull/elev0/L0e080a.wav" +[ 9, 17 ] = wave (0) : "./MITfull/elev0/L0e085a.wav" +[ 9, 18 ] = wave (0) : "./MITfull/elev0/L0e090a.wav" +[ 9, 19 ] = wave (0) : "./MITfull/elev0/L0e095a.wav" +[ 9, 20 ] = wave (0) : "./MITfull/elev0/L0e100a.wav" +[ 9, 21 ] = wave (0) : "./MITfull/elev0/L0e105a.wav" +[ 9, 22 ] = wave (0) : "./MITfull/elev0/L0e110a.wav" +[ 9, 23 ] = wave (0) : "./MITfull/elev0/L0e115a.wav" +[ 9, 24 ] = wave (0) : "./MITfull/elev0/L0e120a.wav" +[ 9, 25 ] = wave (0) : "./MITfull/elev0/L0e125a.wav" +[ 9, 26 ] = wave (0) : "./MITfull/elev0/L0e130a.wav" +[ 9, 27 ] = wave (0) : "./MITfull/elev0/L0e135a.wav" +[ 9, 28 ] = wave (0) : "./MITfull/elev0/L0e140a.wav" +[ 9, 29 ] = wave (0) : "./MITfull/elev0/L0e145a.wav" +[ 9, 30 ] = wave (0) : "./MITfull/elev0/L0e150a.wav" +[ 9, 31 ] = wave (0) : "./MITfull/elev0/L0e155a.wav" +[ 9, 32 ] = wave (0) : "./MITfull/elev0/L0e160a.wav" +[ 9, 33 ] = wave (0) : "./MITfull/elev0/L0e165a.wav" +[ 9, 34 ] = wave (0) : "./MITfull/elev0/L0e170a.wav" +[ 9, 35 ] = wave (0) : "./MITfull/elev0/L0e175a.wav" +[ 9, 36 ] = wave (0) : "./MITfull/elev0/L0e180a.wav" +[ 9, 37 ] = wave (0) : "./MITfull/elev0/L0e185a.wav" +[ 9, 38 ] = wave (0) : "./MITfull/elev0/L0e190a.wav" +[ 9, 39 ] = wave (0) : "./MITfull/elev0/L0e195a.wav" +[ 9, 40 ] = wave (0) : "./MITfull/elev0/L0e200a.wav" +[ 9, 41 ] = wave (0) : "./MITfull/elev0/L0e205a.wav" +[ 9, 42 ] = wave (0) : "./MITfull/elev0/L0e210a.wav" +[ 9, 43 ] = wave (0) : "./MITfull/elev0/L0e215a.wav" +[ 9, 44 ] = wave (0) : "./MITfull/elev0/L0e220a.wav" +[ 9, 45 ] = wave (0) : "./MITfull/elev0/L0e225a.wav" +[ 9, 46 ] = wave (0) : "./MITfull/elev0/L0e230a.wav" +[ 9, 47 ] = wave (0) : "./MITfull/elev0/L0e235a.wav" +[ 9, 48 ] = wave (0) : "./MITfull/elev0/L0e240a.wav" +[ 9, 49 ] = wave (0) : "./MITfull/elev0/L0e245a.wav" +[ 9, 50 ] = wave (0) : "./MITfull/elev0/L0e250a.wav" +[ 9, 51 ] = wave (0) : "./MITfull/elev0/L0e255a.wav" +[ 9, 52 ] = wave (0) : "./MITfull/elev0/L0e260a.wav" +[ 9, 53 ] = wave (0) : "./MITfull/elev0/L0e265a.wav" +[ 9, 54 ] = wave (0) : "./MITfull/elev0/L0e270a.wav" +[ 9, 55 ] = wave (0) : "./MITfull/elev0/L0e275a.wav" +[ 9, 56 ] = wave (0) : "./MITfull/elev0/L0e280a.wav" +[ 9, 57 ] = wave (0) : "./MITfull/elev0/L0e285a.wav" +[ 9, 58 ] = wave (0) : "./MITfull/elev0/L0e290a.wav" +[ 9, 59 ] = wave (0) : "./MITfull/elev0/L0e295a.wav" +[ 9, 60 ] = wave (0) : "./MITfull/elev0/L0e300a.wav" +[ 9, 61 ] = wave (0) : "./MITfull/elev0/L0e305a.wav" +[ 9, 62 ] = wave (0) : "./MITfull/elev0/L0e310a.wav" +[ 9, 63 ] = wave (0) : "./MITfull/elev0/L0e315a.wav" +[ 9, 64 ] = wave (0) : "./MITfull/elev0/L0e320a.wav" +[ 9, 65 ] = wave (0) : "./MITfull/elev0/L0e325a.wav" +[ 9, 66 ] = wave (0) : "./MITfull/elev0/L0e330a.wav" +[ 9, 67 ] = wave (0) : "./MITfull/elev0/L0e335a.wav" +[ 9, 68 ] = wave (0) : "./MITfull/elev0/L0e340a.wav" +[ 9, 69 ] = wave (0) : "./MITfull/elev0/L0e345a.wav" +[ 9, 70 ] = wave (0) : "./MITfull/elev0/L0e350a.wav" +[ 9, 71 ] = wave (0) : "./MITfull/elev0/L0e355a.wav" + +[ 10, 0 ] = wave (0) : "./MITfull/elev10/L10e000a.wav" +[ 10, 1 ] = wave (0) : "./MITfull/elev10/L10e005a.wav" +[ 10, 2 ] = wave (0) : "./MITfull/elev10/L10e010a.wav" +[ 10, 3 ] = wave (0) : "./MITfull/elev10/L10e015a.wav" +[ 10, 4 ] = wave (0) : "./MITfull/elev10/L10e020a.wav" +[ 10, 5 ] = wave (0) : "./MITfull/elev10/L10e025a.wav" +[ 10, 6 ] = wave (0) : "./MITfull/elev10/L10e030a.wav" +[ 10, 7 ] = wave (0) : "./MITfull/elev10/L10e035a.wav" +[ 10, 8 ] = wave (0) : "./MITfull/elev10/L10e040a.wav" +[ 10, 9 ] = wave (0) : "./MITfull/elev10/L10e045a.wav" +[ 10, 10 ] = wave (0) : "./MITfull/elev10/L10e050a.wav" +[ 10, 11 ] = wave (0) : "./MITfull/elev10/L10e055a.wav" +[ 10, 12 ] = wave (0) : "./MITfull/elev10/L10e060a.wav" +[ 10, 13 ] = wave (0) : "./MITfull/elev10/L10e065a.wav" +[ 10, 14 ] = wave (0) : "./MITfull/elev10/L10e070a.wav" +[ 10, 15 ] = wave (0) : "./MITfull/elev10/L10e075a.wav" +[ 10, 16 ] = wave (0) : "./MITfull/elev10/L10e080a.wav" +[ 10, 17 ] = wave (0) : "./MITfull/elev10/L10e085a.wav" +[ 10, 18 ] = wave (0) : "./MITfull/elev10/L10e090a.wav" +[ 10, 19 ] = wave (0) : "./MITfull/elev10/L10e095a.wav" +[ 10, 20 ] = wave (0) : "./MITfull/elev10/L10e100a.wav" +[ 10, 21 ] = wave (0) : "./MITfull/elev10/L10e105a.wav" +[ 10, 22 ] = wave (0) : "./MITfull/elev10/L10e110a.wav" +[ 10, 23 ] = wave (0) : "./MITfull/elev10/L10e115a.wav" +[ 10, 24 ] = wave (0) : "./MITfull/elev10/L10e120a.wav" +[ 10, 25 ] = wave (0) : "./MITfull/elev10/L10e125a.wav" +[ 10, 26 ] = wave (0) : "./MITfull/elev10/L10e130a.wav" +[ 10, 27 ] = wave (0) : "./MITfull/elev10/L10e135a.wav" +[ 10, 28 ] = wave (0) : "./MITfull/elev10/L10e140a.wav" +[ 10, 29 ] = wave (0) : "./MITfull/elev10/L10e145a.wav" +[ 10, 30 ] = wave (0) : "./MITfull/elev10/L10e150a.wav" +[ 10, 31 ] = wave (0) : "./MITfull/elev10/L10e155a.wav" +[ 10, 32 ] = wave (0) : "./MITfull/elev10/L10e160a.wav" +[ 10, 33 ] = wave (0) : "./MITfull/elev10/L10e165a.wav" +[ 10, 34 ] = wave (0) : "./MITfull/elev10/L10e170a.wav" +[ 10, 35 ] = wave (0) : "./MITfull/elev10/L10e175a.wav" +[ 10, 36 ] = wave (0) : "./MITfull/elev10/L10e180a.wav" +[ 10, 37 ] = wave (0) : "./MITfull/elev10/L10e185a.wav" +[ 10, 38 ] = wave (0) : "./MITfull/elev10/L10e190a.wav" +[ 10, 39 ] = wave (0) : "./MITfull/elev10/L10e195a.wav" +[ 10, 40 ] = wave (0) : "./MITfull/elev10/L10e200a.wav" +[ 10, 41 ] = wave (0) : "./MITfull/elev10/L10e205a.wav" +[ 10, 42 ] = wave (0) : "./MITfull/elev10/L10e210a.wav" +[ 10, 43 ] = wave (0) : "./MITfull/elev10/L10e215a.wav" +[ 10, 44 ] = wave (0) : "./MITfull/elev10/L10e220a.wav" +[ 10, 45 ] = wave (0) : "./MITfull/elev10/L10e225a.wav" +[ 10, 46 ] = wave (0) : "./MITfull/elev10/L10e230a.wav" +[ 10, 47 ] = wave (0) : "./MITfull/elev10/L10e235a.wav" +[ 10, 48 ] = wave (0) : "./MITfull/elev10/L10e240a.wav" +[ 10, 49 ] = wave (0) : "./MITfull/elev10/L10e245a.wav" +[ 10, 50 ] = wave (0) : "./MITfull/elev10/L10e250a.wav" +[ 10, 51 ] = wave (0) : "./MITfull/elev10/L10e255a.wav" +[ 10, 52 ] = wave (0) : "./MITfull/elev10/L10e260a.wav" +[ 10, 53 ] = wave (0) : "./MITfull/elev10/L10e265a.wav" +[ 10, 54 ] = wave (0) : "./MITfull/elev10/L10e270a.wav" +[ 10, 55 ] = wave (0) : "./MITfull/elev10/L10e275a.wav" +[ 10, 56 ] = wave (0) : "./MITfull/elev10/L10e280a.wav" +[ 10, 57 ] = wave (0) : "./MITfull/elev10/L10e285a.wav" +[ 10, 58 ] = wave (0) : "./MITfull/elev10/L10e290a.wav" +[ 10, 59 ] = wave (0) : "./MITfull/elev10/L10e295a.wav" +[ 10, 60 ] = wave (0) : "./MITfull/elev10/L10e300a.wav" +[ 10, 61 ] = wave (0) : "./MITfull/elev10/L10e305a.wav" +[ 10, 62 ] = wave (0) : "./MITfull/elev10/L10e310a.wav" +[ 10, 63 ] = wave (0) : "./MITfull/elev10/L10e315a.wav" +[ 10, 64 ] = wave (0) : "./MITfull/elev10/L10e320a.wav" +[ 10, 65 ] = wave (0) : "./MITfull/elev10/L10e325a.wav" +[ 10, 66 ] = wave (0) : "./MITfull/elev10/L10e330a.wav" +[ 10, 67 ] = wave (0) : "./MITfull/elev10/L10e335a.wav" +[ 10, 68 ] = wave (0) : "./MITfull/elev10/L10e340a.wav" +[ 10, 69 ] = wave (0) : "./MITfull/elev10/L10e345a.wav" +[ 10, 70 ] = wave (0) : "./MITfull/elev10/L10e350a.wav" +[ 10, 71 ] = wave (0) : "./MITfull/elev10/L10e355a.wav" + +[ 11, 0 ] = wave (0) : "./MITfull/elev20/L20e000a.wav" +[ 11, 1 ] = wave (0) : "./MITfull/elev20/L20e005a.wav" +[ 11, 2 ] = wave (0) : "./MITfull/elev20/L20e010a.wav" +[ 11, 3 ] = wave (0) : "./MITfull/elev20/L20e015a.wav" +[ 11, 4 ] = wave (0) : "./MITfull/elev20/L20e020a.wav" +[ 11, 5 ] = wave (0) : "./MITfull/elev20/L20e025a.wav" +[ 11, 6 ] = wave (0) : "./MITfull/elev20/L20e030a.wav" +[ 11, 7 ] = wave (0) : "./MITfull/elev20/L20e035a.wav" +[ 11, 8 ] = wave (0) : "./MITfull/elev20/L20e040a.wav" +[ 11, 9 ] = wave (0) : "./MITfull/elev20/L20e045a.wav" +[ 11, 10 ] = wave (0) : "./MITfull/elev20/L20e050a.wav" +[ 11, 11 ] = wave (0) : "./MITfull/elev20/L20e055a.wav" +[ 11, 12 ] = wave (0) : "./MITfull/elev20/L20e060a.wav" +[ 11, 13 ] = wave (0) : "./MITfull/elev20/L20e065a.wav" +[ 11, 14 ] = wave (0) : "./MITfull/elev20/L20e070a.wav" +[ 11, 15 ] = wave (0) : "./MITfull/elev20/L20e075a.wav" +[ 11, 16 ] = wave (0) : "./MITfull/elev20/L20e080a.wav" +[ 11, 17 ] = wave (0) : "./MITfull/elev20/L20e085a.wav" +[ 11, 18 ] = wave (0) : "./MITfull/elev20/L20e090a.wav" +[ 11, 19 ] = wave (0) : "./MITfull/elev20/L20e095a.wav" +[ 11, 20 ] = wave (0) : "./MITfull/elev20/L20e100a.wav" +[ 11, 21 ] = wave (0) : "./MITfull/elev20/L20e105a.wav" +[ 11, 22 ] = wave (0) : "./MITfull/elev20/L20e110a.wav" +[ 11, 23 ] = wave (0) : "./MITfull/elev20/L20e115a.wav" +[ 11, 24 ] = wave (0) : "./MITfull/elev20/L20e120a.wav" +[ 11, 25 ] = wave (0) : "./MITfull/elev20/L20e125a.wav" +[ 11, 26 ] = wave (0) : "./MITfull/elev20/L20e130a.wav" +[ 11, 27 ] = wave (0) : "./MITfull/elev20/L20e135a.wav" +[ 11, 28 ] = wave (0) : "./MITfull/elev20/L20e140a.wav" +[ 11, 29 ] = wave (0) : "./MITfull/elev20/L20e145a.wav" +[ 11, 30 ] = wave (0) : "./MITfull/elev20/L20e150a.wav" +[ 11, 31 ] = wave (0) : "./MITfull/elev20/L20e155a.wav" +[ 11, 32 ] = wave (0) : "./MITfull/elev20/L20e160a.wav" +[ 11, 33 ] = wave (0) : "./MITfull/elev20/L20e165a.wav" +[ 11, 34 ] = wave (0) : "./MITfull/elev20/L20e170a.wav" +[ 11, 35 ] = wave (0) : "./MITfull/elev20/L20e175a.wav" +[ 11, 36 ] = wave (0) : "./MITfull/elev20/L20e180a.wav" +[ 11, 37 ] = wave (0) : "./MITfull/elev20/L20e185a.wav" +[ 11, 38 ] = wave (0) : "./MITfull/elev20/L20e190a.wav" +[ 11, 39 ] = wave (0) : "./MITfull/elev20/L20e195a.wav" +[ 11, 40 ] = wave (0) : "./MITfull/elev20/L20e200a.wav" +[ 11, 41 ] = wave (0) : "./MITfull/elev20/L20e205a.wav" +[ 11, 42 ] = wave (0) : "./MITfull/elev20/L20e210a.wav" +[ 11, 43 ] = wave (0) : "./MITfull/elev20/L20e215a.wav" +[ 11, 44 ] = wave (0) : "./MITfull/elev20/L20e220a.wav" +[ 11, 45 ] = wave (0) : "./MITfull/elev20/L20e225a.wav" +[ 11, 46 ] = wave (0) : "./MITfull/elev20/L20e230a.wav" +[ 11, 47 ] = wave (0) : "./MITfull/elev20/L20e235a.wav" +[ 11, 48 ] = wave (0) : "./MITfull/elev20/L20e240a.wav" +[ 11, 49 ] = wave (0) : "./MITfull/elev20/L20e245a.wav" +[ 11, 50 ] = wave (0) : "./MITfull/elev20/L20e250a.wav" +[ 11, 51 ] = wave (0) : "./MITfull/elev20/L20e255a.wav" +[ 11, 52 ] = wave (0) : "./MITfull/elev20/L20e260a.wav" +[ 11, 53 ] = wave (0) : "./MITfull/elev20/L20e265a.wav" +[ 11, 54 ] = wave (0) : "./MITfull/elev20/L20e270a.wav" +[ 11, 55 ] = wave (0) : "./MITfull/elev20/L20e275a.wav" +[ 11, 56 ] = wave (0) : "./MITfull/elev20/L20e280a.wav" +[ 11, 57 ] = wave (0) : "./MITfull/elev20/L20e285a.wav" +[ 11, 58 ] = wave (0) : "./MITfull/elev20/L20e290a.wav" +[ 11, 59 ] = wave (0) : "./MITfull/elev20/L20e295a.wav" +[ 11, 60 ] = wave (0) : "./MITfull/elev20/L20e300a.wav" +[ 11, 61 ] = wave (0) : "./MITfull/elev20/L20e305a.wav" +[ 11, 62 ] = wave (0) : "./MITfull/elev20/L20e310a.wav" +[ 11, 63 ] = wave (0) : "./MITfull/elev20/L20e315a.wav" +[ 11, 64 ] = wave (0) : "./MITfull/elev20/L20e320a.wav" +[ 11, 65 ] = wave (0) : "./MITfull/elev20/L20e325a.wav" +[ 11, 66 ] = wave (0) : "./MITfull/elev20/L20e330a.wav" +[ 11, 67 ] = wave (0) : "./MITfull/elev20/L20e335a.wav" +[ 11, 68 ] = wave (0) : "./MITfull/elev20/L20e340a.wav" +[ 11, 69 ] = wave (0) : "./MITfull/elev20/L20e345a.wav" +[ 11, 70 ] = wave (0) : "./MITfull/elev20/L20e350a.wav" +[ 11, 71 ] = wave (0) : "./MITfull/elev20/L20e355a.wav" + +[ 12, 0 ] = wave (0) : "./MITfull/elev30/L30e000a.wav" +[ 12, 1 ] = wave (0) : "./MITfull/elev30/L30e006a.wav" +[ 12, 2 ] = wave (0) : "./MITfull/elev30/L30e012a.wav" +[ 12, 3 ] = wave (0) : "./MITfull/elev30/L30e018a.wav" +[ 12, 4 ] = wave (0) : "./MITfull/elev30/L30e024a.wav" +[ 12, 5 ] = wave (0) : "./MITfull/elev30/L30e030a.wav" +[ 12, 6 ] = wave (0) : "./MITfull/elev30/L30e036a.wav" +[ 12, 7 ] = wave (0) : "./MITfull/elev30/L30e042a.wav" +[ 12, 8 ] = wave (0) : "./MITfull/elev30/L30e048a.wav" +[ 12, 9 ] = wave (0) : "./MITfull/elev30/L30e054a.wav" +[ 12, 10 ] = wave (0) : "./MITfull/elev30/L30e060a.wav" +[ 12, 11 ] = wave (0) : "./MITfull/elev30/L30e066a.wav" +[ 12, 12 ] = wave (0) : "./MITfull/elev30/L30e072a.wav" +[ 12, 13 ] = wave (0) : "./MITfull/elev30/L30e078a.wav" +[ 12, 14 ] = wave (0) : "./MITfull/elev30/L30e084a.wav" +[ 12, 15 ] = wave (0) : "./MITfull/elev30/L30e090a.wav" +[ 12, 16 ] = wave (0) : "./MITfull/elev30/L30e096a.wav" +[ 12, 17 ] = wave (0) : "./MITfull/elev30/L30e102a.wav" +[ 12, 18 ] = wave (0) : "./MITfull/elev30/L30e108a.wav" +[ 12, 19 ] = wave (0) : "./MITfull/elev30/L30e114a.wav" +[ 12, 20 ] = wave (0) : "./MITfull/elev30/L30e120a.wav" +[ 12, 21 ] = wave (0) : "./MITfull/elev30/L30e126a.wav" +[ 12, 22 ] = wave (0) : "./MITfull/elev30/L30e132a.wav" +[ 12, 23 ] = wave (0) : "./MITfull/elev30/L30e138a.wav" +[ 12, 24 ] = wave (0) : "./MITfull/elev30/L30e144a.wav" +[ 12, 25 ] = wave (0) : "./MITfull/elev30/L30e150a.wav" +[ 12, 26 ] = wave (0) : "./MITfull/elev30/L30e156a.wav" +[ 12, 27 ] = wave (0) : "./MITfull/elev30/L30e162a.wav" +[ 12, 28 ] = wave (0) : "./MITfull/elev30/L30e168a.wav" +[ 12, 29 ] = wave (0) : "./MITfull/elev30/L30e174a.wav" +[ 12, 30 ] = wave (0) : "./MITfull/elev30/L30e180a.wav" +[ 12, 31 ] = wave (0) : "./MITfull/elev30/L30e186a.wav" +[ 12, 32 ] = wave (0) : "./MITfull/elev30/L30e192a.wav" +[ 12, 33 ] = wave (0) : "./MITfull/elev30/L30e198a.wav" +[ 12, 34 ] = wave (0) : "./MITfull/elev30/L30e204a.wav" +[ 12, 35 ] = wave (0) : "./MITfull/elev30/L30e210a.wav" +[ 12, 36 ] = wave (0) : "./MITfull/elev30/L30e216a.wav" +[ 12, 37 ] = wave (0) : "./MITfull/elev30/L30e222a.wav" +[ 12, 38 ] = wave (0) : "./MITfull/elev30/L30e228a.wav" +[ 12, 39 ] = wave (0) : "./MITfull/elev30/L30e234a.wav" +[ 12, 40 ] = wave (0) : "./MITfull/elev30/L30e240a.wav" +[ 12, 41 ] = wave (0) : "./MITfull/elev30/L30e246a.wav" +[ 12, 42 ] = wave (0) : "./MITfull/elev30/L30e252a.wav" +[ 12, 43 ] = wave (0) : "./MITfull/elev30/L30e258a.wav" +[ 12, 44 ] = wave (0) : "./MITfull/elev30/L30e264a.wav" +[ 12, 45 ] = wave (0) : "./MITfull/elev30/L30e270a.wav" +[ 12, 46 ] = wave (0) : "./MITfull/elev30/L30e276a.wav" +[ 12, 47 ] = wave (0) : "./MITfull/elev30/L30e282a.wav" +[ 12, 48 ] = wave (0) : "./MITfull/elev30/L30e288a.wav" +[ 12, 49 ] = wave (0) : "./MITfull/elev30/L30e294a.wav" +[ 12, 50 ] = wave (0) : "./MITfull/elev30/L30e300a.wav" +[ 12, 51 ] = wave (0) : "./MITfull/elev30/L30e306a.wav" +[ 12, 52 ] = wave (0) : "./MITfull/elev30/L30e312a.wav" +[ 12, 53 ] = wave (0) : "./MITfull/elev30/L30e318a.wav" +[ 12, 54 ] = wave (0) : "./MITfull/elev30/L30e324a.wav" +[ 12, 55 ] = wave (0) : "./MITfull/elev30/L30e330a.wav" +[ 12, 56 ] = wave (0) : "./MITfull/elev30/L30e336a.wav" +[ 12, 57 ] = wave (0) : "./MITfull/elev30/L30e342a.wav" +[ 12, 58 ] = wave (0) : "./MITfull/elev30/L30e348a.wav" +[ 12, 59 ] = wave (0) : "./MITfull/elev30/L30e354a.wav" + +[ 13, 0 ] = wave (0) : "./MITfull/elev40/L40e000a.wav" +[ 13, 1 ] = wave (0) : "./MITfull/elev40/L40e006a.wav" +[ 13, 2 ] = wave (0) : "./MITfull/elev40/L40e013a.wav" +[ 13, 3 ] = wave (0) : "./MITfull/elev40/L40e019a.wav" +[ 13, 4 ] = wave (0) : "./MITfull/elev40/L40e026a.wav" +[ 13, 5 ] = wave (0) : "./MITfull/elev40/L40e032a.wav" +[ 13, 6 ] = wave (0) : "./MITfull/elev40/L40e039a.wav" +[ 13, 7 ] = wave (0) : "./MITfull/elev40/L40e045a.wav" +[ 13, 8 ] = wave (0) : "./MITfull/elev40/L40e051a.wav" +[ 13, 9 ] = wave (0) : "./MITfull/elev40/L40e058a.wav" +[ 13, 10 ] = wave (0) : "./MITfull/elev40/L40e064a.wav" +[ 13, 11 ] = wave (0) : "./MITfull/elev40/L40e071a.wav" +[ 13, 12 ] = wave (0) : "./MITfull/elev40/L40e077a.wav" +[ 13, 13 ] = wave (0) : "./MITfull/elev40/L40e084a.wav" +[ 13, 14 ] = wave (0) : "./MITfull/elev40/L40e090a.wav" +[ 13, 15 ] = wave (0) : "./MITfull/elev40/L40e096a.wav" +[ 13, 16 ] = wave (0) : "./MITfull/elev40/L40e103a.wav" +[ 13, 17 ] = wave (0) : "./MITfull/elev40/L40e109a.wav" +[ 13, 18 ] = wave (0) : "./MITfull/elev40/L40e116a.wav" +[ 13, 19 ] = wave (0) : "./MITfull/elev40/L40e122a.wav" +[ 13, 20 ] = wave (0) : "./MITfull/elev40/L40e129a.wav" +[ 13, 21 ] = wave (0) : "./MITfull/elev40/L40e135a.wav" +[ 13, 22 ] = wave (0) : "./MITfull/elev40/L40e141a.wav" +[ 13, 23 ] = wave (0) : "./MITfull/elev40/L40e148a.wav" +[ 13, 24 ] = wave (0) : "./MITfull/elev40/L40e154a.wav" +[ 13, 25 ] = wave (0) : "./MITfull/elev40/L40e161a.wav" +[ 13, 26 ] = wave (0) : "./MITfull/elev40/L40e167a.wav" +[ 13, 27 ] = wave (0) : "./MITfull/elev40/L40e174a.wav" +[ 13, 28 ] = wave (0) : "./MITfull/elev40/L40e180a.wav" +[ 13, 29 ] = wave (0) : "./MITfull/elev40/L40e186a.wav" +[ 13, 30 ] = wave (0) : "./MITfull/elev40/L40e193a.wav" +[ 13, 31 ] = wave (0) : "./MITfull/elev40/L40e199a.wav" +[ 13, 32 ] = wave (0) : "./MITfull/elev40/L40e206a.wav" +[ 13, 33 ] = wave (0) : "./MITfull/elev40/L40e212a.wav" +[ 13, 34 ] = wave (0) : "./MITfull/elev40/L40e219a.wav" +[ 13, 35 ] = wave (0) : "./MITfull/elev40/L40e225a.wav" +[ 13, 36 ] = wave (0) : "./MITfull/elev40/L40e231a.wav" +[ 13, 37 ] = wave (0) : "./MITfull/elev40/L40e238a.wav" +[ 13, 38 ] = wave (0) : "./MITfull/elev40/L40e244a.wav" +[ 13, 39 ] = wave (0) : "./MITfull/elev40/L40e251a.wav" +[ 13, 40 ] = wave (0) : "./MITfull/elev40/L40e257a.wav" +[ 13, 41 ] = wave (0) : "./MITfull/elev40/L40e264a.wav" +[ 13, 42 ] = wave (0) : "./MITfull/elev40/L40e270a.wav" +[ 13, 43 ] = wave (0) : "./MITfull/elev40/L40e276a.wav" +[ 13, 44 ] = wave (0) : "./MITfull/elev40/L40e283a.wav" +[ 13, 45 ] = wave (0) : "./MITfull/elev40/L40e289a.wav" +[ 13, 46 ] = wave (0) : "./MITfull/elev40/L40e296a.wav" +[ 13, 47 ] = wave (0) : "./MITfull/elev40/L40e302a.wav" +[ 13, 48 ] = wave (0) : "./MITfull/elev40/L40e309a.wav" +[ 13, 49 ] = wave (0) : "./MITfull/elev40/L40e315a.wav" +[ 13, 50 ] = wave (0) : "./MITfull/elev40/L40e321a.wav" +[ 13, 51 ] = wave (0) : "./MITfull/elev40/L40e328a.wav" +[ 13, 52 ] = wave (0) : "./MITfull/elev40/L40e334a.wav" +[ 13, 53 ] = wave (0) : "./MITfull/elev40/L40e341a.wav" +[ 13, 54 ] = wave (0) : "./MITfull/elev40/L40e347a.wav" +[ 13, 55 ] = wave (0) : "./MITfull/elev40/L40e354a.wav" + +[ 14, 0 ] = wave (0) : "./MITfull/elev50/L50e000a.wav" +[ 14, 1 ] = wave (0) : "./MITfull/elev50/L50e008a.wav" +[ 14, 2 ] = wave (0) : "./MITfull/elev50/L50e016a.wav" +[ 14, 3 ] = wave (0) : "./MITfull/elev50/L50e024a.wav" +[ 14, 4 ] = wave (0) : "./MITfull/elev50/L50e032a.wav" +[ 14, 5 ] = wave (0) : "./MITfull/elev50/L50e040a.wav" +[ 14, 6 ] = wave (0) : "./MITfull/elev50/L50e048a.wav" +[ 14, 7 ] = wave (0) : "./MITfull/elev50/L50e056a.wav" +[ 14, 8 ] = wave (0) : "./MITfull/elev50/L50e064a.wav" +[ 14, 9 ] = wave (0) : "./MITfull/elev50/L50e072a.wav" +[ 14, 10 ] = wave (0) : "./MITfull/elev50/L50e080a.wav" +[ 14, 11 ] = wave (0) : "./MITfull/elev50/L50e088a.wav" +[ 14, 12 ] = wave (0) : "./MITfull/elev50/L50e096a.wav" +[ 14, 13 ] = wave (0) : "./MITfull/elev50/L50e104a.wav" +[ 14, 14 ] = wave (0) : "./MITfull/elev50/L50e112a.wav" +[ 14, 15 ] = wave (0) : "./MITfull/elev50/L50e120a.wav" +[ 14, 16 ] = wave (0) : "./MITfull/elev50/L50e128a.wav" +[ 14, 17 ] = wave (0) : "./MITfull/elev50/L50e136a.wav" +[ 14, 18 ] = wave (0) : "./MITfull/elev50/L50e144a.wav" +[ 14, 19 ] = wave (0) : "./MITfull/elev50/L50e152a.wav" +[ 14, 20 ] = wave (0) : "./MITfull/elev50/L50e160a.wav" +[ 14, 21 ] = wave (0) : "./MITfull/elev50/L50e168a.wav" +[ 14, 22 ] = wave (0) : "./MITfull/elev50/L50e176a.wav" +[ 14, 23 ] = wave (0) : "./MITfull/elev50/L50e184a.wav" +[ 14, 24 ] = wave (0) : "./MITfull/elev50/L50e192a.wav" +[ 14, 25 ] = wave (0) : "./MITfull/elev50/L50e200a.wav" +[ 14, 26 ] = wave (0) : "./MITfull/elev50/L50e208a.wav" +[ 14, 27 ] = wave (0) : "./MITfull/elev50/L50e216a.wav" +[ 14, 28 ] = wave (0) : "./MITfull/elev50/L50e224a.wav" +[ 14, 29 ] = wave (0) : "./MITfull/elev50/L50e232a.wav" +[ 14, 30 ] = wave (0) : "./MITfull/elev50/L50e240a.wav" +[ 14, 31 ] = wave (0) : "./MITfull/elev50/L50e248a.wav" +[ 14, 32 ] = wave (0) : "./MITfull/elev50/L50e256a.wav" +[ 14, 33 ] = wave (0) : "./MITfull/elev50/L50e264a.wav" +[ 14, 34 ] = wave (0) : "./MITfull/elev50/L50e272a.wav" +[ 14, 35 ] = wave (0) : "./MITfull/elev50/L50e280a.wav" +[ 14, 36 ] = wave (0) : "./MITfull/elev50/L50e288a.wav" +[ 14, 37 ] = wave (0) : "./MITfull/elev50/L50e296a.wav" +[ 14, 38 ] = wave (0) : "./MITfull/elev50/L50e304a.wav" +[ 14, 39 ] = wave (0) : "./MITfull/elev50/L50e312a.wav" +[ 14, 40 ] = wave (0) : "./MITfull/elev50/L50e320a.wav" +[ 14, 41 ] = wave (0) : "./MITfull/elev50/L50e328a.wav" +[ 14, 42 ] = wave (0) : "./MITfull/elev50/L50e336a.wav" +[ 14, 43 ] = wave (0) : "./MITfull/elev50/L50e344a.wav" +[ 14, 44 ] = wave (0) : "./MITfull/elev50/L50e352a.wav" + +[ 15, 0 ] = wave (0) : "./MITfull/elev60/L60e000a.wav" +[ 15, 1 ] = wave (0) : "./MITfull/elev60/L60e010a.wav" +[ 15, 2 ] = wave (0) : "./MITfull/elev60/L60e020a.wav" +[ 15, 3 ] = wave (0) : "./MITfull/elev60/L60e030a.wav" +[ 15, 4 ] = wave (0) : "./MITfull/elev60/L60e040a.wav" +[ 15, 5 ] = wave (0) : "./MITfull/elev60/L60e050a.wav" +[ 15, 6 ] = wave (0) : "./MITfull/elev60/L60e060a.wav" +[ 15, 7 ] = wave (0) : "./MITfull/elev60/L60e070a.wav" +[ 15, 8 ] = wave (0) : "./MITfull/elev60/L60e080a.wav" +[ 15, 9 ] = wave (0) : "./MITfull/elev60/L60e090a.wav" +[ 15, 10 ] = wave (0) : "./MITfull/elev60/L60e100a.wav" +[ 15, 11 ] = wave (0) : "./MITfull/elev60/L60e110a.wav" +[ 15, 12 ] = wave (0) : "./MITfull/elev60/L60e120a.wav" +[ 15, 13 ] = wave (0) : "./MITfull/elev60/L60e130a.wav" +[ 15, 14 ] = wave (0) : "./MITfull/elev60/L60e140a.wav" +[ 15, 15 ] = wave (0) : "./MITfull/elev60/L60e150a.wav" +[ 15, 16 ] = wave (0) : "./MITfull/elev60/L60e160a.wav" +[ 15, 17 ] = wave (0) : "./MITfull/elev60/L60e170a.wav" +[ 15, 18 ] = wave (0) : "./MITfull/elev60/L60e180a.wav" +[ 15, 19 ] = wave (0) : "./MITfull/elev60/L60e190a.wav" +[ 15, 20 ] = wave (0) : "./MITfull/elev60/L60e200a.wav" +[ 15, 21 ] = wave (0) : "./MITfull/elev60/L60e210a.wav" +[ 15, 22 ] = wave (0) : "./MITfull/elev60/L60e220a.wav" +[ 15, 23 ] = wave (0) : "./MITfull/elev60/L60e230a.wav" +[ 15, 24 ] = wave (0) : "./MITfull/elev60/L60e240a.wav" +[ 15, 25 ] = wave (0) : "./MITfull/elev60/L60e250a.wav" +[ 15, 26 ] = wave (0) : "./MITfull/elev60/L60e260a.wav" +[ 15, 27 ] = wave (0) : "./MITfull/elev60/L60e270a.wav" +[ 15, 28 ] = wave (0) : "./MITfull/elev60/L60e280a.wav" +[ 15, 29 ] = wave (0) : "./MITfull/elev60/L60e290a.wav" +[ 15, 30 ] = wave (0) : "./MITfull/elev60/L60e300a.wav" +[ 15, 31 ] = wave (0) : "./MITfull/elev60/L60e310a.wav" +[ 15, 32 ] = wave (0) : "./MITfull/elev60/L60e320a.wav" +[ 15, 33 ] = wave (0) : "./MITfull/elev60/L60e330a.wav" +[ 15, 34 ] = wave (0) : "./MITfull/elev60/L60e340a.wav" +[ 15, 35 ] = wave (0) : "./MITfull/elev60/L60e350a.wav" + +[ 16, 0 ] = wave (0) : "./MITfull/elev70/L70e000a.wav" +[ 16, 1 ] = wave (0) : "./MITfull/elev70/L70e015a.wav" +[ 16, 2 ] = wave (0) : "./MITfull/elev70/L70e030a.wav" +[ 16, 3 ] = wave (0) : "./MITfull/elev70/L70e045a.wav" +[ 16, 4 ] = wave (0) : "./MITfull/elev70/L70e060a.wav" +[ 16, 5 ] = wave (0) : "./MITfull/elev70/L70e075a.wav" +[ 16, 6 ] = wave (0) : "./MITfull/elev70/L70e090a.wav" +[ 16, 7 ] = wave (0) : "./MITfull/elev70/L70e105a.wav" +[ 16, 8 ] = wave (0) : "./MITfull/elev70/L70e120a.wav" +[ 16, 9 ] = wave (0) : "./MITfull/elev70/L70e135a.wav" +[ 16, 10 ] = wave (0) : "./MITfull/elev70/L70e150a.wav" +[ 16, 11 ] = wave (0) : "./MITfull/elev70/L70e165a.wav" +[ 16, 12 ] = wave (0) : "./MITfull/elev70/L70e180a.wav" +[ 16, 13 ] = wave (0) : "./MITfull/elev70/L70e195a.wav" +[ 16, 14 ] = wave (0) : "./MITfull/elev70/L70e210a.wav" +[ 16, 15 ] = wave (0) : "./MITfull/elev70/L70e225a.wav" +[ 16, 16 ] = wave (0) : "./MITfull/elev70/L70e240a.wav" +[ 16, 17 ] = wave (0) : "./MITfull/elev70/L70e255a.wav" +[ 16, 18 ] = wave (0) : "./MITfull/elev70/L70e270a.wav" +[ 16, 19 ] = wave (0) : "./MITfull/elev70/L70e285a.wav" +[ 16, 20 ] = wave (0) : "./MITfull/elev70/L70e300a.wav" +[ 16, 21 ] = wave (0) : "./MITfull/elev70/L70e315a.wav" +[ 16, 22 ] = wave (0) : "./MITfull/elev70/L70e330a.wav" +[ 16, 23 ] = wave (0) : "./MITfull/elev70/L70e345a.wav" + +[ 17, 0 ] = wave (0) : "./MITfull/elev80/L80e000a.wav" +[ 17, 1 ] = wave (0) : "./MITfull/elev80/L80e030a.wav" +[ 17, 2 ] = wave (0) : "./MITfull/elev80/L80e060a.wav" +[ 17, 3 ] = wave (0) : "./MITfull/elev80/L80e090a.wav" +[ 17, 4 ] = wave (0) : "./MITfull/elev80/L80e120a.wav" +[ 17, 5 ] = wave (0) : "./MITfull/elev80/L80e150a.wav" +[ 17, 6 ] = wave (0) : "./MITfull/elev80/L80e180a.wav" +[ 17, 7 ] = wave (0) : "./MITfull/elev80/L80e210a.wav" +[ 17, 8 ] = wave (0) : "./MITfull/elev80/L80e240a.wav" +[ 17, 9 ] = wave (0) : "./MITfull/elev80/L80e270a.wav" +[ 17, 10 ] = wave (0) : "./MITfull/elev80/L80e300a.wav" +[ 17, 11 ] = wave (0) : "./MITfull/elev80/L80e330a.wav" + +[ 18, 0 ] = wave (0) : "./MITfull/elev90/L90e000a.wav" + diff --git a/libs/openal-soft/utils/alsoft-config/CMakeLists.txt b/libs/openal-soft/utils/alsoft-config/CMakeLists.txt new file mode 100644 index 00000000..a6707a3d --- /dev/null +++ b/libs/openal-soft/utils/alsoft-config/CMakeLists.txt @@ -0,0 +1,29 @@ +project(alsoft-config) + +include_directories("${alsoft-config_BINARY_DIR}") + +# Need Qt 4.8.0 or newer for the iconset theme attribute to work +find_package(Qt4 4.8.0 COMPONENTS QtCore QtGui) +if(QT4_FOUND) + include(${QT_USE_FILE}) + + set(alsoft-config_SRCS main.cpp + mainwindow.cpp + ) + + set(alsoft-config_UIS mainwindow.ui) + QT4_WRAP_UI(UIS ${alsoft-config_UIS}) + + set(alsoft-config_MOCS mainwindow.h) + QT4_WRAP_CPP(MOCS ${alsoft-config_MOCS}) + + add_executable(alsoft-config ${alsoft-config_SRCS} ${UIS} ${RSCS} ${TRS} ${MOCS}) + target_link_libraries(alsoft-config ${QT_LIBRARIES}) + set_target_properties(alsoft-config PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${OpenAL_BINARY_DIR}) + + install(TARGETS alsoft-config + RUNTIME DESTINATION bin + LIBRARY DESTINATION "lib${LIB_SUFFIX}" + ARCHIVE DESTINATION "lib${LIB_SUFFIX}" + ) +endif() diff --git a/libs/openal-soft/utils/alsoft-config/main.cpp b/libs/openal-soft/utils/alsoft-config/main.cpp new file mode 100644 index 00000000..b48f94ec --- /dev/null +++ b/libs/openal-soft/utils/alsoft-config/main.cpp @@ -0,0 +1,11 @@ +#include "mainwindow.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/libs/openal-soft/utils/alsoft-config/mainwindow.cpp b/libs/openal-soft/utils/alsoft-config/mainwindow.cpp new file mode 100644 index 00000000..1d5a3dcd --- /dev/null +++ b/libs/openal-soft/utils/alsoft-config/mainwindow.cpp @@ -0,0 +1,660 @@ +#include +#include +#include +#include +#include "mainwindow.h" +#include "ui_mainwindow.h" + +namespace { +static const struct { + char backend_name[16]; + char menu_string[32]; +} backendMenuList[] = { +#ifdef Q_OS_WIN32 + { "mmdevapi", "Add MMDevAPI" }, + { "dsound", "Add DirectSound" }, + { "winmm", "Add Windows Multimedia" }, +#endif +#ifdef Q_OS_MAC + { "core", "Add CoreAudio" }, +#endif + { "pulse", "Add PulseAudio" }, +#ifdef Q_OS_UNIX + { "alsa", "Add ALSA" }, + { "oss", "Add OSS" }, + { "solaris", "Add Solaris" }, + { "sndio", "Add SndIO" }, + { "qsa", "Add QSA" }, +#endif + { "port", "Add PortAudio" }, + { "opensl", "Add OpenSL" }, + { "null", "Add Null Output" }, + { "wave", "Add Wave Writer" }, + { "", "" } +}; + +static QString getDefaultConfigName() +{ +#ifdef Q_OS_WIN32 + static const char fname[] = "alsoft.ini"; + QByteArray base = qgetenv("AppData"); +#else + static const char fname[] = "alsoft.conf"; + QByteArray base = qgetenv("XDG_CONFIG_HOME"); + if(base.isEmpty()) + { + base = qgetenv("HOME"); + if(base.isEmpty() == false) + base += "/.config"; + } +#endif + if(base.isEmpty() == false) + return base +'/'+ fname; + return fname; +} + +static QString getBaseDataPath() +{ +#ifdef Q_OS_WIN32 + QByteArray base = qgetenv("AppData"); +#else + QByteArray base = qgetenv("XDG_DATA_HOME"); + if(base.isEmpty()) + { + base = qgetenv("HOME"); + if(!base.isEmpty()) + base += "/.local/share"; + } +#endif + return base; +} + +static QStringList getAllDataPaths(QString append=QString()) +{ + QStringList list; + list.append(getBaseDataPath()); +#ifdef Q_OS_WIN32 + // TODO: Common AppData path +#else + QString paths = qgetenv("XDG_DATA_DIRS"); + if(paths.isEmpty()) + paths = "/usr/local/share/:/usr/share/"; + list += paths.split(QChar(':'), QString::SkipEmptyParts); +#endif + QStringList::iterator iter = list.begin(); + while(iter != list.end()) + { + if(iter->isEmpty()) + iter = list.erase(iter); + else + { + iter->append(append); + iter++; + } + } + return list; +} +} + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow), + mPeriodSizeValidator(NULL), + mPeriodCountValidator(NULL), + mSourceCountValidator(NULL), + mEffectSlotValidator(NULL), + mSourceSendValidator(NULL), + mSampleRateValidator(NULL), + mReverbBoostValidator(NULL) +{ + ui->setupUi(this); + + mPeriodSizeValidator = new QIntValidator(64, 8192, this); + ui->periodSizeEdit->setValidator(mPeriodSizeValidator); + mPeriodCountValidator = new QIntValidator(2, 16, this); + ui->periodCountEdit->setValidator(mPeriodCountValidator); + + mSourceCountValidator = new QIntValidator(0, 256, this); + ui->srcCountLineEdit->setValidator(mSourceCountValidator); + mEffectSlotValidator = new QIntValidator(0, 16, this); + ui->effectSlotLineEdit->setValidator(mEffectSlotValidator); + mSourceSendValidator = new QIntValidator(0, 4, this); + ui->srcSendLineEdit->setValidator(mSourceSendValidator); + mSampleRateValidator = new QIntValidator(8000, 192000, this); + ui->sampleRateCombo->lineEdit()->setValidator(mSampleRateValidator); + + mReverbBoostValidator = new QDoubleValidator(-12.0, +12.0, 1, this); + ui->reverbBoostEdit->setValidator(mReverbBoostValidator); + + connect(ui->actionLoad, SIGNAL(triggered()), this, SLOT(loadConfigFromFile())); + connect(ui->actionSave_As, SIGNAL(triggered()), this, SLOT(saveConfigAsFile())); + + connect(ui->applyButton, SIGNAL(clicked()), this, SLOT(saveCurrentConfig())); + + connect(ui->periodSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(updatePeriodSizeEdit(int))); + connect(ui->periodSizeEdit, SIGNAL(editingFinished()), this, SLOT(updatePeriodSizeSlider())); + connect(ui->periodCountSlider, SIGNAL(valueChanged(int)), this, SLOT(updatePeriodCountEdit(int))); + connect(ui->periodCountEdit, SIGNAL(editingFinished()), this, SLOT(updatePeriodCountSlider())); + + connect(ui->hrtfAddButton, SIGNAL(clicked()), this, SLOT(addHrtfFile())); + connect(ui->hrtfRemoveButton, SIGNAL(clicked()), this, SLOT(removeHrtfFile())); + connect(ui->hrtfFileList, SIGNAL(itemSelectionChanged()), this, SLOT(updateHrtfRemoveButton())); + + ui->enabledBackendList->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->enabledBackendList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showEnabledBackendMenu(QPoint))); + + ui->disabledBackendList->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->disabledBackendList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showDisabledBackendMenu(QPoint))); + + connect(ui->reverbBoostSlider, SIGNAL(valueChanged(int)), this, SLOT(updateReverbBoostEdit(int))); + connect(ui->reverbBoostEdit, SIGNAL(textEdited(QString)), this, SLOT(updateReverbBoostSlider(QString))); + + loadConfig(getDefaultConfigName()); +} + +MainWindow::~MainWindow() +{ + delete ui; + delete mPeriodSizeValidator; + delete mPeriodCountValidator; + delete mSourceCountValidator; + delete mEffectSlotValidator; + delete mSourceSendValidator; + delete mSampleRateValidator; + delete mReverbBoostValidator; +} + +void MainWindow::loadConfigFromFile() +{ + QString fname = QFileDialog::getOpenFileName(this, tr("Select Files")); + if(fname.isEmpty() == false) + loadConfig(fname); +} + +void MainWindow::loadConfig(const QString &fname) +{ + QSettings settings(fname, QSettings::IniFormat); + + QString sampletype = settings.value("sample-type").toString(); + ui->sampleFormatCombo->setCurrentIndex(0); + if(sampletype.isEmpty() == false) + { + for(int i = 1;i < ui->sampleFormatCombo->count();i++) + { + QString item = ui->sampleFormatCombo->itemText(i); + if(item.startsWith(sampletype)) + { + ui->sampleFormatCombo->setCurrentIndex(i); + break; + } + } + } + + QString channelconfig = settings.value("channels").toString(); + ui->channelConfigCombo->setCurrentIndex(0); + if(channelconfig.isEmpty() == false) + { + for(int i = 1;i < ui->channelConfigCombo->count();i++) + { + QString item = ui->channelConfigCombo->itemText(i); + if(item.startsWith(channelconfig)) + { + ui->channelConfigCombo->setCurrentIndex(i); + break; + } + } + } + + QString srate = settings.value("frequency").toString(); + if(srate.isEmpty()) + ui->sampleRateCombo->setCurrentIndex(0); + else + { + ui->sampleRateCombo->lineEdit()->clear(); + ui->sampleRateCombo->lineEdit()->insert(srate); + } + + ui->srcCountLineEdit->clear(); + ui->srcCountLineEdit->insert(settings.value("sources").toString()); + ui->effectSlotLineEdit->clear(); + ui->effectSlotLineEdit->insert(settings.value("slots").toString()); + ui->srcSendLineEdit->clear(); + ui->srcSendLineEdit->insert(settings.value("sends").toString()); + + QString resampler = settings.value("resampler").toString().trimmed(); + if(resampler.isEmpty()) + ui->resamplerComboBox->setCurrentIndex(0); + else + { + for(int i = 1;i < ui->resamplerComboBox->count();i++) + { + QString item = ui->resamplerComboBox->itemText(i); + int end = item.indexOf(' '); + if(end < 0) end = item.size(); + if(resampler.size() == end && resampler.compare(item.leftRef(end), Qt::CaseInsensitive) == 0) + { + ui->resamplerComboBox->setCurrentIndex(i); + break; + } + } + } + + int periodsize = settings.value("period_size").toInt(); + ui->periodSizeEdit->clear(); + if(periodsize >= 64) + { + ui->periodSizeEdit->insert(QString::number(periodsize)); + updatePeriodSizeSlider(); + } + + int periodcount = settings.value("periods").toInt(); + ui->periodCountEdit->clear(); + if(periodcount >= 2) + { + ui->periodCountEdit->insert(QString::number(periodcount)); + updatePeriodCountSlider(); + } + + QStringList disabledCpuExts = settings.value("disable-cpu-exts").toStringList(); + if(disabledCpuExts.size() == 1) + disabledCpuExts = disabledCpuExts[0].split(QChar(',')); + std::transform(disabledCpuExts.begin(), disabledCpuExts.end(), + disabledCpuExts.begin(), std::mem_fun_ref(&QString::trimmed)); + ui->enableSSECheckBox->setChecked(!disabledCpuExts.contains("sse", Qt::CaseInsensitive)); + ui->enableSSE2CheckBox->setChecked(!disabledCpuExts.contains("sse2", Qt::CaseInsensitive)); + ui->enableSSE41CheckBox->setChecked(!disabledCpuExts.contains("sse4.1", Qt::CaseInsensitive)); + ui->enableNeonCheckBox->setChecked(!disabledCpuExts.contains("neon", Qt::CaseInsensitive)); + + if(settings.value("hrtf").toString() == QString()) + ui->hrtfEnableButton->setChecked(true); + else + { + if(settings.value("hrtf", true).toBool()) + ui->hrtfForceButton->setChecked(true); + else + ui->hrtfDisableButton->setChecked(true); + } + + QStringList hrtf_tables = settings.value("hrtf_tables").toStringList(); + if(hrtf_tables.size() == 1) + hrtf_tables = hrtf_tables[0].split(QChar(',')); + std::transform(hrtf_tables.begin(), hrtf_tables.end(), + hrtf_tables.begin(), std::mem_fun_ref(&QString::trimmed)); + ui->hrtfFileList->clear(); + ui->hrtfFileList->addItems(hrtf_tables); + updateHrtfRemoveButton(); + + ui->enabledBackendList->clear(); + ui->disabledBackendList->clear(); + QStringList drivers = settings.value("drivers").toStringList(); + if(drivers.size() == 0) + ui->backendCheckBox->setChecked(true); + else + { + if(drivers.size() == 1) + drivers = drivers[0].split(QChar(',')); + std::transform(drivers.begin(), drivers.end(), + drivers.begin(), std::mem_fun_ref(&QString::trimmed)); + + bool lastWasEmpty = false; + foreach(const QString &backend, drivers) + { + lastWasEmpty = backend.isEmpty(); + if(!backend.startsWith(QChar('-')) && !lastWasEmpty) + ui->enabledBackendList->addItem(backend); + else if(backend.size() > 1) + ui->disabledBackendList->addItem(backend.right(backend.size()-1)); + } + ui->backendCheckBox->setChecked(lastWasEmpty); + } + + QString defaultreverb = settings.value("default-reverb").toString().toLower(); + ui->defaultReverbComboBox->setCurrentIndex(0); + if(defaultreverb.isEmpty() == false) + { + for(int i = 0;i < ui->defaultReverbComboBox->count();i++) + { + if(defaultreverb.compare(ui->defaultReverbComboBox->itemText(i).toLower()) == 0) + { + ui->defaultReverbComboBox->setCurrentIndex(i); + break; + } + } + } + + ui->emulateEaxCheckBox->setChecked(settings.value("reverb/emulate-eax", false).toBool()); + ui->reverbBoostEdit->clear(); + ui->reverbBoostEdit->insert(settings.value("reverb/boost").toString()); + + QStringList excludefx = settings.value("excludefx").toStringList(); + if(excludefx.size() == 1) + excludefx = excludefx[0].split(QChar(',')); + std::transform(excludefx.begin(), excludefx.end(), + excludefx.begin(), std::mem_fun_ref(&QString::trimmed)); + ui->enableEaxReverbCheck->setChecked(!excludefx.contains("eaxreverb", Qt::CaseInsensitive)); + ui->enableStdReverbCheck->setChecked(!excludefx.contains("reverb", Qt::CaseInsensitive)); + ui->enableChorusCheck->setChecked(!excludefx.contains("chorus", Qt::CaseInsensitive)); + ui->enableCompressorCheck->setChecked(!excludefx.contains("compressor", Qt::CaseInsensitive)); + ui->enableDistortionCheck->setChecked(!excludefx.contains("distortion", Qt::CaseInsensitive)); + ui->enableEchoCheck->setChecked(!excludefx.contains("echo", Qt::CaseInsensitive)); + ui->enableEqualizerCheck->setChecked(!excludefx.contains("equalizer", Qt::CaseInsensitive)); + ui->enableFlangerCheck->setChecked(!excludefx.contains("flanger", Qt::CaseInsensitive)); + ui->enableModulatorCheck->setChecked(!excludefx.contains("modulator", Qt::CaseInsensitive)); + ui->enableDedicatedCheck->setChecked(!excludefx.contains("dedicated", Qt::CaseInsensitive)); +} + +void MainWindow::saveCurrentConfig() +{ + saveConfig(getDefaultConfigName()); + QMessageBox::information(this, tr("Information"), + tr("Applications using OpenAL need to be restarted for changes to take effect.")); +} + +void MainWindow::saveConfigAsFile() +{ + QString fname = QFileDialog::getOpenFileName(this, tr("Select Files")); + if(fname.isEmpty() == false) + saveConfig(fname); +} + +void MainWindow::saveConfig(const QString &fname) const +{ + QSettings settings(fname, QSettings::IniFormat); + + /* HACK: Compound any stringlist values into a comma-separated string. */ + QStringList allkeys = settings.allKeys(); + foreach(const QString &key, allkeys) + { + QStringList vals = settings.value(key).toStringList(); + if(vals.size() > 1) + settings.setValue(key, vals.join(QChar(','))); + } + + QString str = ui->sampleFormatCombo->currentText(); + str.truncate(str.indexOf('-')); + settings.setValue("sample-type", str.trimmed()); + + str = ui->channelConfigCombo->currentText(); + str.truncate(str.indexOf('-')); + settings.setValue("channels", str.trimmed()); + + uint rate = ui->sampleRateCombo->currentText().toUInt(); + if(rate == 0) + settings.setValue("frequency", QString()); + else + settings.setValue("frequency", rate); + + settings.setValue("period_size", ui->periodSizeEdit->text()); + settings.setValue("periods", ui->periodCountEdit->text()); + + settings.setValue("sources", ui->srcCountLineEdit->text()); + settings.setValue("slots", ui->effectSlotLineEdit->text()); + + if(ui->resamplerComboBox->currentIndex() == 0) + settings.setValue("resampler", QString()); + else + { + str = ui->resamplerComboBox->currentText(); + settings.setValue("resampler", str.split(' ').first().toLower()); + } + + QStringList strlist; + if(!ui->enableSSECheckBox->isChecked()) + strlist.append("sse"); + if(!ui->enableSSE2CheckBox->isChecked()) + strlist.append("sse2"); + if(!ui->enableSSE41CheckBox->isChecked()) + strlist.append("sse4.1"); + if(!ui->enableNeonCheckBox->isChecked()) + strlist.append("neon"); + settings.setValue("disable-cpu-exts", strlist.join(QChar(','))); + + if(ui->hrtfForceButton->isChecked()) + settings.setValue("hrtf", "true"); + else if(ui->hrtfDisableButton->isChecked()) + settings.setValue("hrtf", "false"); + else + settings.setValue("hrtf", QString()); + + strlist.clear(); + QList items = ui->hrtfFileList->findItems("*", Qt::MatchWildcard); + foreach(const QListWidgetItem *item, items) + strlist.append(item->text()); + settings.setValue("hrtf_tables", strlist.join(QChar(','))); + + strlist.clear(); + items = ui->enabledBackendList->findItems("*", Qt::MatchWildcard); + foreach(const QListWidgetItem *item, items) + strlist.append(item->text()); + items = ui->disabledBackendList->findItems("*", Qt::MatchWildcard); + foreach(const QListWidgetItem *item, items) + strlist.append(QChar('-')+item->text()); + if(strlist.size() == 0 && !ui->backendCheckBox->isChecked()) + strlist.append("-all"); + else if(ui->backendCheckBox->isChecked()) + strlist.append(QString()); + settings.setValue("drivers", strlist.join(QChar(','))); + + // TODO: Remove check when we can properly match global values. + if(ui->defaultReverbComboBox->currentIndex() == 0) + settings.setValue("default-reverb", QString()); + else + { + str = ui->defaultReverbComboBox->currentText().toLower(); + settings.setValue("default-reverb", str); + } + + if(ui->emulateEaxCheckBox->isChecked()) + settings.setValue("reverb/emulate-eax", "true"); + else + settings.setValue("reverb/emulate-eax", QString()/*"false"*/); + + // TODO: Remove check when we can properly match global values. + if(ui->reverbBoostSlider->sliderPosition() == 0) + settings.setValue("reverb/boost", QString()); + else + settings.setValue("reverb/boost", ui->reverbBoostEdit->text()); + + strlist.clear(); + if(!ui->enableEaxReverbCheck->isChecked()) + strlist.append("eaxreverb"); + if(!ui->enableStdReverbCheck->isChecked()) + strlist.append("reverb"); + if(!ui->enableChorusCheck->isChecked()) + strlist.append("chorus"); + if(!ui->enableDistortionCheck->isChecked()) + strlist.append("distortion"); + if(!ui->enableCompressorCheck->isChecked()) + strlist.append("compressor"); + if(!ui->enableEchoCheck->isChecked()) + strlist.append("echo"); + if(!ui->enableEqualizerCheck->isChecked()) + strlist.append("equalizer"); + if(!ui->enableFlangerCheck->isChecked()) + strlist.append("flanger"); + if(!ui->enableModulatorCheck->isChecked()) + strlist.append("modulator"); + if(!ui->enableDedicatedCheck->isChecked()) + strlist.append("dedicated"); + settings.setValue("excludefx", strlist.join(QChar(','))); + + /* Remove empty keys + * FIXME: Should only remove keys whose value matches the globally-specified value. + */ + allkeys = settings.allKeys(); + foreach(const QString &key, allkeys) + { + str = settings.value(key).toString(); + if(str == QString()) + settings.remove(key); + } +} + + +void MainWindow::updatePeriodSizeEdit(int size) +{ + ui->periodSizeEdit->clear(); + if(size >= 64) + { + size = (size+32)&~0x3f; + ui->periodSizeEdit->insert(QString::number(size)); + } +} + +void MainWindow::updatePeriodSizeSlider() +{ + int pos = ui->periodSizeEdit->text().toInt(); + if(pos >= 64) + { + if(pos > 8192) + pos = 8192; + ui->periodSizeSlider->setSliderPosition(pos); + } +} + +void MainWindow::updatePeriodCountEdit(int count) +{ + ui->periodCountEdit->clear(); + if(count >= 2) + ui->periodCountEdit->insert(QString::number(count)); +} + +void MainWindow::updatePeriodCountSlider() +{ + int pos = ui->periodCountEdit->text().toInt(); + if(pos < 2) + pos = 0; + else if(pos > 16) + pos = 16; + ui->periodCountSlider->setSliderPosition(pos); +} + + +void MainWindow::addHrtfFile() +{ + const QStringList datapaths = getAllDataPaths("/openal/hrtf"); + QStringList fnames = QFileDialog::getOpenFileNames(this, tr("Select Files"), + datapaths.empty() ? QString() : datapaths[0], + "HRTF Datasets(*.mhr);;All Files(*.*)"); + if(fnames.isEmpty() == false) + { + for(QStringList::iterator iter = fnames.begin();iter != fnames.end();iter++) + { + QStringList::const_iterator path = datapaths.constBegin(); + for(;path != datapaths.constEnd();path++) + { + QDir hrtfdir(*path); + if(!hrtfdir.isAbsolute()) + continue; + + const QString relname = hrtfdir.relativeFilePath(*iter); + if(!relname.startsWith("..")) + { + // If filename is within this path, use the relative pathname + ui->hrtfFileList->addItem(relname); + break; + } + } + if(path == datapaths.constEnd()) + { + // Filename is not within any data path, use the absolute pathname + ui->hrtfFileList->addItem(*iter); + } + } + } +} + +void MainWindow::removeHrtfFile() +{ + QList selected = ui->hrtfFileList->selectedItems(); + foreach(QListWidgetItem *item, selected) + delete item; +} + +void MainWindow::updateHrtfRemoveButton() +{ + ui->hrtfRemoveButton->setEnabled(ui->hrtfFileList->selectedItems().size() != 0); +} + +void MainWindow::showEnabledBackendMenu(QPoint pt) +{ + QMap actionMap; + + pt = ui->enabledBackendList->mapToGlobal(pt); + + QMenu ctxmenu; + QAction *removeAction = ctxmenu.addAction(QIcon::fromTheme("list-remove"), "Remove"); + if(ui->enabledBackendList->selectedItems().size() == 0) + removeAction->setEnabled(false); + ctxmenu.addSeparator(); + for(size_t i = 0;backendMenuList[i].backend_name[0];i++) + { + QAction *action = ctxmenu.addAction(backendMenuList[i].menu_string); + actionMap[action] = backendMenuList[i].backend_name; + if(ui->enabledBackendList->findItems(backendMenuList[i].backend_name, Qt::MatchFixedString).size() != 0 || + ui->disabledBackendList->findItems(backendMenuList[i].backend_name, Qt::MatchFixedString).size() != 0) + action->setEnabled(false); + } + + QAction *gotAction = ctxmenu.exec(pt); + if(gotAction == removeAction) + { + QList selected = ui->enabledBackendList->selectedItems(); + foreach(QListWidgetItem *item, selected) + delete item; + } + else if(gotAction != NULL) + { + QMap::const_iterator iter = actionMap.find(gotAction); + if(iter != actionMap.end()) + ui->enabledBackendList->addItem(iter.value()); + } +} + +void MainWindow::showDisabledBackendMenu(QPoint pt) +{ + QMap actionMap; + + pt = ui->disabledBackendList->mapToGlobal(pt); + + QMenu ctxmenu; + QAction *removeAction = ctxmenu.addAction(QIcon::fromTheme("list-remove"), "Remove"); + if(ui->disabledBackendList->selectedItems().size() == 0) + removeAction->setEnabled(false); + ctxmenu.addSeparator(); + for(size_t i = 0;backendMenuList[i].backend_name[0];i++) + { + QAction *action = ctxmenu.addAction(backendMenuList[i].menu_string); + actionMap[action] = backendMenuList[i].backend_name; + if(ui->disabledBackendList->findItems(backendMenuList[i].backend_name, Qt::MatchFixedString).size() != 0 || + ui->enabledBackendList->findItems(backendMenuList[i].backend_name, Qt::MatchFixedString).size() != 0) + action->setEnabled(false); + } + + QAction *gotAction = ctxmenu.exec(pt); + if(gotAction == removeAction) + { + QList selected = ui->disabledBackendList->selectedItems(); + foreach(QListWidgetItem *item, selected) + delete item; + } + else if(gotAction != NULL) + { + QMap::const_iterator iter = actionMap.find(gotAction); + if(iter != actionMap.end()) + ui->disabledBackendList->addItem(iter.value()); + } +} + +void MainWindow::updateReverbBoostEdit(int value) +{ + ui->reverbBoostEdit->clear(); + if(value != 0) + ui->reverbBoostEdit->insert(QString::number(value/10.0, 'f', 1)); +} + +void MainWindow::updateReverbBoostSlider(QString value) +{ + int pos = int(value.toFloat()*10.0f); + ui->reverbBoostSlider->setSliderPosition(pos); +} diff --git a/libs/openal-soft/utils/alsoft-config/mainwindow.h b/libs/openal-soft/utils/alsoft-config/mainwindow.h new file mode 100644 index 00000000..0421aabb --- /dev/null +++ b/libs/openal-soft/utils/alsoft-config/mainwindow.h @@ -0,0 +1,56 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private slots: + void saveCurrentConfig(); + + void saveConfigAsFile(); + void loadConfigFromFile(); + + void updatePeriodSizeEdit(int size); + void updatePeriodSizeSlider(); + void updatePeriodCountEdit(int size); + void updatePeriodCountSlider(); + + void addHrtfFile(); + void removeHrtfFile(); + + void updateHrtfRemoveButton(); + + void showEnabledBackendMenu(QPoint pt); + void showDisabledBackendMenu(QPoint pt); + + void updateReverbBoostEdit(int size); + void updateReverbBoostSlider(QString value); + +private: + Ui::MainWindow *ui; + + QValidator *mPeriodSizeValidator; + QValidator *mPeriodCountValidator; + QValidator *mSourceCountValidator; + QValidator *mEffectSlotValidator; + QValidator *mSourceSendValidator; + QValidator *mSampleRateValidator; + QValidator *mReverbBoostValidator; + + void loadConfig(const QString &fname); + void saveConfig(const QString &fname) const; +}; + +#endif // MAINWINDOW_H diff --git a/libs/openal-soft/utils/alsoft-config/mainwindow.ui b/libs/openal-soft/utils/alsoft-config/mainwindow.ui new file mode 100644 index 00000000..3c92abf6 --- /dev/null +++ b/libs/openal-soft/utils/alsoft-config/mainwindow.ui @@ -0,0 +1,1464 @@ + + + MainWindow + + + + 0 + 0 + 564 + 454 + + + + OpenAL Soft Configuration + + + + + + + + + + + 470 + 405 + 81 + 25 + + + + Apply + + + + + + + + + + + 10 + 0 + 541 + 401 + + + + 0 + + + + Playback + + + + + 120 + 20 + 188 + 22 + + + + The output sample type. Currently, all mixing is done with 32-bit +float and converted to the output sample type as needed. + + + QComboBox::AdjustToContents + + + + - Autodetect - + + + + + int8 - signed 8-bit int + + + + + uint8 - unsigned 8-bit int + + + + + int16 - signed 16-bit int + + + + + uint16 - unsigned 16-bit int + + + + + int32 - signed 32-bit int + + + + + uint32 - unsigned 32-bit int + + + + + float32 - 32-bit float + + + + + + + 10 + 20 + 101 + 21 + + + + Sample Format: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 10 + 50 + 101 + 21 + + + + Channels: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 120 + 50 + 227 + 22 + + + + The output channel configuration. Note that not all backends +can properly detect the channel configuration and may default +to stereo output. + + + QComboBox::AdjustToContents + + + + - Autodetect - + + + + + mono - 1-channel Mono + + + + + stereo - 2-channel Stereo + + + + + quad - 4-channel Quadraphonic + + + + + surround51 - 5.1 Surround Sound + + + + + surround61 - 6.1 Surround Sound + + + + + surround71 - 7.1 Surround Sound + + + + + + + 120 + 80 + 111 + 22 + + + + The playback/mixing sample rate. + + + true + + + QComboBox::NoInsert + + + QComboBox::AdjustToContents + + + + - Autodetect - + + + + + 96000 + + + + + 48000 + + + + + 44100 + + + + + 32000 + + + + + 22050 + + + + + 16000 + + + + + 11025 + + + + + 8000 + + + + + + + 10 + 80 + 101 + 21 + + + + Sample Rate: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 10 + 200 + 511 + 161 + + + + HRTF (Stereo only) + + + + + 20 + 30 + 71 + 21 + + + + Allows applications to request HRTF mixing. + + + Enable + + + true + + + + + + 20 + 50 + 71 + 21 + + + + Does not allow HRTF mixing, even when requested. + + + Disable + + + + + + 20 + 70 + 71 + 21 + + + + Attempts to force HRTF mixing, even if applications request not +to do it. This may override the channel configuration and +sample rate. + + + Force + + + + + + 110 + 30 + 301 + 121 + + + + A list of files containing HRTF data sets. The listed data sets +are used in place of the default sets. The filenames may +contain these markers, which will be replaced as needed: +%r - Device sampling rate +%% - Percent sign (%) + + + false + + + QAbstractItemView::InternalMove + + + true + + + QAbstractItemView::ExtendedSelection + + + Qt::ElideNone + + + + + + 419 + 30 + 81 + 25 + + + + Add... + + + + + + + + false + + + + + + 419 + 60 + 81 + 25 + + + + Remove + + + + + + + + + + + + 10 + 110 + 511 + 91 + + + + Buffer Metrics + + + + + 260 + 20 + 241 + 51 + + + + 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. + + + + + 20 + 0 + 201 + 21 + + + + Period Count + + + Qt::AlignCenter + + + + + + 70 + 20 + 160 + 23 + + + + 1 + + + 16 + + + 1 + + + 2 + + + 1 + + + true + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 1 + + + + + + 20 + 20 + 51 + 22 + + + + 4 + + + + + + + 10 + 20 + 241 + 51 + + + + The update period size, in sample frames. This is the number of +frames needed for each mixing update. + + + + + 60 + 20 + 160 + 23 + + + + 0 + + + 8192 + + + 64 + + + 1024 + + + 0 + + + true + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 512 + + + + + + 10 + 0 + 201 + 21 + + + + Period Samples + + + Qt::AlignCenter + + + + + + 10 + 20 + 51 + 22 + + + + 1024 + + + + + + + + Resources + + + + + 190 + 20 + 51 + 22 + + + + 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. + + + + + + 3 + + + true + + + 256 + + + + + + 10 + 20 + 171 + 21 + + + + Number of Sound Sources: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 10 + 50 + 171 + 21 + + + + Number of Effect Slots: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 190 + 50 + 51 + 22 + + + + 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. + + + + + + 1 + + + true + + + 4 + + + + + + 10 + 80 + 171 + 21 + + + + Number of Source Sends: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 190 + 80 + 51 + 22 + + + + The number of auxiliary sends per source. When not specified, +it allows the app to request how many it wants. The maximum +value currently possible is 4. + + + 1 + + + Auto + + + + + + 30 + 120 + 71 + 21 + + + + Resampler: + + + + + + 110 + 120 + 185 + 22 + + + + The resampling method used when mixing sources. + + + QComboBox::AdjustToContents + + + + - Default - + + + + + Point (low quality, fast) + + + + + Linear (basic quality, fast) + + + + + Cubic Spline (good quality) + + + + + + + 10 + 150 + 511 + 91 + + + + Enables use of specific CPU extensions. Certain methods may +utilize CPU extensions when detected, and disabling these can +be useful for preventing those extensions from being used. + + + CPU Extensions + + + + + 180 + 20 + 71 + 31 + + + + SSE + + + true + + + + + + 180 + 50 + 71 + 31 + + + + SSE2 + + + true + + + + + + 260 + 50 + 71 + 31 + + + + Neon + + + true + + + + + + 260 + 20 + 71 + 31 + + + + SSE4.1 + + + true + + + + + + + Backends + + + + + 170 + 200 + 161 + 21 + + + + When checked, allows all other available backends not listed in the priority or disabled lists. + + + Allow Other Backends + + + true + + + + + + 40 + 40 + 191 + 151 + + + + The backend driver list order. Unknown backends and +duplicated names are ignored. + + + QAbstractItemView::InternalMove + + + + + + 40 + 20 + 191 + 20 + + + + Priority Backends: + + + + + + 270 + 40 + 191 + 151 + + + + Disabled backend driver list. + + + + + + 270 + 20 + 191 + 20 + + + + Disabled Backends: + + + + + + Effects + + + + + 10 + 60 + 161 + 21 + + + + Uses a simpler reverb method to emulate the EAX reverb +effect. This may slightly improve performance at the cost of +some quality. + + + Qt::RightToLeft + + + Emulate EAX Reverb: + + + + + + 10 + 100 + 511 + 61 + + + + Global amplification for reverb output, expressed in decibels. ++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. + + + Reverb Boost + + + + + 10 + 30 + 391 + 23 + + + + + + + -120 + + + 120 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 10 + + + + + + 410 + 30 + 51 + 22 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 0.0 + + + + + + 460 + 30 + 31 + 21 + + + + dB + + + + + + + 10 + 170 + 511 + 191 + + + + Specifies which effects apps can recognize. Disabling effects +can help for apps that try to use ones that are too intensive +for the system to handle. + + + Enabled Effects + + + + + 70 + 30 + 131 + 21 + + + + EAX Reverb + + + true + + + + + + 70 + 60 + 131 + 21 + + + + Standard Reverb + + + true + + + + + + 70 + 90 + 131 + 21 + + + + Chorus + + + true + + + + + + 70 + 150 + 131 + 21 + + + + Distortion + + + true + + + + + + 320 + 30 + 131 + 21 + + + + Echo + + + true + + + + + + 320 + 60 + 131 + 21 + + + + Equalizer + + + true + + + + + + 320 + 90 + 131 + 21 + + + + Flanger + + + true + + + + + + 320 + 120 + 131 + 21 + + + + Ring Modulator + + + true + + + + + + 320 + 150 + 131 + 21 + + + + Enables both the Dedicated Dialog and Dedicated LFE effects +added by the ALC_EXT_DEDICATED extension. + + + Dedicated ... + + + true + + + + + + 70 + 120 + 111 + 21 + + + + Compressor + + + true + + + + + + + 10 + 20 + 141 + 21 + + + + Default Reverb Effect: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 160 + 20 + 131 + 22 + + + + QComboBox::AdjustToContents + + + + None + + + + + Generic + + + + + PaddedCell + + + + + Room + + + + + Bathroom + + + + + Livingroom + + + + + Stoneroom + + + + + Auditorium + + + + + ConcertHall + + + + + Cave + + + + + Arena + + + + + Hangar + + + + + CarpetedHallway + + + + + Hallway + + + + + StoneCorridor + + + + + Alley + + + + + Forest + + + + + City + + + + + Mountains + + + + + Quarry + + + + + Plain + + + + + ParkingLot + + + + + SewerPipe + + + + + Underwater + + + + + Drugged + + + + + Dizzy + + + + + Psychotic + + + + + + + + + + 0 + 0 + 564 + 19 + + + + + &File + + + + + + + + + + + + + + + + &Quit + + + + + + + + + + Save &As... + + + Save Configuration As + + + + + + + + + + &Load... + + + Load Configuration File + + + + + + + + actionQuit + activated() + MainWindow + close() + + + -1 + -1 + + + 267 + 181 + + + + + + ShowHRTFContextMenu(QPoint) + + diff --git a/libs/openal-soft/utils/makehrtf.c b/libs/openal-soft/utils/makehrtf.c new file mode 100644 index 00000000..0a1bd043 --- /dev/null +++ b/libs/openal-soft/utils/makehrtf.c @@ -0,0 +1,2749 @@ +/* + * HRTF utility for producing and demonstrating the process of creating an + * OpenAL Soft compatible HRIR data set. + * + * Copyright (C) 2011-2014 Christopher Fitzgerald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Or visit: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * -------------------------------------------------------------------------- + * + * A big thanks goes out to all those whose work done in the field of + * binaural sound synthesis using measured HRTFs makes this utility and the + * OpenAL Soft implementation possible. + * + * The algorithm for diffuse-field equalization was adapted from the work + * done by Rio Emmanuel and Larcher Veronique of IRCAM and Bill Gardner of + * MIT Media Laboratory. It operates as follows: + * + * 1. Take the FFT of each HRIR and only keep the magnitude responses. + * 2. Calculate the diffuse-field power-average of all HRIRs weighted by + * their contribution to the total surface area covered by their + * measurement. + * 3. Take the diffuse-field average and limit its magnitude range. + * 4. Equalize the responses by using the inverse of the diffuse-field + * average. + * 5. Reconstruct the minimum-phase responses. + * 5. Zero the DC component. + * 6. IFFT the result and truncate to the desired-length minimum-phase FIR. + * + * The spherical head algorithm for calculating propagation delay was adapted + * from the paper: + * + * Modeling Interaural Time Difference Assuming a Spherical Head + * Joel David Miller + * Music 150, Musical Acoustics, Stanford University + * December 2, 2001 + * + * The formulae for calculating the Kaiser window metrics are from the + * the textbook: + * + * Discrete-Time Signal Processing + * Alan V. Oppenheim and Ronald W. Schafer + * Prentice-Hall Signal Processing Series + * 1999 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif + +// Rely (if naively) on OpenAL's header for the types used for serialization. +#include "AL/al.h" +#include "AL/alext.h" + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +#ifndef HUGE_VAL +#define HUGE_VAL (1.0 / 0.0) +#endif + +// The epsilon used to maintain signal stability. +#define EPSILON (1e-15) + +// Constants for accessing the token reader's ring buffer. +#define TR_RING_BITS (16) +#define TR_RING_SIZE (1 << TR_RING_BITS) +#define TR_RING_MASK (TR_RING_SIZE - 1) + +// The token reader's load interval in bytes. +#define TR_LOAD_SIZE (TR_RING_SIZE >> 2) + +// The maximum identifier length used when processing the data set +// definition. +#define MAX_IDENT_LEN (16) + +// The maximum path length used when processing filenames. +#define MAX_PATH_LEN (256) + +// The limits for the sample 'rate' metric in the data set definition and for +// resampling. +#define MIN_RATE (32000) +#define MAX_RATE (96000) + +// The limits for the HRIR 'points' metric in the data set definition. +#define MIN_POINTS (16) +#define MAX_POINTS (8192) + +// The limits to the number of 'azimuths' listed in the data set definition. +#define MIN_EV_COUNT (5) +#define MAX_EV_COUNT (128) + +// The limits for each of the 'azimuths' listed in the data set definition. +#define MIN_AZ_COUNT (1) +#define MAX_AZ_COUNT (128) + +// The limits for the listener's head 'radius' in the data set definition. +#define MIN_RADIUS (0.05) +#define MAX_RADIUS (0.15) + +// The limits for the 'distance' from source to listener in the definition +// file. +#define MIN_DISTANCE (0.5) +#define MAX_DISTANCE (2.5) + +// The maximum number of channels that can be addressed for a WAVE file +// source listed in the data set definition. +#define MAX_WAVE_CHANNELS (65535) + +// The limits to the byte size for a binary source listed in the definition +// file. +#define MIN_BIN_SIZE (2) +#define MAX_BIN_SIZE (4) + +// The minimum number of significant bits for binary sources listed in the +// data set definition. The maximum is calculated from the byte size. +#define MIN_BIN_BITS (16) + +// The limits to the number of significant bits for an ASCII source listed in +// the data set definition. +#define MIN_ASCII_BITS (16) +#define MAX_ASCII_BITS (32) + +// The limits to the FFT window size override on the command line. +#define MIN_FFTSIZE (512) +#define MAX_FFTSIZE (16384) + +// The limits to the equalization range limit on the command line. +#define MIN_LIMIT (2.0) +#define MAX_LIMIT (120.0) + +// The limits to the truncation window size on the command line. +#define MIN_TRUNCSIZE (8) +#define MAX_TRUNCSIZE (128) + +// The limits to the custom head radius on the command line. +#define MIN_CUSTOM_RADIUS (0.05) +#define MAX_CUSTOM_RADIUS (0.15) + +// The truncation window size must be a multiple of the below value to allow +// for vectorized convolution. +#define MOD_TRUNCSIZE (8) + +// The defaults for the command line options. +#define DEFAULT_EQUALIZE (1) +#define DEFAULT_SURFACE (1) +#define DEFAULT_LIMIT (24.0) +#define DEFAULT_TRUNCSIZE (32) +#define DEFAULT_HEAD_MODEL (HM_DATASET) +#define DEFAULT_CUSTOM_RADIUS (0.0) + +// The four-character-codes for RIFF/RIFX WAVE file chunks. +#define FOURCC_RIFF (0x46464952) // 'RIFF' +#define FOURCC_RIFX (0x58464952) // 'RIFX' +#define FOURCC_WAVE (0x45564157) // 'WAVE' +#define FOURCC_FMT (0x20746D66) // 'fmt ' +#define FOURCC_DATA (0x61746164) // 'data' +#define FOURCC_LIST (0x5453494C) // 'LIST' +#define FOURCC_WAVL (0x6C766177) // 'wavl' +#define FOURCC_SLNT (0x746E6C73) // 'slnt' + +// The supported wave formats. +#define WAVE_FORMAT_PCM (0x0001) +#define WAVE_FORMAT_IEEE_FLOAT (0x0003) +#define WAVE_FORMAT_EXTENSIBLE (0xFFFE) + +// The maximum propagation delay value supported by OpenAL Soft. +#define MAX_HRTD (63.0) + +// The OpenAL Soft HRTF format marker. It stands for minimum-phase head +// response protocol 01. +#define MHR_FORMAT ("MinPHR01") + +// Byte order for the serialization routines. +enum ByteOrderT { + BO_NONE = 0, + BO_LITTLE , + BO_BIG +}; + +// Source format for the references listed in the data set definition. +enum SourceFormatT { + SF_NONE = 0, + SF_WAVE , // RIFF/RIFX WAVE file. + SF_BIN_LE , // Little-endian binary file. + SF_BIN_BE , // Big-endian binary file. + SF_ASCII // ASCII text file. +}; + +// Element types for the references listed in the data set definition. +enum ElementTypeT { + ET_NONE = 0, + ET_INT , // Integer elements. + ET_FP // Floating-point elements. +}; + +// Head model used for calculating the impulse delays. +enum HeadModelT { + HM_NONE = 0, + HM_DATASET , // Measure the onset from the dataset. + HM_SPHERE // Calculate the onset using a spherical head model. +}; + +// Desired output format from the command line. +enum OutputFormatT { + OF_NONE = 0, + OF_MHR , // OpenAL Soft MHR data set file. + OF_TABLE // OpenAL Soft built-in table file (used when compiling). +}; + +// Unsigned integer type. +typedef unsigned int uint; + +// Serialization types. The trailing digit indicates the number of bits. +typedef ALubyte uint8; + +typedef ALint int32; +typedef ALuint uint32; +typedef ALuint64SOFT uint64; + +typedef enum ByteOrderT ByteOrderT; +typedef enum SourceFormatT SourceFormatT; +typedef enum ElementTypeT ElementTypeT; +typedef enum HeadModelT HeadModelT; +typedef enum OutputFormatT OutputFormatT; + +typedef struct TokenReaderT TokenReaderT; +typedef struct SourceRefT SourceRefT; +typedef struct HrirDataT HrirDataT; +typedef struct ResamplerT ResamplerT; + +// Token reader state for parsing the data set definition. +struct TokenReaderT { + FILE * mFile; + const char * mName; + uint mLine, + mColumn; + char mRing [TR_RING_SIZE]; + size_t mIn, + mOut; +}; + +// Source reference state used when loading sources. +struct SourceRefT { + SourceFormatT mFormat; + ElementTypeT mType; + uint mSize; + int mBits; + uint mChannel, + mSkip, + mOffset; + char mPath [MAX_PATH_LEN + 1]; +}; + +// The HRIR metrics and data set used when loading, processing, and storing +// the resulting HRTF. +struct HrirDataT { + uint mIrRate, + mIrCount, + mIrSize, + mIrPoints, + mFftSize, + mEvCount, + mEvStart, + mAzCount [MAX_EV_COUNT], + mEvOffset [MAX_EV_COUNT]; + double mRadius, + mDistance, + * mHrirs, + * mHrtds, + mMaxHrtd; +}; + +// The resampler metrics and FIR filter. +struct ResamplerT { + uint mP, + mQ, + mM, + mL; + double * mF; +}; + +/* Token reader routines for parsing text files. Whitespace is not + * significant. It can process tokens as identifiers, numbers (integer and + * floating-point), strings, and operators. Strings must be encapsulated by + * double-quotes and cannot span multiple lines. + */ + +// Setup the reader on the given file. The filename can be NULL if no error +// output is desired. +static void TrSetup (FILE * fp, const char * filename, TokenReaderT * tr) { + const char * name = NULL; + char ch; + + tr -> mFile = fp; + name = filename; + // If a filename was given, store a pointer to the base name. + if (filename != NULL) { + while ((ch = (* filename)) != '\0') { + if ((ch == '/') || (ch == '\\')) + name = filename + 1; + filename ++; + } + } + tr -> mName = name; + tr -> mLine = 1; + tr -> mColumn = 1; + tr -> mIn = 0; + tr -> mOut = 0; +} + +// Prime the reader's ring buffer, and return a result indicating that there +// is text to process. +static int TrLoad (TokenReaderT * tr) { + size_t toLoad, in, count; + + toLoad = TR_RING_SIZE - (tr -> mIn - tr -> mOut); + if ((toLoad >= TR_LOAD_SIZE) && (! feof (tr -> mFile))) { + // Load TR_LOAD_SIZE (or less if at the end of the file) per read. + toLoad = TR_LOAD_SIZE; + in = tr -> mIn & TR_RING_MASK; + count = TR_RING_SIZE - in; + if (count < toLoad) { + tr -> mIn += fread (& tr -> mRing [in], 1, count, tr -> mFile); + tr -> mIn += fread (& tr -> mRing [0], 1, toLoad - count, tr -> mFile); + } else { + tr -> mIn += fread (& tr -> mRing [in], 1, toLoad, tr -> mFile); + } + if (tr -> mOut >= TR_RING_SIZE) { + tr -> mOut -= TR_RING_SIZE; + tr -> mIn -= TR_RING_SIZE; + } + } + if (tr -> mIn > tr -> mOut) + return (1); + return (0); +} + +// Error display routine. Only displays when the base name is not NULL. +static void TrErrorVA (const TokenReaderT * tr, uint line, uint column, const char * format, va_list argPtr) { + if (tr -> mName != NULL) { + fprintf (stderr, "Error (%s:%u:%u): ", tr -> mName, line, column); + vfprintf (stderr, format, argPtr); + } +} + +// Used to display an error at a saved line/column. +static void TrErrorAt (const TokenReaderT * tr, uint line, uint column, const char * format, ...) { + va_list argPtr; + + va_start (argPtr, format); + TrErrorVA (tr, line, column, format, argPtr); + va_end (argPtr); +} + +// Used to display an error at the current line/column. +static void TrError (const TokenReaderT * tr, const char * format, ...) { + va_list argPtr; + + va_start (argPtr, format); + TrErrorVA (tr, tr -> mLine, tr -> mColumn, format, argPtr); + va_end (argPtr); +} + +// Skips to the next line. +static void TrSkipLine (TokenReaderT * tr) { + char ch; + + while (TrLoad (tr)) { + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + tr -> mOut ++; + if (ch == '\n') { + tr -> mLine ++; + tr -> mColumn = 1; + break; + } + tr -> mColumn ++; + } +} + +// Skips to the next token. +static int TrSkipWhitespace (TokenReaderT * tr) { + char ch; + + while (TrLoad (tr)) { + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + if (isspace (ch)) { + tr -> mOut ++; + if (ch == '\n') { + tr -> mLine ++; + tr -> mColumn = 1; + } else { + tr -> mColumn ++; + } + } else if (ch == '#') { + TrSkipLine (tr); + } else { + return (1); + } + } + return (0); +} + +// Get the line and/or column of the next token (or the end of input). +static void TrIndication (TokenReaderT * tr, uint * line, uint * column) { + TrSkipWhitespace (tr); + if (line != NULL) + (* line) = tr -> mLine; + if (column != NULL) + (* column) = tr -> mColumn; +} + +// Checks to see if a token is the given operator. It does not display any +// errors and will not proceed to the next token. +static int TrIsOperator (TokenReaderT * tr, const char * op) { + size_t out, len; + char ch; + + if (! TrSkipWhitespace (tr)) + return (0); + out = tr -> mOut; + len = 0; + while ((op [len] != '\0') && (out < tr -> mIn)) { + ch = tr -> mRing [out & TR_RING_MASK]; + if (ch != op [len]) + break; + len ++; + out ++; + } + if (op [len] == '\0') + return (1); + return (0); +} + +/* The TrRead*() routines obtain the value of a matching token type. They + * display type, form, and boundary errors and will proceed to the next + * token. + */ + +// Reads and validates an identifier token. +static int TrReadIdent (TokenReaderT * tr, const uint maxLen, char * ident) { + uint col, len; + char ch; + + col = tr -> mColumn; + if (TrSkipWhitespace (tr)) { + col = tr -> mColumn; + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + if ((ch == '_') || isalpha (ch)) { + len = 0; + do { + if (len < maxLen) + ident [len] = ch; + len ++; + tr -> mOut ++; + if (! TrLoad (tr)) + break; + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + } while ((ch == '_') || isdigit (ch) || isalpha (ch)); + tr -> mColumn += len; + if (len > maxLen) { + TrErrorAt (tr, tr -> mLine, col, "Identifier is too long.\n"); + return (0); + } + ident [len] = '\0'; + return (1); + } + } + TrErrorAt (tr, tr -> mLine, col, "Expected an identifier.\n"); + return (0); +} + +// Reads and validates (including bounds) an integer token. +static int TrReadInt (TokenReaderT * tr, const int loBound, const int hiBound, int * value) { + uint col, digis, len; + char ch, temp [64 + 1]; + + col = tr -> mColumn; + if (TrSkipWhitespace (tr)) { + col = tr -> mColumn; + len = 0; + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + if ((ch == '+') || (ch == '-')) { + temp [len] = ch; + len ++; + tr -> mOut ++; + } + digis = 0; + while (TrLoad (tr)) { + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + if (! isdigit (ch)) + break; + if (len < 64) + temp [len] = ch; + len ++; + digis ++; + tr -> mOut ++; + } + tr -> mColumn += len; + if ((digis > 0) && (ch != '.') && (! isalpha (ch))) { + if (len > 64) { + TrErrorAt (tr, tr -> mLine, col, "Integer is too long."); + return (0); + } + temp [len] = '\0'; + (* value) = strtol (temp, NULL, 10); + if (((* value) < loBound) || ((* value) > hiBound)) { + TrErrorAt (tr, tr -> mLine, col, "Expected a value from %d to %d.\n", loBound, hiBound); + return (0); + } + return (1); + } + } + TrErrorAt (tr, tr -> mLine, col, "Expected an integer.\n"); + return (0); +} + +// Reads and validates (including bounds) a float token. +static int TrReadFloat (TokenReaderT * tr, const double loBound, const double hiBound, double * value) { + uint col, digis, len; + char ch, temp [64 + 1]; + + col = tr -> mColumn; + if (TrSkipWhitespace (tr)) { + col = tr -> mColumn; + len = 0; + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + if ((ch == '+') || (ch == '-')) { + temp [len] = ch; + len ++; + tr -> mOut ++; + } + digis = 0; + while (TrLoad (tr)) { + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + if (! isdigit (ch)) + break; + if (len < 64) + temp [len] = ch; + len ++; + digis ++; + tr -> mOut ++; + } + if (ch == '.') { + if (len < 64) + temp [len] = ch; + len ++; + tr -> mOut ++; + } + while (TrLoad (tr)) { + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + if (! isdigit (ch)) + break; + if (len < 64) + temp [len] = ch; + len ++; + digis ++; + tr -> mOut ++; + } + if (digis > 0) { + if ((ch == 'E') || (ch == 'e')) { + if (len < 64) + temp [len] = ch; + len ++; + digis = 0; + tr -> mOut ++; + if ((ch == '+') || (ch == '-')) { + if (len < 64) + temp [len] = ch; + len ++; + tr -> mOut ++; + } + while (TrLoad (tr)) { + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + if (! isdigit (ch)) + break; + if (len < 64) + temp [len] = ch; + len ++; + digis ++; + tr -> mOut ++; + } + } + tr -> mColumn += len; + if ((digis > 0) && (ch != '.') && (! isalpha (ch))) { + if (len > 64) { + TrErrorAt (tr, tr -> mLine, col, "Float is too long."); + return (0); + } + temp [len] = '\0'; + (* value) = strtod (temp, NULL); + if (((* value) < loBound) || ((* value) > hiBound)) { + TrErrorAt (tr, tr -> mLine, col, "Expected a value from %f to %f.\n", loBound, hiBound); + return (0); + } + return (1); + } + } else { + tr -> mColumn += len; + } + } + TrErrorAt (tr, tr -> mLine, col, "Expected a float.\n"); + return (0); +} + +// Reads and validates a string token. +static int TrReadString (TokenReaderT * tr, const uint maxLen, char * text) { + uint col, len; + char ch; + + col = tr -> mColumn; + if (TrSkipWhitespace (tr)) { + col = tr -> mColumn; + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + if (ch == '\"') { + tr -> mOut ++; + len = 0; + while (TrLoad (tr)) { + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + tr -> mOut ++; + if (ch == '\"') + break; + if (ch == '\n') { + TrErrorAt (tr, tr -> mLine, col, "Unterminated string at end of line.\n"); + return (0); + } + if (len < maxLen) + text [len] = ch; + len ++; + } + if (ch != '\"') { + tr -> mColumn += 1 + len; + TrErrorAt (tr, tr -> mLine, col, "Unterminated string at end of input.\n"); + return (0); + } + tr -> mColumn += 2 + len; + if (len > maxLen) { + TrErrorAt (tr, tr -> mLine, col, "String is too long.\n"); + return (0); + } + text [len] = '\0'; + return (1); + } + } + TrErrorAt (tr, tr -> mLine, col, "Expected a string.\n"); + return (0); +} + +// Reads and validates the given operator. +static int TrReadOperator (TokenReaderT * tr, const char * op) { + uint col, len; + char ch; + + col = tr -> mColumn; + if (TrSkipWhitespace (tr)) { + col = tr -> mColumn; + len = 0; + while ((op [len] != '\0') && TrLoad (tr)) { + ch = tr -> mRing [tr -> mOut & TR_RING_MASK]; + if (ch != op [len]) + break; + len ++; + tr -> mOut ++; + } + tr -> mColumn += len; + if (op [len] == '\0') + return (1); + } + TrErrorAt (tr, tr -> mLine, col, "Expected '%s' operator.\n", op); + return (0); +} + +/* Performs a string substitution. Any case-insensitive occurrences of the + * pattern string are replaced with the replacement string. The result is + * truncated if necessary. + */ +static int StrSubst (const char * in, const char * pat, const char * rep, const size_t maxLen, char * out) { + size_t inLen, patLen, repLen; + size_t si, di; + int truncated; + + inLen = strlen (in); + patLen = strlen (pat); + repLen = strlen (rep); + si = 0; + di = 0; + truncated = 0; + while ((si < inLen) && (di < maxLen)) { + if (patLen <= (inLen - si)) { + if (strncasecmp (& in [si], pat, patLen) == 0) { + if (repLen > (maxLen - di)) { + repLen = maxLen - di; + truncated = 1; + } + strncpy (& out [di], rep, repLen); + si += patLen; + di += repLen; + } + } + out [di] = in [si]; + si ++; + di ++; + } + if (si < inLen) + truncated = 1; + out [di] = '\0'; + return (! truncated); +} + +// Provide missing math routines for MSVC versions < 1800 (Visual Studio 2013). +#if defined(_MSC_VER) && _MSC_VER < 1800 +static double round (double val) { + if (val < 0.0) + return (ceil (val - 0.5)); + return (floor (val + 0.5)); +} + +static double fmin (double a, double b) { + return ((a < b) ? a : b); +} + +static double fmax (double a, double b) { + return ((a > b) ? a : b); +} +#endif + +// Simple clamp routine. +static double Clamp (const double val, const double lower, const double upper) { + return (fmin (fmax (val, lower), upper)); +} + +// Performs linear interpolation. +static double Lerp (const double a, const double b, const double f) { + return (a + (f * (b - a))); +} + +// Performs a high-passed triangular probability density function dither from +// a double to an integer. It assumes the input sample is already scaled. +static int HpTpdfDither (const double in, int * hpHist) { + const double PRNG_SCALE = 1.0 / (RAND_MAX + 1.0); + int prn; + double out; + + prn = rand (); + out = round (in + (PRNG_SCALE * (prn - (* hpHist)))); + (* hpHist) = prn; + return ((int) out); +} + +// Allocates an array of doubles. +static double *CreateArray(size_t n) +{ + double *a; + + if(n == 0) n = 1; + a = calloc(n, sizeof(double)); + if(a == NULL) + { + fprintf(stderr, "Error: Out of memory.\n"); + exit(-1); + } + return a; +} + +// Frees an array of doubles. +static void DestroyArray(double *a) +{ free(a); } + +// Complex number routines. All outputs must be non-NULL. + +// Magnitude/absolute value. +static double ComplexAbs (const double r, const double i) { + return (sqrt ((r * r) + (i * i))); +} + +// Multiply. +static void ComplexMul (const double aR, const double aI, const double bR, const double bI, double * outR, double * outI) { + (* outR) = (aR * bR) - (aI * bI); + (* outI) = (aI * bR) + (aR * bI); +} + +// Base-e exponent. +static void ComplexExp (const double inR, const double inI, double * outR, double * outI) { + double e; + + e = exp (inR); + (* outR) = e * cos (inI); + (* outI) = e * sin (inI); +} + +/* Fast Fourier transform routines. The number of points must be a power of + * two. In-place operation is possible only if both the real and imaginary + * parts are in-place together. + */ + +// Performs bit-reversal ordering. +static void FftArrange (const uint n, const double * inR, const double * inI, double * outR, double * outI) { + uint rk, k, m; + double tempR, tempI; + + if ((inR == outR) && (inI == outI)) { + // Handle in-place arrangement. + rk = 0; + for (k = 0; k < n; k ++) { + if (rk > k) { + tempR = inR [rk]; + tempI = inI [rk]; + outR [rk] = inR [k]; + outI [rk] = inI [k]; + outR [k] = tempR; + outI [k] = tempI; + } + m = n; + while (rk & (m >>= 1)) + rk &= ~m; + rk |= m; + } + } else { + // Handle copy arrangement. + rk = 0; + for (k = 0; k < n; k ++) { + outR [rk] = inR [k]; + outI [rk] = inI [k]; + m = n; + while (rk & (m >>= 1)) + rk &= ~m; + rk |= m; + } + } +} + +// Performs the summation. +static void FftSummation (const uint n, const double s, double * re, double * im) { + double pi; + uint m, m2; + double vR, vI, wR, wI; + uint i, k, mk; + double tR, tI; + + pi = s * M_PI; + for (m = 1, m2 = 2; m < n; m <<= 1, m2 <<= 1) { + // v = Complex (-2.0 * sin (0.5 * pi / m) * sin (0.5 * pi / m), -sin (pi / m)) + vR = sin (0.5 * pi / m); + vR = -2.0 * vR * vR; + vI = -sin (pi / m); + // w = Complex (1.0, 0.0) + wR = 1.0; + wI = 0.0; + for (i = 0; i < m; i ++) { + for (k = i; k < n; k += m2) { + mk = k + m; + // t = ComplexMul (w, out [km2]) + tR = (wR * re [mk]) - (wI * im [mk]); + tI = (wR * im [mk]) + (wI * re [mk]); + // out [mk] = ComplexSub (out [k], t) + re [mk] = re [k] - tR; + im [mk] = im [k] - tI; + // out [k] = ComplexAdd (out [k], t) + re [k] += tR; + im [k] += tI; + } + // t = ComplexMul (v, w) + tR = (vR * wR) - (vI * wI); + tI = (vR * wI) + (vI * wR); + // w = ComplexAdd (w, t) + wR += tR; + wI += tI; + } + } +} + +// Performs a forward FFT. +static void FftForward (const uint n, const double * inR, const double * inI, double * outR, double * outI) { + FftArrange (n, inR, inI, outR, outI); + FftSummation (n, 1.0, outR, outI); +} + +// Performs an inverse FFT. +static void FftInverse (const uint n, const double * inR, const double * inI, double * outR, double * outI) { + double f; + uint i; + + FftArrange (n, inR, inI, outR, outI); + FftSummation (n, -1.0, outR, outI); + f = 1.0 / n; + for (i = 0; i < n; i ++) { + outR [i] *= f; + outI [i] *= f; + } +} + +/* Calculate the complex helical sequence (or discrete-time analytical + * signal) of the given input using the Hilbert transform. Given the + * negative natural logarithm of a signal's magnitude response, the imaginary + * components can be used as the angles for minimum-phase reconstruction. + */ +static void Hilbert (const uint n, const double * in, double * outR, double * outI) { + uint i; + + if (in == outR) { + // Handle in-place operation. + for (i = 0; i < n; i ++) + outI [i] = 0.0; + } else { + // Handle copy operation. + for (i = 0; i < n; i ++) { + outR [i] = in [i]; + outI [i] = 0.0; + } + } + FftForward (n, outR, outI, outR, outI); + /* Currently the Fourier routines operate only on point counts that are + * powers of two. If that changes and n is odd, the following conditional + * should be: i < (n + 1) / 2. + */ + for (i = 1; i < (n / 2); i ++) { + outR [i] *= 2.0; + outI [i] *= 2.0; + } + // If n is odd, the following increment should be skipped. + i ++; + for (; i < n; i ++) { + outR [i] = 0.0; + outI [i] = 0.0; + } + FftInverse (n, outR, outI, outR, outI); +} + +/* Calculate the magnitude response of the given input. This is used in + * place of phase decomposition, since the phase residuals are discarded for + * minimum phase reconstruction. The mirrored half of the response is also + * discarded. + */ +static void MagnitudeResponse (const uint n, const double * inR, const double * inI, double * out) { + const uint m = 1 + (n / 2); + uint i; + + for (i = 0; i < m; i ++) + out [i] = fmax (ComplexAbs (inR [i], inI [i]), EPSILON); +} + +/* Apply a range limit (in dB) to the given magnitude response. This is used + * to adjust the effects of the diffuse-field average on the equalization + * process. + */ +static void LimitMagnitudeResponse (const uint n, const double limit, const double * in, double * out) { + const uint m = 1 + (n / 2); + double halfLim; + uint i, lower, upper; + double ave; + + halfLim = limit / 2.0; + // Convert the response to dB. + for (i = 0; i < m; i ++) + out [i] = 20.0 * log10 (in [i]); + // Use six octaves to calculate the average magnitude of the signal. + lower = ((uint) ceil (n / pow (2.0, 8.0))) - 1; + upper = ((uint) floor (n / pow (2.0, 2.0))) - 1; + ave = 0.0; + for (i = lower; i <= upper; i ++) + ave += out [i]; + ave /= upper - lower + 1; + // Keep the response within range of the average magnitude. + for (i = 0; i < m; i ++) + out [i] = Clamp (out [i], ave - halfLim, ave + halfLim); + // Convert the response back to linear magnitude. + for (i = 0; i < m; i ++) + out [i] = pow (10.0, out [i] / 20.0); +} + +/* Reconstructs the minimum-phase component for the given magnitude response + * of a signal. This is equivalent to phase recomposition, sans the missing + * residuals (which were discarded). The mirrored half of the response is + * reconstructed. + */ +static void MinimumPhase (const uint n, const double * in, double * outR, double * outI) { + const uint m = 1 + (n / 2); + double * mags = NULL; + uint i; + double aR, aI; + + mags = CreateArray (n); + for (i = 0; i < m; i ++) { + mags [i] = fmax (in [i], EPSILON); + outR [i] = -log (mags [i]); + } + for (; i < n; i ++) { + mags [i] = mags [n - i]; + outR [i] = outR [n - i]; + } + Hilbert (n, outR, outR, outI); + // Remove any DC offset the filter has. + outR [0] = 0.0; + outI [0] = 0.0; + for (i = 1; i < n; i ++) { + ComplexExp (0.0, outI [i], & aR, & aI); + ComplexMul (mags [i], 0.0, aR, aI, & outR [i], & outI [i]); + } + DestroyArray (mags); +} + +/* This is the normalized cardinal sine (sinc) function. + * + * sinc(x) = { 1, x = 0 + * { sin(pi x) / (pi x), otherwise. + */ +static double Sinc (const double x) { + if (fabs (x) < EPSILON) + return (1.0); + return (sin (M_PI * x) / (M_PI * x)); +} + +/* The zero-order modified Bessel function of the first kind, used for the + * Kaiser window. + * + * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k) + * = sum_{k=0}^inf ((x / 2)^k / k!)^2 + */ +static double BesselI_0 (const double x) { + double term, sum, x2, y, last_sum; + int k; + + // Start at k=1 since k=0 is trivial. + term = 1.0; + sum = 1.0; + x2 = x / 2.0; + k = 1; + // Let the integration converge until the term of the sum is no longer + // significant. + do { + y = x2 / k; + k ++; + last_sum = sum; + term *= y * y; + sum += term; + } while (sum != last_sum); + return (sum); +} + +/* Calculate a Kaiser window from the given beta value and a normalized k + * [-1, 1]. + * + * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1 + * { 0, elsewhere. + * + * Where k can be calculated as: + * + * k = i / l, where -l <= i <= l. + * + * or: + * + * k = 2 i / M - 1, where 0 <= i <= M. + */ +static double Kaiser (const double b, const double k) { + double k2; + + k2 = Clamp (k, -1.0, 1.0); + if ((k < -1.0) || (k > 1.0)) + return (0.0); + k2 *= k2; + return (BesselI_0 (b * sqrt (1.0 - k2)) / BesselI_0 (b)); +} + +// Calculates the greatest common divisor of a and b. +static uint Gcd (const uint a, const uint b) { + uint x, y, z; + + x = a; + y = b; + while (y > 0) { + z = y; + y = x % y; + x = z; + } + return (x); +} + +/* Calculates the size (order) of the Kaiser window. Rejection is in dB and + * the transition width is normalized frequency (0.5 is nyquist). + * + * M = { ceil((r - 7.95) / (2.285 2 pi f_t)), r > 21 + * { ceil(5.79 / 2 pi f_t), r <= 21. + * + */ +static uint CalcKaiserOrder (const double rejection, const double transition) { + double w_t; + + w_t = 2.0 * M_PI * transition; + if (rejection > 21.0) + return ((uint) ceil ((rejection - 7.95) / (2.285 * w_t))); + return ((uint) ceil (5.79 / w_t)); +} + +// Calculates the beta value of the Kaiser window. Rejection is in dB. +static double CalcKaiserBeta (const double rejection) { + if (rejection > 50.0) + return (0.1102 * (rejection - 8.7)); + else if (rejection >= 21.0) + return ((0.5842 * pow (rejection - 21.0, 0.4)) + + (0.07886 * (rejection - 21.0))); + else + return (0.0); +} + +/* Calculates a point on the Kaiser-windowed sinc filter for the given half- + * width, beta, gain, and cutoff. The point is specified in non-normalized + * samples, from 0 to M, where M = (2 l + 1). + * + * w(k) 2 p f_t sinc(2 f_t x) + * + * x -- centered sample index (i - l) + * k -- normalized and centered window index (x / l) + * w(k) -- window function (Kaiser) + * p -- gain compensation factor when sampling + * f_t -- normalized center frequency (or cutoff; 0.5 is nyquist) + */ +static double SincFilter (const int l, const double b, const double gain, const double cutoff, const int i) { + return (Kaiser (b, ((double) (i - l)) / l) * 2.0 * gain * cutoff * Sinc (2.0 * cutoff * (i - l))); +} + +/* This is a polyphase sinc-filtered resampler. + * + * Upsample Downsample + * + * p/q = 3/2 p/q = 3/5 + * + * M-+-+-+-> M-+-+-+-> + * -------------------+ ---------------------+ + * p s * f f f f|f| | p s * f f f f f | + * | 0 * 0 0 0|0|0 | | 0 * 0 0 0 0|0| | + * v 0 * 0 0|0|0 0 | v 0 * 0 0 0|0|0 | + * s * f|f|f f f | s * f f|f|f f | + * 0 * |0|0 0 0 0 | 0 * 0|0|0 0 0 | + * --------+=+--------+ 0 * |0|0 0 0 0 | + * d . d .|d|. d . d ----------+=+--------+ + * d . . . .|d|. . . . + * q-> + * q-+-+-+-> + * + * P_f(i,j) = q i mod p + pj + * P_s(i,j) = floor(q i / p) - j + * d[i=0..N-1] = sum_{j=0}^{floor((M - 1) / p)} { + * { f[P_f(i,j)] s[P_s(i,j)], P_f(i,j) < M + * { 0, P_f(i,j) >= M. } + */ + +// Calculate the resampling metrics and build the Kaiser-windowed sinc filter +// that's used to cut frequencies above the destination nyquist. +static void ResamplerSetup (ResamplerT * rs, const uint srcRate, const uint dstRate) { + uint gcd, l; + double cutoff, width, beta; + int i; + + gcd = Gcd (srcRate, dstRate); + rs -> mP = dstRate / gcd; + rs -> mQ = srcRate / gcd; + /* The cutoff is adjusted by half the transition width, so the transition + * ends before the nyquist (0.5). Both are scaled by the downsampling + * factor. + */ + if (rs -> mP > rs -> mQ) { + cutoff = 0.45 / rs -> mP; + width = 0.1 / rs -> mP; + } else { + cutoff = 0.45 / rs -> mQ; + width = 0.1 / rs -> mQ; + } + // A rejection of -180 dB is used for the stop band. + l = CalcKaiserOrder (180.0, width) / 2; + beta = CalcKaiserBeta (180.0); + rs -> mM = (2 * l) + 1; + rs -> mL = l; + rs -> mF = CreateArray (rs -> mM); + for (i = 0; i < ((int) rs -> mM); i ++) + rs -> mF [i] = SincFilter ((int) l, beta, rs -> mP, cutoff, i); +} + +// Clean up after the resampler. +static void ResamplerClear (ResamplerT * rs) { + DestroyArray (rs -> mF); + rs -> mF = NULL; +} + +// Perform the upsample-filter-downsample resampling operation using a +// polyphase filter implementation. +static void ResamplerRun (ResamplerT * rs, const uint inN, const double * in, const uint outN, double * out) { + const uint p = rs -> mP, q = rs -> mQ, m = rs -> mM, l = rs -> mL; + const double * f = rs -> mF; + double * work = NULL; + uint i; + double r; + uint j_f, j_s; + + if (outN == 0) + return; + + // Handle in-place operation. + if (in == out) + work = CreateArray (outN); + else + work = out; + // Resample the input. + for (i = 0; i < outN; i ++) { + r = 0.0; + // Input starts at l to compensate for the filter delay. This will + // drop any build-up from the first half of the filter. + j_f = (l + (q * i)) % p; + j_s = (l + (q * i)) / p; + while (j_f < m) { + // Only take input when 0 <= j_s < inN. This single unsigned + // comparison catches both cases. + if (j_s < inN) + r += f [j_f] * in [j_s]; + j_f += p; + j_s --; + } + work [i] = r; + } + // Clean up after in-place operation. + if (in == out) { + for (i = 0; i < outN; i ++) + out [i] = work [i]; + DestroyArray (work); + } +} + +// Read a binary value of the specified byte order and byte size from a file, +// storing it as a 32-bit unsigned integer. +static int ReadBin4 (FILE * fp, const char * filename, const ByteOrderT order, const uint bytes, uint32 * out) { + uint8 in [4]; + uint32 accum; + uint i; + + if (fread (in, 1, bytes, fp) != bytes) { + fprintf (stderr, "Error: Bad read from file '%s'.\n", filename); + return (0); + } + accum = 0; + switch (order) { + case BO_LITTLE : + for (i = 0; i < bytes; i ++) + accum = (accum << 8) | in [bytes - i - 1]; + break; + case BO_BIG : + for (i = 0; i < bytes; i ++) + accum = (accum << 8) | in [i]; + break; + default : + break; + } + (* out) = accum; + return (1); +} + +// Read a binary value of the specified byte order from a file, storing it as +// a 64-bit unsigned integer. +static int ReadBin8 (FILE * fp, const char * filename, const ByteOrderT order, uint64 * out) { + uint8 in [8]; + uint64 accum; + uint i; + + if (fread (in, 1, 8, fp) != 8) { + fprintf (stderr, "Error: Bad read from file '%s'.\n", filename); + return (0); + } + accum = 0ULL; + switch (order) { + case BO_LITTLE : + for (i = 0; i < 8; i ++) + accum = (accum << 8) | in [8 - i - 1]; + break; + case BO_BIG : + for (i = 0; i < 8; i ++) + accum = (accum << 8) | in [i]; + break; + default : + break; + } + (* out) = accum; + return (1); +} + +// Write an ASCII string to a file. +static int WriteAscii (const char * out, FILE * fp, const char * filename) { + size_t len; + + len = strlen (out); + if (fwrite (out, 1, len, fp) != len) { + fclose (fp); + fprintf (stderr, "Error: Bad write to file '%s'.\n", filename); + return (0); + } + return (1); +} + +// Write a binary value of the given byte order and byte size to a file, +// loading it from a 32-bit unsigned integer. +static int WriteBin4 (const ByteOrderT order, const uint bytes, const uint32 in, FILE * fp, const char * filename) { + uint8 out [4]; + uint i; + + switch (order) { + case BO_LITTLE : + for (i = 0; i < bytes; i ++) + out [i] = (in >> (i * 8)) & 0x000000FF; + break; + case BO_BIG : + for (i = 0; i < bytes; i ++) + out [bytes - i - 1] = (in >> (i * 8)) & 0x000000FF; + break; + default : + break; + } + if (fwrite (out, 1, bytes, fp) != bytes) { + fprintf (stderr, "Error: Bad write to file '%s'.\n", filename); + return (0); + } + return (1); +} + +/* Read a binary value of the specified type, byte order, and byte size from + * a file, converting it to a double. For integer types, the significant + * bits are used to normalize the result. The sign of bits determines + * whether they are padded toward the MSB (negative) or LSB (positive). + * Floating-point types are not normalized. + */ +static int ReadBinAsDouble (FILE * fp, const char * filename, const ByteOrderT order, const ElementTypeT type, const uint bytes, const int bits, double * out) { + union { + uint32 ui; + int32 i; + float f; + } v4; + union { + uint64 ui; + double f; + } v8; + + (* out) = 0.0; + if (bytes > 4) { + if (! ReadBin8 (fp, filename, order, & v8 . ui)) + return (0); + if (type == ET_FP) + (* out) = v8 . f; + } else { + if (! ReadBin4 (fp, filename, order, bytes, & v4 . ui)) + return (0); + if (type == ET_FP) { + (* out) = (double) v4 . f; + } else { + if (bits > 0) + v4 . ui >>= (8 * bytes) - ((uint) bits); + else + v4 . ui &= (0xFFFFFFFF >> (32 + bits)); + if (v4 . ui & ((uint) (1 << (abs (bits) - 1)))) + v4 . ui |= (0xFFFFFFFF << abs (bits)); + (* out) = v4 . i / ((double) (1 << (abs (bits) - 1))); + } + } + return (1); +} + +/* Read an ascii value of the specified type from a file, converting it to a + * double. For integer types, the significant bits are used to normalize the + * result. The sign of the bits should always be positive. This also skips + * up to one separator character before the element itself. + */ +static int ReadAsciiAsDouble (TokenReaderT * tr, const char * filename, const ElementTypeT type, const uint bits, double * out) { + int v; + + if (TrIsOperator (tr, ",")) + TrReadOperator (tr, ","); + else if (TrIsOperator (tr, ":")) + TrReadOperator (tr, ":"); + else if (TrIsOperator (tr, ";")) + TrReadOperator (tr, ";"); + else if (TrIsOperator (tr, "|")) + TrReadOperator (tr, "|"); + if (type == ET_FP) { + if (! TrReadFloat (tr, -HUGE_VAL, HUGE_VAL, out)) { + fprintf (stderr, "Error: Bad read from file '%s'.\n", filename); + return (0); + } + } else { + if (! TrReadInt (tr, -(1 << (bits - 1)), (1 << (bits - 1)) - 1, & v)) { + fprintf (stderr, "Error: Bad read from file '%s'.\n", filename); + return (0); + } + (* out) = v / ((double) ((1 << (bits - 1)) - 1)); + } + return (1); +} + +// Read the RIFF/RIFX WAVE format chunk from a file, validating it against +// the source parameters and data set metrics. +static int ReadWaveFormat (FILE * fp, const ByteOrderT order, const uint hrirRate, SourceRefT * src) { + uint32 fourCC, chunkSize; + uint32 format, channels, rate, dummy, block, size, bits; + + chunkSize = 0; + do { + if (chunkSize > 0) + fseek (fp, (long) chunkSize, SEEK_CUR); + if ((! ReadBin4 (fp, src -> mPath, BO_LITTLE, 4, & fourCC)) || + (! ReadBin4 (fp, src -> mPath, order, 4, & chunkSize))) + return (0); + } while (fourCC != FOURCC_FMT); + if ((! ReadBin4 (fp, src -> mPath, order, 2, & format)) || + (! ReadBin4 (fp, src -> mPath, order, 2, & channels)) || + (! ReadBin4 (fp, src -> mPath, order, 4, & rate)) || + (! ReadBin4 (fp, src -> mPath, order, 4, & dummy)) || + (! ReadBin4 (fp, src -> mPath, order, 2, & block))) + return (0); + block /= channels; + if (chunkSize > 14) { + if (! ReadBin4 (fp, src -> mPath, order, 2, & size)) + return (0); + size /= 8; + if (block > size) + size = block; + } else { + size = block; + } + if (format == WAVE_FORMAT_EXTENSIBLE) { + fseek (fp, 2, SEEK_CUR); + if (! ReadBin4 (fp, src -> mPath, order, 2, & bits)) + return (0); + if (bits == 0) + bits = 8 * size; + fseek (fp, 4, SEEK_CUR); + if (! ReadBin4 (fp, src -> mPath, order, 2, & format)) + return (0); + fseek (fp, (long) (chunkSize - 26), SEEK_CUR); + } else { + bits = 8 * size; + if (chunkSize > 14) + fseek (fp, (long) (chunkSize - 16), SEEK_CUR); + else + fseek (fp, (long) (chunkSize - 14), SEEK_CUR); + } + if ((format != WAVE_FORMAT_PCM) && (format != WAVE_FORMAT_IEEE_FLOAT)) { + fprintf (stderr, "Error: Unsupported WAVE format in file '%s'.\n", src -> mPath); + return (0); + } + if (src -> mChannel >= channels) { + fprintf (stderr, "Error: Missing source channel in WAVE file '%s'.\n", src -> mPath); + return (0); + } + if (rate != hrirRate) { + fprintf (stderr, "Error: Mismatched source sample rate in WAVE file '%s'.\n", src -> mPath); + return (0); + } + if (format == WAVE_FORMAT_PCM) { + if ((size < 2) || (size > 4)) { + fprintf (stderr, "Error: Unsupported sample size in WAVE file '%s'.\n", src -> mPath); + return (0); + } + if ((bits < 16) || (bits > (8 * size))) { + fprintf (stderr, "Error: Bad significant bits in WAVE file '%s'.\n", src -> mPath); + return (0); + } + src -> mType = ET_INT; + } else { + if ((size != 4) && (size != 8)) { + fprintf (stderr, "Error: Unsupported sample size in WAVE file '%s'.\n", src -> mPath); + return (0); + } + src -> mType = ET_FP; + } + src -> mSize = size; + src -> mBits = (int) bits; + src -> mSkip = channels; + return (1); +} + +// Read a RIFF/RIFX WAVE data chunk, converting all elements to doubles. +static int ReadWaveData (FILE * fp, const SourceRefT * src, const ByteOrderT order, const uint n, double * hrir) { + int pre, post, skip; + uint i; + + pre = (int) (src -> mSize * src -> mChannel); + post = (int) (src -> mSize * (src -> mSkip - src -> mChannel - 1)); + skip = 0; + for (i = 0; i < n; i ++) { + skip += pre; + if (skip > 0) + fseek (fp, skip, SEEK_CUR); + if (! ReadBinAsDouble (fp, src -> mPath, order, src -> mType, src -> mSize, src -> mBits, & hrir [i])) + return (0); + skip = post; + } + if (skip > 0) + fseek (fp, skip, SEEK_CUR); + return (1); +} + +// Read the RIFF/RIFX WAVE list or data chunk, converting all elements to +// doubles. +static int ReadWaveList (FILE * fp, const SourceRefT * src, const ByteOrderT order, const uint n, double * hrir) { + uint32 fourCC, chunkSize, listSize, count; + uint block, skip, offset, i; + double lastSample; + + for (;;) { + if ((! ReadBin4 (fp, src -> mPath, BO_LITTLE, 4, & fourCC)) || + (! ReadBin4 (fp, src -> mPath, order, 4, & chunkSize))) + return (0); + if (fourCC == FOURCC_DATA) { + block = src -> mSize * src -> mSkip; + count = chunkSize / block; + if (count < (src -> mOffset + n)) { + fprintf (stderr, "Error: Bad read from file '%s'.\n", src -> mPath); + return (0); + } + fseek (fp, (long) (src -> mOffset * block), SEEK_CUR); + if (! ReadWaveData (fp, src, order, n, & hrir [0])) + return (0); + return (1); + } else if (fourCC == FOURCC_LIST) { + if (! ReadBin4 (fp, src -> mPath, BO_LITTLE, 4, & fourCC)) + return (0); + chunkSize -= 4; + if (fourCC == FOURCC_WAVL) + break; + } + if (chunkSize > 0) + fseek (fp, (long) chunkSize, SEEK_CUR); + } + listSize = chunkSize; + block = src -> mSize * src -> mSkip; + skip = src -> mOffset; + offset = 0; + lastSample = 0.0; + while ((offset < n) && (listSize > 8)) { + if ((! ReadBin4 (fp, src -> mPath, BO_LITTLE, 4, & fourCC)) || + (! ReadBin4 (fp, src -> mPath, order, 4, & chunkSize))) + return (0); + listSize -= 8 + chunkSize; + if (fourCC == FOURCC_DATA) { + count = chunkSize / block; + if (count > skip) { + fseek (fp, (long) (skip * block), SEEK_CUR); + chunkSize -= skip * block; + count -= skip; + skip = 0; + if (count > (n - offset)) + count = n - offset; + if (! ReadWaveData (fp, src, order, count, & hrir [offset])) + return (0); + chunkSize -= count * block; + offset += count; + lastSample = hrir [offset - 1]; + } else { + skip -= count; + count = 0; + } + } else if (fourCC == FOURCC_SLNT) { + if (! ReadBin4 (fp, src -> mPath, order, 4, & count)) + return (0); + chunkSize -= 4; + if (count > skip) { + count -= skip; + skip = 0; + if (count > (n - offset)) + count = n - offset; + for (i = 0; i < count; i ++) + hrir [offset + i] = lastSample; + offset += count; + } else { + skip -= count; + count = 0; + } + } + if (chunkSize > 0) + fseek (fp, (long) chunkSize, SEEK_CUR); + } + if (offset < n) { + fprintf (stderr, "Error: Bad read from file '%s'.\n", src -> mPath); + return (0); + } + return (1); +} + +// Load a source HRIR from a RIFF/RIFX WAVE file. +static int LoadWaveSource (FILE * fp, SourceRefT * src, const uint hrirRate, const uint n, double * hrir) { + uint32 fourCC, dummy; + ByteOrderT order; + + if ((! ReadBin4 (fp, src -> mPath, BO_LITTLE, 4, & fourCC)) || + (! ReadBin4 (fp, src -> mPath, BO_LITTLE, 4, & dummy))) + return (0); + if (fourCC == FOURCC_RIFF) { + order = BO_LITTLE; + } else if (fourCC == FOURCC_RIFX) { + order = BO_BIG; + } else { + fprintf (stderr, "Error: No RIFF/RIFX chunk in file '%s'.\n", src -> mPath); + return (0); + } + if (! ReadBin4 (fp, src -> mPath, BO_LITTLE, 4, & fourCC)) + return (0); + if (fourCC != FOURCC_WAVE) { + fprintf (stderr, "Error: Not a RIFF/RIFX WAVE file '%s'.\n", src -> mPath); + return (0); + } + if (! ReadWaveFormat (fp, order, hrirRate, src)) + return (0); + if (! ReadWaveList (fp, src, order, n, hrir)) + return (0); + return (1); +} + +// Load a source HRIR from a binary file. +static int LoadBinarySource (FILE * fp, const SourceRefT * src, const ByteOrderT order, const uint n, double * hrir) { + uint i; + + fseek (fp, (long) src -> mOffset, SEEK_SET); + for (i = 0; i < n; i ++) { + if (! ReadBinAsDouble (fp, src -> mPath, order, src -> mType, src -> mSize, src -> mBits, & hrir [i])) + return (0); + if (src -> mSkip > 0) + fseek (fp, (long) src -> mSkip, SEEK_CUR); + } + return (1); +} + +// Load a source HRIR from an ASCII text file containing a list of elements +// separated by whitespace or common list operators (',', ';', ':', '|'). +static int LoadAsciiSource (FILE * fp, const SourceRefT * src, const uint n, double * hrir) { + TokenReaderT tr; + uint i, j; + double dummy; + + TrSetup (fp, NULL, & tr); + for (i = 0; i < src -> mOffset; i ++) { + if (! ReadAsciiAsDouble (& tr, src -> mPath, src -> mType, (uint) src -> mBits, & dummy)) + return (0); + } + for (i = 0; i < n; i ++) { + if (! ReadAsciiAsDouble (& tr, src -> mPath, src -> mType, (uint) src -> mBits, & hrir [i])) + return (0); + for (j = 0; j < src -> mSkip; j ++) { + if (! ReadAsciiAsDouble (& tr, src -> mPath, src -> mType, (uint) src -> mBits, & dummy)) + return (0); + } + } + return (1); +} + +// Load a source HRIR from a supported file type. +static int LoadSource (SourceRefT * src, const uint hrirRate, const uint n, double * hrir) { + FILE * fp = NULL; + int result; + + if (src -> mFormat == SF_ASCII) + fp = fopen (src -> mPath, "r"); + else + fp = fopen (src -> mPath, "rb"); + if (fp == NULL) { + fprintf (stderr, "Error: Could not open source file '%s'.\n", src -> mPath); + return (0); + } + if (src -> mFormat == SF_WAVE) + result = LoadWaveSource (fp, src, hrirRate, n, hrir); + else if (src -> mFormat == SF_BIN_LE) + result = LoadBinarySource (fp, src, BO_LITTLE, n, hrir); + else if (src -> mFormat == SF_BIN_BE) + result = LoadBinarySource (fp, src, BO_BIG, n, hrir); + else + result = LoadAsciiSource (fp, src, n, hrir); + fclose (fp); + return (result); +} + +// Calculate the onset time of an HRIR and average it with any existing +// timing for its elevation and azimuth. +static void AverageHrirOnset (const double * hrir, const double f, const uint ei, const uint ai, const HrirDataT * hData) { + double mag; + uint n, i, j; + + mag = 0.0; + n = hData -> mIrPoints; + for (i = 0; i < n; i ++) + mag = fmax (fabs (hrir [i]), mag); + mag *= 0.15; + for (i = 0; i < n; i ++) { + if (fabs (hrir [i]) >= mag) + break; + } + j = hData -> mEvOffset [ei] + ai; + hData -> mHrtds [j] = Lerp (hData -> mHrtds [j], ((double) i) / hData -> mIrRate, f); +} + +// Calculate the magnitude response of an HRIR and average it with any +// existing responses for its elevation and azimuth. +static void AverageHrirMagnitude (const double * hrir, const double f, const uint ei, const uint ai, const HrirDataT * hData) { + double * re = NULL, * im = NULL; + uint n, m, i, j; + + n = hData -> mFftSize; + re = CreateArray (n); + im = CreateArray (n); + for (i = 0; i < hData -> mIrPoints; i ++) { + re [i] = hrir [i]; + im [i] = 0.0; + } + for (; i < n; i ++) { + re [i] = 0.0; + im [i] = 0.0; + } + FftForward (n, re, im, re, im); + MagnitudeResponse (n, re, im, re); + m = 1 + (n / 2); + j = (hData -> mEvOffset [ei] + ai) * hData -> mIrSize; + for (i = 0; i < m; i ++) + hData -> mHrirs [j + i] = Lerp (hData -> mHrirs [j + i], re [i], f); + DestroyArray (im); + DestroyArray (re); +} + +/* Calculate the contribution of each HRIR to the diffuse-field average based + * on the area of its surface patch. All patches are centered at the HRIR + * coordinates on the unit sphere and are measured by solid angle. + */ +static void CalculateDfWeights (const HrirDataT * hData, double * weights) { + uint ei; + double evs, sum, ev, up_ev, down_ev, solidAngle; + + evs = 90.0 / (hData -> mEvCount - 1); + sum = 0.0; + for (ei = hData -> mEvStart; ei < hData -> mEvCount; ei ++) { + // For each elevation, calculate the upper and lower limits of the + // patch band. + ev = -90.0 + (ei * 2.0 * evs); + if (ei < (hData -> mEvCount - 1)) + up_ev = (ev + evs) * M_PI / 180.0; + else + up_ev = M_PI / 2.0; + if (ei > 0) + down_ev = (ev - evs) * M_PI / 180.0; + else + down_ev = -M_PI / 2.0; + // Calculate the area of the patch band. + solidAngle = 2.0 * M_PI * (sin (up_ev) - sin (down_ev)); + // Each weight is the area of one patch. + weights [ei] = solidAngle / hData -> mAzCount [ei]; + // Sum the total surface area covered by the HRIRs. + sum += solidAngle; + } + // Normalize the weights given the total surface coverage. + for (ei = hData -> mEvStart; ei < hData -> mEvCount; ei ++) + weights [ei] /= sum; +} + +/* Calculate the diffuse-field average from the given magnitude responses of + * the HRIR set. Weighting can be applied to compensate for the varying + * surface area covered by each HRIR. The final average can then be limited + * by the specified magnitude range (in positive dB; 0.0 to skip). + */ +static void CalculateDiffuseFieldAverage (const HrirDataT * hData, const int weighted, const double limit, double * dfa) { + double * weights = NULL; + uint ei, ai, count, step, start, end, m, j, i; + double weight; + + weights = CreateArray (hData -> mEvCount); + if (weighted) { + // Use coverage weighting to calculate the average. + CalculateDfWeights (hData, weights); + } else { + // If coverage weighting is not used, the weights still need to be + // averaged by the number of HRIRs. + count = 0; + for (ei = hData -> mEvStart; ei < hData -> mEvCount; ei ++) + count += hData -> mAzCount [ei]; + for (ei = hData -> mEvStart; ei < hData -> mEvCount; ei ++) + weights [ei] = 1.0 / count; + } + ei = hData -> mEvStart; + ai = 0; + step = hData -> mIrSize; + start = hData -> mEvOffset [ei] * step; + end = hData -> mIrCount * step; + m = 1 + (hData -> mFftSize / 2); + for (i = 0; i < m; i ++) + dfa [i] = 0.0; + for (j = start; j < end; j += step) { + // Get the weight for this HRIR's contribution. + weight = weights [ei]; + // Add this HRIR's weighted power average to the total. + for (i = 0; i < m; i ++) + dfa [i] += weight * hData -> mHrirs [j + i] * hData -> mHrirs [j + i]; + // Determine the next weight to use. + ai ++; + if (ai >= hData -> mAzCount [ei]) { + ei ++; + ai = 0; + } + } + // Finish the average calculation and keep it from being too small. + for (i = 0; i < m; i ++) + dfa [i] = fmax (sqrt (dfa [i]), EPSILON); + // Apply a limit to the magnitude range of the diffuse-field average if + // desired. + if (limit > 0.0) + LimitMagnitudeResponse (hData -> mFftSize, limit, dfa, dfa); + DestroyArray (weights); +} + +// Perform diffuse-field equalization on the magnitude responses of the HRIR +// set using the given average response. +static void DiffuseFieldEqualize (const double * dfa, const HrirDataT * hData) { + uint step, start, end, m, j, i; + + step = hData -> mIrSize; + start = hData -> mEvOffset [hData -> mEvStart] * step; + end = hData -> mIrCount * step; + m = 1 + (hData -> mFftSize / 2); + for (j = start; j < end; j += step) { + for (i = 0; i < m; i ++) + hData -> mHrirs [j + i] /= dfa [i]; + } +} + +// Perform minimum-phase reconstruction using the magnitude responses of the +// HRIR set. +static void ReconstructHrirs (const HrirDataT * hData) { + double * re = NULL, * im = NULL; + uint step, start, end, n, j, i; + + step = hData -> mIrSize; + start = hData -> mEvOffset [hData -> mEvStart] * step; + end = hData -> mIrCount * step; + n = hData -> mFftSize; + re = CreateArray (n); + im = CreateArray (n); + for (j = start; j < end; j += step) { + MinimumPhase (n, & hData -> mHrirs [j], re, im); + FftInverse (n, re, im, re, im); + for (i = 0; i < hData -> mIrPoints; i ++) + hData -> mHrirs [j + i] = re [i]; + } + DestroyArray (im); + DestroyArray (re); +} + +// Resamples the HRIRs for use at the given sampling rate. +static void ResampleHrirs (const uint rate, HrirDataT * hData) { + ResamplerT rs; + uint n, step, start, end, j; + + ResamplerSetup (& rs, hData -> mIrRate, rate); + n = hData -> mIrPoints; + step = hData -> mIrSize; + start = hData -> mEvOffset [hData -> mEvStart] * step; + end = hData -> mIrCount * step; + for (j = start; j < end; j += step) + ResamplerRun (& rs, n, & hData -> mHrirs [j], n, & hData -> mHrirs [j]); + ResamplerClear (& rs); + hData -> mIrRate = rate; +} + +/* Given an elevation index and an azimuth, calculate the indices of the two + * HRIRs that bound the coordinate along with a factor for calculating the + * continous HRIR using interpolation. + */ +static void CalcAzIndices (const HrirDataT * hData, const uint ei, const double az, uint * j0, uint * j1, double * jf) { + double af; + uint ai; + + af = ((2.0 * M_PI) + az) * hData -> mAzCount [ei] / (2.0 * M_PI); + ai = ((uint) af) % hData -> mAzCount [ei]; + af -= floor (af); + (* j0) = hData -> mEvOffset [ei] + ai; + (* j1) = hData -> mEvOffset [ei] + ((ai + 1) % hData -> mAzCount [ei]); + (* jf) = af; +} + +// Synthesize any missing onset timings at the bottom elevations. This just +// blends between slightly exaggerated known onsets. Not an accurate model. +static void SynthesizeOnsets (HrirDataT * hData) { + uint oi, e, a, j0, j1; + double t, of, jf; + + oi = hData -> mEvStart; + t = 0.0; + for (a = 0; a < hData -> mAzCount [oi]; a ++) + t += hData -> mHrtds [hData -> mEvOffset [oi] + a]; + hData -> mHrtds [0] = 1.32e-4 + (t / hData -> mAzCount [oi]); + for (e = 1; e < hData -> mEvStart; e ++) { + of = ((double) e) / hData -> mEvStart; + for (a = 0; a < hData -> mAzCount [e]; a ++) { + CalcAzIndices (hData, oi, a * 2.0 * M_PI / hData -> mAzCount [e], & j0, & j1, & jf); + hData -> mHrtds [hData -> mEvOffset [e] + a] = Lerp (hData -> mHrtds [0], Lerp (hData -> mHrtds [j0], hData -> mHrtds [j1], jf), of); + } + } +} + +/* Attempt to synthesize any missing HRIRs at the bottom elevations. Right + * now this just blends the lowest elevation HRIRs together and applies some + * attenuation and high frequency damping. It is a simple, if inaccurate + * model. + */ +static void SynthesizeHrirs (HrirDataT * hData) { + uint oi, a, e, step, n, i, j; + double of, b; + uint j0, j1; + double jf; + double lp [4], s0, s1; + + if (hData -> mEvStart <= 0) + return; + step = hData -> mIrSize; + oi = hData -> mEvStart; + n = hData -> mIrPoints; + for (i = 0; i < n; i ++) + hData -> mHrirs [i] = 0.0; + for (a = 0; a < hData -> mAzCount [oi]; a ++) { + j = (hData -> mEvOffset [oi] + a) * step; + for (i = 0; i < n; i ++) + hData -> mHrirs [i] += hData -> mHrirs [j + i] / hData -> mAzCount [oi]; + } + for (e = 1; e < hData -> mEvStart; e ++) { + of = ((double) e) / hData -> mEvStart; + b = (1.0 - of) * (3.5e-6 * hData -> mIrRate); + for (a = 0; a < hData -> mAzCount [e]; a ++) { + j = (hData -> mEvOffset [e] + a) * step; + CalcAzIndices (hData, oi, a * 2.0 * M_PI / hData -> mAzCount [e], & j0, & j1, & jf); + j0 *= step; + j1 *= step; + lp [0] = 0.0; + lp [1] = 0.0; + lp [2] = 0.0; + lp [3] = 0.0; + for (i = 0; i < n; i ++) { + s0 = hData -> mHrirs [i]; + s1 = Lerp (hData -> mHrirs [j0 + i], hData -> mHrirs [j1 + i], jf); + s0 = Lerp (s0, s1, of); + lp [0] = Lerp (s0, lp [0], b); + lp [1] = Lerp (lp [0], lp [1], b); + lp [2] = Lerp (lp [1], lp [2], b); + lp [3] = Lerp (lp [2], lp [3], b); + hData -> mHrirs [j + i] = lp [3]; + } + } + } + b = 3.5e-6 * hData -> mIrRate; + lp [0] = 0.0; + lp [1] = 0.0; + lp [2] = 0.0; + lp [3] = 0.0; + for (i = 0; i < n; i ++) { + s0 = hData -> mHrirs [i]; + lp [0] = Lerp (s0, lp [0], b); + lp [1] = Lerp (lp [0], lp [1], b); + lp [2] = Lerp (lp [1], lp [2], b); + lp [3] = Lerp (lp [2], lp [3], b); + hData -> mHrirs [i] = lp [3]; + } + hData -> mEvStart = 0; +} + +// The following routines assume a full set of HRIRs for all elevations. + +// Normalize the HRIR set and slightly attenuate the result. +static void NormalizeHrirs (const HrirDataT * hData) { + uint step, end, n, j, i; + double maxLevel; + + step = hData -> mIrSize; + end = hData -> mIrCount * step; + n = hData -> mIrPoints; + maxLevel = 0.0; + for (j = 0; j < end; j += step) { + for (i = 0; i < n; i ++) + maxLevel = fmax (fabs (hData -> mHrirs [j + i]), maxLevel); + } + maxLevel = 1.01 * maxLevel; + for (j = 0; j < end; j += step) { + for (i = 0; i < n; i ++) + hData -> mHrirs [j + i] /= maxLevel; + } +} + +// Calculate the left-ear time delay using a spherical head model. +static double CalcLTD (const double ev, const double az, const double rad, const double dist) { + double azp, dlp, l, al; + + azp = asin (cos (ev) * sin (az)); + dlp = sqrt ((dist * dist) + (rad * rad) + (2.0 * dist * rad * sin (azp))); + l = sqrt ((dist * dist) - (rad * rad)); + al = (0.5 * M_PI) + azp; + if (dlp > l) + dlp = l + (rad * (al - acos (rad / dist))); + return (dlp / 343.3); +} + +// Calculate the effective head-related time delays for each minimum-phase +// HRIR. +static void CalculateHrtds (const HeadModelT model, const double radius, HrirDataT * hData) { + double minHrtd, maxHrtd; + uint e, a, j; + double t; + + minHrtd = 1000.0; + maxHrtd = -1000.0; + for (e = 0; e < hData -> mEvCount; e ++) { + for (a = 0; a < hData -> mAzCount [e]; a ++) { + j = hData -> mEvOffset [e] + a; + if (model == HM_DATASET) { + t = hData -> mHrtds [j] * radius / hData -> mRadius; + } else { + t = CalcLTD ((-90.0 + (e * 180.0 / (hData -> mEvCount - 1))) * M_PI / 180.0, + (a * 360.0 / hData -> mAzCount [e]) * M_PI / 180.0, + radius, hData -> mDistance); + } + hData -> mHrtds [j] = t; + maxHrtd = fmax (t, maxHrtd); + minHrtd = fmin (t, minHrtd); + } + } + maxHrtd -= minHrtd; + for (j = 0; j < hData -> mIrCount; j ++) + hData -> mHrtds [j] -= minHrtd; + hData -> mMaxHrtd = maxHrtd; +} + +// Store the OpenAL Soft HRTF data set. +static int StoreMhr (const HrirDataT * hData, const char * filename) { + FILE * fp = NULL; + uint e, step, end, n, j, i; + int hpHist, v; + + if ((fp = fopen (filename, "wb")) == NULL) { + fprintf (stderr, "Error: Could not open MHR file '%s'.\n", filename); + return (0); + } + if (! WriteAscii (MHR_FORMAT, fp, filename)) + return (0); + if (! WriteBin4 (BO_LITTLE, 4, (uint32) hData -> mIrRate, fp, filename)) + return (0); + if (! WriteBin4 (BO_LITTLE, 1, (uint32) hData -> mIrPoints, fp, filename)) + return (0); + if (! WriteBin4 (BO_LITTLE, 1, (uint32) hData -> mEvCount, fp, filename)) + return (0); + for (e = 0; e < hData -> mEvCount; e ++) { + if (! WriteBin4 (BO_LITTLE, 1, (uint32) hData -> mAzCount [e], fp, filename)) + return (0); + } + step = hData -> mIrSize; + end = hData -> mIrCount * step; + n = hData -> mIrPoints; + srand (0x31DF840C); + for (j = 0; j < end; j += step) { + hpHist = 0; + for (i = 0; i < n; i ++) { + v = HpTpdfDither (32767.0 * hData -> mHrirs [j + i], & hpHist); + if (! WriteBin4 (BO_LITTLE, 2, (uint32) v, fp, filename)) + return (0); + } + } + for (j = 0; j < hData -> mIrCount; j ++) { + v = (int) fmin (round (hData -> mIrRate * hData -> mHrtds [j]), MAX_HRTD); + if (! WriteBin4 (BO_LITTLE, 1, (uint32) v, fp, filename)) + return (0); + } + fclose (fp); + return (1); +} + +// Store the OpenAL Soft built-in table. +static int StoreTable (const HrirDataT * hData, const char * filename) { + FILE * fp = NULL; + uint step, end, n, j, i; + int hpHist, v; + char text [128 + 1]; + + if ((fp = fopen (filename, "wb")) == NULL) { + fprintf (stderr, "Error: Could not open table file '%s'.\n", filename); + return (0); + } + snprintf (text, 128, "/* Elevation metrics */\n" + "static const ALubyte defaultAzCount[%u] = { ", hData -> mEvCount); + if (! WriteAscii (text, fp, filename)) + return (0); + for (i = 0; i < hData -> mEvCount; i ++) { + snprintf (text, 128, "%u, ", hData -> mAzCount [i]); + if (! WriteAscii (text, fp, filename)) + return (0); + } + snprintf (text, 128, "};\n" + "static const ALushort defaultEvOffset[%u] = { ", hData -> mEvCount); + if (! WriteAscii (text, fp, filename)) + return (0); + for (i = 0; i < hData -> mEvCount; i ++) { + snprintf (text, 128, "%u, ", hData -> mEvOffset [i]); + if (! WriteAscii (text, fp, filename)) + return (0); + } + step = hData -> mIrSize; + end = hData -> mIrCount * step; + n = hData -> mIrPoints; + snprintf (text, 128, "};\n\n" + "/* HRIR Coefficients */\n" + "static const ALshort defaultCoeffs[%u] =\n{\n", hData -> mIrCount * n); + if (! WriteAscii (text, fp, filename)) + return (0); + srand (0x31DF840C); + for (j = 0; j < end; j += step) { + if (! WriteAscii (" ", fp, filename)) + return (0); + hpHist = 0; + for (i = 0; i < n; i ++) { + v = HpTpdfDither (32767.0 * hData -> mHrirs [j + i], & hpHist); + snprintf (text, 128, " %+d,", v); + if (! WriteAscii (text, fp, filename)) + return (0); + } + if (! WriteAscii ("\n", fp, filename)) + return (0); + } + snprintf (text, 128, "};\n\n" + "/* HRIR Delays */\n" + "static const ALubyte defaultDelays[%u] =\n{\n" + " ", hData -> mIrCount); + if (! WriteAscii (text, fp, filename)) + return (0); + for (j = 0; j < hData -> mIrCount; j ++) { + v = (int) fmin (round (hData -> mIrRate * hData -> mHrtds [j]), MAX_HRTD); + snprintf (text, 128, " %d,", v); + if (! WriteAscii (text, fp, filename)) + return (0); + } + if (! WriteAscii ("\n};\n\n" + "/* Default HRTF Definition */\n", fp, filename)) + return (0); + snprintf (text, 128, "static const struct Hrtf DefaultHrtf = {\n" + " %u, %u, %u, defaultAzCount, defaultEvOffset,\n", + hData -> mIrRate, hData -> mIrPoints, hData -> mEvCount); + if (! WriteAscii (text, fp, filename)) + return (0); + if (! WriteAscii (" defaultCoeffs, defaultDelays, NULL\n" + "};\n", fp, filename)) + return (0); + fclose (fp); + return (1); +} + +// Process the data set definition to read and validate the data set metrics. +static int ProcessMetrics (TokenReaderT * tr, const uint fftSize, const uint truncSize, HrirDataT * hData) { + char ident [MAX_IDENT_LEN + 1]; + uint line, col; + int intVal; + uint points; + double fpVal; + int hasRate = 0, hasPoints = 0, hasAzimuths = 0; + int hasRadius = 0, hasDistance = 0; + + while (! (hasRate && hasPoints && hasAzimuths && hasRadius && hasDistance)) { + TrIndication (tr, & line, & col); + if (! TrReadIdent (tr, MAX_IDENT_LEN, ident)) + return (0); + if (strcasecmp (ident, "rate") == 0) { + if (hasRate) { + TrErrorAt (tr, line, col, "Redefinition of 'rate'.\n"); + return (0); + } + if (! TrReadOperator (tr, "=")) + return (0); + if (! TrReadInt (tr, MIN_RATE, MAX_RATE, & intVal)) + return (0); + hData -> mIrRate = (uint) intVal; + hasRate = 1; + } else if (strcasecmp (ident, "points") == 0) { + if (hasPoints) { + TrErrorAt (tr, line, col, "Redefinition of 'points'.\n"); + return (0); + } + if (! TrReadOperator (tr, "=")) + return (0); + TrIndication (tr, & line, & col); + if (! TrReadInt (tr, MIN_POINTS, MAX_POINTS, & intVal)) + return (0); + points = (uint) intVal; + if ((fftSize > 0) && (points > fftSize)) { + TrErrorAt (tr, line, col, "Value exceeds the overridden FFT size.\n"); + return (0); + } + if (points < truncSize) { + TrErrorAt (tr, line, col, "Value is below the truncation size.\n"); + return (0); + } + hData -> mIrPoints = points; + hData -> mFftSize = fftSize; + if (fftSize <= 0) { + points = 1; + while (points < (4 * hData -> mIrPoints)) + points <<= 1; + hData -> mFftSize = points; + hData -> mIrSize = 1 + (points / 2); + } else { + hData -> mFftSize = fftSize; + hData -> mIrSize = 1 + (fftSize / 2); + if (points > hData -> mIrSize) + hData -> mIrSize = points; + } + hasPoints = 1; + } else if (strcasecmp (ident, "azimuths") == 0) { + if (hasAzimuths) { + TrErrorAt (tr, line, col, "Redefinition of 'azimuths'.\n"); + return (0); + } + if (! TrReadOperator (tr, "=")) + return (0); + hData -> mIrCount = 0; + hData -> mEvCount = 0; + hData -> mEvOffset [0] = 0; + for (;;) { + if (! TrReadInt (tr, MIN_AZ_COUNT, MAX_AZ_COUNT, & intVal)) + return (0); + hData -> mAzCount [hData -> mEvCount] = (uint) intVal; + hData -> mIrCount += (uint) intVal; + hData -> mEvCount ++; + if (! TrIsOperator (tr, ",")) + break; + if (hData -> mEvCount >= MAX_EV_COUNT) { + TrError (tr, "Exceeded the maximum of %d elevations.\n", MAX_EV_COUNT); + return (0); + } + hData -> mEvOffset [hData -> mEvCount] = hData -> mEvOffset [hData -> mEvCount - 1] + ((uint) intVal); + TrReadOperator (tr, ","); + } + if (hData -> mEvCount < MIN_EV_COUNT) { + TrErrorAt (tr, line, col, "Did not reach the minimum of %d azimuth counts.\n", MIN_EV_COUNT); + return (0); + } + hasAzimuths = 1; + } else if (strcasecmp (ident, "radius") == 0) { + if (hasRadius) { + TrErrorAt (tr, line, col, "Redefinition of 'radius'.\n"); + return (0); + } + if (! TrReadOperator (tr, "=")) + return (0); + if (! TrReadFloat (tr, MIN_RADIUS, MAX_RADIUS, & fpVal)) + return (0); + hData -> mRadius = fpVal; + hasRadius = 1; + } else if (strcasecmp (ident, "distance") == 0) { + if (hasDistance) { + TrErrorAt (tr, line, col, "Redefinition of 'distance'.\n"); + return (0); + } + if (! TrReadOperator (tr, "=")) + return (0); + if (! TrReadFloat (tr, MIN_DISTANCE, MAX_DISTANCE, & fpVal)) + return (0); + hData -> mDistance = fpVal; + hasDistance = 1; + } else { + TrErrorAt (tr, line, col, "Expected a metric name.\n"); + return (0); + } + TrSkipWhitespace (tr); + } + return (1); +} + +// Parse an index pair from the data set definition. +static int ReadIndexPair (TokenReaderT * tr, const HrirDataT * hData, uint * ei, uint * ai) { + int intVal; + + if (! TrReadInt (tr, 0, (int) hData -> mEvCount, & intVal)) + return (0); + (* ei) = (uint) intVal; + if (! TrReadOperator (tr, ",")) + return (0); + if (! TrReadInt (tr, 0, (int) hData -> mAzCount [(* ei)], & intVal)) + return (0); + (* ai) = (uint) intVal; + return (1); +} + +// Match the source format from a given identifier. +static SourceFormatT MatchSourceFormat (const char * ident) { + if (strcasecmp (ident, "wave") == 0) + return (SF_WAVE); + else if (strcasecmp (ident, "bin_le") == 0) + return (SF_BIN_LE); + else if (strcasecmp (ident, "bin_be") == 0) + return (SF_BIN_BE); + else if (strcasecmp (ident, "ascii") == 0) + return (SF_ASCII); + return (SF_NONE); +} + +// Match the source element type from a given identifier. +static ElementTypeT MatchElementType (const char * ident) { + if (strcasecmp (ident, "int") == 0) + return (ET_INT); + else if (strcasecmp (ident, "fp") == 0) + return (ET_FP); + return (ET_NONE); +} + +// Parse and validate a source reference from the data set definition. +static int ReadSourceRef (TokenReaderT * tr, SourceRefT * src) { + uint line, col; + char ident [MAX_IDENT_LEN + 1]; + int intVal; + + TrIndication (tr, & line, & col); + if (! TrReadIdent (tr, MAX_IDENT_LEN, ident)) + return (0); + src -> mFormat = MatchSourceFormat (ident); + if (src -> mFormat == SF_NONE) { + TrErrorAt (tr, line, col, "Expected a source format.\n"); + return (0); + } + if (! TrReadOperator (tr, "(")) + return (0); + if (src -> mFormat == SF_WAVE) { + if (! TrReadInt (tr, 0, MAX_WAVE_CHANNELS, & intVal)) + return (0); + src -> mType = ET_NONE; + src -> mSize = 0; + src -> mBits = 0; + src -> mChannel = (uint) intVal; + src -> mSkip = 0; + } else { + TrIndication (tr, & line, & col); + if (! TrReadIdent (tr, MAX_IDENT_LEN, ident)) + return (0); + src -> mType = MatchElementType (ident); + if (src -> mType == ET_NONE) { + TrErrorAt (tr, line, col, "Expected a source element type.\n"); + return (0); + } + if ((src -> mFormat == SF_BIN_LE) || (src -> mFormat == SF_BIN_BE)) { + if (! TrReadOperator (tr, ",")) + return (0); + if (src -> mType == ET_INT) { + if (! TrReadInt (tr, MIN_BIN_SIZE, MAX_BIN_SIZE, & intVal)) + return (0); + src -> mSize = (uint) intVal; + if (TrIsOperator (tr, ",")) { + TrReadOperator (tr, ","); + TrIndication (tr, & line, & col); + if (! TrReadInt (tr, -2147483647 - 1, 2147483647, & intVal)) + return (0); + if ((abs (intVal) < MIN_BIN_BITS) || (((uint) abs (intVal)) > (8 * src -> mSize))) { + TrErrorAt (tr, line, col, "Expected a value of (+/-) %d to %d.\n", MIN_BIN_BITS, 8 * src -> mSize); + return (0); + } + src -> mBits = intVal; + } else { + src -> mBits = (int) (8 * src -> mSize); + } + } else { + TrIndication (tr, & line, & col); + if (! TrReadInt (tr, -2147483647 - 1, 2147483647, & intVal)) + return (0); + if ((intVal != 4) && (intVal != 8)) { + TrErrorAt (tr, line, col, "Expected a value of 4 or 8.\n"); + return (0); + } + src -> mSize = (uint) intVal; + src -> mBits = 0; + } + } else if ((src -> mFormat == SF_ASCII) && (src -> mType == ET_INT)) { + if (! TrReadOperator (tr, ",")) + return (0); + if (! TrReadInt (tr, MIN_ASCII_BITS, MAX_ASCII_BITS, & intVal)) + return (0); + src -> mSize = 0; + src -> mBits = intVal; + } else { + src -> mSize = 0; + src -> mBits = 0; + } + if (TrIsOperator (tr, ";")) { + TrReadOperator (tr, ";"); + if (! TrReadInt (tr, 0, 0x7FFFFFFF, & intVal)) + return (0); + src -> mSkip = (uint) intVal; + } else { + src -> mSkip = 0; + } + } + if (! TrReadOperator (tr, ")")) + return (0); + if (TrIsOperator (tr, "@")) { + TrReadOperator (tr, "@"); + if (! TrReadInt (tr, 0, 0x7FFFFFFF, & intVal)) + return (0); + src -> mOffset = (uint) intVal; + } else { + src -> mOffset = 0; + } + if (! TrReadOperator (tr, ":")) + return (0); + if (! TrReadString (tr, MAX_PATH_LEN, src -> mPath)) + return (0); + return (1); +} + +// Process the list of sources in the data set definition. +static int ProcessSources (const HeadModelT model, TokenReaderT * tr, HrirDataT * hData) { + uint * setCount = NULL, * setFlag = NULL; + double * hrir = NULL; + uint line, col, ei, ai; + SourceRefT src; + double factor; + + setCount = (uint *) calloc (hData -> mEvCount, sizeof (uint)); + setFlag = (uint *) calloc (hData -> mIrCount, sizeof (uint)); + hrir = CreateArray (hData -> mIrPoints); + while (TrIsOperator (tr, "[")) { + TrIndication (tr, & line, & col); + TrReadOperator (tr, "["); + if (ReadIndexPair (tr, hData, & ei, & ai)) { + if (TrReadOperator (tr, "]")) { + if (! setFlag [hData -> mEvOffset [ei] + ai]) { + if (TrReadOperator (tr, "=")) { + factor = 1.0; + for (;;) { + if (ReadSourceRef (tr, & src)) { + if (LoadSource (& src, hData -> mIrRate, hData -> mIrPoints, hrir)) { + if (model == HM_DATASET) + AverageHrirOnset (hrir, 1.0 / factor, ei, ai, hData); + AverageHrirMagnitude (hrir, 1.0 / factor, ei, ai, hData); + factor += 1.0; + if (! TrIsOperator (tr, "+")) + break; + TrReadOperator (tr, "+"); + continue; + } + } + DestroyArray (hrir); + free (setFlag); + free (setCount); + return (0); + } + setFlag [hData -> mEvOffset [ei] + ai] = 1; + setCount [ei] ++; + continue; + } + } else { + TrErrorAt (tr, line, col, "Redefinition of source.\n"); + } + } + } + DestroyArray (hrir); + free (setFlag); + free (setCount); + return (0); + } + ei = 0; + while ((ei < hData -> mEvCount) && (setCount [ei] < 1)) + ei ++; + if (ei < hData -> mEvCount) { + hData -> mEvStart = ei; + while ((ei < hData -> mEvCount) && (setCount [ei] == hData -> mAzCount [ei])) + ei ++; + if (ei >= hData -> mEvCount) { + if (! TrLoad (tr)) { + DestroyArray (hrir); + free (setFlag); + free (setCount); + return (1); + } else { + TrError (tr, "Errant data at end of source list.\n"); + } + } else { + TrError (tr, "Missing sources for elevation index %d.\n", ei); + } + } else { + TrError (tr, "Missing source references.\n"); + } + DestroyArray (hrir); + free (setFlag); + free (setCount); + return (0); +} + +/* Parse the data set definition and process the source data, storing the + * resulting data set as desired. If the input name is NULL it will read + * from standard input. + */ +static int ProcessDefinition (const char * inName, const uint outRate, const uint fftSize, const int equalize, const int surface, const double limit, const uint truncSize, const HeadModelT model, const double radius, const OutputFormatT outFormat, const char * outName) { + FILE * fp = NULL; + TokenReaderT tr; + HrirDataT hData; + double * dfa = NULL; + char rateStr [8 + 1], expName [MAX_PATH_LEN]; + + hData . mIrRate = 0; + hData . mIrPoints = 0; + hData . mFftSize = 0; + hData . mIrSize = 0; + hData . mIrCount = 0; + hData . mEvCount = 0; + hData . mRadius = 0; + hData . mDistance = 0; + fprintf (stdout, "Reading HRIR definition...\n"); + if (inName != NULL) { + fp = fopen (inName, "r"); + if (fp == NULL) { + fprintf (stderr, "Error: Could not open definition file '%s'\n", inName); + return (0); + } + TrSetup (fp, inName, & tr); + } else { + fp = stdin; + TrSetup (fp, "", & tr); + } + if (! ProcessMetrics (& tr, fftSize, truncSize, & hData)) { + if (inName != NULL) + fclose (fp); + return (0); + } + hData . mHrirs = CreateArray (hData . mIrCount * hData . mIrSize); + hData . mHrtds = CreateArray (hData . mIrCount); + if (! ProcessSources (model, & tr, & hData)) { + DestroyArray (hData . mHrtds); + DestroyArray (hData . mHrirs); + if (inName != NULL) + fclose (fp); + return (0); + } + if (inName != NULL) + fclose (fp); + if (equalize) { + dfa = CreateArray (1 + (hData . mFftSize / 2)); + fprintf (stdout, "Calculating diffuse-field average...\n"); + CalculateDiffuseFieldAverage (& hData, surface, limit, dfa); + fprintf (stdout, "Performing diffuse-field equalization...\n"); + DiffuseFieldEqualize (dfa, & hData); + DestroyArray (dfa); + } + fprintf (stdout, "Performing minimum phase reconstruction...\n"); + ReconstructHrirs (& hData); + if ((outRate != 0) && (outRate != hData . mIrRate)) { + fprintf (stdout, "Resampling HRIRs...\n"); + ResampleHrirs (outRate, & hData); + } + fprintf (stdout, "Truncating minimum-phase HRIRs...\n"); + hData . mIrPoints = truncSize; + fprintf (stdout, "Synthesizing missing elevations...\n"); + if (model == HM_DATASET) + SynthesizeOnsets (& hData); + SynthesizeHrirs (& hData); + fprintf (stdout, "Normalizing final HRIRs...\n"); + NormalizeHrirs (& hData); + fprintf (stdout, "Calculating impulse delays...\n"); + CalculateHrtds (model, (radius > DEFAULT_CUSTOM_RADIUS) ? radius : hData . mRadius, & hData); + snprintf (rateStr, 8, "%u", hData . mIrRate); + StrSubst (outName, "%r", rateStr, MAX_PATH_LEN, expName); + switch (outFormat) { + case OF_MHR : + fprintf (stdout, "Creating MHR data set file...\n"); + if (! StoreMhr (& hData, expName)) + return (0); + break; + case OF_TABLE : + fprintf (stderr, "Creating OpenAL Soft table file...\n"); + if (! StoreTable (& hData, expName)) + return (0); + break; + default : + break; + } + DestroyArray (hData . mHrtds); + DestroyArray (hData . mHrirs); + return (1); +} + +// Standard command line dispatch. +int main (const int argc, const char * argv []) { + const char * inName = NULL, * outName = NULL; + OutputFormatT outFormat; + int argi; + uint outRate, fftSize; + int equalize, surface; + double limit; + uint truncSize; + HeadModelT model; + double radius; + char * end = NULL; + + if (argc < 2) { + fprintf (stderr, "Error: No command specified. See '%s -h' for help.\n", argv [0]); + return (-1); + } + if ((strcmp (argv [1], "--help") == 0) || (strcmp (argv [1], "-h") == 0)) { + fprintf (stdout, "HRTF Processing and Composition Utility\n\n"); + fprintf (stdout, "Usage: %s [