aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/alc.cpp
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2018-11-14 04:15:44 -0800
committerChris Robinson <[email protected]>2018-11-14 04:15:44 -0800
commit3021a426c027fb88bf13b36ad825b9686001a5c9 (patch)
tree3f1bad723273dae81b932554bdab0f5ef39a408c /Alc/alc.cpp
parentdfcb6d3e6df973d4d92b37f1d8decd833575d281 (diff)
Convert ALc.c to C++
Diffstat (limited to 'Alc/alc.cpp')
-rw-r--r--Alc/alc.cpp4696
1 files changed, 4696 insertions, 0 deletions
diff --git a/Alc/alc.cpp b/Alc/alc.cpp
new file mode 100644
index 00000000..b46f1986
--- /dev/null
+++ b/Alc/alc.cpp
@@ -0,0 +1,4696 @@
+/**
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include "version.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <atomic>
+#include <vector>
+#include <string>
+
+#include "alMain.h"
+#include "alSource.h"
+#include "alListener.h"
+#include "alSource.h"
+#include "alBuffer.h"
+#include "alFilter.h"
+#include "alEffect.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "mastering.h"
+#include "bformatdec.h"
+#include "alu.h"
+#include "alconfig.h"
+#include "ringbuffer.h"
+
+#include "fpu_modes.h"
+#include "cpu_caps.h"
+#include "compat.h"
+#include "threads.h"
+#include "alstring.h"
+#include "almalloc.h"
+
+#include "backends/base.h"
+
+
+/************************************************
+ * Backends
+ ************************************************/
+struct BackendInfo {
+ const char *name;
+ ALCbackendFactory* (*getFactory)(void);
+};
+
+static struct BackendInfo BackendList[] = {
+#ifdef HAVE_JACK
+ { "jack", ALCjackBackendFactory_getFactory },
+#endif
+#ifdef HAVE_PULSEAUDIO
+ { "pulse", ALCpulseBackendFactory_getFactory },
+#endif
+#ifdef HAVE_ALSA
+ { "alsa", ALCalsaBackendFactory_getFactory },
+#endif
+#ifdef HAVE_COREAUDIO
+ { "core", ALCcoreAudioBackendFactory_getFactory },
+#endif
+#ifdef HAVE_SOLARIS
+ { "solaris", ALCsolarisBackendFactory_getFactory },
+#endif
+#ifdef HAVE_SNDIO
+ { "sndio", SndioBackendFactory_getFactory },
+#endif
+#ifdef HAVE_OSS
+ { "oss", ALCossBackendFactory_getFactory },
+#endif
+#ifdef HAVE_QSA
+ { "qsa", ALCqsaBackendFactory_getFactory },
+#endif
+#ifdef HAVE_WASAPI
+ { "wasapi", ALCwasapiBackendFactory_getFactory },
+#endif
+#ifdef HAVE_DSOUND
+ { "dsound", ALCdsoundBackendFactory_getFactory },
+#endif
+#ifdef HAVE_WINMM
+ { "winmm", ALCwinmmBackendFactory_getFactory },
+#endif
+#ifdef HAVE_PORTAUDIO
+ { "port", ALCportBackendFactory_getFactory },
+#endif
+#ifdef HAVE_OPENSL
+ { "opensl", ALCopenslBackendFactory_getFactory },
+#endif
+#ifdef HAVE_SDL2
+ { "sdl2", ALCsdl2BackendFactory_getFactory },
+#endif
+
+ { "null", ALCnullBackendFactory_getFactory },
+#ifdef HAVE_WAVE
+ { "wave", ALCwaveBackendFactory_getFactory },
+#endif
+};
+static ALsizei BackendListSize = COUNTOF(BackendList);
+#undef EmptyFuncs
+
+static struct BackendInfo PlaybackBackend;
+static struct BackendInfo CaptureBackend;
+
+
+/************************************************
+ * Functions, enums, and errors
+ ************************************************/
+#define DECL(x) { #x, (ALCvoid*)(x) }
+static const struct {
+ const ALCchar *funcName;
+ ALCvoid *address;
+} 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(alcGetStringiSOFT),
+ DECL(alcResetDeviceSOFT),
+
+ 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(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(alGetStringiSOFT),
+
+ DECL(alBufferStorageSOFT),
+ DECL(alMapBufferSOFT),
+ DECL(alUnmapBufferSOFT),
+ DECL(alFlushMappedBufferSOFT),
+
+ DECL(alEventControlSOFT),
+ DECL(alEventCallbackSOFT),
+ DECL(alGetPointerSOFT),
+ DECL(alGetPointervSOFT),
+};
+#undef DECL
+
+#define DECL(x) { #x, (x) }
+static const struct {
+ const ALCchar *enumName;
+ ALCenum value;
+} alcEnumerations[] = {
+ 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_BFORMAT3D_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_HRTF_SOFT),
+ DECL(ALC_DONT_CARE_SOFT),
+ DECL(ALC_HRTF_STATUS_SOFT),
+ DECL(ALC_HRTF_DISABLED_SOFT),
+ DECL(ALC_HRTF_ENABLED_SOFT),
+ DECL(ALC_HRTF_DENIED_SOFT),
+ DECL(ALC_HRTF_REQUIRED_SOFT),
+ DECL(ALC_HRTF_HEADPHONES_DETECTED_SOFT),
+ DECL(ALC_HRTF_UNSUPPORTED_FORMAT_SOFT),
+ DECL(ALC_NUM_HRTF_SPECIFIERS_SOFT),
+ DECL(ALC_HRTF_SPECIFIER_SOFT),
+ DECL(ALC_HRTF_ID_SOFT),
+
+ DECL(ALC_AMBISONIC_LAYOUT_SOFT),
+ DECL(ALC_AMBISONIC_SCALING_SOFT),
+ DECL(ALC_AMBISONIC_ORDER_SOFT),
+ DECL(ALC_ACN_SOFT),
+ DECL(ALC_FUMA_SOFT),
+ DECL(ALC_N3D_SOFT),
+ DECL(ALC_SN3D_SOFT),
+
+ DECL(ALC_OUTPUT_LIMITER_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_BYTE_OFFSET),
+ DECL(AL_SOURCE_TYPE),
+ DECL(AL_STATIC),
+ DECL(AL_STREAMING),
+ DECL(AL_UNDETERMINED),
+ DECL(AL_METERS_PER_UNIT),
+ DECL(AL_LOOP_POINTS_SOFT),
+ 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_FORMAT_BFORMAT2D_8),
+ DECL(AL_FORMAT_BFORMAT2D_16),
+ DECL(AL_FORMAT_BFORMAT2D_FLOAT32),
+ DECL(AL_FORMAT_BFORMAT2D_MULAW),
+ DECL(AL_FORMAT_BFORMAT3D_8),
+ DECL(AL_FORMAT_BFORMAT3D_16),
+ DECL(AL_FORMAT_BFORMAT3D_FLOAT32),
+ DECL(AL_FORMAT_BFORMAT3D_MULAW),
+
+ DECL(AL_FREQUENCY),
+ DECL(AL_BITS),
+ DECL(AL_CHANNELS),
+ DECL(AL_SIZE),
+ DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT),
+ DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT),
+
+ DECL(AL_SOURCE_RADIUS),
+
+ DECL(AL_STEREO_ANGLES),
+
+ 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_GAIN_LIMIT_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),
+ DECL(AL_EFFECT_PITCH_SHIFTER),
+ DECL(AL_EFFECT_FREQUENCY_SHIFTER),
+#if 0
+ DECL(AL_EFFECT_VOCAL_MORPHER),
+#endif
+ DECL(AL_EFFECT_RING_MODULATOR),
+ DECL(AL_EFFECT_AUTOWAH),
+ DECL(AL_EFFECT_COMPRESSOR),
+ DECL(AL_EFFECT_EQUALIZER),
+ DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT),
+ DECL(AL_EFFECT_DEDICATED_DIALOGUE),
+
+ DECL(AL_EFFECTSLOT_EFFECT),
+ DECL(AL_EFFECTSLOT_GAIN),
+ DECL(AL_EFFECTSLOT_AUXILIARY_SEND_AUTO),
+ DECL(AL_EFFECTSLOT_NULL),
+
+ 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_FREQUENCY_SHIFTER_FREQUENCY),
+ DECL(AL_FREQUENCY_SHIFTER_LEFT_DIRECTION),
+ DECL(AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION),
+
+ DECL(AL_RING_MODULATOR_FREQUENCY),
+ DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF),
+ DECL(AL_RING_MODULATOR_WAVEFORM),
+
+ DECL(AL_PITCH_SHIFTER_COARSE_TUNE),
+ DECL(AL_PITCH_SHIFTER_FINE_TUNE),
+
+ 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),
+
+ DECL(AL_AUTOWAH_ATTACK_TIME),
+ DECL(AL_AUTOWAH_RELEASE_TIME),
+ DECL(AL_AUTOWAH_RESONANCE),
+ DECL(AL_AUTOWAH_PEAK_GAIN),
+
+ DECL(AL_NUM_RESAMPLERS_SOFT),
+ DECL(AL_DEFAULT_RESAMPLER_SOFT),
+ DECL(AL_SOURCE_RESAMPLER_SOFT),
+ DECL(AL_RESAMPLER_NAME_SOFT),
+
+ DECL(AL_SOURCE_SPATIALIZE_SOFT),
+ DECL(AL_AUTO_SOFT),
+
+ DECL(AL_MAP_READ_BIT_SOFT),
+ DECL(AL_MAP_WRITE_BIT_SOFT),
+ DECL(AL_MAP_PERSISTENT_BIT_SOFT),
+ DECL(AL_PRESERVE_DATA_BIT_SOFT),
+
+ DECL(AL_EVENT_CALLBACK_FUNCTION_SOFT),
+ DECL(AL_EVENT_CALLBACK_USER_PARAM_SOFT),
+ DECL(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT),
+ DECL(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT),
+ DECL(AL_EVENT_TYPE_ERROR_SOFT),
+ DECL(AL_EVENT_TYPE_PERFORMANCE_SOFT),
+ DECL(AL_EVENT_TYPE_DEPRECATED_SOFT),
+};
+#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_BFORMAT "
+ "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_BFORMAT "
+ "AL_EXT_MULAW_MCFORMATS "
+ "AL_EXT_OFFSET "
+ "AL_EXT_source_distance_model "
+ "AL_EXT_SOURCE_RADIUS "
+ "AL_EXT_STEREO_ANGLES "
+ "AL_LOKI_quadriphonic "
+ "AL_SOFT_block_alignment "
+ "AL_SOFT_deferred_updates "
+ "AL_SOFT_direct_channels "
+ "AL_SOFTX_events "
+ "AL_SOFTX_filter_gain_ex "
+ "AL_SOFT_gain_clamp_ex "
+ "AL_SOFT_loop_points "
+ "AL_SOFTX_map_buffer "
+ "AL_SOFT_MSADPCM "
+ "AL_SOFT_source_latency "
+ "AL_SOFT_source_length "
+ "AL_SOFT_source_resampler "
+ "AL_SOFT_source_spatialize";
+
+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(nullptr);
+
+/* 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;
+
+/* Flag to specify if alcSuspendContext/alcProcessContext should defer/process
+ * updates.
+ */
+static ALCboolean SuspendDefers = ALC_TRUE;
+
+
+/************************************************
+ * 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_SOFT_device_clock ALC_SOFT_HRTF "
+ "ALC_SOFT_loopback ALC_SOFT_output_limiter 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 std::atomic<ALCdevice*> DeviceList{nullptr};
+
+static almtx_t ListLock;
+static inline void LockLists(void)
+{
+ int ret = almtx_lock(&ListLock);
+ assert(ret == althrd_success);
+}
+static inline void UnlockLists(void)
+{
+ int ret = almtx_unlock(&ListLock);
+ assert(ret == 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:
+ althrd_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, nullptr, 0) == 1))
+ ConeScale *= 0.5f;
+
+ str = getenv("__ALSOFT_REVERSE_Z");
+ if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
+ ZScale *= -1.0f;
+
+ str = getenv("__ALSOFT_REVERB_IGNORES_SOUND_SPEED");
+ if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
+ OverrideReverbSpeedOfSound = AL_TRUE;
+
+ ret = altss_create(&LocalContext, ReleaseThreadCtx);
+ assert(ret == althrd_success);
+
+ ret = almtx_init(&ListLock, almtx_recursive);
+ assert(ret == althrd_success);
+}
+
+static void alc_initconfig(void)
+{
+ const char *devs, *str;
+ int capfilter;
+ float valf;
+ int i, n;
+
+ str = getenv("ALSOFT_LOGLEVEL");
+ if(str)
+ {
+ long lvl = strtol(str, nullptr, 0);
+ if(lvl >= NoLog && lvl <= LogRef)
+ LogLevel = static_cast<enum LogLevel>(lvl);
+ }
+
+ str = getenv("ALSOFT_LOGFILE");
+ if(str && str[0])
+ {
+#ifdef _WIN32
+ std::wstring wname{utf8_to_wstr(str)};
+ FILE *logfile = _wfopen(wname.c_str(), L"wt");
+#else
+ FILE *logfile = fopen(str, "wt");
+#endif
+ if(logfile) LogFile = logfile;
+ else ERR("Failed to open log file '%s'\n", str);
+ }
+
+ TRACE("Initializing library v%s-%s %s\n", ALSOFT_VERSION,
+ ALSOFT_GIT_COMMIT_HASH, ALSOFT_GIT_BRANCH);
+ {
+ char buf[1024] = "";
+ int len = 0;
+
+ if(BackendListSize > 0)
+ len += snprintf(buf, sizeof(buf), "%s", BackendList[0].name);
+ for(i = 1;i < BackendListSize;i++)
+ len += snprintf(buf+len, sizeof(buf)-len, ", %s", BackendList[i].name);
+ TRACE("Supported backends: %s\n", buf);
+ }
+ ReadALConfig();
+
+ str = getenv("__ALSOFT_SUSPEND_CONTEXT");
+ if(str && *str)
+ {
+ if(strcasecmp(str, "ignore") == 0)
+ {
+ SuspendDefers = ALC_FALSE;
+ TRACE("Selected context suspend behavior, \"ignore\"\n");
+ }
+ else
+ ERR("Unhandled context suspend behavior setting: \"%s\"\n", str);
+ }
+
+ capfilter = 0;
+#if defined(HAVE_SSE4_1)
+ capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1;
+#elif defined(HAVE_SSE3)
+ capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3;
+#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(nullptr, nullptr, "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 == 4 && strncasecmp(str, "sse3", len) == 0)
+ capfilter &= ~CPU_CAP_SSE3;
+ 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(nullptr, nullptr, "rt-prio", &RTPrioLevel);
+
+ aluInit();
+ aluInitMixer();
+
+ str = getenv("ALSOFT_TRAP_ERROR");
+ if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
+ {
+ TrapALError = AL_TRUE;
+ TrapALCError = AL_TRUE;
+ }
+ else
+ {
+ str = getenv("ALSOFT_TRAP_AL_ERROR");
+ if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
+ TrapALError = AL_TRUE;
+ TrapALError = GetConfigValueBool(nullptr, nullptr, "trap-al-error", TrapALError);
+
+ str = getenv("ALSOFT_TRAP_ALC_ERROR");
+ if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
+ TrapALCError = ALC_TRUE;
+ TrapALCError = GetConfigValueBool(nullptr, nullptr, "trap-alc-error", TrapALCError);
+ }
+
+ if(ConfigValueFloat(nullptr, "reverb", "boost", &valf))
+ ReverbBoost *= powf(10.0f, valf / 20.0f);
+
+ if(((devs=getenv("ALSOFT_DRIVERS")) && devs[0]) ||
+ ConfigValueStr(nullptr, nullptr, "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--;
+#ifdef HAVE_WASAPI
+ /* HACK: For backwards compatibility, convert backend references of
+ * mmdevapi to wasapi. This should eventually be removed.
+ */
+ if(len == 8 && strncmp(devs, "mmdevapi", len) == 0)
+ {
+ devs = "wasapi";
+ len = 6;
+ }
+#endif
+ for(n = i;n < BackendListSize;n++)
+ {
+ if(len == strlen(BackendList[n].name) &&
+ strncmp(BackendList[n].name, devs, len) == 0)
+ {
+ if(delitem)
+ {
+ for(;n+1 < BackendListSize;n++)
+ BackendList[n] = BackendList[n+1];
+ BackendListSize--;
+ }
+ else
+ {
+ struct BackendInfo Bkp = BackendList[n];
+ for(;n > i;n--)
+ BackendList[n] = BackendList[n-1];
+ BackendList[n] = Bkp;
+
+ i++;
+ }
+ break;
+ }
+ }
+ } while(next++);
+
+ if(endlist)
+ BackendListSize = i;
+ }
+
+ for(n = i = 0;i < BackendListSize && (!PlaybackBackend.name || !CaptureBackend.name);i++)
+ {
+ ALCbackendFactory *factory;
+ BackendList[n] = BackendList[i];
+
+ factory = BackendList[n].getFactory();
+ if(!V0(factory,init)())
+ {
+ WARN("Failed to initialize backend \"%s\"\n", BackendList[n].name);
+ continue;
+ }
+
+ TRACE("Initialized backend \"%s\"\n", BackendList[n].name);
+ if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback))
+ {
+ PlaybackBackend = BackendList[n];
+ TRACE("Added \"%s\" for playback\n", PlaybackBackend.name);
+ }
+ if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture))
+ {
+ CaptureBackend = BackendList[n];
+ TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
+ }
+ n++;
+ }
+ BackendListSize = n;
+
+ {
+ ALCbackendFactory *factory = ALCloopbackFactory_getFactory();
+ V0(factory,init)();
+ }
+
+ if(!PlaybackBackend.name)
+ WARN("No playback backend available!\n");
+ if(!CaptureBackend.name)
+ WARN("No capture backend available!\n");
+
+ if(ConfigValueStr(nullptr, nullptr, "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;n < EFFECTLIST_SIZE;n++)
+ {
+ if(len == strlen(EffectList[n].name) &&
+ strncmp(EffectList[n].name, str, len) == 0)
+ DisabledEffects[EffectList[n].type] = AL_TRUE;
+ }
+ } while(next++);
+ }
+
+ InitEffect(&DefaultEffect);
+ str = getenv("ALSOFT_DEFAULT_REVERB");
+ if((str && str[0]) || ConfigValueStr(nullptr, nullptr, "default-reverb", &str))
+ LoadReverbPreset(str, &DefaultEffect);
+}
+#define DO_INITCONFIG() alcall_once(&alc_config_once, alc_initconfig)
+
+
+/************************************************
+ * Library deinitialization
+ ************************************************/
+static void alc_cleanup(void)
+{
+ AL_STRING_DEINIT(alcAllDevicesList);
+ AL_STRING_DEINIT(alcCaptureDeviceList);
+
+ free(alcDefaultAllDevicesSpecifier);
+ alcDefaultAllDevicesSpecifier = nullptr;
+ free(alcCaptureDefaultDeviceSpecifier);
+ alcCaptureDefaultDeviceSpecifier = nullptr;
+
+ if(ALCdevice *dev{DeviceList.exchange(nullptr)})
+ {
+ ALCuint num = 0;
+ do {
+ num++;
+ dev = ATOMIC_LOAD(&dev->next, almemory_order_relaxed);
+ } while(dev != nullptr);
+ ERR("%u device%s not closed\n", num, (num>1)?"s":"");
+ }
+}
+
+static void alc_deinit_safe(void)
+{
+ alc_cleanup();
+
+ FreeHrtfs();
+ FreeALConfig();
+
+ almtx_destroy(&ListLock);
+ altss_delete(LocalContext);
+
+ if(LogFile != stderr)
+ fclose(LogFile);
+ LogFile = nullptr;
+
+ althrd_deinit();
+}
+
+static void alc_deinit(void)
+{
+ int i;
+
+ alc_cleanup();
+
+ memset(&PlaybackBackend, 0, sizeof(PlaybackBackend));
+ memset(&CaptureBackend, 0, sizeof(CaptureBackend));
+
+ for(i = 0;i < BackendListSize;i++)
+ {
+ 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, struct BackendInfo *backendinfo, enum DevProbe type)
+{
+ DO_INITCONFIG();
+
+ LockLists();
+ alstr_clear(list);
+
+ if(backendinfo->getFactory)
+ {
+ ALCbackendFactory *factory = backendinfo->getFactory();
+ V(factory,probe)(type, list);
+ }
+
+ UnlockLists();
+}
+static void ProbeAllDevicesList(void)
+{ ProbeDevices(&alcAllDevicesList, &PlaybackBackend, ALL_DEVICE_PROBE); }
+static void ProbeCaptureDeviceList(void)
+{ ProbeDevices(&alcCaptureDeviceList, &CaptureBackend, CAPTURE_DEVICE_PROBE); }
+
+
+/************************************************
+ * 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 DevFmtX51Rear: return "5.1 Surround (Rear)";
+ case DevFmtX61: return "6.1 Surround";
+ case DevFmtX71: return "7.1 Surround";
+ case DevFmtAmbi3D: return "Ambisonic 3D";
+ }
+ return "(unknown channels)";
+}
+
+ALsizei 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;
+}
+ALsizei ChannelsFromDevFmt(enum DevFmtChannels chans, ALsizei ambiorder)
+{
+ switch(chans)
+ {
+ case DevFmtMono: return 1;
+ case DevFmtStereo: return 2;
+ case DevFmtQuad: return 4;
+ case DevFmtX51: return 6;
+ case DevFmtX51Rear: return 6;
+ case DevFmtX61: return 7;
+ case DevFmtX71: return 8;
+ case DevFmtAmbi3D: return (ambiorder >= 3) ? 16 :
+ (ambiorder == 2) ? 9 :
+ (ambiorder == 1) ? 4 : 1;
+ }
+ return 0;
+}
+
+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;
+}
+
+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;
+}
+
+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:
+ case ALC_BFORMAT3D_SOFT:
+ return ALC_TRUE;
+ }
+ return ALC_FALSE;
+}
+
+static ALCboolean IsValidAmbiLayout(ALCenum layout)
+{
+ switch(layout)
+ {
+ case ALC_ACN_SOFT:
+ case ALC_FUMA_SOFT:
+ return ALC_TRUE;
+ }
+ return ALC_FALSE;
+}
+
+static ALCboolean IsValidAmbiScaling(ALCenum scaling)
+{
+ switch(scaling)
+ {
+ case ALC_N3D_SOFT:
+ case ALC_SN3D_SOFT:
+ case ALC_FUMA_SOFT:
+ return ALC_TRUE;
+ }
+ return ALC_FALSE;
+}
+
+/************************************************
+ * Miscellaneous ALC helpers
+ ************************************************/
+
+/* SetDefaultWFXChannelOrder
+ *
+ * Sets the default channel order used by WaveFormatEx.
+ */
+void SetDefaultWFXChannelOrder(ALCdevice *device)
+{
+ ALsizei i;
+
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ device->RealOut.ChannelName[i] = InvalidChannel;
+
+ switch(device->FmtChans)
+ {
+ case DevFmtMono:
+ device->RealOut.ChannelName[0] = FrontCenter;
+ break;
+ case DevFmtStereo:
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ break;
+ case DevFmtQuad:
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = BackLeft;
+ device->RealOut.ChannelName[3] = BackRight;
+ break;
+ case DevFmtX51:
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = FrontCenter;
+ device->RealOut.ChannelName[3] = LFE;
+ device->RealOut.ChannelName[4] = SideLeft;
+ device->RealOut.ChannelName[5] = SideRight;
+ break;
+ case DevFmtX51Rear:
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = FrontCenter;
+ device->RealOut.ChannelName[3] = LFE;
+ device->RealOut.ChannelName[4] = BackLeft;
+ device->RealOut.ChannelName[5] = BackRight;
+ break;
+ case DevFmtX61:
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = FrontCenter;
+ device->RealOut.ChannelName[3] = LFE;
+ device->RealOut.ChannelName[4] = BackCenter;
+ device->RealOut.ChannelName[5] = SideLeft;
+ device->RealOut.ChannelName[6] = SideRight;
+ break;
+ case DevFmtX71:
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = FrontCenter;
+ device->RealOut.ChannelName[3] = LFE;
+ device->RealOut.ChannelName[4] = BackLeft;
+ device->RealOut.ChannelName[5] = BackRight;
+ device->RealOut.ChannelName[6] = SideLeft;
+ device->RealOut.ChannelName[7] = SideRight;
+ break;
+ case DevFmtAmbi3D:
+ device->RealOut.ChannelName[0] = Aux0;
+ if(device->AmbiOrder > 0)
+ {
+ device->RealOut.ChannelName[1] = Aux1;
+ device->RealOut.ChannelName[2] = Aux2;
+ device->RealOut.ChannelName[3] = Aux3;
+ }
+ if(device->AmbiOrder > 1)
+ {
+ device->RealOut.ChannelName[4] = Aux4;
+ device->RealOut.ChannelName[5] = Aux5;
+ device->RealOut.ChannelName[6] = Aux6;
+ device->RealOut.ChannelName[7] = Aux7;
+ device->RealOut.ChannelName[8] = Aux8;
+ }
+ if(device->AmbiOrder > 2)
+ {
+ device->RealOut.ChannelName[9] = Aux9;
+ device->RealOut.ChannelName[10] = Aux10;
+ device->RealOut.ChannelName[11] = Aux11;
+ device->RealOut.ChannelName[12] = Aux12;
+ device->RealOut.ChannelName[13] = Aux13;
+ device->RealOut.ChannelName[14] = Aux14;
+ device->RealOut.ChannelName[15] = Aux15;
+ }
+ break;
+ }
+}
+
+/* SetDefaultChannelOrder
+ *
+ * Sets the default channel order used by most non-WaveFormatEx-based APIs.
+ */
+void SetDefaultChannelOrder(ALCdevice *device)
+{
+ ALsizei i;
+
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ device->RealOut.ChannelName[i] = InvalidChannel;
+
+ switch(device->FmtChans)
+ {
+ case DevFmtX51Rear:
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = BackLeft;
+ device->RealOut.ChannelName[3] = BackRight;
+ device->RealOut.ChannelName[4] = FrontCenter;
+ device->RealOut.ChannelName[5] = LFE;
+ return;
+ case DevFmtX71:
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = BackLeft;
+ device->RealOut.ChannelName[3] = BackRight;
+ device->RealOut.ChannelName[4] = FrontCenter;
+ device->RealOut.ChannelName[5] = LFE;
+ device->RealOut.ChannelName[6] = SideLeft;
+ device->RealOut.ChannelName[7] = SideRight;
+ return;
+
+ /* Same as WFX order */
+ case DevFmtMono:
+ case DevFmtStereo:
+ case DevFmtQuad:
+ case DevFmtX51:
+ case DevFmtX61:
+ case DevFmtAmbi3D:
+ SetDefaultWFXChannelOrder(device);
+ break;
+ }
+}
+
+
+/* ALCcontext_DeferUpdates
+ *
+ * Defers/suspends updates for the given context's listener and sources. This
+ * does *NOT* stop mixing, but rather prevents certain property changes from
+ * taking effect.
+ */
+void ALCcontext_DeferUpdates(ALCcontext *context)
+{
+ ATOMIC_STORE_SEQ(&context->DeferUpdates, AL_TRUE);
+}
+
+/* ALCcontext_ProcessUpdates
+ *
+ * Resumes update processing after being deferred.
+ */
+void ALCcontext_ProcessUpdates(ALCcontext *context)
+{
+ almtx_lock(&context->PropLock);
+ if(ATOMIC_EXCHANGE_SEQ(&context->DeferUpdates, AL_FALSE))
+ {
+ /* Tell the mixer to stop applying updates, then wait for any active
+ * updating to finish, before providing updates.
+ */
+ ATOMIC_STORE_SEQ(&context->HoldUpdates, AL_TRUE);
+ while((ATOMIC_LOAD(&context->UpdateCount, almemory_order_acquire)&1) != 0)
+ althrd_yield();
+
+ if(!ATOMIC_EXCHANGE(&context->PropsClean, AL_TRUE, almemory_order_acq_rel))
+ UpdateContextProps(context);
+ if(!ATOMIC_EXCHANGE(&context->Listener->PropsClean, AL_TRUE, almemory_order_acq_rel))
+ UpdateListenerProps(context);
+ UpdateAllEffectSlotProps(context);
+ UpdateAllSourceProps(context);
+
+ /* Now with all updates declared, let the mixer continue applying them
+ * so they all happen at once.
+ */
+ ATOMIC_STORE_SEQ(&context->HoldUpdates, AL_FALSE);
+ }
+ almtx_unlock(&context->PropLock);
+}
+
+
+/* alcSetError
+ *
+ * Stores the latest ALC device error
+ */
+static void alcSetError(ALCdevice *device, ALCenum errorCode)
+{
+ WARN("Error generated on device %p, code 0x%04x\n", device, 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_SEQ(&device->LastError, errorCode);
+ else
+ ATOMIC_STORE_SEQ(&LastNullDeviceError, errorCode);
+}
+
+
+static struct Compressor *CreateDeviceLimiter(const ALCdevice *device, const ALfloat threshold)
+{
+ return CompressorInit(device->RealOut.NumChannels, device->Frequency,
+ AL_TRUE, AL_TRUE, AL_TRUE, AL_TRUE, AL_TRUE, 0.001f, 0.002f,
+ 0.0f, 0.0f, threshold, INFINITY, 0.0f, 0.020f, 0.200f);
+}
+
+/* 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. Must not be called while the device is running/
+ * mixing.
+ */
+static inline void UpdateClockBase(ALCdevice *device)
+{
+ IncrementRef(&device->MixCount);
+ device->ClockBase += device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency;
+ device->SamplesDone = 0;
+ IncrementRef(&device->MixCount);
+}
+
+/* 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)
+{
+ enum HrtfRequestMode hrtf_userreq = Hrtf_Default;
+ enum HrtfRequestMode hrtf_appreq = Hrtf_Default;
+ ALCenum gainLimiter = device->LimiterState;
+ const ALsizei old_sends = device->NumAuxSends;
+ ALsizei new_sends = device->NumAuxSends;
+ enum DevFmtChannels oldChans;
+ enum DevFmtType oldType;
+ ALboolean update_failed;
+ ALCsizei hrtf_id = -1;
+ ALCcontext *context;
+ ALCuint oldFreq;
+ size_t size;
+ ALCsizei i;
+ int val;
+
+ // Check for attributes
+ if(device->Type == Loopback)
+ {
+ ALCsizei numMono, numStereo, numSends;
+ ALCenum alayout = AL_NONE;
+ ALCenum ascale = AL_NONE;
+ ALCenum schans = AL_NONE;
+ ALCenum stype = AL_NONE;
+ ALCsizei attrIdx = 0;
+ ALCsizei aorder = 0;
+ ALCuint freq = 0;
+
+ if(!attrList)
+ {
+ WARN("Missing attributes for loopback device\n");
+ return ALC_INVALID_VALUE;
+ }
+
+ numMono = device->NumMonoSources;
+ numStereo = device->NumStereoSources;
+ numSends = old_sends;
+
+#define TRACE_ATTR(a, v) TRACE("Loopback %s = %d\n", #a, v)
+ while(attrList[attrIdx])
+ {
+ switch(attrList[attrIdx])
+ {
+ case ALC_FORMAT_CHANNELS_SOFT:
+ schans = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_FORMAT_CHANNELS_SOFT, schans);
+ if(!IsValidALCChannels(schans))
+ return ALC_INVALID_VALUE;
+ break;
+
+ case ALC_FORMAT_TYPE_SOFT:
+ stype = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_FORMAT_TYPE_SOFT, stype);
+ if(!IsValidALCType(stype))
+ return ALC_INVALID_VALUE;
+ break;
+
+ case ALC_FREQUENCY:
+ freq = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_FREQUENCY, freq);
+ if(freq < MIN_OUTPUT_RATE)
+ return ALC_INVALID_VALUE;
+ break;
+
+ case ALC_AMBISONIC_LAYOUT_SOFT:
+ alayout = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_AMBISONIC_LAYOUT_SOFT, alayout);
+ if(!IsValidAmbiLayout(alayout))
+ return ALC_INVALID_VALUE;
+ break;
+
+ case ALC_AMBISONIC_SCALING_SOFT:
+ ascale = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_AMBISONIC_SCALING_SOFT, ascale);
+ if(!IsValidAmbiScaling(ascale))
+ return ALC_INVALID_VALUE;
+ break;
+
+ case ALC_AMBISONIC_ORDER_SOFT:
+ aorder = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_AMBISONIC_ORDER_SOFT, aorder);
+ if(aorder < 1 || aorder > MAX_AMBI_ORDER)
+ return ALC_INVALID_VALUE;
+ break;
+
+ case ALC_MONO_SOURCES:
+ numMono = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_MONO_SOURCES, numMono);
+ numMono = maxi(numMono, 0);
+ break;
+
+ case ALC_STEREO_SOURCES:
+ numStereo = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_STEREO_SOURCES, numStereo);
+ numStereo = maxi(numStereo, 0);
+ break;
+
+ case ALC_MAX_AUXILIARY_SENDS:
+ numSends = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends);
+ numSends = clampi(numSends, 0, MAX_SENDS);
+ break;
+
+ case ALC_HRTF_SOFT:
+ TRACE_ATTR(ALC_HRTF_SOFT, attrList[attrIdx + 1]);
+ if(attrList[attrIdx + 1] == ALC_FALSE)
+ hrtf_appreq = Hrtf_Disable;
+ else if(attrList[attrIdx + 1] == ALC_TRUE)
+ hrtf_appreq = Hrtf_Enable;
+ else
+ hrtf_appreq = Hrtf_Default;
+ break;
+
+ case ALC_HRTF_ID_SOFT:
+ hrtf_id = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id);
+ break;
+
+ case ALC_OUTPUT_LIMITER_SOFT:
+ gainLimiter = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_OUTPUT_LIMITER_SOFT, gainLimiter);
+ break;
+
+ default:
+ TRACE("Loopback 0x%04X = %d (0x%x)\n", attrList[attrIdx],
+ attrList[attrIdx + 1], attrList[attrIdx + 1]);
+ break;
+ }
+
+ attrIdx += 2;
+ }
+#undef TRACE_ATTR
+
+ if(!schans || !stype || !freq)
+ {
+ WARN("Missing format for loopback device\n");
+ return ALC_INVALID_VALUE;
+ }
+ if(schans == ALC_BFORMAT3D_SOFT && (!alayout || !ascale || !aorder))
+ {
+ WARN("Missing ambisonic info for loopback device\n");
+ return ALC_INVALID_VALUE;
+ }
+
+ if((device->Flags&DEVICE_RUNNING))
+ V0(device->Backend,stop)();
+ device->Flags &= ~DEVICE_RUNNING;
+
+ UpdateClockBase(device);
+
+ device->Frequency = freq;
+ device->FmtChans = static_cast<enum DevFmtChannels>(schans);
+ device->FmtType = static_cast<enum DevFmtType>(stype);
+ if(schans == ALC_BFORMAT3D_SOFT)
+ {
+ device->AmbiOrder = aorder;
+ device->AmbiLayout = static_cast<enum AmbiLayout>(alayout);
+ device->AmbiScale = static_cast<enum AmbiNorm>(ascale);
+ }
+
+ if(numMono > INT_MAX-numStereo)
+ numMono = INT_MAX-numStereo;
+ numMono += numStereo;
+ if(ConfigValueInt(nullptr, nullptr, "sources", &numMono))
+ {
+ if(numMono <= 0)
+ numMono = 256;
+ }
+ else
+ numMono = maxi(numMono, 256);
+ numStereo = mini(numStereo, numMono);
+ numMono -= numStereo;
+ device->SourcesMax = numMono + numStereo;
+
+ device->NumMonoSources = numMono;
+ device->NumStereoSources = numStereo;
+
+ if(ConfigValueInt(nullptr, nullptr, "sends", &new_sends))
+ new_sends = mini(numSends, clampi(new_sends, 0, MAX_SENDS));
+ else
+ new_sends = numSends;
+ }
+ else if(attrList && attrList[0])
+ {
+ ALCsizei numMono, numStereo, numSends;
+ ALCsizei attrIdx = 0;
+ ALCuint freq;
+
+ /* 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;
+
+ UpdateClockBase(device);
+
+ freq = device->Frequency;
+ numMono = device->NumMonoSources;
+ numStereo = device->NumStereoSources;
+ numSends = old_sends;
+
+#define TRACE_ATTR(a, v) TRACE("%s = %d\n", #a, v)
+ while(attrList[attrIdx])
+ {
+ switch(attrList[attrIdx])
+ {
+ case ALC_FREQUENCY:
+ freq = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_FREQUENCY, freq);
+ device->Flags |= DEVICE_FREQUENCY_REQUEST;
+ break;
+
+ case ALC_MONO_SOURCES:
+ numMono = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_MONO_SOURCES, numMono);
+ numMono = maxi(numMono, 0);
+ break;
+
+ case ALC_STEREO_SOURCES:
+ numStereo = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_STEREO_SOURCES, numStereo);
+ numStereo = maxi(numStereo, 0);
+ break;
+
+ case ALC_MAX_AUXILIARY_SENDS:
+ numSends = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends);
+ numSends = clampi(numSends, 0, MAX_SENDS);
+ break;
+
+ case ALC_HRTF_SOFT:
+ TRACE_ATTR(ALC_HRTF_SOFT, attrList[attrIdx + 1]);
+ if(attrList[attrIdx + 1] == ALC_FALSE)
+ hrtf_appreq = Hrtf_Disable;
+ else if(attrList[attrIdx + 1] == ALC_TRUE)
+ hrtf_appreq = Hrtf_Enable;
+ else
+ hrtf_appreq = Hrtf_Default;
+ break;
+
+ case ALC_HRTF_ID_SOFT:
+ hrtf_id = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id);
+ break;
+
+ case ALC_OUTPUT_LIMITER_SOFT:
+ gainLimiter = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_OUTPUT_LIMITER_SOFT, gainLimiter);
+ break;
+
+ default:
+ TRACE("0x%04X = %d (0x%x)\n", attrList[attrIdx],
+ attrList[attrIdx + 1], attrList[attrIdx + 1]);
+ break;
+ }
+
+ attrIdx += 2;
+ }
+#undef TRACE_ATTR
+
+ ConfigValueUInt(alstr_get_cstr(device->DeviceName), nullptr, "frequency", &freq);
+ freq = maxu(freq, MIN_OUTPUT_RATE);
+
+ 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;
+
+ device->Frequency = freq;
+
+ if(numMono > INT_MAX-numStereo)
+ numMono = INT_MAX-numStereo;
+ numMono += numStereo;
+ if(ConfigValueInt(alstr_get_cstr(device->DeviceName), nullptr, "sources", &numMono))
+ {
+ if(numMono <= 0)
+ numMono = 256;
+ }
+ else
+ numMono = maxi(numMono, 256);
+ numStereo = mini(numStereo, numMono);
+ numMono -= numStereo;
+ device->SourcesMax = numMono + numStereo;
+
+ device->NumMonoSources = numMono;
+ device->NumStereoSources = numStereo;
+
+ if(ConfigValueInt(alstr_get_cstr(device->DeviceName), nullptr, "sends", &new_sends))
+ new_sends = mini(numSends, clampi(new_sends, 0, MAX_SENDS));
+ else
+ new_sends = numSends;
+ }
+
+ if((device->Flags&DEVICE_RUNNING))
+ return ALC_NO_ERROR;
+
+ al_free(device->Uhj_Encoder);
+ device->Uhj_Encoder = nullptr;
+
+ al_free(device->Bs2b);
+ device->Bs2b = nullptr;
+
+ al_free(device->ChannelDelay[0].Buffer);
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ {
+ device->ChannelDelay[i].Length = 0;
+ device->ChannelDelay[i].Buffer = nullptr;
+ }
+
+ al_free(device->Dry.Buffer);
+ device->Dry.Buffer = nullptr;
+ device->Dry.NumChannels = 0;
+ device->FOAOut.Buffer = nullptr;
+ device->FOAOut.NumChannels = 0;
+ device->RealOut.Buffer = nullptr;
+ device->RealOut.NumChannels = 0;
+
+ UpdateClockBase(device);
+ device->FixedLatency = 0;
+
+ device->DitherSeed = DITHER_RNG_SEED;
+
+ /*************************************************************************
+ * Update device format request if HRTF is requested
+ */
+ device->HrtfStatus = ALC_HRTF_DISABLED_SOFT;
+ if(device->Type != Loopback)
+ {
+ const char *hrtf;
+ if(ConfigValueStr(alstr_get_cstr(device->DeviceName), nullptr, "hrtf", &hrtf))
+ {
+ if(strcasecmp(hrtf, "true") == 0)
+ hrtf_userreq = Hrtf_Enable;
+ else if(strcasecmp(hrtf, "false") == 0)
+ hrtf_userreq = Hrtf_Disable;
+ else if(strcasecmp(hrtf, "auto") != 0)
+ ERR("Unexpected hrtf value: %s\n", hrtf);
+ }
+
+ if(hrtf_userreq == Hrtf_Enable || (hrtf_userreq != Hrtf_Disable && hrtf_appreq == Hrtf_Enable))
+ {
+ struct Hrtf *hrtf = nullptr;
+ if(VECTOR_SIZE(device->HrtfList) == 0)
+ {
+ VECTOR_DEINIT(device->HrtfList);
+ device->HrtfList = EnumerateHrtf(device->DeviceName);
+ }
+ if(VECTOR_SIZE(device->HrtfList) > 0)
+ {
+ if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->HrtfList))
+ hrtf = GetLoadedHrtf(VECTOR_ELEM(device->HrtfList, hrtf_id).hrtf);
+ else
+ hrtf = GetLoadedHrtf(VECTOR_ELEM(device->HrtfList, 0).hrtf);
+ }
+
+ if(hrtf)
+ {
+ device->FmtChans = DevFmtStereo;
+ device->Frequency = hrtf->sampleRate;
+ device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_FREQUENCY_REQUEST;
+ if(device->HrtfHandle)
+ Hrtf_DecRef(device->HrtfHandle);
+ device->HrtfHandle = hrtf;
+ }
+ else
+ {
+ hrtf_userreq = Hrtf_Default;
+ hrtf_appreq = Hrtf_Disable;
+ device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
+ }
+ }
+ }
+
+ 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(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;
+ }
+
+ if((device->UpdateSize&3) != 0)
+ {
+ 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);
+ }
+
+ TRACE("Post-reset: %s, %s, %uhz, %u update size x%d\n",
+ DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+ device->Frequency, device->UpdateSize, device->NumUpdates
+ );
+
+ aluInitRenderer(device, hrtf_id, hrtf_appreq, hrtf_userreq);
+ TRACE("Channel config, Dry: %d, FOA: %d, Real: %d\n", device->Dry.NumChannels,
+ device->FOAOut.NumChannels, device->RealOut.NumChannels);
+
+ /* Allocate extra channels for any post-filter output. */
+ size = (device->Dry.NumChannels + device->FOAOut.NumChannels +
+ device->RealOut.NumChannels)*sizeof(device->Dry.Buffer[0]);
+
+ TRACE("Allocating " SZFMT " channels, " SZFMT " bytes\n", size/sizeof(device->Dry.Buffer[0]), size);
+ device->Dry.Buffer = static_cast<float(*)[BUFFERSIZE]>(al_calloc(16, size));
+ if(!device->Dry.Buffer)
+ {
+ ERR("Failed to allocate " SZFMT " bytes for mix buffer\n", size);
+ return ALC_INVALID_DEVICE;
+ }
+
+ if(device->RealOut.NumChannels != 0)
+ device->RealOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels +
+ device->FOAOut.NumChannels;
+ else
+ {
+ device->RealOut.Buffer = device->Dry.Buffer;
+ device->RealOut.NumChannels = device->Dry.NumChannels;
+ }
+
+ if(device->FOAOut.NumChannels != 0)
+ device->FOAOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels;
+ else
+ {
+ device->FOAOut.Buffer = device->Dry.Buffer;
+ device->FOAOut.NumChannels = device->Dry.NumChannels;
+ }
+
+ device->NumAuxSends = new_sends;
+ TRACE("Max sources: %d (%d + %d), effect slots: %d, sends: %d\n",
+ device->SourcesMax, device->NumMonoSources, device->NumStereoSources,
+ device->AuxiliaryEffectSlotMax, device->NumAuxSends);
+
+ device->DitherDepth = 0.0f;
+ if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), nullptr, "dither", 1))
+ {
+ ALint depth = 0;
+ ConfigValueInt(alstr_get_cstr(device->DeviceName), nullptr, "dither-depth", &depth);
+ if(depth <= 0)
+ {
+ switch(device->FmtType)
+ {
+ case DevFmtByte:
+ case DevFmtUByte:
+ depth = 8;
+ break;
+ case DevFmtShort:
+ case DevFmtUShort:
+ depth = 16;
+ break;
+ case DevFmtInt:
+ case DevFmtUInt:
+ case DevFmtFloat:
+ break;
+ }
+ }
+
+ if(depth > 0)
+ {
+ depth = clampi(depth, 2, 24);
+ device->DitherDepth = powf(2.0f, (ALfloat)(depth-1));
+ }
+ }
+ if(!(device->DitherDepth > 0.0f))
+ TRACE("Dithering disabled\n");
+ else
+ TRACE("Dithering enabled (%g-bit, %g)\n", log2f(device->DitherDepth)+1.0f,
+ device->DitherDepth);
+
+ device->LimiterState = gainLimiter;
+ if(ConfigValueBool(alstr_get_cstr(device->DeviceName), nullptr, "output-limiter", &val))
+ gainLimiter = val ? ALC_TRUE : ALC_FALSE;
+
+ /* Valid values for gainLimiter are ALC_DONT_CARE_SOFT, ALC_TRUE, and
+ * ALC_FALSE. For ALC_DONT_CARE_SOFT, use the limiter for integer-based
+ * output (where samples must be clamped), and don't for floating-point
+ * (which can take unclamped samples).
+ */
+ if(gainLimiter == ALC_DONT_CARE_SOFT)
+ {
+ switch(device->FmtType)
+ {
+ case DevFmtByte:
+ case DevFmtUByte:
+ case DevFmtShort:
+ case DevFmtUShort:
+ case DevFmtInt:
+ case DevFmtUInt:
+ gainLimiter = ALC_TRUE;
+ break;
+ case DevFmtFloat:
+ gainLimiter = ALC_FALSE;
+ break;
+ }
+ }
+ if(gainLimiter != ALC_FALSE)
+ {
+ ALfloat thrshld = 1.0f;
+ switch(device->FmtType)
+ {
+ case DevFmtByte:
+ case DevFmtUByte:
+ thrshld = 127.0f / 128.0f;
+ break;
+ case DevFmtShort:
+ case DevFmtUShort:
+ thrshld = 32767.0f / 32768.0f;
+ break;
+ case DevFmtInt:
+ case DevFmtUInt:
+ case DevFmtFloat:
+ break;
+ }
+ if(device->DitherDepth > 0.0f)
+ thrshld -= 1.0f / device->DitherDepth;
+
+ al_free(device->Limiter);
+ device->Limiter = CreateDeviceLimiter(device, log10f(thrshld) * 20.0f);
+ device->FixedLatency += (ALuint)(GetCompressorLookAhead(device->Limiter) *
+ DEVICE_CLOCK_RES / device->Frequency);
+ }
+ else
+ {
+ al_free(device->Limiter);
+ device->Limiter = nullptr;
+ }
+ TRACE("Output limiter %s\n", device->Limiter ? "enabled" : "disabled");
+
+ aluSelectPostProcess(device);
+
+ TRACE("Fixed device latency: %uns\n", device->FixedLatency);
+
+ /* Need to delay returning failure until replacement Send arrays have been
+ * allocated with the appropriate size.
+ */
+ update_failed = AL_FALSE;
+ START_MIXER_MODE();
+ context = ATOMIC_LOAD_SEQ(&device->ContextList);
+ while(context)
+ {
+ SourceSubList *sublist, *subend;
+ struct ALvoiceProps *vprops;
+ ALsizei pos;
+
+ if(context->DefaultSlot)
+ {
+ ALeffectslot *slot = context->DefaultSlot;
+ ALeffectState *state = slot->Effect.State;
+
+ state->OutBuffer = device->Dry.Buffer;
+ state->OutChannels = device->Dry.NumChannels;
+ if(V(state,deviceUpdate)(device) == AL_FALSE)
+ update_failed = AL_TRUE;
+ else
+ UpdateEffectSlotProps(slot, context);
+ }
+
+ almtx_lock(&context->PropLock);
+ almtx_lock(&context->EffectSlotLock);
+ for(pos = 0;pos < (ALsizei)VECTOR_SIZE(context->EffectSlotList);pos++)
+ {
+ ALeffectslot *slot = VECTOR_ELEM(context->EffectSlotList, pos);
+ ALeffectState *state = slot->Effect.State;
+
+ state->OutBuffer = device->Dry.Buffer;
+ state->OutChannels = device->Dry.NumChannels;
+ if(V(state,deviceUpdate)(device) == AL_FALSE)
+ update_failed = AL_TRUE;
+ else
+ UpdateEffectSlotProps(slot, context);
+ }
+ almtx_unlock(&context->EffectSlotLock);
+
+ almtx_lock(&context->SourceLock);
+ sublist = VECTOR_BEGIN(context->SourceList);
+ subend = VECTOR_END(context->SourceList);
+ for(;sublist != subend;++sublist)
+ {
+ ALuint64 usemask = ~sublist->FreeMask;
+ while(usemask)
+ {
+ ALsizei idx = CTZ64(usemask);
+ ALsource *source = sublist->Sources + idx;
+
+ usemask &= ~(U64(1) << idx);
+
+ if(old_sends != device->NumAuxSends)
+ {
+ ALvoid *sends = al_calloc(16, device->NumAuxSends*sizeof(source->Send[0]));
+ ALsizei s;
+
+ memcpy(sends, source->Send,
+ mini(device->NumAuxSends, old_sends)*sizeof(source->Send[0])
+ );
+ for(s = device->NumAuxSends;s < old_sends;s++)
+ {
+ if(source->Send[s].Slot)
+ DecrementRef(&source->Send[s].Slot->ref);
+ source->Send[s].Slot = nullptr;
+ }
+ al_free(source->Send);
+ source->Send = static_cast<decltype(source->Send)>(sends);
+ for(s = old_sends;s < device->NumAuxSends;s++)
+ {
+ source->Send[s].Slot = nullptr;
+ source->Send[s].Gain = 1.0f;
+ source->Send[s].GainHF = 1.0f;
+ source->Send[s].HFReference = LOWPASSFREQREF;
+ source->Send[s].GainLF = 1.0f;
+ source->Send[s].LFReference = HIGHPASSFREQREF;
+ }
+ }
+
+ ATOMIC_STORE(&source->PropsClean, AL_FALSE, almemory_order_release);
+ }
+ }
+
+ /* Clear any pre-existing voice property structs, in case the number of
+ * auxiliary sends is changing. Active sources will have updates
+ * respecified in UpdateAllSourceProps.
+ */
+ vprops = ATOMIC_EXCHANGE_PTR(&context->FreeVoiceProps, static_cast<ALvoiceProps*>(nullptr),
+ almemory_order_acq_rel);
+ while(vprops)
+ {
+ struct ALvoiceProps *next = ATOMIC_LOAD(&vprops->next, almemory_order_relaxed);
+ al_free(vprops);
+ vprops = next;
+ }
+
+ AllocateVoices(context, context->MaxVoices, old_sends);
+ for(pos = 0;pos < context->VoiceCount;pos++)
+ {
+ ALvoice *voice = context->Voices[pos];
+
+ al_free(ATOMIC_EXCHANGE_PTR(&voice->Update, static_cast<ALvoiceProps*>(nullptr),
+ almemory_order_acq_rel));
+
+ if(ATOMIC_LOAD(&voice->Source, almemory_order_acquire) == nullptr)
+ continue;
+
+ if(device->AvgSpeakerDist > 0.0f)
+ {
+ /* Reinitialize the NFC filters for new parameters. */
+ ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
+ (device->AvgSpeakerDist * device->Frequency);
+ for(i = 0;i < voice->NumChannels;i++)
+ NfcFilterCreate(&voice->Direct.Params[i].NFCtrlFilter, 0.0f, w1);
+ }
+ }
+ almtx_unlock(&context->SourceLock);
+
+ ATOMIC_STORE(&context->PropsClean, AL_TRUE, almemory_order_release);
+ UpdateContextProps(context);
+ ATOMIC_STORE(&context->Listener->PropsClean, AL_TRUE, almemory_order_release);
+ UpdateListenerProps(context);
+ UpdateAllSourceProps(context);
+ almtx_unlock(&context->PropLock);
+
+ context = ATOMIC_LOAD(&context->next, almemory_order_relaxed);
+ }
+ END_MIXER_MODE();
+ if(update_failed)
+ return ALC_INVALID_DEVICE;
+
+ if(!(device->Flags&DEVICE_PAUSED))
+ {
+ if(V0(device->Backend,start)() == ALC_FALSE)
+ return ALC_INVALID_DEVICE;
+ device->Flags |= DEVICE_RUNNING;
+ }
+
+ return ALC_NO_ERROR;
+}
+
+
+static void InitDevice(ALCdevice *device, enum DeviceType type)
+{
+ ALsizei i;
+
+ InitRef(&device->ref, 1);
+ ATOMIC_INIT(&device->Connected, ALC_TRUE);
+ device->Type = type;
+ ATOMIC_INIT(&device->LastError, ALC_NO_ERROR);
+
+ device->Flags = 0;
+ device->Render_Mode = NormalRender;
+ device->AvgSpeakerDist = 0.0f;
+ device->LimiterState = ALC_DONT_CARE_SOFT;
+
+ ATOMIC_INIT(&device->ContextList, static_cast<ALCcontext*>(nullptr));
+
+ device->ClockBase = 0;
+ device->SamplesDone = 0;
+ device->FixedLatency = 0;
+
+ device->SourcesMax = 0;
+ device->AuxiliaryEffectSlotMax = 0;
+ device->NumAuxSends = 0;
+
+ device->Dry.Buffer = nullptr;
+ device->Dry.NumChannels = 0;
+ device->FOAOut.Buffer = nullptr;
+ device->FOAOut.NumChannels = 0;
+ device->RealOut.Buffer = nullptr;
+ device->RealOut.NumChannels = 0;
+
+ AL_STRING_INIT(device->DeviceName);
+
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ {
+ device->ChannelDelay[i].Gain = 1.0f;
+ device->ChannelDelay[i].Length = 0;
+ device->ChannelDelay[i].Buffer = nullptr;
+ }
+
+ device->HrtfName = nullptr;
+ VECTOR_INIT(device->HrtfList);
+ device->HrtfHandle = nullptr;
+ device->Hrtf = nullptr;
+ device->Bs2b = nullptr;
+ device->Uhj_Encoder = nullptr;
+ device->AmbiDecoder = nullptr;
+ device->AmbiUp = nullptr;
+ device->Stablizer = nullptr;
+ device->Limiter = nullptr;
+
+ VECTOR_INIT(device->BufferList);
+ almtx_init(&device->BufferLock, almtx_plain);
+
+ VECTOR_INIT(device->EffectList);
+ almtx_init(&device->EffectLock, almtx_plain);
+
+ VECTOR_INIT(device->FilterList);
+ almtx_init(&device->FilterLock, almtx_plain);
+
+ almtx_init(&device->BackendLock, almtx_plain);
+ device->Backend = nullptr;
+
+ ATOMIC_INIT(&device->next, static_cast<ALCdevice*>(nullptr));
+}
+
+/* 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)
+{
+ ALsizei i;
+
+ TRACE("%p\n", device);
+
+ if(device->Backend)
+ DELETE_OBJ(device->Backend);
+ device->Backend = nullptr;
+
+ almtx_destroy(&device->BackendLock);
+
+ ReleaseALBuffers(device);
+#define FREE_BUFFERSUBLIST(x) al_free((x)->Buffers)
+ VECTOR_FOR_EACH(BufferSubList, device->BufferList, FREE_BUFFERSUBLIST);
+#undef FREE_BUFFERSUBLIST
+ VECTOR_DEINIT(device->BufferList);
+ almtx_destroy(&device->BufferLock);
+
+ ReleaseALEffects(device);
+#define FREE_EFFECTSUBLIST(x) al_free((x)->Effects)
+ VECTOR_FOR_EACH(EffectSubList, device->EffectList, FREE_EFFECTSUBLIST);
+#undef FREE_EFFECTSUBLIST
+ VECTOR_DEINIT(device->EffectList);
+ almtx_destroy(&device->EffectLock);
+
+ ReleaseALFilters(device);
+#define FREE_FILTERSUBLIST(x) al_free((x)->Filters)
+ VECTOR_FOR_EACH(FilterSubList, device->FilterList, FREE_FILTERSUBLIST);
+#undef FREE_FILTERSUBLIST
+ VECTOR_DEINIT(device->FilterList);
+ almtx_destroy(&device->FilterLock);
+
+ al_free(device->HrtfName);
+ device->HrtfName = nullptr;
+ FreeHrtfList(&device->HrtfList);
+ if(device->HrtfHandle)
+ Hrtf_DecRef(device->HrtfHandle);
+ device->HrtfHandle = nullptr;
+ al_free(device->Hrtf);
+ device->Hrtf = nullptr;
+
+ al_free(device->Bs2b);
+ device->Bs2b = nullptr;
+
+ al_free(device->Uhj_Encoder);
+ device->Uhj_Encoder = nullptr;
+
+ bformatdec_free(&device->AmbiDecoder);
+ ambiup_free(&device->AmbiUp);
+
+ al_free(device->Stablizer);
+ device->Stablizer = nullptr;
+
+ al_free(device->Limiter);
+ device->Limiter = nullptr;
+
+ al_free(device->ChannelDelay[0].Buffer);
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ {
+ device->ChannelDelay[i].Gain = 1.0f;
+ device->ChannelDelay[i].Length = 0;
+ device->ChannelDelay[i].Buffer = nullptr;
+ }
+
+ AL_STRING_DEINIT(device->DeviceName);
+
+ al_free(device->Dry.Buffer);
+ device->Dry.Buffer = nullptr;
+ device->Dry.NumChannels = 0;
+ device->FOAOut.Buffer = nullptr;
+ device->FOAOut.NumChannels = 0;
+ device->RealOut.Buffer = nullptr;
+ device->RealOut.NumChannels = 0;
+
+ 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 ALCboolean VerifyDevice(ALCdevice **device)
+{
+ LockLists();
+ ALCdevice *tmpDevice{DeviceList.load()};
+ while(tmpDevice)
+ {
+ if(tmpDevice == *device)
+ {
+ ALCdevice_IncRef(tmpDevice);
+ UnlockLists();
+ return ALC_TRUE;
+ }
+ tmpDevice = ATOMIC_LOAD(&tmpDevice->next, almemory_order_relaxed);
+ }
+ UnlockLists();
+
+ *device = nullptr;
+ return ALC_FALSE;
+}
+
+
+/* InitContext
+ *
+ * Initializes context fields
+ */
+static ALvoid InitContext(ALCcontext *Context)
+{
+ ALlistener *listener = Context->Listener;
+ struct ALeffectslotArray *auxslots;
+
+ //Initialise listener
+ listener->Gain = 1.0f;
+ listener->Position[0] = 0.0f;
+ listener->Position[1] = 0.0f;
+ listener->Position[2] = 0.0f;
+ listener->Velocity[0] = 0.0f;
+ listener->Velocity[1] = 0.0f;
+ listener->Velocity[2] = 0.0f;
+ listener->Forward[0] = 0.0f;
+ listener->Forward[1] = 0.0f;
+ listener->Forward[2] = -1.0f;
+ listener->Up[0] = 0.0f;
+ listener->Up[1] = 1.0f;
+ listener->Up[2] = 0.0f;
+ ATOMIC_INIT(&listener->PropsClean, AL_TRUE);
+
+ ATOMIC_INIT(&listener->Update, static_cast<ALlistenerProps*>(nullptr));
+
+ //Validate Context
+ InitRef(&Context->UpdateCount, 0);
+ ATOMIC_INIT(&Context->HoldUpdates, AL_FALSE);
+ Context->GainBoost = 1.0f;
+ almtx_init(&Context->PropLock, almtx_plain);
+ ATOMIC_INIT(&Context->LastError, AL_NO_ERROR);
+ VECTOR_INIT(Context->SourceList);
+ Context->NumSources = 0;
+ almtx_init(&Context->SourceLock, almtx_plain);
+ VECTOR_INIT(Context->EffectSlotList);
+ almtx_init(&Context->EffectSlotLock, almtx_plain);
+
+ if(Context->DefaultSlot)
+ {
+ auxslots = static_cast<ALeffectslotArray*>(al_calloc(DEF_ALIGN,
+ FAM_SIZE(struct ALeffectslotArray, slot, 1)));
+ auxslots->count = 1;
+ auxslots->slot[0] = Context->DefaultSlot;
+ }
+ else
+ {
+ auxslots = static_cast<ALeffectslotArray*>(al_calloc(DEF_ALIGN,
+ sizeof(struct ALeffectslotArray)));
+ auxslots->count = 0;
+ }
+ ATOMIC_INIT(&Context->ActiveAuxSlots, auxslots);
+
+ //Set globals
+ Context->DistanceModel = DefaultDistanceModel;
+ Context->SourceDistanceModel = AL_FALSE;
+ Context->DopplerFactor = 1.0f;
+ Context->DopplerVelocity = 1.0f;
+ Context->SpeedOfSound = SPEEDOFSOUNDMETRESPERSEC;
+ Context->MetersPerUnit = AL_DEFAULT_METERS_PER_UNIT;
+ ATOMIC_INIT(&Context->PropsClean, AL_TRUE);
+ ATOMIC_INIT(&Context->DeferUpdates, AL_FALSE);
+ alsem_init(&Context->EventSem, 0);
+ Context->AsyncEvents = nullptr;
+ ATOMIC_INIT(&Context->EnabledEvts, 0u);
+ almtx_init(&Context->EventCbLock, almtx_plain);
+ Context->EventCb = nullptr;
+ Context->EventParam = nullptr;
+
+ ATOMIC_INIT(&Context->Update, static_cast<ALcontextProps*>(nullptr));
+ ATOMIC_INIT(&Context->FreeContextProps, static_cast<ALcontextProps*>(nullptr));
+ ATOMIC_INIT(&Context->FreeListenerProps, static_cast<ALlistenerProps*>(nullptr));
+ ATOMIC_INIT(&Context->FreeVoiceProps, static_cast<ALvoiceProps*>(nullptr));
+ ATOMIC_INIT(&Context->FreeEffectslotProps, static_cast<ALeffectslotProps*>(nullptr));
+
+ Context->ExtensionList = alExtList;
+
+
+ listener->Params.Matrix = IdentityMatrixf;
+ aluVectorSet(&listener->Params.Velocity, 0.0f, 0.0f, 0.0f, 0.0f);
+ listener->Params.Gain = listener->Gain;
+ listener->Params.MetersPerUnit = Context->MetersPerUnit;
+ listener->Params.DopplerFactor = Context->DopplerFactor;
+ listener->Params.SpeedOfSound = Context->SpeedOfSound * Context->DopplerVelocity;
+ listener->Params.ReverbSpeedOfSound = listener->Params.SpeedOfSound *
+ listener->Params.MetersPerUnit;
+ listener->Params.SourceDistanceModel = Context->SourceDistanceModel;
+ listener->Params.DistanceModel = Context->DistanceModel;
+
+
+ Context->AsyncEvents = ll_ringbuffer_create(63, sizeof(AsyncEvent), false);
+ if(althrd_create(&Context->EventThread, EventThread, Context) != althrd_success)
+ ERR("Failed to start event thread! Expect problems.\n");
+}
+
+
+/* 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 void FreeContext(ALCcontext *context)
+{
+ ALlistener *listener = context->Listener;
+ struct ALeffectslotArray *auxslots;
+ struct ALeffectslotProps *eprops;
+ struct ALlistenerProps *lprops;
+ struct ALcontextProps *cprops;
+ struct ALvoiceProps *vprops;
+ size_t count;
+ ALsizei i;
+
+ TRACE("%p\n", context);
+
+ if((cprops=ATOMIC_LOAD(&context->Update, almemory_order_acquire)) != nullptr)
+ {
+ TRACE("Freed unapplied context update %p\n", cprops);
+ al_free(cprops);
+ }
+
+ count = 0;
+ cprops = ATOMIC_LOAD(&context->FreeContextProps, almemory_order_acquire);
+ while(cprops)
+ {
+ struct ALcontextProps *next = ATOMIC_LOAD(&cprops->next, almemory_order_acquire);
+ al_free(cprops);
+ cprops = next;
+ ++count;
+ }
+ TRACE("Freed " SZFMT " context property object%s\n", count, (count==1)?"":"s");
+
+ if(context->DefaultSlot)
+ {
+ DeinitEffectSlot(context->DefaultSlot);
+ context->DefaultSlot = nullptr;
+ }
+
+ auxslots = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots,
+ static_cast<ALeffectslotArray*>(nullptr), almemory_order_relaxed);
+ al_free(auxslots);
+
+ ReleaseALSources(context);
+#define FREE_SOURCESUBLIST(x) al_free((x)->Sources)
+ VECTOR_FOR_EACH(SourceSubList, context->SourceList, FREE_SOURCESUBLIST);
+#undef FREE_SOURCESUBLIST
+ VECTOR_DEINIT(context->SourceList);
+ context->NumSources = 0;
+ almtx_destroy(&context->SourceLock);
+
+ count = 0;
+ eprops = ATOMIC_LOAD(&context->FreeEffectslotProps, almemory_order_relaxed);
+ while(eprops)
+ {
+ struct ALeffectslotProps *next = ATOMIC_LOAD(&eprops->next, almemory_order_relaxed);
+ if(eprops->State) ALeffectState_DecRef(eprops->State);
+ al_free(eprops);
+ eprops = next;
+ ++count;
+ }
+ TRACE("Freed " SZFMT " AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
+
+ ReleaseALAuxiliaryEffectSlots(context);
+#define FREE_EFFECTSLOTPTR(x) al_free(*(x))
+ VECTOR_FOR_EACH(ALeffectslotPtr, context->EffectSlotList, FREE_EFFECTSLOTPTR);
+#undef FREE_EFFECTSLOTPTR
+ VECTOR_DEINIT(context->EffectSlotList);
+ almtx_destroy(&context->EffectSlotLock);
+
+ count = 0;
+ vprops = ATOMIC_LOAD(&context->FreeVoiceProps, almemory_order_relaxed);
+ while(vprops)
+ {
+ struct ALvoiceProps *next = ATOMIC_LOAD(&vprops->next, almemory_order_relaxed);
+ al_free(vprops);
+ vprops = next;
+ ++count;
+ }
+ TRACE("Freed " SZFMT " voice property object%s\n", count, (count==1)?"":"s");
+
+ for(i = 0;i < context->VoiceCount;i++)
+ DeinitVoice(context->Voices[i]);
+ al_free(context->Voices);
+ context->Voices = nullptr;
+ context->VoiceCount = 0;
+ context->MaxVoices = 0;
+
+ if((lprops=ATOMIC_LOAD(&listener->Update, almemory_order_acquire)) != nullptr)
+ {
+ TRACE("Freed unapplied listener update %p\n", lprops);
+ al_free(lprops);
+ }
+ count = 0;
+ lprops = ATOMIC_LOAD(&context->FreeListenerProps, almemory_order_acquire);
+ while(lprops)
+ {
+ struct ALlistenerProps *next = ATOMIC_LOAD(&lprops->next, almemory_order_acquire);
+ al_free(lprops);
+ lprops = next;
+ ++count;
+ }
+ TRACE("Freed " SZFMT " listener property object%s\n", count, (count==1)?"":"s");
+
+ almtx_destroy(&context->EventCbLock);
+ alsem_destroy(&context->EventSem);
+
+ ll_ringbuffer_free(context->AsyncEvents);
+ context->AsyncEvents = nullptr;
+
+ almtx_destroy(&context->PropLock);
+
+ ALCdevice_DecRef(context->Device);
+ context->Device = nullptr;
+
+ //Invalidate context
+ memset(context, 0, sizeof(ALCcontext));
+ al_free(context);
+}
+
+/* ReleaseContext
+ *
+ * Removes the context reference from the given device and removes it from
+ * being current on the running thread or globally. Returns true if other
+ * contexts still exist on the device.
+ */
+static bool ReleaseContext(ALCcontext *context, ALCdevice *device)
+{
+ static const AsyncEvent kill_evt = ASYNC_EVENT(EventType_KillThread);
+ ALCcontext *origctx, *newhead;
+ bool ret = true;
+
+ if(altss_get(LocalContext) == context)
+ {
+ WARN("%p released while current on thread\n", context);
+ altss_set(LocalContext, nullptr);
+ ALCcontext_DecRef(context);
+ }
+
+ origctx = context;
+ if(ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&GlobalContext, &origctx, static_cast<ALCcontext*>(nullptr)))
+ ALCcontext_DecRef(context);
+
+ V0(device->Backend,lock)();
+ origctx = context;
+ newhead = ATOMIC_LOAD(&context->next, almemory_order_relaxed);
+ if(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&device->ContextList, &origctx, newhead))
+ {
+ ALCcontext *list;
+ do {
+ /* origctx is what the desired context failed to match. Try
+ * swapping out the next one in the list.
+ */
+ list = origctx;
+ origctx = context;
+ } while(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&list->next, &origctx, newhead));
+ }
+ else
+ ret = !!newhead;
+ V0(device->Backend,unlock)();
+
+ /* Make sure the context is finished and no longer processing in the mixer
+ * before sending the message queue kill event. The backend's lock does
+ * this, although waiting for a non-odd mix count would work too.
+ */
+
+ while(ll_ringbuffer_write(context->AsyncEvents, (const char*)&kill_evt, 1) == 0)
+ althrd_yield();
+ alsem_post(&context->EventSem);
+ althrd_join(context->EventThread, nullptr);
+
+ ALCcontext_DecRef(context);
+ return ret;
+}
+
+static void ALCcontext_IncRef(ALCcontext *context)
+{
+ uint ref = IncrementRef(&context->ref);
+ TRACEREF("%p increasing refcount to %u\n", context, ref);
+}
+
+void ALCcontext_DecRef(ALCcontext *context)
+{
+ uint ref = DecrementRef(&context->ref);
+ TRACEREF("%p decreasing refcount to %u\n", context, ref);
+ if(ref == 0) FreeContext(context);
+}
+
+static void ReleaseThreadCtx(void *ptr)
+{
+ ALCcontext *context = static_cast<ALCcontext*>(ptr);
+ uint ref = DecrementRef(&context->ref);
+ TRACEREF("%p decreasing refcount to %u\n", context, ref);
+ ERR("Context %p current for thread being destroyed, possible leak!\n", context);
+}
+
+/* VerifyContext
+ *
+ * Checks that the given context is valid, and increments its reference count.
+ */
+static ALCboolean VerifyContext(ALCcontext **context)
+{
+ LockLists();
+ ALCdevice *dev{DeviceList.load()};
+ while(dev)
+ {
+ ALCcontext *ctx = ATOMIC_LOAD(&dev->ContextList, almemory_order_acquire);
+ while(ctx)
+ {
+ if(ctx == *context)
+ {
+ ALCcontext_IncRef(ctx);
+ UnlockLists();
+ return ALC_TRUE;
+ }
+ ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
+ }
+ dev = ATOMIC_LOAD(&dev->next, almemory_order_relaxed);
+ }
+ UnlockLists();
+
+ *context = nullptr;
+ return ALC_FALSE;
+}
+
+
+/* GetContextRef
+ *
+ * Returns the currently active context for this thread, and adds a reference
+ * without locking it.
+ */
+ALCcontext *GetContextRef(void)
+{
+ ALCcontext *context{static_cast<ALCcontext*>(altss_get(LocalContext))};
+ if(context)
+ ALCcontext_IncRef(context);
+ else
+ {
+ LockLists();
+ context = ATOMIC_LOAD_SEQ(&GlobalContext);
+ if(context) ALCcontext_IncRef(context);
+ UnlockLists();
+ }
+
+ return context;
+}
+
+
+void AllocateVoices(ALCcontext *context, ALsizei num_voices, ALsizei old_sends)
+{
+ ALCdevice *device = context->Device;
+ ALsizei num_sends = device->NumAuxSends;
+ struct ALvoiceProps *props;
+ size_t sizeof_props;
+ size_t sizeof_voice;
+ ALvoice **voices;
+ ALvoice *voice;
+ ALsizei v = 0;
+ size_t size;
+
+ if(num_voices == context->MaxVoices && num_sends == old_sends)
+ return;
+
+ /* Allocate the voice pointers, voices, and the voices' stored source
+ * property set (including the dynamically-sized Send[] array) in one
+ * chunk.
+ */
+ sizeof_voice = RoundUp(FAM_SIZE(ALvoice, Send, num_sends), 16);
+ sizeof_props = RoundUp(FAM_SIZE(struct ALvoiceProps, Send, num_sends), 16);
+ size = sizeof(ALvoice*) + sizeof_voice + sizeof_props;
+
+ voices = static_cast<ALvoice**>(al_calloc(16, RoundUp(size*num_voices, 16)));
+ /* The voice and property objects are stored interleaved since they're
+ * paired together.
+ */
+ voice = (ALvoice*)((char*)voices + RoundUp(num_voices*sizeof(ALvoice*), 16));
+ props = (struct ALvoiceProps*)((char*)voice + sizeof_voice);
+
+ if(context->Voices)
+ {
+ const ALsizei v_count = mini(context->VoiceCount, num_voices);
+ const ALsizei s_count = mini(old_sends, num_sends);
+
+ for(;v < v_count;v++)
+ {
+ ALvoice *old_voice = context->Voices[v];
+ ALsizei i;
+
+ /* Copy the old voice data and source property set to the new
+ * storage.
+ */
+ memcpy(voice, old_voice, sizeof(*voice));
+ for(i = 0;i < s_count;i++)
+ voice->Send[i] = old_voice->Send[i];
+
+ memcpy(props, old_voice->Props, sizeof(*props));
+ for(i = 0;i < s_count;i++)
+ props->Send[i] = old_voice->Props->Send[i];
+
+ /* Set this voice's property set pointer and voice reference. */
+ voice->Props = props;
+ voices[v] = voice;
+
+ /* Increment pointers to the next storage space. */
+ voice = (ALvoice*)((char*)props + sizeof_props);
+ props = (struct ALvoiceProps*)((char*)voice + sizeof_voice);
+ }
+ /* Deinit any left over voices that weren't copied over to the new
+ * array. NOTE: If this does anything, v equals num_voices and
+ * num_voices is less than VoiceCount, so the following loop won't do
+ * anything.
+ */
+ for(;v < context->VoiceCount;v++)
+ DeinitVoice(context->Voices[v]);
+ }
+ /* Finish setting the voices' property set pointers and references. */
+ for(;v < num_voices;v++)
+ {
+ ATOMIC_INIT(&voice->Update, static_cast<ALvoiceProps*>(nullptr));
+
+ voice->Props = props;
+ voices[v] = voice;
+
+ voice = (ALvoice*)((char*)props + sizeof_props);
+ props = (struct ALvoiceProps*)((char*)voice + sizeof_voice);
+ }
+
+ al_free(context->Voices);
+ context->Voices = voices;
+ context->MaxVoices = num_voices;
+ context->VoiceCount = mini(context->VoiceCount, num_voices);
+}
+
+
+/************************************************
+ * 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_SEQ(&device->LastError, ALC_NO_ERROR);
+ ALCdevice_DecRef(device);
+ }
+ else
+ errorCode = ATOMIC_EXCHANGE_SEQ(&LastNullDeviceError, ALC_NO_ERROR);
+
+ return errorCode;
+}
+
+
+/* alcSuspendContext
+ *
+ * Suspends updates for the given context
+ */
+ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *context)
+{
+ if(!SuspendDefers)
+ return;
+
+ if(!VerifyContext(&context))
+ alcSetError(nullptr, ALC_INVALID_CONTEXT);
+ else
+ {
+ ALCcontext_DeferUpdates(context);
+ ALCcontext_DecRef(context);
+ }
+}
+
+/* alcProcessContext
+ *
+ * Resumes processing updates for the given context
+ */
+ALC_API ALCvoid ALC_APIENTRY alcProcessContext(ALCcontext *context)
+{
+ if(!SuspendDefers)
+ return;
+
+ if(!VerifyContext(&context))
+ alcSetError(nullptr, ALC_INVALID_CONTEXT);
+ else
+ {
+ ALCcontext_ProcessUpdates(context);
+ ALCcontext_DecRef(context);
+ }
+}
+
+
+/* alcGetString
+ *
+ * Returns information about the device, and error strings
+ */
+ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param)
+{
+ const ALCchar *value = nullptr;
+
+ 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 = alstr_get_cstr(Device->DeviceName);
+ ALCdevice_DecRef(Device);
+ }
+ else
+ {
+ ProbeAllDevicesList();
+ value = alstr_get_cstr(alcAllDevicesList);
+ }
+ break;
+
+ case ALC_CAPTURE_DEVICE_SPECIFIER:
+ if(VerifyDevice(&Device))
+ {
+ value = alstr_get_cstr(Device->DeviceName);
+ ALCdevice_DecRef(Device);
+ }
+ else
+ {
+ ProbeCaptureDeviceList();
+ value = alstr_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(alstr_empty(alcAllDevicesList))
+ ProbeAllDevicesList();
+
+ VerifyDevice(&Device);
+
+ free(alcDefaultAllDevicesSpecifier);
+ alcDefaultAllDevicesSpecifier = strdup(alstr_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(alstr_empty(alcCaptureDeviceList))
+ ProbeCaptureDeviceList();
+
+ VerifyDevice(&Device);
+
+ free(alcCaptureDefaultDeviceSpecifier);
+ alcCaptureDefaultDeviceSpecifier = strdup(alstr_get_cstr(alcCaptureDeviceList));
+ 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;
+
+ case ALC_HRTF_SPECIFIER_SOFT:
+ if(!VerifyDevice(&Device))
+ alcSetError(nullptr, ALC_INVALID_DEVICE);
+ else
+ {
+ almtx_lock(&Device->BackendLock);
+ value = ((Device->HrtfHandle && Device->HrtfName) ? Device->HrtfName : "");
+ almtx_unlock(&Device->BackendLock);
+ ALCdevice_DecRef(Device);
+ }
+ break;
+
+ default:
+ VerifyDevice(&Device);
+ alcSetError(Device, ALC_INVALID_ENUM);
+ if(Device) ALCdevice_DecRef(Device);
+ break;
+ }
+
+ return value;
+}
+
+
+static inline ALCsizei NumAttrsForDevice(ALCdevice *device)
+{
+ if(device->Type == Capture) return 9;
+ if(device->Type != Loopback) return 29;
+ if(device->FmtChans == DevFmtAmbi3D)
+ return 35;
+ return 29;
+}
+
+static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
+{
+ ALCsizei i;
+
+ if(size <= 0 || values == nullptr)
+ {
+ 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:
+ case ALC_AMBISONIC_LAYOUT_SOFT:
+ case ALC_AMBISONIC_SCALING_SOFT:
+ case ALC_AMBISONIC_ORDER_SOFT:
+ case ALC_MAX_AMBISONIC_ORDER_SOFT:
+ alcSetError(nullptr, ALC_INVALID_DEVICE);
+ return 0;
+
+ default:
+ alcSetError(nullptr, ALC_INVALID_ENUM);
+ return 0;
+ }
+ return 0;
+ }
+
+ if(device->Type == Capture)
+ {
+ switch(param)
+ {
+ case ALC_ATTRIBUTES_SIZE:
+ values[0] = NumAttrsForDevice(device);
+ return 1;
+
+ case ALC_ALL_ATTRIBUTES:
+ if(size < NumAttrsForDevice(device))
+ {
+ alcSetError(device, ALC_INVALID_VALUE);
+ return 0;
+ }
+
+ i = 0;
+ almtx_lock(&device->BackendLock);
+ values[i++] = ALC_MAJOR_VERSION;
+ values[i++] = alcMajorVersion;
+ values[i++] = ALC_MINOR_VERSION;
+ values[i++] = alcMinorVersion;
+ values[i++] = ALC_CAPTURE_SAMPLES;
+ values[i++] = V0(device->Backend,availableSamples)();
+ values[i++] = ALC_CONNECTED;
+ values[i++] = ATOMIC_LOAD(&device->Connected, almemory_order_relaxed);
+ almtx_unlock(&device->BackendLock);
+
+ values[i++] = 0;
+ return i;
+
+ case ALC_MAJOR_VERSION:
+ values[0] = alcMajorVersion;
+ return 1;
+ case ALC_MINOR_VERSION:
+ values[0] = alcMinorVersion;
+ return 1;
+
+ case ALC_CAPTURE_SAMPLES:
+ almtx_lock(&device->BackendLock);
+ values[0] = V0(device->Backend,availableSamples)();
+ almtx_unlock(&device->BackendLock);
+ return 1;
+
+ case ALC_CONNECTED:
+ values[0] = ATOMIC_LOAD(&device->Connected, almemory_order_acquire);
+ return 1;
+
+ default:
+ alcSetError(device, ALC_INVALID_ENUM);
+ return 0;
+ }
+ return 0;
+ }
+
+ /* render device */
+ switch(param)
+ {
+ case ALC_ATTRIBUTES_SIZE:
+ values[0] = NumAttrsForDevice(device);
+ return 1;
+
+ case ALC_ALL_ATTRIBUTES:
+ if(size < NumAttrsForDevice(device))
+ {
+ alcSetError(device, ALC_INVALID_VALUE);
+ return 0;
+ }
+
+ i = 0;
+ almtx_lock(&device->BackendLock);
+ values[i++] = ALC_MAJOR_VERSION;
+ values[i++] = alcMajorVersion;
+ values[i++] = ALC_MINOR_VERSION;
+ values[i++] = alcMinorVersion;
+ values[i++] = ALC_EFX_MAJOR_VERSION;
+ values[i++] = alcEFXMajorVersion;
+ values[i++] = ALC_EFX_MINOR_VERSION;
+ values[i++] = alcEFXMinorVersion;
+
+ 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
+ {
+ if(device->FmtChans == DevFmtAmbi3D)
+ {
+ values[i++] = ALC_AMBISONIC_LAYOUT_SOFT;
+ values[i++] = device->AmbiLayout;
+
+ values[i++] = ALC_AMBISONIC_SCALING_SOFT;
+ values[i++] = device->AmbiScale;
+
+ values[i++] = ALC_AMBISONIC_ORDER_SOFT;
+ values[i++] = device->AmbiOrder;
+ }
+
+ 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->HrtfHandle ? ALC_TRUE : ALC_FALSE);
+
+ values[i++] = ALC_HRTF_STATUS_SOFT;
+ values[i++] = device->HrtfStatus;
+
+ values[i++] = ALC_OUTPUT_LIMITER_SOFT;
+ values[i++] = device->Limiter ? ALC_TRUE : ALC_FALSE;
+
+ values[i++] = ALC_MAX_AMBISONIC_ORDER_SOFT;
+ values[i++] = MAX_AMBI_ORDER;
+ almtx_unlock(&device->BackendLock);
+
+ values[i++] = 0;
+ return i;
+
+ 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_FREQUENCY:
+ values[0] = device->Frequency;
+ return 1;
+
+ case ALC_REFRESH:
+ if(device->Type == Loopback)
+ {
+ alcSetError(device, ALC_INVALID_DEVICE);
+ return 0;
+ }
+ almtx_lock(&device->BackendLock);
+ values[0] = device->Frequency / device->UpdateSize;
+ almtx_unlock(&device->BackendLock);
+ 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_AMBISONIC_LAYOUT_SOFT:
+ if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D)
+ {
+ alcSetError(device, ALC_INVALID_DEVICE);
+ return 0;
+ }
+ values[0] = device->AmbiLayout;
+ return 1;
+
+ case ALC_AMBISONIC_SCALING_SOFT:
+ if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D)
+ {
+ alcSetError(device, ALC_INVALID_DEVICE);
+ return 0;
+ }
+ values[0] = device->AmbiScale;
+ return 1;
+
+ case ALC_AMBISONIC_ORDER_SOFT:
+ if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D)
+ {
+ alcSetError(device, ALC_INVALID_DEVICE);
+ return 0;
+ }
+ values[0] = device->AmbiOrder;
+ 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] = ATOMIC_LOAD(&device->Connected, almemory_order_acquire);
+ return 1;
+
+ case ALC_HRTF_SOFT:
+ values[0] = (device->HrtfHandle ? ALC_TRUE : ALC_FALSE);
+ return 1;
+
+ case ALC_HRTF_STATUS_SOFT:
+ values[0] = device->HrtfStatus;
+ return 1;
+
+ case ALC_NUM_HRTF_SPECIFIERS_SOFT:
+ almtx_lock(&device->BackendLock);
+ FreeHrtfList(&device->HrtfList);
+ device->HrtfList = EnumerateHrtf(device->DeviceName);
+ values[0] = (ALCint)VECTOR_SIZE(device->HrtfList);
+ almtx_unlock(&device->BackendLock);
+ return 1;
+
+ case ALC_OUTPUT_LIMITER_SOFT:
+ values[0] = device->Limiter ? ALC_TRUE : ALC_FALSE;
+ return 1;
+
+ case ALC_MAX_AMBISONIC_ORDER_SOFT:
+ values[0] = MAX_AMBI_ORDER;
+ 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)
+{
+ VerifyDevice(&device);
+ if(size <= 0 || values == nullptr)
+ 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)
+{
+ ALsizei i;
+
+ VerifyDevice(&device);
+ if(size <= 0 || values == nullptr)
+ alcSetError(device, ALC_INVALID_VALUE);
+ else if(!device || device->Type == Capture)
+ {
+ std::vector<ALCint> ivals(size);
+ size = GetIntegerv(device, pname, size, ivals.data());
+ for(i = 0;i < size;i++)
+ values[i] = ivals[i];
+ }
+ else /* render device */
+ {
+ ClockLatency clock;
+ ALuint64 basecount;
+ ALuint samplecount;
+ ALuint refcount;
+
+ switch(pname)
+ {
+ case ALC_ATTRIBUTES_SIZE:
+ *values = NumAttrsForDevice(device)+4;
+ break;
+
+ case ALC_ALL_ATTRIBUTES:
+ if(size < NumAttrsForDevice(device)+4)
+ alcSetError(device, ALC_INVALID_VALUE);
+ else
+ {
+ i = 0;
+ almtx_lock(&device->BackendLock);
+ 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
+ {
+ if(device->FmtChans == DevFmtAmbi3D)
+ {
+ values[i++] = ALC_AMBISONIC_LAYOUT_SOFT;
+ values[i++] = device->AmbiLayout;
+
+ values[i++] = ALC_AMBISONIC_SCALING_SOFT;
+ values[i++] = device->AmbiScale;
+
+ values[i++] = ALC_AMBISONIC_ORDER_SOFT;
+ values[i++] = device->AmbiOrder;
+ }
+
+ 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->HrtfHandle ? ALC_TRUE : ALC_FALSE);
+
+ values[i++] = ALC_HRTF_STATUS_SOFT;
+ values[i++] = device->HrtfStatus;
+
+ values[i++] = ALC_OUTPUT_LIMITER_SOFT;
+ values[i++] = device->Limiter ? ALC_TRUE : ALC_FALSE;
+
+ clock = GetClockLatency(device);
+ values[i++] = ALC_DEVICE_CLOCK_SOFT;
+ values[i++] = clock.ClockTime;
+
+ values[i++] = ALC_DEVICE_LATENCY_SOFT;
+ values[i++] = clock.Latency;
+ almtx_unlock(&device->BackendLock);
+
+ values[i++] = 0;
+ }
+ break;
+
+ case ALC_DEVICE_CLOCK_SOFT:
+ almtx_lock(&device->BackendLock);
+ do {
+ while(((refcount=ReadRef(&device->MixCount))&1) != 0)
+ althrd_yield();
+ basecount = device->ClockBase;
+ samplecount = device->SamplesDone;
+ } while(refcount != ReadRef(&device->MixCount));
+ *values = basecount + (samplecount*DEVICE_CLOCK_RES/device->Frequency);
+ almtx_unlock(&device->BackendLock);
+ break;
+
+ case ALC_DEVICE_LATENCY_SOFT:
+ almtx_lock(&device->BackendLock);
+ clock = GetClockLatency(device);
+ almtx_unlock(&device->BackendLock);
+ *values = clock.Latency;
+ break;
+
+ case ALC_DEVICE_CLOCK_LATENCY_SOFT:
+ if(size < 2)
+ alcSetError(device, ALC_INVALID_VALUE);
+ else
+ {
+ almtx_lock(&device->BackendLock);
+ clock = GetClockLatency(device);
+ almtx_unlock(&device->BackendLock);
+ values[0] = clock.ClockTime;
+ values[1] = clock.Latency;
+ }
+ break;
+
+ default:
+ std::vector<ALCint> ivals(size);
+ size = GetIntegerv(device, pname, size, ivals.data());
+ for(i = 0;i < size;i++)
+ values[i] = ivals[i];
+ 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;
+
+ 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, ' ')) != nullptr)
+ {
+ 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 = nullptr;
+
+ if(!funcName)
+ {
+ VerifyDevice(&device);
+ alcSetError(device, ALC_INVALID_VALUE);
+ if(device) ALCdevice_DecRef(device);
+ }
+ else
+ {
+ size_t i = 0;
+ for(i = 0;i < COUNTOF(alcFunctions);i++)
+ {
+ if(strcmp(alcFunctions[i].funcName, funcName) == 0)
+ {
+ ptr = alcFunctions[i].address;
+ break;
+ }
+ }
+ }
+
+ 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)
+ {
+ VerifyDevice(&device);
+ alcSetError(device, ALC_INVALID_VALUE);
+ if(device) ALCdevice_DecRef(device);
+ }
+ else
+ {
+ size_t i = 0;
+ for(i = 0;i < COUNTOF(alcEnumerations);i++)
+ {
+ if(strcmp(alcEnumerations[i].enumName, enumName) == 0)
+ {
+ val = alcEnumerations[i].value;
+ break;
+ }
+ }
+ }
+
+ 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;
+ ALfloat valf;
+ ALCenum err;
+
+ /* Explicitly hold the list lock while taking the BackendLock in case the
+ * device is asynchronously destropyed, to ensure this new context is
+ * properly cleaned up after being made.
+ */
+ LockLists();
+ if(!VerifyDevice(&device) || device->Type == Capture ||
+ !ATOMIC_LOAD(&device->Connected, almemory_order_relaxed))
+ {
+ UnlockLists();
+ alcSetError(device, ALC_INVALID_DEVICE);
+ if(device) ALCdevice_DecRef(device);
+ return nullptr;
+ }
+ almtx_lock(&device->BackendLock);
+ UnlockLists();
+
+ ATOMIC_STORE_SEQ(&device->LastError, ALC_NO_ERROR);
+
+ if(device->Type == Playback && DefaultEffect.type != AL_EFFECT_NULL)
+ ALContext = static_cast<ALCcontext*>(al_calloc(16,
+ sizeof(ALCcontext)+sizeof(ALlistener)+sizeof(ALeffectslot)));
+ else
+ ALContext = static_cast<ALCcontext*>(al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener)));
+ if(!ALContext)
+ {
+ almtx_unlock(&device->BackendLock);
+
+ alcSetError(device, ALC_OUT_OF_MEMORY);
+ ALCdevice_DecRef(device);
+ return nullptr;
+ }
+
+ InitRef(&ALContext->ref, 1);
+ ALContext->Listener = (ALlistener*)ALContext->_listener_mem;
+ ALContext->DefaultSlot = nullptr;
+
+ ALContext->Voices = nullptr;
+ ALContext->VoiceCount = 0;
+ ALContext->MaxVoices = 0;
+ ATOMIC_INIT(&ALContext->ActiveAuxSlots, static_cast<ALeffectslotArray*>(nullptr));
+ ALContext->Device = device;
+ ATOMIC_INIT(&ALContext->next, static_cast<ALCcontext*>(nullptr));
+
+ if((err=UpdateDeviceParams(device, attrList)) != ALC_NO_ERROR)
+ {
+ almtx_unlock(&device->BackendLock);
+
+ al_free(ALContext);
+ ALContext = nullptr;
+
+ alcSetError(device, err);
+ if(err == ALC_INVALID_DEVICE)
+ {
+ V0(device->Backend,lock)();
+ aluHandleDisconnect(device, "Device update failure");
+ V0(device->Backend,unlock)();
+ }
+ ALCdevice_DecRef(device);
+ return nullptr;
+ }
+ AllocateVoices(ALContext, 256, device->NumAuxSends);
+
+ if(DefaultEffect.type != AL_EFFECT_NULL && device->Type == Playback)
+ {
+ ALContext->DefaultSlot = (ALeffectslot*)(ALContext->_listener_mem + sizeof(ALlistener));
+ if(InitEffectSlot(ALContext->DefaultSlot) == AL_NO_ERROR)
+ aluInitEffectPanning(ALContext->DefaultSlot);
+ else
+ {
+ ALContext->DefaultSlot = nullptr;
+ ERR("Failed to initialize the default effect slot\n");
+ }
+ }
+
+ ALCdevice_IncRef(ALContext->Device);
+ InitContext(ALContext);
+
+ if(ConfigValueFloat(alstr_get_cstr(device->DeviceName), nullptr, "volume-adjust", &valf))
+ {
+ if(!isfinite(valf))
+ ERR("volume-adjust must be finite: %f\n", valf);
+ else
+ {
+ ALfloat db = clampf(valf, -24.0f, 24.0f);
+ if(db != valf)
+ WARN("volume-adjust clamped: %f, range: +/-%f\n", valf, 24.0f);
+ ALContext->GainBoost = powf(10.0f, db/20.0f);
+ TRACE("volume-adjust gain: %f\n", ALContext->GainBoost);
+ }
+ }
+ UpdateListenerProps(ALContext);
+
+ {
+ ALCcontext *head = ATOMIC_LOAD_SEQ(&device->ContextList);
+ do {
+ ATOMIC_STORE(&ALContext->next, head, almemory_order_relaxed);
+ } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(&device->ContextList, &head,
+ ALContext) == 0);
+ }
+ almtx_unlock(&device->BackendLock);
+
+ if(ALContext->DefaultSlot)
+ {
+ if(InitializeEffect(ALContext, ALContext->DefaultSlot, &DefaultEffect) == AL_NO_ERROR)
+ UpdateEffectSlotProps(ALContext->DefaultSlot, ALContext);
+ else
+ ERR("Failed to initialize the default effect\n");
+ }
+
+ 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();
+ if(!VerifyContext(&context))
+ {
+ UnlockLists();
+ alcSetError(nullptr, ALC_INVALID_CONTEXT);
+ return;
+ }
+
+ Device = context->Device;
+ if(Device)
+ {
+ almtx_lock(&Device->BackendLock);
+ if(!ReleaseContext(context, Device))
+ {
+ V0(Device->Backend,stop)();
+ Device->Flags &= ~DEVICE_RUNNING;
+ }
+ almtx_unlock(&Device->BackendLock);
+ }
+ UnlockLists();
+
+ ALCcontext_DecRef(context);
+}
+
+
+/* alcGetCurrentContext
+ *
+ * Returns the currently active context on the calling thread
+ */
+ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void)
+{
+ ALCcontext *Context{static_cast<ALCcontext*>(altss_get(LocalContext))};
+ if(!Context) Context = ATOMIC_LOAD_SEQ(&GlobalContext);
+ return Context;
+}
+
+/* alcGetThreadContext
+ *
+ * Returns the currently active thread-local context
+ */
+ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void)
+{
+ return static_cast<ALCcontext*>(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 nullptr */
+ if(context && !VerifyContext(&context))
+ {
+ alcSetError(nullptr, ALC_INVALID_CONTEXT);
+ return ALC_FALSE;
+ }
+ /* context's reference count is already incremented */
+ context = ATOMIC_EXCHANGE_PTR_SEQ(&GlobalContext, context);
+ if(context) ALCcontext_DecRef(context);
+
+ if((context=static_cast<ALCcontext*>(altss_get(LocalContext))) != nullptr)
+ {
+ altss_set(LocalContext, nullptr);
+ 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)
+{
+ /* context must be valid or nullptr */
+ if(context && !VerifyContext(&context))
+ {
+ alcSetError(nullptr, ALC_INVALID_CONTEXT);
+ return ALC_FALSE;
+ }
+ /* context's reference count is already incremented */
+ ALCcontext *old{static_cast<ALCcontext*>(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)
+{
+ if(!VerifyContext(&Context))
+ {
+ alcSetError(nullptr, ALC_INVALID_CONTEXT);
+ return nullptr;
+ }
+ ALCdevice *Device{Context->Device};
+ ALCcontext_DecRef(Context);
+
+ return Device;
+}
+
+
+/* alcOpenDevice
+ *
+ * Opens the named device.
+ */
+ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
+{
+ ALCbackendFactory *factory;
+ const ALCchar *fmt;
+ ALCdevice *device;
+ ALCenum err;
+
+ DO_INITCONFIG();
+
+ if(!PlaybackBackend.name)
+ {
+ alcSetError(nullptr, ALC_INVALID_VALUE);
+ return nullptr;
+ }
+
+ if(deviceName && (!deviceName[0] || strcasecmp(deviceName, alcDefaultName) == 0 || strcasecmp(deviceName, "openal-soft") == 0
+#ifdef _WIN32
+ /* Some old Windows apps hardcode these expecting OpenAL to use a
+ * specific audio API, even when they're not enumerated. Creative's
+ * router effectively ignores them too.
+ */
+ || strcasecmp(deviceName, "DirectSound3D") == 0 || strcasecmp(deviceName, "DirectSound") == 0
+ || strcasecmp(deviceName, "MMSYSTEM") == 0
+#endif
+ ))
+ deviceName = nullptr;
+
+ device = static_cast<ALCdevice*>(al_calloc(16, sizeof(ALCdevice)));
+ if(!device)
+ {
+ alcSetError(nullptr, ALC_OUT_OF_MEMORY);
+ return nullptr;
+ }
+
+ //Validate device
+ InitDevice(device, Playback);
+
+ //Set output format
+ device->FmtChans = DevFmtChannelsDefault;
+ device->FmtType = DevFmtTypeDefault;
+ device->Frequency = DEFAULT_OUTPUT_RATE;
+ device->IsHeadphones = AL_FALSE;
+ device->AmbiLayout = AmbiLayout_Default;
+ device->AmbiScale = AmbiNorm_Default;
+ device->LimiterState = ALC_TRUE;
+ device->NumUpdates = 3;
+ device->UpdateSize = 1024;
+
+ device->SourcesMax = 256;
+ device->AuxiliaryEffectSlotMax = 64;
+ device->NumAuxSends = DEFAULT_SENDS;
+
+ if(ConfigValueStr(deviceName, nullptr, "channels", &fmt))
+ {
+ static const struct {
+ const char name[16];
+ enum DevFmtChannels chans;
+ ALsizei order;
+ } chanlist[] = {
+ { "mono", DevFmtMono, 0 },
+ { "stereo", DevFmtStereo, 0 },
+ { "quad", DevFmtQuad, 0 },
+ { "surround51", DevFmtX51, 0 },
+ { "surround61", DevFmtX61, 0 },
+ { "surround71", DevFmtX71, 0 },
+ { "surround51rear", DevFmtX51Rear, 0 },
+ { "ambi1", DevFmtAmbi3D, 1 },
+ { "ambi2", DevFmtAmbi3D, 2 },
+ { "ambi3", DevFmtAmbi3D, 3 },
+ };
+ size_t i;
+
+ for(i = 0;i < COUNTOF(chanlist);i++)
+ {
+ if(strcasecmp(chanlist[i].name, fmt) == 0)
+ {
+ device->FmtChans = chanlist[i].chans;
+ device->AmbiOrder = chanlist[i].order;
+ device->Flags |= DEVICE_CHANNELS_REQUEST;
+ break;
+ }
+ }
+ if(i == COUNTOF(chanlist))
+ ERR("Unsupported channels: %s\n", fmt);
+ }
+ if(ConfigValueStr(deviceName, nullptr, "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);
+ }
+
+ if(ConfigValueUInt(deviceName, nullptr, "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(deviceName, nullptr, "periods", &device->NumUpdates);
+ device->NumUpdates = clampu(device->NumUpdates, 2, 16);
+
+ ConfigValueUInt(deviceName, nullptr, "period_size", &device->UpdateSize);
+ device->UpdateSize = clampu(device->UpdateSize, 64, 8192);
+ if((CPUCapFlags&(CPU_CAP_SSE|CPU_CAP_NEON)) != 0)
+ device->UpdateSize = (device->UpdateSize+3)&~3;
+
+ ConfigValueUInt(deviceName, nullptr, "sources", &device->SourcesMax);
+ if(device->SourcesMax == 0) device->SourcesMax = 256;
+
+ ConfigValueUInt(deviceName, nullptr, "slots", &device->AuxiliaryEffectSlotMax);
+ if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 64;
+ else device->AuxiliaryEffectSlotMax = minu(device->AuxiliaryEffectSlotMax, INT_MAX);
+
+ if(ConfigValueInt(deviceName, nullptr, "sends", &device->NumAuxSends))
+ device->NumAuxSends = clampi(
+ DEFAULT_SENDS, 0, clampi(device->NumAuxSends, 0, MAX_SENDS)
+ );
+
+ device->NumStereoSources = 1;
+ device->NumMonoSources = device->SourcesMax - device->NumStereoSources;
+
+ factory = PlaybackBackend.getFactory();
+ device->Backend = V(factory,createBackend)(device, ALCbackend_Playback);
+ if(!device->Backend)
+ {
+ FreeDevice(device);
+ alcSetError(nullptr, ALC_OUT_OF_MEMORY);
+ return nullptr;
+ }
+
+ // Find a playback device to open
+ if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR)
+ {
+ FreeDevice(device);
+ alcSetError(nullptr, err);
+ return nullptr;
+ }
+
+ if(ConfigValueStr(alstr_get_cstr(device->DeviceName), nullptr, "ambi-format", &fmt))
+ {
+ if(strcasecmp(fmt, "fuma") == 0)
+ {
+ device->AmbiLayout = AmbiLayout_FuMa;
+ device->AmbiScale = AmbiNorm_FuMa;
+ }
+ else if(strcasecmp(fmt, "acn+sn3d") == 0)
+ {
+ device->AmbiLayout = AmbiLayout_ACN;
+ device->AmbiScale = AmbiNorm_SN3D;
+ }
+ else if(strcasecmp(fmt, "acn+n3d") == 0)
+ {
+ device->AmbiLayout = AmbiLayout_ACN;
+ device->AmbiScale = AmbiNorm_N3D;
+ }
+ else
+ ERR("Unsupported ambi-format: %s\n", fmt);
+ }
+
+ {
+ ALCdevice *head{DeviceList.load()};
+ do {
+ ATOMIC_STORE(&device->next, head, almemory_order_relaxed);
+ } while(!DeviceList.compare_exchange_weak(head, device));
+ }
+
+ TRACE("Created device %p, \"%s\"\n", device, alstr_get_cstr(device->DeviceName));
+ return device;
+}
+
+/* alcCloseDevice
+ *
+ * Closes the given device.
+ */
+ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
+{
+ LockLists();
+ ALCdevice *iter{DeviceList.load()};
+ do {
+ if(iter == device)
+ break;
+ iter = ATOMIC_LOAD(&iter->next, almemory_order_relaxed);
+ } while(iter != nullptr);
+ if(!iter || iter->Type == Capture)
+ {
+ alcSetError(iter, ALC_INVALID_DEVICE);
+ UnlockLists();
+ return ALC_FALSE;
+ }
+ almtx_lock(&device->BackendLock);
+
+ ALCdevice *origdev{device};
+ ALCdevice *nextdev{ATOMIC_LOAD(&device->next, almemory_order_relaxed)};
+ if(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&DeviceList, &origdev, nextdev))
+ {
+ ALCdevice *list;
+ do {
+ list = origdev;
+ origdev = device;
+ } while(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&list->next, &origdev, nextdev));
+ }
+ UnlockLists();
+
+ ALCcontext *ctx{ATOMIC_LOAD_SEQ(&device->ContextList)};
+ while(ctx != nullptr)
+ {
+ ALCcontext *next = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
+ WARN("Releasing context %p\n", ctx);
+ ReleaseContext(ctx, device);
+ ctx = next;
+ }
+ if((device->Flags&DEVICE_RUNNING))
+ V0(device->Backend,stop)();
+ device->Flags &= ~DEVICE_RUNNING;
+ almtx_unlock(&device->BackendLock);
+
+ ALCdevice_DecRef(device);
+
+ return ALC_TRUE;
+}
+
+
+/************************************************
+ * ALC capture functions
+ ************************************************/
+ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples)
+{
+ ALCbackendFactory *factory;
+ ALCdevice *device = nullptr;
+ ALCenum err;
+
+ DO_INITCONFIG();
+
+ if(!CaptureBackend.name)
+ {
+ alcSetError(nullptr, ALC_INVALID_VALUE);
+ return nullptr;
+ }
+
+ if(samples <= 0)
+ {
+ alcSetError(nullptr, ALC_INVALID_VALUE);
+ return nullptr;
+ }
+
+ if(deviceName && (!deviceName[0] || strcasecmp(deviceName, alcDefaultName) == 0 || strcasecmp(deviceName, "openal-soft") == 0))
+ deviceName = nullptr;
+
+ device = static_cast<ALCdevice*>(al_calloc(16, sizeof(ALCdevice)));
+ if(!device)
+ {
+ alcSetError(nullptr, ALC_OUT_OF_MEMORY);
+ return nullptr;
+ }
+
+ //Validate device
+ InitDevice(device, Capture);
+
+ device->Frequency = frequency;
+ device->Flags |= DEVICE_FREQUENCY_REQUEST;
+
+ if(DecomposeDevFormat(format, &device->FmtChans, &device->FmtType) == AL_FALSE)
+ {
+ FreeDevice(device);
+ alcSetError(nullptr, ALC_INVALID_ENUM);
+ return nullptr;
+ }
+ device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_SAMPLE_TYPE_REQUEST;
+ device->IsHeadphones = AL_FALSE;
+ device->AmbiOrder = 0;
+ device->AmbiLayout = AmbiLayout_Default;
+ device->AmbiScale = AmbiNorm_Default;
+
+ device->UpdateSize = samples;
+ device->NumUpdates = 1;
+
+ factory = CaptureBackend.getFactory();
+ device->Backend = V(factory,createBackend)(device, ALCbackend_Capture);
+ if(!device->Backend)
+ {
+ FreeDevice(device);
+ alcSetError(nullptr, ALC_OUT_OF_MEMORY);
+ return nullptr;
+ }
+
+ TRACE("Capture format: %s, %s, %uhz, %u update size x%d\n",
+ DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+ device->Frequency, device->UpdateSize, device->NumUpdates
+ );
+ if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR)
+ {
+ FreeDevice(device);
+ alcSetError(nullptr, err);
+ return nullptr;
+ }
+
+ {
+ ALCdevice *head{DeviceList.load()};
+ do {
+ ATOMIC_STORE(&device->next, head, almemory_order_relaxed);
+ } while(!DeviceList.compare_exchange_weak(head, device));
+ }
+
+ TRACE("Created device %p, \"%s\"\n", device, alstr_get_cstr(device->DeviceName));
+ return device;
+}
+
+ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
+{
+
+ LockLists();
+ ALCdevice *iter{DeviceList.load()};
+ do {
+ if(iter == device)
+ break;
+ iter = ATOMIC_LOAD(&iter->next, almemory_order_relaxed);
+ } while(iter != nullptr);
+ if(!iter || iter->Type != Capture)
+ {
+ alcSetError(iter, ALC_INVALID_DEVICE);
+ UnlockLists();
+ return ALC_FALSE;
+ }
+
+ ALCdevice *origdev{device};
+ ALCdevice *nextdev{ATOMIC_LOAD(&device->next, almemory_order_relaxed)};
+ if(!DeviceList.compare_exchange_strong(origdev, nextdev))
+ {
+ ALCdevice *list;
+ do {
+ list = origdev;
+ origdev = device;
+ } while(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&list->next, &origdev, nextdev));
+ }
+ UnlockLists();
+
+ almtx_lock(&device->BackendLock);
+ if((device->Flags&DEVICE_RUNNING))
+ V0(device->Backend,stop)();
+ device->Flags &= ~DEVICE_RUNNING;
+ almtx_unlock(&device->BackendLock);
+
+ ALCdevice_DecRef(device);
+
+ return ALC_TRUE;
+}
+
+ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
+{
+ if(!VerifyDevice(&device) || device->Type != Capture)
+ alcSetError(device, ALC_INVALID_DEVICE);
+ else
+ {
+ almtx_lock(&device->BackendLock);
+ if(!ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
+ alcSetError(device, ALC_INVALID_DEVICE);
+ else if(!(device->Flags&DEVICE_RUNNING))
+ {
+ if(V0(device->Backend,start)())
+ device->Flags |= DEVICE_RUNNING;
+ else
+ {
+ aluHandleDisconnect(device, "Device start failure");
+ alcSetError(device, ALC_INVALID_DEVICE);
+ }
+ }
+ almtx_unlock(&device->BackendLock);
+ }
+
+ if(device) ALCdevice_DecRef(device);
+}
+
+ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
+{
+ if(!VerifyDevice(&device) || device->Type != Capture)
+ alcSetError(device, ALC_INVALID_DEVICE);
+ else
+ {
+ almtx_lock(&device->BackendLock);
+ if((device->Flags&DEVICE_RUNNING))
+ V0(device->Backend,stop)();
+ device->Flags &= ~DEVICE_RUNNING;
+ almtx_unlock(&device->BackendLock);
+ }
+
+ if(device) ALCdevice_DecRef(device);
+}
+
+ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
+{
+ if(!VerifyDevice(&device) || device->Type != Capture)
+ alcSetError(device, ALC_INVALID_DEVICE);
+ else
+ {
+ ALCenum err = ALC_INVALID_VALUE;
+
+ almtx_lock(&device->BackendLock);
+ if(samples >= 0 && V0(device->Backend,availableSamples)() >= (ALCuint)samples)
+ err = V(device->Backend,captureSamples)(buffer, samples);
+ almtx_unlock(&device->BackendLock);
+
+ 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(nullptr, ALC_INVALID_VALUE);
+ return nullptr;
+ }
+
+ device = static_cast<ALCdevice*>(al_calloc(16, sizeof(ALCdevice)));
+ if(!device)
+ {
+ alcSetError(nullptr, ALC_OUT_OF_MEMORY);
+ return nullptr;
+ }
+
+ //Validate device
+ InitDevice(device, Loopback);
+
+ device->SourcesMax = 256;
+ device->AuxiliaryEffectSlotMax = 64;
+ device->NumAuxSends = DEFAULT_SENDS;
+
+ //Set output format
+ device->NumUpdates = 0;
+ device->UpdateSize = 0;
+
+ device->Frequency = DEFAULT_OUTPUT_RATE;
+ device->FmtChans = DevFmtChannelsDefault;
+ device->FmtType = DevFmtTypeDefault;
+ device->IsHeadphones = AL_FALSE;
+ device->AmbiLayout = AmbiLayout_Default;
+ device->AmbiScale = AmbiNorm_Default;
+
+ ConfigValueUInt(nullptr, nullptr, "sources", &device->SourcesMax);
+ if(device->SourcesMax == 0) device->SourcesMax = 256;
+
+ ConfigValueUInt(nullptr, nullptr, "slots", &device->AuxiliaryEffectSlotMax);
+ if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 64;
+ else device->AuxiliaryEffectSlotMax = minu(device->AuxiliaryEffectSlotMax, INT_MAX);
+
+ if(ConfigValueInt(nullptr, nullptr, "sends", &device->NumAuxSends))
+ device->NumAuxSends = clampi(
+ DEFAULT_SENDS, 0, clampi(device->NumAuxSends, 0, MAX_SENDS)
+ );
+
+ device->NumStereoSources = 1;
+ device->NumMonoSources = device->SourcesMax - device->NumStereoSources;
+
+ factory = ALCloopbackFactory_getFactory();
+ device->Backend = V(factory,createBackend)(device, ALCbackend_Loopback);
+ if(!device->Backend)
+ {
+ al_free(device);
+ alcSetError(nullptr, ALC_OUT_OF_MEMORY);
+ return nullptr;
+ }
+
+ // Open the "backend"
+ V(device->Backend,open)("Loopback");
+
+ {
+ ALCdevice *head{DeviceList.load()};
+ do {
+ ATOMIC_STORE(&device->next, head, almemory_order_relaxed);
+ } while(!DeviceList.compare_exchange_weak(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(!VerifyDevice(&device) || device->Type != Loopback)
+ alcSetError(device, ALC_INVALID_DEVICE);
+ else if(freq <= 0)
+ alcSetError(device, ALC_INVALID_VALUE);
+ else
+ {
+ if(IsValidALCType(type) && IsValidALCChannels(channels) && 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(!VerifyDevice(&device) || device->Type != Loopback)
+ alcSetError(device, ALC_INVALID_DEVICE);
+ else if(samples < 0 || (samples > 0 && buffer == nullptr))
+ alcSetError(device, ALC_INVALID_VALUE);
+ else
+ {
+ V0(device->Backend,lock)();
+ aluMixData(device, buffer, samples);
+ V0(device->Backend,unlock)();
+ }
+ 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(!VerifyDevice(&device) || device->Type != Playback)
+ alcSetError(device, ALC_INVALID_DEVICE);
+ else
+ {
+ almtx_lock(&device->BackendLock);
+ if((device->Flags&DEVICE_RUNNING))
+ V0(device->Backend,stop)();
+ device->Flags &= ~DEVICE_RUNNING;
+ device->Flags |= DEVICE_PAUSED;
+ almtx_unlock(&device->BackendLock);
+ }
+ if(device) ALCdevice_DecRef(device);
+}
+
+/* alcDeviceResumeSOFT
+ *
+ * Resume the DSP to restart audio processing.
+ */
+ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device)
+{
+ if(!VerifyDevice(&device) || device->Type != Playback)
+ alcSetError(device, ALC_INVALID_DEVICE);
+ else
+ {
+ almtx_lock(&device->BackendLock);
+ if((device->Flags&DEVICE_PAUSED))
+ {
+ device->Flags &= ~DEVICE_PAUSED;
+ if(ATOMIC_LOAD_SEQ(&device->ContextList) != nullptr)
+ {
+ if(V0(device->Backend,start)() != ALC_FALSE)
+ device->Flags |= DEVICE_RUNNING;
+ else
+ {
+ V0(device->Backend,lock)();
+ aluHandleDisconnect(device, "Device start failure");
+ V0(device->Backend,unlock)();
+ alcSetError(device, ALC_INVALID_DEVICE);
+ }
+ }
+ }
+ almtx_unlock(&device->BackendLock);
+ }
+ if(device) ALCdevice_DecRef(device);
+}
+
+
+/************************************************
+ * ALC HRTF functions
+ ************************************************/
+
+/* alcGetStringiSOFT
+ *
+ * Gets a string parameter at the given index.
+ */
+ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index)
+{
+ const ALCchar *str = nullptr;
+
+ if(!VerifyDevice(&device) || device->Type == Capture)
+ alcSetError(device, ALC_INVALID_DEVICE);
+ else switch(paramName)
+ {
+ case ALC_HRTF_SPECIFIER_SOFT:
+ if(index >= 0 && (size_t)index < VECTOR_SIZE(device->HrtfList))
+ str = VECTOR_ELEM(device->HrtfList, index).name;
+ else
+ alcSetError(device, ALC_INVALID_VALUE);
+ break;
+
+ default:
+ alcSetError(device, ALC_INVALID_ENUM);
+ break;
+ }
+ if(device) ALCdevice_DecRef(device);
+
+ return str;
+}
+
+/* alcResetDeviceSOFT
+ *
+ * Resets the given device output, using the specified attribute list.
+ */
+ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs)
+{
+ ALCenum err;
+
+ LockLists();
+ if(!VerifyDevice(&device) || device->Type == Capture ||
+ !ATOMIC_LOAD(&device->Connected, almemory_order_relaxed))
+ {
+ UnlockLists();
+ alcSetError(device, ALC_INVALID_DEVICE);
+ if(device) ALCdevice_DecRef(device);
+ return ALC_FALSE;
+ }
+ almtx_lock(&device->BackendLock);
+ UnlockLists();
+
+ err = UpdateDeviceParams(device, attribs);
+ almtx_unlock(&device->BackendLock);
+
+ if(err != ALC_NO_ERROR)
+ {
+ alcSetError(device, err);
+ if(err == ALC_INVALID_DEVICE)
+ {
+ V0(device->Backend,lock)();
+ aluHandleDisconnect(device, "Device start failure");
+ V0(device->Backend,unlock)();
+ }
+ ALCdevice_DecRef(device);
+ return ALC_FALSE;
+ }
+ ALCdevice_DecRef(device);
+
+ return ALC_TRUE;
+}