aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2019-07-28 18:56:04 -0700
committerChris Robinson <[email protected]>2019-07-28 18:56:04 -0700
commitcb3e96e75640730b9391f0d2d922eecd9ee2ce79 (patch)
tree23520551bddb2a80354e44da47f54201fdc084f0 /Alc
parent93e60919c8f387c36c267ca9faa1ac653254aea6 (diff)
Rename Alc to alc
Diffstat (limited to 'Alc')
-rw-r--r--Alc/alc.cpp4342
-rw-r--r--Alc/alcmain.h534
-rw-r--r--Alc/alconfig.cpp545
-rw-r--r--Alc/alconfig.h20
-rw-r--r--Alc/alcontext.h217
-rw-r--r--Alc/alu.cpp1798
-rw-r--r--Alc/alu.h466
-rw-r--r--Alc/ambdec.cpp436
-rw-r--r--Alc/ambdec.h48
-rw-r--r--Alc/ambidefs.h119
-rw-r--r--Alc/backends/alsa.cpp1288
-rw-r--r--Alc/backends/alsa.h19
-rw-r--r--Alc/backends/base.cpp58
-rw-r--r--Alc/backends/base.h78
-rw-r--r--Alc/backends/coreaudio.cpp709
-rw-r--r--Alc/backends/coreaudio.h19
-rw-r--r--Alc/backends/dsound.cpp938
-rw-r--r--Alc/backends/dsound.h19
-rw-r--r--Alc/backends/jack.cpp562
-rw-r--r--Alc/backends/jack.h19
-rw-r--r--Alc/backends/loopback.cpp80
-rw-r--r--Alc/backends/loopback.h19
-rw-r--r--Alc/backends/null.cpp184
-rw-r--r--Alc/backends/null.h19
-rw-r--r--Alc/backends/opensl.cpp936
-rw-r--r--Alc/backends/opensl.h19
-rw-r--r--Alc/backends/oss.cpp751
-rw-r--r--Alc/backends/oss.h19
-rw-r--r--Alc/backends/portaudio.cpp463
-rw-r--r--Alc/backends/portaudio.h19
-rw-r--r--Alc/backends/pulseaudio.cpp1532
-rw-r--r--Alc/backends/pulseaudio.h19
-rw-r--r--Alc/backends/qsa.cpp953
-rw-r--r--Alc/backends/qsa.h19
-rw-r--r--Alc/backends/sdl2.cpp223
-rw-r--r--Alc/backends/sdl2.h19
-rw-r--r--Alc/backends/sndio.cpp495
-rw-r--r--Alc/backends/sndio.h19
-rw-r--r--Alc/backends/solaris.cpp302
-rw-r--r--Alc/backends/solaris.h19
-rw-r--r--Alc/backends/wasapi.cpp1763
-rw-r--r--Alc/backends/wasapi.h19
-rw-r--r--Alc/backends/wave.cpp402
-rw-r--r--Alc/backends/wave.h19
-rw-r--r--Alc/backends/winmm.cpp640
-rw-r--r--Alc/backends/winmm.h19
-rw-r--r--Alc/bformatdec.cpp200
-rw-r--r--Alc/bformatdec.h62
-rw-r--r--Alc/bs2b.cpp188
-rw-r--r--Alc/bs2b.h90
-rw-r--r--Alc/compat.h121
-rw-r--r--Alc/converter.cpp367
-rw-r--r--Alc/converter.h70
-rw-r--r--Alc/cpu_caps.h16
-rw-r--r--Alc/effects/autowah.cpp298
-rw-r--r--Alc/effects/base.h196
-rw-r--r--Alc/effects/chorus.cpp538
-rw-r--r--Alc/effects/compressor.cpp222
-rw-r--r--Alc/effects/dedicated.cpp159
-rw-r--r--Alc/effects/distortion.cpp269
-rw-r--r--Alc/effects/echo.cpp271
-rw-r--r--Alc/effects/equalizer.cpp337
-rw-r--r--Alc/effects/fshifter.cpp301
-rw-r--r--Alc/effects/modulator.cpp279
-rw-r--r--Alc/effects/null.cpp164
-rw-r--r--Alc/effects/pshifter.cpp405
-rw-r--r--Alc/effects/reverb.cpp2102
-rw-r--r--Alc/effects/vmorpher.cpp430
-rw-r--r--Alc/filters/biquad.cpp127
-rw-r--r--Alc/filters/biquad.h113
-rw-r--r--Alc/filters/nfc.cpp391
-rw-r--r--Alc/filters/nfc.h58
-rw-r--r--Alc/filters/splitter.cpp115
-rw-r--r--Alc/filters/splitter.h50
-rw-r--r--Alc/fpu_modes.h25
-rw-r--r--Alc/helpers.cpp851
-rw-r--r--Alc/hrtf.cpp1400
-rw-r--r--Alc/hrtf.h124
-rw-r--r--Alc/inprogext.h92
-rw-r--r--Alc/logging.h64
-rw-r--r--Alc/mastering.cpp479
-rw-r--r--Alc/mastering.h104
-rw-r--r--Alc/mixer/defs.h59
-rw-r--r--Alc/mixer/hrtfbase.h138
-rw-r--r--Alc/mixer/mixer_c.cpp208
-rw-r--r--Alc/mixer/mixer_neon.cpp307
-rw-r--r--Alc/mixer/mixer_sse.cpp262
-rw-r--r--Alc/mixer/mixer_sse2.cpp84
-rw-r--r--Alc/mixer/mixer_sse3.cpp0
-rw-r--r--Alc/mixer/mixer_sse41.cpp85
-rw-r--r--Alc/mixvoice.cpp954
-rw-r--r--Alc/panning.cpp964
-rw-r--r--Alc/ringbuffer.cpp253
-rw-r--r--Alc/ringbuffer.h99
-rw-r--r--Alc/uhjfilter.cpp131
-rw-r--r--Alc/uhjfilter.h54
-rw-r--r--Alc/vector.h15
97 files changed, 0 insertions, 35896 deletions
diff --git a/Alc/alc.cpp b/Alc/alc.cpp
deleted file mode 100644
index 00f90d91..00000000
--- a/Alc/alc.cpp
+++ /dev/null
@@ -1,4342 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 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 <exception>
-#include <algorithm>
-#include <array>
-#include <atomic>
-#include <cctype>
-#include <chrono>
-#include <climits>
-#include <cmath>
-#include <csignal>
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <functional>
-#include <iterator>
-#include <limits>
-#include <memory>
-#include <mutex>
-#include <new>
-#include <numeric>
-#include <string>
-#include <thread>
-#include <utility>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "AL/alext.h"
-#include "AL/efx.h"
-
-#include "alAuxEffectSlot.h"
-#include "alcmain.h"
-#include "alEffect.h"
-#include "alError.h"
-#include "alFilter.h"
-#include "alListener.h"
-#include "alSource.h"
-#include "albyte.h"
-#include "alconfig.h"
-#include "alcontext.h"
-#include "alexcpt.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-#include "aloptional.h"
-#include "alspan.h"
-#include "alu.h"
-#include "ambidefs.h"
-#include "atomic.h"
-#include "bformatdec.h"
-#include "bs2b.h"
-#include "compat.h"
-#include "cpu_caps.h"
-#include "effects/base.h"
-#include "filters/nfc.h"
-#include "filters/splitter.h"
-#include "fpu_modes.h"
-#include "hrtf.h"
-#include "inprogext.h"
-#include "logging.h"
-#include "mastering.h"
-#include "opthelpers.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "uhjfilter.h"
-#include "vecmat.h"
-#include "vector.h"
-
-#include "backends/base.h"
-#include "backends/null.h"
-#include "backends/loopback.h"
-#ifdef HAVE_JACK
-#include "backends/jack.h"
-#endif
-#ifdef HAVE_PULSEAUDIO
-#include "backends/pulseaudio.h"
-#endif
-#ifdef HAVE_ALSA
-#include "backends/alsa.h"
-#endif
-#ifdef HAVE_WASAPI
-#include "backends/wasapi.h"
-#endif
-#ifdef HAVE_COREAUDIO
-#include "backends/coreaudio.h"
-#endif
-#ifdef HAVE_OPENSL
-#include "backends/opensl.h"
-#endif
-#ifdef HAVE_SOLARIS
-#include "backends/solaris.h"
-#endif
-#ifdef HAVE_SNDIO
-#include "backends/sndio.h"
-#endif
-#ifdef HAVE_OSS
-#include "backends/oss.h"
-#endif
-#ifdef HAVE_QSA
-#include "backends/qsa.h"
-#endif
-#ifdef HAVE_DSOUND
-#include "backends/dsound.h"
-#endif
-#ifdef HAVE_WINMM
-#include "backends/winmm.h"
-#endif
-#ifdef HAVE_PORTAUDIO
-#include "backends/portaudio.h"
-#endif
-#ifdef HAVE_SDL2
-#include "backends/sdl2.h"
-#endif
-#ifdef HAVE_WAVE
-#include "backends/wave.h"
-#endif
-
-
-namespace {
-
-using namespace std::placeholders;
-using std::chrono::seconds;
-using std::chrono::nanoseconds;
-
-
-/************************************************
- * Backends
- ************************************************/
-struct BackendInfo {
- const char *name;
- BackendFactory& (*getFactory)(void);
-};
-
-BackendInfo BackendList[] = {
-#ifdef HAVE_JACK
- { "jack", JackBackendFactory::getFactory },
-#endif
-#ifdef HAVE_PULSEAUDIO
- { "pulse", PulseBackendFactory::getFactory },
-#endif
-#ifdef HAVE_ALSA
- { "alsa", AlsaBackendFactory::getFactory },
-#endif
-#ifdef HAVE_WASAPI
- { "wasapi", WasapiBackendFactory::getFactory },
-#endif
-#ifdef HAVE_COREAUDIO
- { "core", CoreAudioBackendFactory::getFactory },
-#endif
-#ifdef HAVE_OPENSL
- { "opensl", OSLBackendFactory::getFactory },
-#endif
-#ifdef HAVE_SOLARIS
- { "solaris", SolarisBackendFactory::getFactory },
-#endif
-#ifdef HAVE_SNDIO
- { "sndio", SndIOBackendFactory::getFactory },
-#endif
-#ifdef HAVE_OSS
- { "oss", OSSBackendFactory::getFactory },
-#endif
-#ifdef HAVE_QSA
- { "qsa", QSABackendFactory::getFactory },
-#endif
-#ifdef HAVE_DSOUND
- { "dsound", DSoundBackendFactory::getFactory },
-#endif
-#ifdef HAVE_WINMM
- { "winmm", WinMMBackendFactory::getFactory },
-#endif
-#ifdef HAVE_PORTAUDIO
- { "port", PortBackendFactory::getFactory },
-#endif
-#ifdef HAVE_SDL2
- { "sdl2", SDL2BackendFactory::getFactory },
-#endif
-
- { "null", NullBackendFactory::getFactory },
-#ifdef HAVE_WAVE
- { "wave", WaveBackendFactory::getFactory },
-#endif
-};
-auto BackendListEnd = std::end(BackendList);
-
-BackendFactory *PlaybackFactory{};
-BackendFactory *CaptureFactory{};
-
-
-/************************************************
- * Functions, enums, and errors
- ************************************************/
-#define DECL(x) { #x, (ALCvoid*)(x) }
-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) }
-constexpr 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),
- DECL(AL_EFFECT_VOCAL_MORPHER),
- 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
-
-constexpr ALCchar alcNoError[] = "No Error";
-constexpr ALCchar alcErrInvalidDevice[] = "Invalid Device";
-constexpr ALCchar alcErrInvalidContext[] = "Invalid Context";
-constexpr ALCchar alcErrInvalidEnum[] = "Invalid Enum";
-constexpr ALCchar alcErrInvalidValue[] = "Invalid Value";
-constexpr ALCchar alcErrOutOfMemory[] = "Out of Memory";
-
-
-/************************************************
- * Global variables
- ************************************************/
-
-/* Enumerated device names */
-constexpr ALCchar alcDefaultName[] = "OpenAL Soft\0";
-
-std::string alcAllDevicesList;
-std::string alcCaptureDeviceList;
-
-/* Default is always the first in the list */
-std::string alcDefaultAllDevicesSpecifier;
-std::string alcCaptureDefaultDeviceSpecifier;
-
-/* Default context extensions */
-constexpr 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_effect_chain "
- "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";
-
-std::atomic<ALCenum> LastNullDeviceError{ALC_NO_ERROR};
-
-/* Thread-local current context */
-void ReleaseThreadCtx(ALCcontext *context)
-{
- auto ref = DecrementRef(&context->ref);
- TRACEREF("ALCcontext %p decreasing refcount to %u\n", context, ref);
- ERR("Context %p current for thread being destroyed, possible leak!\n", context);
-}
-
-std::atomic<void(*)(ALCcontext*)> ThreadCtxProc{ReleaseThreadCtx};
-class ThreadCtx {
- ALCcontext *ctx{nullptr};
-
-public:
- ~ThreadCtx()
- {
- auto destruct = ThreadCtxProc.load();
- if(destruct && ctx)
- destruct(ctx);
- ctx = nullptr;
- }
-
- ALCcontext *get() const noexcept { return ctx; }
- void set(ALCcontext *ctx_) noexcept { ctx = ctx_; }
-};
-thread_local ThreadCtx LocalContext;
-/* Process-wide current context */
-std::atomic<ALCcontext*> GlobalContext{nullptr};
-
-/* Flag to trap ALC device errors */
-bool TrapALCError{false};
-
-/* One-time configuration init control */
-std::once_flag alc_config_once{};
-
-/* Default effect that applies to sources that don't have an effect on send 0 */
-ALeffect DefaultEffect;
-
-/* Flag to specify if alcSuspendContext/alcProcessContext should defer/process
- * updates.
- */
-bool SuspendDefers{true};
-
-
-/************************************************
- * ALC information
- ************************************************/
-constexpr ALCchar alcNoDeviceExtList[] =
- "ALC_ENUMERATE_ALL_EXT "
- "ALC_ENUMERATION_EXT "
- "ALC_EXT_CAPTURE "
- "ALC_EXT_thread_local_context "
- "ALC_SOFT_loopback";
-constexpr 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";
-constexpr ALCint alcMajorVersion = 1;
-constexpr ALCint alcMinorVersion = 1;
-
-constexpr ALCint alcEFXMajorVersion = 1;
-constexpr ALCint alcEFXMinorVersion = 0;
-
-
-/* To avoid extraneous allocations, a 0-sized FlexArray<ALCcontext*> is defined
- * globally as a sharable object.
- */
-al::FlexArray<ALCcontext*> EmptyContextArray{0u};
-
-
-void ALCdevice_IncRef(ALCdevice *device)
-{
- auto ref = IncrementRef(&device->ref);
- TRACEREF("ALCdevice %p increasing refcount to %u\n", device, ref);
-}
-
-void ALCdevice_DecRef(ALCdevice *device)
-{
- auto ref = DecrementRef(&device->ref);
- TRACEREF("ALCdevice %p decreasing refcount to %u\n", device, ref);
- if(UNLIKELY(ref == 0)) delete device;
-}
-
-/* Simple RAII device reference. Takes the reference of the provided ALCdevice,
- * and decrements it when leaving scope. Movable (transfer reference) but not
- * copyable (no new references).
- */
-class DeviceRef {
- ALCdevice *mDev{nullptr};
-
- void reset() noexcept
- {
- if(mDev)
- ALCdevice_DecRef(mDev);
- mDev = nullptr;
- }
-
-public:
- DeviceRef() noexcept = default;
- DeviceRef(DeviceRef&& rhs) noexcept : mDev{rhs.mDev}
- { rhs.mDev = nullptr; }
- explicit DeviceRef(ALCdevice *dev) noexcept : mDev(dev) { }
- ~DeviceRef() { reset(); }
-
- DeviceRef& operator=(const DeviceRef&) = delete;
- DeviceRef& operator=(DeviceRef&& rhs) noexcept
- {
- std::swap(mDev, rhs.mDev);
- return *this;
- }
-
- operator bool() const noexcept { return mDev != nullptr; }
-
- ALCdevice* operator->() const noexcept { return mDev; }
- ALCdevice* get() const noexcept { return mDev; }
-
- ALCdevice* release() noexcept
- {
- ALCdevice *ret{mDev};
- mDev = nullptr;
- return ret;
- }
-};
-
-inline bool operator==(const DeviceRef &lhs, const ALCdevice *rhs) noexcept
-{ return lhs.get() == rhs; }
-inline bool operator!=(const DeviceRef &lhs, const ALCdevice *rhs) noexcept
-{ return !(lhs == rhs); }
-inline bool operator<(const DeviceRef &lhs, const ALCdevice *rhs) noexcept
-{ return lhs.get() < rhs; }
-
-
-/************************************************
- * Device lists
- ************************************************/
-al::vector<DeviceRef> DeviceList;
-al::vector<ContextRef> ContextList;
-
-std::recursive_mutex ListLock;
-
-
-void alc_initconfig(void)
-{
- const char *str{getenv("ALSOFT_LOGLEVEL")};
- if(str)
- {
- long lvl = strtol(str, nullptr, 0);
- if(lvl >= NoLog && lvl <= LogRef)
- gLogLevel = static_cast<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) gLogFile = 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);
- {
- std::string names;
- if(std::begin(BackendList) == BackendListEnd)
- names += "(none)";
- else
- {
- const al::span<const BackendInfo> infos{std::begin(BackendList), BackendListEnd};
- names += infos[0].name;
- for(const auto &backend : infos.subspan(1))
- {
- names += ", ";
- names += backend.name;
- }
- }
- TRACE("Supported backends: %s\n", names.c_str());
- }
- ReadALConfig();
-
- str = getenv("__ALSOFT_SUSPEND_CONTEXT");
- if(str && *str)
- {
- if(strcasecmp(str, "ignore") == 0)
- {
- SuspendDefers = false;
- TRACE("Selected context suspend behavior, \"ignore\"\n");
- }
- else
- ERR("Unhandled context suspend behavior setting: \"%s\"\n", str);
- }
-
- int 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(auto cpuopt = ConfigValueStr(nullptr, nullptr, "disable-cpu-exts"))
- {
- str = cpuopt->c_str();
- if(strcasecmp(str, "all") == 0)
- capfilter = 0;
- else
- {
- const char *next = str;
- do {
- str = next;
- while(isspace(str[0]))
- str++;
- next = strchr(str, ',');
-
- if(!str[0] || str[0] == ',')
- continue;
-
- size_t len{next ? static_cast<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
-#define DEF_MIXER_PRIO 1
-#else
-#define DEF_MIXER_PRIO 0
-#endif
- RTPrioLevel = ConfigValueInt(nullptr, nullptr, "rt-prio").value_or(DEF_MIXER_PRIO);
-#undef DEF_MIXER_PRIO
-
- aluInit();
- aluInitMixer();
-
- str = getenv("ALSOFT_TRAP_ERROR");
- if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
- {
- TrapALError = true;
- TrapALCError = true;
- }
- else
- {
- str = getenv("ALSOFT_TRAP_AL_ERROR");
- if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
- TrapALError = 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 = true;
- TrapALCError = !!GetConfigValueBool(nullptr, nullptr, "trap-alc-error", TrapALCError);
- }
-
- if(auto boostopt = ConfigValueFloat(nullptr, "reverb", "boost"))
- {
- const float valf{std::isfinite(*boostopt) ? clampf(*boostopt, -24.0f, 24.0f) : 0.0f};
- ReverbBoost *= std::pow(10.0f, valf / 20.0f);
- }
-
- auto devopt = ConfigValueStr(nullptr, nullptr, "drivers");
- if(const char *devs{getenv("ALSOFT_DRIVERS")})
- {
- if(devs[0])
- devopt = devs;
- }
- if(devopt)
- {
- auto backendlist_cur = std::begin(BackendList);
-
- bool endlist{true};
- const char *next{devopt->c_str()};
- do {
- const char *devs{next};
- while(isspace(devs[0]))
- devs++;
- next = strchr(devs, ',');
-
- const bool delitem{devs[0] == '-'};
- if(devs[0] == '-') devs++;
-
- if(!devs[0] || devs[0] == ',')
- {
- endlist = false;
- continue;
- }
- endlist = true;
-
- size_t len{next ? (static_cast<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
-
- auto find_backend = [devs,len](const BackendInfo &backend) -> bool
- { return len == strlen(backend.name) && strncmp(backend.name, devs, len) == 0; };
- auto this_backend = std::find_if(std::begin(BackendList), BackendListEnd,
- find_backend);
-
- if(this_backend == BackendListEnd)
- continue;
-
- if(delitem)
- BackendListEnd = std::move(this_backend+1, BackendListEnd, this_backend);
- else
- backendlist_cur = std::rotate(backendlist_cur, this_backend, this_backend+1);
- } while(next++);
-
- if(endlist)
- BackendListEnd = backendlist_cur;
- }
-
- auto init_backend = [](BackendInfo &backend) -> bool
- {
- if(PlaybackFactory && CaptureFactory)
- return true;
-
- BackendFactory &factory = backend.getFactory();
- if(!factory.init())
- {
- WARN("Failed to initialize backend \"%s\"\n", backend.name);
- return true;
- }
-
- TRACE("Initialized backend \"%s\"\n", backend.name);
- if(!PlaybackFactory && factory.querySupport(BackendType::Playback))
- {
- PlaybackFactory = &factory;
- TRACE("Added \"%s\" for playback\n", backend.name);
- }
- if(!CaptureFactory && factory.querySupport(BackendType::Capture))
- {
- CaptureFactory = &factory;
- TRACE("Added \"%s\" for capture\n", backend.name);
- }
- return false;
- };
- BackendListEnd = std::remove_if(std::begin(BackendList), BackendListEnd, init_backend);
-
- LoopbackBackendFactory::getFactory().init();
-
- if(!PlaybackFactory)
- WARN("No playback backend available!\n");
- if(!CaptureFactory)
- WARN("No capture backend available!\n");
-
- if(auto exclopt = ConfigValueStr(nullptr, nullptr, "excludefx"))
- {
- const char *next{exclopt->c_str()};
- do {
- str = next;
- next = strchr(str, ',');
-
- if(!str[0] || next == str)
- continue;
-
- size_t len{next ? static_cast<size_t>(next-str) : strlen(str)};
- for(const EffectList &effectitem : gEffectList)
- {
- if(len == strlen(effectitem.name) &&
- strncmp(effectitem.name, str, len) == 0)
- DisabledEffects[effectitem.type] = AL_TRUE;
- }
- } while(next++);
- }
-
- InitEffect(&DefaultEffect);
- auto defrevopt = ConfigValueStr(nullptr, nullptr, "default-reverb");
- if((str=getenv("ALSOFT_DEFAULT_REVERB")) && str[0])
- defrevopt = str;
- if(defrevopt) LoadReverbPreset(defrevopt->c_str(), &DefaultEffect);
-}
-#define DO_INITCONFIG() std::call_once(alc_config_once, [](){alc_initconfig();})
-
-
-/************************************************
- * Device enumeration
- ************************************************/
-void ProbeAllDevicesList()
-{
- DO_INITCONFIG();
-
- std::lock_guard<std::recursive_mutex> _{ListLock};
- alcAllDevicesList.clear();
- if(PlaybackFactory)
- PlaybackFactory->probe(DevProbe::Playback, &alcAllDevicesList);
-}
-void ProbeCaptureDeviceList()
-{
- DO_INITCONFIG();
-
- std::lock_guard<std::recursive_mutex> _{ListLock};
- alcCaptureDeviceList.clear();
- if(CaptureFactory)
- CaptureFactory->probe(DevProbe::Capture, &alcCaptureDeviceList);
-}
-
-} // namespace
-
-/* Mixing thread piority level */
-ALint RTPrioLevel;
-
-FILE *gLogFile{stderr};
-#ifdef _DEBUG
-LogLevel gLogLevel{LogWarning};
-#else
-LogLevel gLogLevel{LogError};
-#endif
-
-/************************************************
- * Library initialization
- ************************************************/
-#if defined(_WIN32) && !defined(AL_LIBTYPE_STATIC)
-BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/)
-{
- 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*)module, &module);
- break;
-
- case DLL_PROCESS_DETACH:
- break;
- }
- return TRUE;
-}
-#endif
-
-/************************************************
- * Device format information
- ************************************************/
-const ALCchar *DevFmtTypeString(DevFmtType type) noexcept
-{
- 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(DevFmtChannels chans) noexcept
-{
- 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(DevFmtType type) noexcept
-{
- 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(DevFmtChannels chans, ALsizei ambiorder) noexcept
-{
- 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+1) * (ambiorder+1);
- }
- return 0;
-}
-
-struct DevFmtPair { DevFmtChannels chans; DevFmtType type; };
-static al::optional<DevFmtPair> DecomposeDevFormat(ALenum format)
-{
- static const struct {
- ALenum format;
- DevFmtChannels channels;
- 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 },
- };
-
- for(const auto &item : list)
- {
- if(item.format == format)
- return al::make_optional(DevFmtPair{item.channels, item.type});
- }
-
- return al::nullopt;
-}
-
-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)
-{
- device->RealOut.ChannelIndex.fill(-1);
-
- switch(device->FmtChans)
- {
- case DevFmtMono:
- device->RealOut.ChannelIndex[FrontCenter] = 0;
- break;
- case DevFmtStereo:
- device->RealOut.ChannelIndex[FrontLeft] = 0;
- device->RealOut.ChannelIndex[FrontRight] = 1;
- break;
- case DevFmtQuad:
- device->RealOut.ChannelIndex[FrontLeft] = 0;
- device->RealOut.ChannelIndex[FrontRight] = 1;
- device->RealOut.ChannelIndex[BackLeft] = 2;
- device->RealOut.ChannelIndex[BackRight] = 3;
- break;
- case DevFmtX51:
- device->RealOut.ChannelIndex[FrontLeft] = 0;
- device->RealOut.ChannelIndex[FrontRight] = 1;
- device->RealOut.ChannelIndex[FrontCenter] = 2;
- device->RealOut.ChannelIndex[LFE] = 3;
- device->RealOut.ChannelIndex[SideLeft] = 4;
- device->RealOut.ChannelIndex[SideRight] = 5;
- break;
- case DevFmtX51Rear:
- device->RealOut.ChannelIndex[FrontLeft] = 0;
- device->RealOut.ChannelIndex[FrontRight] = 1;
- device->RealOut.ChannelIndex[FrontCenter] = 2;
- device->RealOut.ChannelIndex[LFE] = 3;
- device->RealOut.ChannelIndex[BackLeft] = 4;
- device->RealOut.ChannelIndex[BackRight] = 5;
- break;
- case DevFmtX61:
- device->RealOut.ChannelIndex[FrontLeft] = 0;
- device->RealOut.ChannelIndex[FrontRight] = 1;
- device->RealOut.ChannelIndex[FrontCenter] = 2;
- device->RealOut.ChannelIndex[LFE] = 3;
- device->RealOut.ChannelIndex[BackCenter] = 4;
- device->RealOut.ChannelIndex[SideLeft] = 5;
- device->RealOut.ChannelIndex[SideRight] = 6;
- break;
- case DevFmtX71:
- device->RealOut.ChannelIndex[FrontLeft] = 0;
- device->RealOut.ChannelIndex[FrontRight] = 1;
- device->RealOut.ChannelIndex[FrontCenter] = 2;
- device->RealOut.ChannelIndex[LFE] = 3;
- device->RealOut.ChannelIndex[BackLeft] = 4;
- device->RealOut.ChannelIndex[BackRight] = 5;
- device->RealOut.ChannelIndex[SideLeft] = 6;
- device->RealOut.ChannelIndex[SideRight] = 7;
- break;
- case DevFmtAmbi3D:
- device->RealOut.ChannelIndex[Aux0] = 0;
- if(device->mAmbiOrder > 0)
- {
- device->RealOut.ChannelIndex[Aux1] = 1;
- device->RealOut.ChannelIndex[Aux2] = 2;
- device->RealOut.ChannelIndex[Aux3] = 3;
- }
- if(device->mAmbiOrder > 1)
- {
- device->RealOut.ChannelIndex[Aux4] = 4;
- device->RealOut.ChannelIndex[Aux5] = 5;
- device->RealOut.ChannelIndex[Aux6] = 6;
- device->RealOut.ChannelIndex[Aux7] = 7;
- device->RealOut.ChannelIndex[Aux8] = 8;
- }
- if(device->mAmbiOrder > 2)
- {
- device->RealOut.ChannelIndex[Aux9] = 9;
- device->RealOut.ChannelIndex[Aux10] = 10;
- device->RealOut.ChannelIndex[Aux11] = 11;
- device->RealOut.ChannelIndex[Aux12] = 12;
- device->RealOut.ChannelIndex[Aux13] = 13;
- device->RealOut.ChannelIndex[Aux14] = 14;
- device->RealOut.ChannelIndex[Aux15] = 15;
- }
- break;
- }
-}
-
-/* SetDefaultChannelOrder
- *
- * Sets the default channel order used by most non-WaveFormatEx-based APIs.
- */
-void SetDefaultChannelOrder(ALCdevice *device)
-{
- device->RealOut.ChannelIndex.fill(-1);
-
- switch(device->FmtChans)
- {
- case DevFmtX51Rear:
- device->RealOut.ChannelIndex[FrontLeft] = 0;
- device->RealOut.ChannelIndex[FrontRight] = 1;
- device->RealOut.ChannelIndex[BackLeft] = 2;
- device->RealOut.ChannelIndex[BackRight] = 3;
- device->RealOut.ChannelIndex[FrontCenter] = 4;
- device->RealOut.ChannelIndex[LFE] = 5;
- return;
- case DevFmtX71:
- device->RealOut.ChannelIndex[FrontLeft] = 0;
- device->RealOut.ChannelIndex[FrontRight] = 1;
- device->RealOut.ChannelIndex[BackLeft] = 2;
- device->RealOut.ChannelIndex[BackRight] = 3;
- device->RealOut.ChannelIndex[FrontCenter] = 4;
- device->RealOut.ChannelIndex[LFE] = 5;
- device->RealOut.ChannelIndex[SideLeft] = 6;
- device->RealOut.ChannelIndex[SideRight] = 7;
- 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)
-{
- context->DeferUpdates.store(true);
-}
-
-/* ALCcontext_ProcessUpdates
- *
- * Resumes update processing after being deferred.
- */
-void ALCcontext_ProcessUpdates(ALCcontext *context)
-{
- std::lock_guard<std::mutex> _{context->PropLock};
- if(context->DeferUpdates.exchange(false))
- {
- /* Tell the mixer to stop applying updates, then wait for any active
- * updating to finish, before providing updates.
- */
- context->HoldUpdates.store(true, std::memory_order_release);
- while((context->UpdateCount.load(std::memory_order_acquire)&1) != 0)
- std::this_thread::yield();
-
- if(!context->PropsClean.test_and_set(std::memory_order_acq_rel))
- UpdateContextProps(context);
- if(!context->Listener.PropsClean.test_and_set(std::memory_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.
- */
- context->HoldUpdates.store(false, std::memory_order_release);
- }
-}
-
-
-/* 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)
- device->LastError.store(errorCode);
- else
- LastNullDeviceError.store(errorCode);
-}
-
-
-static std::unique_ptr<Compressor> CreateDeviceLimiter(const ALCdevice *device, const ALfloat threshold)
-{
- return CompressorInit(static_cast<ALuint>(device->RealOut.Buffer.size()), 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 += nanoseconds{seconds{device->SamplesDone}} / 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)
-{
- HrtfRequestMode hrtf_userreq = Hrtf_Default;
- HrtfRequestMode hrtf_appreq = Hrtf_Default;
- ALCenum gainLimiter = device->LimiterState;
- const ALsizei old_sends = device->NumAuxSends;
- ALsizei new_sends = device->NumAuxSends;
- DevFmtChannels oldChans;
- DevFmtType oldType;
- ALboolean update_failed;
- ALCsizei hrtf_id = -1;
- ALCuint oldFreq;
-
- if((!attrList || !attrList[0]) && device->Type == Loopback)
- {
- WARN("Missing attributes for loopback device\n");
- return ALC_INVALID_VALUE;
- }
-
- // Check for attributes
- if(attrList && attrList[0])
- {
- ALCenum alayout{AL_NONE};
- ALCenum ascale{AL_NONE};
- ALCenum schans{AL_NONE};
- ALCenum stype{AL_NONE};
- ALCsizei attrIdx{0};
- ALCsizei aorder{0};
- ALCuint freq{0u};
-
- ALuint numMono{device->NumMonoSources};
- ALuint numStereo{device->NumStereoSources};
- ALsizei numSends{old_sends};
-
-#define TRACE_ATTR(a, v) TRACE("%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);
- break;
-
- case ALC_FORMAT_TYPE_SOFT:
- stype = attrList[attrIdx + 1];
- TRACE_ATTR(ALC_FORMAT_TYPE_SOFT, stype);
- break;
-
- case ALC_FREQUENCY:
- freq = attrList[attrIdx + 1];
- TRACE_ATTR(ALC_FREQUENCY, freq);
- break;
-
- case ALC_AMBISONIC_LAYOUT_SOFT:
- alayout = attrList[attrIdx + 1];
- TRACE_ATTR(ALC_AMBISONIC_LAYOUT_SOFT, alayout);
- break;
-
- case ALC_AMBISONIC_SCALING_SOFT:
- ascale = attrList[attrIdx + 1];
- TRACE_ATTR(ALC_AMBISONIC_SCALING_SOFT, ascale);
- break;
-
- case ALC_AMBISONIC_ORDER_SOFT:
- aorder = attrList[attrIdx + 1];
- TRACE_ATTR(ALC_AMBISONIC_ORDER_SOFT, aorder);
- break;
-
- case ALC_MONO_SOURCES:
- numMono = attrList[attrIdx + 1];
- TRACE_ATTR(ALC_MONO_SOURCES, numMono);
- if(numMono > INT_MAX) numMono = 0;
- break;
-
- case ALC_STEREO_SOURCES:
- numStereo = attrList[attrIdx + 1];
- TRACE_ATTR(ALC_STEREO_SOURCES, numStereo);
- if(numStereo > INT_MAX) 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
-
- const bool loopback{device->Type == Loopback};
- if(loopback)
- {
- if(!schans || !stype || !freq)
- {
- WARN("Missing format for loopback device\n");
- return ALC_INVALID_VALUE;
- }
- if(!IsValidALCChannels(schans) || !IsValidALCType(stype) || freq < MIN_OUTPUT_RATE)
- return ALC_INVALID_VALUE;
- if(schans == ALC_BFORMAT3D_SOFT)
- {
- if(!alayout || !ascale || !aorder)
- {
- WARN("Missing ambisonic info for loopback device\n");
- return ALC_INVALID_VALUE;
- }
- if(!IsValidAmbiLayout(alayout) || !IsValidAmbiScaling(ascale))
- return ALC_INVALID_VALUE;
- if(aorder < 1 || aorder > MAX_AMBI_ORDER)
- return ALC_INVALID_VALUE;
- if((alayout == ALC_FUMA_SOFT || ascale == ALC_FUMA_SOFT) && aorder > 3)
- return ALC_INVALID_VALUE;
- }
- }
-
- /* If a context is already running on the device, stop playback so the
- * device attributes can be updated.
- */
- if(device->Flags.get<DeviceRunning>())
- device->Backend->stop();
- device->Flags.unset<DeviceRunning>();
-
- UpdateClockBase(device);
-
- const char *devname{nullptr};
- if(!loopback)
- {
- devname = device->DeviceName.c_str();
-
- device->BufferSize = DEFAULT_UPDATE_SIZE * DEFAULT_NUM_UPDATES;
- device->UpdateSize = DEFAULT_UPDATE_SIZE;
- device->Frequency = DEFAULT_OUTPUT_RATE;
-
- freq = ConfigValueUInt(devname, nullptr, "frequency").value_or(freq);
- if(freq < 1)
- device->Flags.unset<FrequencyRequest>();
- else
- {
- freq = maxi(freq, MIN_OUTPUT_RATE);
-
- device->UpdateSize = (device->UpdateSize*freq + device->Frequency/2) /
- device->Frequency;
- device->BufferSize = (device->BufferSize*freq + device->Frequency/2) /
- device->Frequency;
-
- device->Frequency = freq;
- device->Flags.set<FrequencyRequest>();
- }
-
- if(auto persizeopt = ConfigValueUInt(devname, nullptr, "period_size"))
- device->UpdateSize = clampu(*persizeopt, 64, 8192);
-
- if(auto peropt = ConfigValueUInt(devname, nullptr, "periods"))
- device->BufferSize = device->UpdateSize * clampu(*peropt, 2, 16);
- else
- device->BufferSize = maxu(device->BufferSize, device->UpdateSize*2);
- }
- else
- {
- device->Frequency = freq;
- device->FmtChans = static_cast<DevFmtChannels>(schans);
- device->FmtType = static_cast<DevFmtType>(stype);
- if(schans == ALC_BFORMAT3D_SOFT)
- {
- device->mAmbiOrder = aorder;
- device->mAmbiLayout = static_cast<AmbiLayout>(alayout);
- device->mAmbiScale = static_cast<AmbiNorm>(ascale);
- }
- }
-
- if(numMono > INT_MAX-numStereo)
- numMono = INT_MAX-numStereo;
- numMono += numStereo;
- if(auto srcsopt = ConfigValueUInt(devname, nullptr, "sources"))
- {
- if(*srcsopt <= 0) numMono = 256;
- else numMono = *srcsopt;
- }
- else
- numMono = maxu(numMono, 256);
- numStereo = minu(numStereo, numMono);
- numMono -= numStereo;
- device->SourcesMax = numMono + numStereo;
-
- device->NumMonoSources = numMono;
- device->NumStereoSources = numStereo;
-
- if(auto sendsopt = ConfigValueInt(devname, nullptr, "sends"))
- new_sends = mini(numSends, clampi(*sendsopt, 0, MAX_SENDS));
- else
- new_sends = numSends;
- }
-
- if(device->Flags.get<DeviceRunning>())
- return ALC_NO_ERROR;
-
- device->AvgSpeakerDist = 0.0f;
- device->Uhj_Encoder = nullptr;
- device->AmbiDecoder = nullptr;
- device->Bs2b = nullptr;
- device->PostProcess = nullptr;
-
- device->Stablizer = nullptr;
- device->Limiter = nullptr;
- device->ChannelDelay.clear();
-
- device->Dry.AmbiMap.fill(BFChannelConfig{});
- device->Dry.Buffer = {};
- std::fill(std::begin(device->NumChannelsPerOrder), std::end(device->NumChannelsPerOrder), 0u);
- device->RealOut.ChannelIndex.fill(-1);
- device->RealOut.Buffer = {};
- device->MixBuffer.clear();
- device->MixBuffer.shrink_to_fit();
-
- UpdateClockBase(device);
- device->FixedLatency = nanoseconds::zero();
-
- device->DitherDepth = 0.0f;
- device->DitherSeed = DITHER_RNG_SEED;
-
- /*************************************************************************
- * Update device format request if HRTF is requested
- */
- device->HrtfStatus = ALC_HRTF_DISABLED_SOFT;
- if(device->Type != Loopback)
- {
- if(auto hrtfopt = ConfigValueStr(device->DeviceName.c_str(), nullptr, "hrtf"))
- {
- const char *hrtf{hrtfopt->c_str()};
- 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))
- {
- HrtfEntry *hrtf{nullptr};
- if(device->HrtfList.empty())
- device->HrtfList = EnumerateHrtf(device->DeviceName.c_str());
- if(!device->HrtfList.empty())
- {
- if(hrtf_id >= 0 && static_cast<size_t>(hrtf_id) < device->HrtfList.size())
- hrtf = GetLoadedHrtf(device->HrtfList[hrtf_id].hrtf);
- else
- hrtf = GetLoadedHrtf(device->HrtfList.front().hrtf);
- }
-
- if(hrtf)
- {
- device->FmtChans = DevFmtStereo;
- device->Frequency = hrtf->sampleRate;
- device->Flags.set<ChannelsRequest, FrequencyRequest>();
- if(HrtfEntry *oldhrtf{device->mHrtf})
- oldhrtf->DecRef();
- device->mHrtf = 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 / %u buffer\n",
- device->Flags.get<ChannelsRequest>()?"*":"", DevFmtChannelsString(device->FmtChans),
- device->Flags.get<SampleTypeRequest>()?"*":"", DevFmtTypeString(device->FmtType),
- device->Flags.get<FrequencyRequest>()?"*":"", device->Frequency,
- device->UpdateSize, device->BufferSize);
-
- try {
- if(device->Backend->reset() == ALC_FALSE)
- return ALC_INVALID_DEVICE;
- }
- catch(std::exception &e) {
- ERR("Device reset failed: %s\n", e.what());
- return ALC_INVALID_DEVICE;
- }
-
- if(device->FmtChans != oldChans && device->Flags.get<ChannelsRequest>())
- {
- ERR("Failed to set %s, got %s instead\n", DevFmtChannelsString(oldChans),
- DevFmtChannelsString(device->FmtChans));
- device->Flags.unset<ChannelsRequest>();
- }
- if(device->FmtType != oldType && device->Flags.get<SampleTypeRequest>())
- {
- ERR("Failed to set %s, got %s instead\n", DevFmtTypeString(oldType),
- DevFmtTypeString(device->FmtType));
- device->Flags.unset<SampleTypeRequest>();
- }
- if(device->Frequency != oldFreq && device->Flags.get<FrequencyRequest>())
- {
- WARN("Failed to set %uhz, got %uhz instead\n", oldFreq, device->Frequency);
- device->Flags.unset<FrequencyRequest>();
- }
-
- TRACE("Post-reset: %s, %s, %uhz, %u / %u buffer\n",
- DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
- device->Frequency, device->UpdateSize, device->BufferSize);
-
- aluInitRenderer(device, hrtf_id, hrtf_appreq, hrtf_userreq);
-
- 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);
-
- /* Enable the stablizer only for formats that have front-left, front-right,
- * and front-center outputs.
- */
- switch(device->FmtChans)
- {
- case DevFmtX51:
- case DevFmtX51Rear:
- case DevFmtX61:
- case DevFmtX71:
- if(GetConfigValueBool(device->DeviceName.c_str(), nullptr, "front-stablizer", 0))
- {
- auto stablizer = al::make_unique<FrontStablizer>();
- /* Initialize band-splitting filters for the front-left and front-
- * right channels, with a crossover at 5khz (could be higher).
- */
- const ALfloat scale{5000.0f / static_cast<ALfloat>(device->Frequency)};
-
- stablizer->LFilter.init(scale);
- stablizer->RFilter = stablizer->LFilter;
-
- device->Stablizer = std::move(stablizer);
- /* NOTE: Don't know why this has to be "copied" into a local static
- * constexpr variable to avoid a reference on
- * FrontStablizer::DelayLength...
- */
- static constexpr size_t StablizerDelay{FrontStablizer::DelayLength};
- device->FixedLatency += nanoseconds{seconds{StablizerDelay}} / device->Frequency;
- }
- break;
- case DevFmtMono:
- case DevFmtStereo:
- case DevFmtQuad:
- case DevFmtAmbi3D:
- break;
- }
- TRACE("Front stablizer %s\n", device->Stablizer ? "enabled" : "disabled");
-
- if(GetConfigValueBool(device->DeviceName.c_str(), nullptr, "dither", 1))
- {
- ALint depth{
- ConfigValueInt(device->DeviceName.c_str(), nullptr, "dither-depth").value_or(0)};
- 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 = std::pow(2.0f, static_cast<ALfloat>(depth-1));
- }
- }
- if(!(device->DitherDepth > 0.0f))
- TRACE("Dithering disabled\n");
- else
- TRACE("Dithering enabled (%d-bit, %g)\n", float2int(std::log2(device->DitherDepth)+0.5f)+1,
- device->DitherDepth);
-
- device->LimiterState = gainLimiter;
- if(auto limopt = ConfigValueBool(device->DeviceName.c_str(), nullptr, "output-limiter"))
- gainLimiter = *limopt ? 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)
- TRACE("Output limiter disabled\n");
- else
- {
- 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;
-
- const float thrshld_dB{std::log10(thrshld) * 20.0f};
- auto limiter = CreateDeviceLimiter(device, thrshld_dB);
- /* Convert the lookahead from samples to nanosamples to nanoseconds. */
- device->FixedLatency += nanoseconds{seconds{limiter->getLookAhead()}} / device->Frequency;
- device->Limiter = std::move(limiter);
- TRACE("Output limiter enabled, %.4fdB limit\n", thrshld_dB);
- }
-
- TRACE("Fixed device latency: %ldns\n", (long)device->FixedLatency.count());
-
- /* Need to delay returning failure until replacement Send arrays have been
- * allocated with the appropriate size.
- */
- update_failed = AL_FALSE;
- FPUCtl mixer_mode{};
- for(ALCcontext *context : *device->mContexts.load())
- {
- if(context->DefaultSlot)
- {
- ALeffectslot *slot = context->DefaultSlot.get();
- aluInitEffectPanning(slot, device);
-
- EffectState *state{slot->Effect.State};
- state->mOutTarget = device->Dry.Buffer;
- if(state->deviceUpdate(device) == AL_FALSE)
- update_failed = AL_TRUE;
- else
- UpdateEffectSlotProps(slot, context);
- }
-
- std::unique_lock<std::mutex> proplock{context->PropLock};
- std::unique_lock<std::mutex> slotlock{context->EffectSlotLock};
- for(auto &sublist : context->EffectSlotList)
- {
- uint64_t usemask = ~sublist.FreeMask;
- while(usemask)
- {
- ALsizei idx = CTZ64(usemask);
- ALeffectslot *slot = sublist.EffectSlots + idx;
-
- usemask &= ~(1_u64 << idx);
-
- aluInitEffectPanning(slot, device);
-
- EffectState *state{slot->Effect.State};
- state->mOutTarget = device->Dry.Buffer;
- if(state->deviceUpdate(device) == AL_FALSE)
- update_failed = AL_TRUE;
- else
- UpdateEffectSlotProps(slot, context);
- }
- }
- slotlock.unlock();
-
- std::unique_lock<std::mutex> srclock{context->SourceLock};
- for(auto &sublist : context->SourceList)
- {
- uint64_t usemask = ~sublist.FreeMask;
- while(usemask)
- {
- ALsizei idx = CTZ64(usemask);
- ALsource *source = sublist.Sources + idx;
-
- usemask &= ~(1_u64 << idx);
-
- if(old_sends != device->NumAuxSends)
- {
- ALsizei s;
- for(s = device->NumAuxSends;s < old_sends;s++)
- {
- if(source->Send[s].Slot)
- DecrementRef(&source->Send[s].Slot->ref);
- source->Send[s].Slot = nullptr;
- }
- source->Send.resize(device->NumAuxSends);
- source->Send.shrink_to_fit();
- 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;
- }
- }
-
- source->PropsClean.clear(std::memory_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.
- */
- ALvoiceProps *vprops{context->FreeVoiceProps.exchange(nullptr, std::memory_order_acq_rel)};
- while(vprops)
- {
- ALvoiceProps *next = vprops->next.load(std::memory_order_relaxed);
- delete vprops;
- vprops = next;
- }
-
- auto voices = context->Voices.get();
- auto voices_end = voices->begin() + context->VoiceCount.load(std::memory_order_relaxed);
- if(device->NumAuxSends < old_sends)
- {
- const ALsizei num_sends{device->NumAuxSends};
- /* Clear extraneous property set sends. */
- auto clear_sends = [num_sends](ALvoice &voice) -> void
- {
- std::fill(std::begin(voice.mProps.Send)+num_sends, std::end(voice.mProps.Send),
- ALvoiceProps::SendData{});
-
- std::fill(voice.mSend.begin()+num_sends, voice.mSend.end(), ALvoice::SendData{});
- auto clear_chan_sends = [num_sends](ALvoice::ChannelData &chandata) -> void
- {
- std::fill(chandata.mWetParams.begin()+num_sends, chandata.mWetParams.end(),
- SendParams{});
- };
- std::for_each(voice.mChans.begin(), voice.mChans.end(), clear_chan_sends);
- };
- std::for_each(voices->begin(), voices_end, clear_sends);
- }
- std::for_each(voices->begin(), voices_end,
- [device](ALvoice &voice) -> void
- {
- delete voice.mUpdate.exchange(nullptr, std::memory_order_acq_rel);
-
- /* Force the voice to stopped if it was stopping. */
- ALvoice::State vstate{ALvoice::Stopping};
- voice.mPlayState.compare_exchange_strong(vstate, ALvoice::Stopped,
- std::memory_order_acquire, std::memory_order_acquire);
- if(voice.mSourceID.load(std::memory_order_relaxed) == 0u)
- return;
-
- if(device->AvgSpeakerDist > 0.0f)
- {
- /* Reinitialize the NFC filters for new parameters. */
- const ALfloat w1{SPEEDOFSOUNDMETRESPERSEC /
- (device->AvgSpeakerDist * device->Frequency)};
- auto init_nfc = [w1](ALvoice::ChannelData &chandata) -> void
- { chandata.mDryParams.NFCtrlFilter.init(w1); };
- std::for_each(voice.mChans.begin(), voice.mChans.begin()+voice.mNumChannels,
- init_nfc);
- }
- }
- );
- srclock.unlock();
-
- context->PropsClean.test_and_set(std::memory_order_release);
- UpdateContextProps(context);
- context->Listener.PropsClean.test_and_set(std::memory_order_release);
- UpdateListenerProps(context);
- UpdateAllSourceProps(context);
- }
- mixer_mode.leave();
- if(update_failed)
- return ALC_INVALID_DEVICE;
-
- if(!device->Flags.get<DevicePaused>())
- {
- if(device->Backend->start() == ALC_FALSE)
- return ALC_INVALID_DEVICE;
- device->Flags.set<DeviceRunning>();
- }
-
- return ALC_NO_ERROR;
-}
-
-
-ALCdevice::ALCdevice(DeviceType type) : Type{type}, mContexts{&EmptyContextArray}
-{
-}
-
-/* ALCdevice::~ALCdevice
- *
- * Frees the device structure, and destroys any objects the app failed to
- * delete. Called once there's no more references on the device.
- */
-ALCdevice::~ALCdevice()
-{
- TRACE("Freeing device %p\n", this);
-
- Backend = nullptr;
-
- size_t count{std::accumulate(BufferList.cbegin(), BufferList.cend(), size_t{0u},
- [](size_t cur, const BufferSubList &sublist) noexcept -> size_t
- { return cur + POPCNT64(~sublist.FreeMask); }
- )};
- if(count > 0)
- WARN("%zu Buffer%s not deleted\n", count, (count==1)?"":"s");
-
- count = std::accumulate(EffectList.cbegin(), EffectList.cend(), size_t{0u},
- [](size_t cur, const EffectSubList &sublist) noexcept -> size_t
- { return cur + POPCNT64(~sublist.FreeMask); }
- );
- if(count > 0)
- WARN("%zu Effect%s not deleted\n", count, (count==1)?"":"s");
-
- count = std::accumulate(FilterList.cbegin(), FilterList.cend(), size_t{0u},
- [](size_t cur, const FilterSubList &sublist) noexcept -> size_t
- { return cur + POPCNT64(~sublist.FreeMask); }
- );
- if(count > 0)
- WARN("%zu Filter%s not deleted\n", count, (count==1)?"":"s");
-
- if(mHrtf)
- mHrtf->DecRef();
- mHrtf = nullptr;
-
- auto *oldarray = mContexts.exchange(nullptr, std::memory_order_relaxed);
- if(oldarray != &EmptyContextArray) delete oldarray;
-}
-
-
-/* VerifyDevice
- *
- * Checks if the device handle is valid, and returns a new reference if so.
- */
-static DeviceRef VerifyDevice(ALCdevice *device)
-{
- std::lock_guard<std::recursive_mutex> _{ListLock};
- auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device);
- if(iter != DeviceList.cend() && *iter == device)
- {
- ALCdevice_IncRef(iter->get());
- return DeviceRef{iter->get()};
- }
- return DeviceRef{};
-}
-
-
-ALCcontext::ALCcontext(ALCdevice *device) : Device{device}
-{
- PropsClean.test_and_set(std::memory_order_relaxed);
-}
-
-/* InitContext
- *
- * Initializes context fields
- */
-static ALvoid InitContext(ALCcontext *Context)
-{
- ALlistener &listener = Context->Listener;
- ALeffectslotArray *auxslots;
-
- //Validate Context
- if(!Context->DefaultSlot)
- auxslots = ALeffectslot::CreatePtrArray(0);
- else
- {
- auxslots = ALeffectslot::CreatePtrArray(1);
- (*auxslots)[0] = Context->DefaultSlot.get();
- }
- Context->ActiveAuxSlots.store(auxslots, std::memory_order_relaxed);
-
- //Set globals
- Context->mDistanceModel = DistanceModel::Default;
- Context->SourceDistanceModel = AL_FALSE;
- Context->DopplerFactor = 1.0f;
- Context->DopplerVelocity = 1.0f;
- Context->SpeedOfSound = SPEEDOFSOUNDMETRESPERSEC;
- Context->MetersPerUnit = AL_DEFAULT_METERS_PER_UNIT;
-
- Context->ExtensionList = alExtList;
-
-
- listener.Params.Matrix = alu::Matrix::Identity();
- listener.Params.Velocity = alu::Vector{};
- 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.mDistanceModel = Context->mDistanceModel;
-
-
- Context->AsyncEvents = CreateRingBuffer(511, sizeof(AsyncEvent), false);
- StartEventThrd(Context);
-}
-
-
-/* ALCcontext::~ALCcontext()
- *
- * Cleans up the context, and destroys any remaining objects the app failed to
- * delete. Called once there's no more references on the context.
- */
-ALCcontext::~ALCcontext()
-{
- TRACE("Freeing context %p\n", this);
-
- ALcontextProps *cprops{Update.exchange(nullptr, std::memory_order_relaxed)};
- if(cprops)
- {
- TRACE("Freed unapplied context update %p\n", cprops);
- al_free(cprops);
- }
- size_t count{0};
- cprops = FreeContextProps.exchange(nullptr, std::memory_order_acquire);
- while(cprops)
- {
- ALcontextProps *next{cprops->next.load(std::memory_order_relaxed)};
- al_free(cprops);
- cprops = next;
- ++count;
- }
- TRACE("Freed %zu context property object%s\n", count, (count==1)?"":"s");
-
- count = std::accumulate(SourceList.cbegin(), SourceList.cend(), size_t{0u},
- [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
- { return cur + POPCNT64(~sublist.FreeMask); }
- );
- if(count > 0)
- WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
- SourceList.clear();
- NumSources = 0;
-
- count = 0;
- ALeffectslotProps *eprops{FreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)};
- while(eprops)
- {
- ALeffectslotProps *next{eprops->next.load(std::memory_order_relaxed)};
- if(eprops->State) eprops->State->DecRef();
- al_free(eprops);
- eprops = next;
- ++count;
- }
- TRACE("Freed %zu AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
-
- delete ActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed);
- DefaultSlot = nullptr;
-
- count = std::accumulate(EffectSlotList.cbegin(), EffectSlotList.cend(), size_t{0u},
- [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
- { return cur + POPCNT64(~sublist.FreeMask); }
- );
- if(count > 0)
- WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
- EffectSlotList.clear();
- NumEffectSlots = 0;
-
- count = 0;
- ALvoiceProps *vprops{FreeVoiceProps.exchange(nullptr, std::memory_order_acquire)};
- while(vprops)
- {
- ALvoiceProps *next{vprops->next.load(std::memory_order_relaxed)};
- delete vprops;
- vprops = next;
- ++count;
- }
- TRACE("Freed %zu voice property object%s\n", count, (count==1)?"":"s");
-
- Voices = nullptr;
- VoiceCount.store(0, std::memory_order_relaxed);
-
- ALlistenerProps *lprops{Listener.Update.exchange(nullptr, std::memory_order_relaxed)};
- if(lprops)
- {
- TRACE("Freed unapplied listener update %p\n", lprops);
- al_free(lprops);
- }
- count = 0;
- lprops = FreeListenerProps.exchange(nullptr, std::memory_order_acquire);
- while(lprops)
- {
- ALlistenerProps *next{lprops->next.load(std::memory_order_relaxed)};
- al_free(lprops);
- lprops = next;
- ++count;
- }
- TRACE("Freed %zu listener property object%s\n", count, (count==1)?"":"s");
-
- if(AsyncEvents)
- {
- count = 0;
- auto evt_vec = AsyncEvents->getReadVector();
- if(evt_vec.first.len > 0)
- {
- al::destroy_n(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf), evt_vec.first.len);
- count += evt_vec.first.len;
- }
- if(evt_vec.second.len > 0)
- {
- al::destroy_n(reinterpret_cast<AsyncEvent*>(evt_vec.second.buf), evt_vec.second.len);
- count += evt_vec.second.len;
- }
- if(count > 0)
- TRACE("Destructed %zu orphaned event%s\n", count, (count==1)?"":"s");
- AsyncEvents->readAdvance(count);
- }
-
- ALCdevice_DecRef(Device);
-}
-
-/* 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)
-{
- if(LocalContext.get() == context)
- {
- WARN("%p released while current on thread\n", context);
- LocalContext.set(nullptr);
- ALCcontext_DecRef(context);
- }
-
- ALCcontext *origctx{context};
- if(GlobalContext.compare_exchange_strong(origctx, nullptr))
- ALCcontext_DecRef(context);
-
- bool ret{};
- {
- using ContextArray = al::FlexArray<ALCcontext*>;
-
- /* First make sure this context exists in the device's list. */
- auto *oldarray = device->mContexts.load(std::memory_order_acquire);
- if(auto toremove = std::count(oldarray->begin(), oldarray->end(), context))
- {
- auto alloc_ctx_array = [](const size_t count) -> ContextArray*
- {
- if(count == 0) return &EmptyContextArray;
- void *ptr{al_calloc(alignof(ContextArray), ContextArray::Sizeof(count))};
- return new (ptr) ContextArray{count};
- };
- auto *newarray = alloc_ctx_array(oldarray->size() - toremove);
-
- /* Copy the current/old context handles to the new array, excluding
- * the given context.
- */
- std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
- std::bind(std::not_equal_to<ALCcontext*>{}, _1, context));
-
- /* Store the new context array in the device. Wait for any current
- * mix to finish before deleting the old array.
- */
- device->mContexts.store(newarray);
- if(oldarray != &EmptyContextArray)
- {
- while((device->MixCount.load(std::memory_order_acquire)&1))
- std::this_thread::yield();
- delete oldarray;
- }
-
- ret = !newarray->empty();
- }
- else
- ret = !oldarray->empty();
- }
-
- StopEventThrd(context);
-
- return ret;
-}
-
-static void ALCcontext_IncRef(ALCcontext *context)
-{
- auto ref = IncrementRef(&context->ref);
- TRACEREF("ALCcontext %p increasing refcount to %u\n", context, ref);
-}
-
-void ALCcontext_DecRef(ALCcontext *context)
-{
- auto ref = DecrementRef(&context->ref);
- TRACEREF("ALCcontext %p decreasing refcount to %u\n", context, ref);
- if(UNLIKELY(ref == 0)) delete context;
-}
-
-/* VerifyContext
- *
- * Checks if the given context is valid, returning a new reference to it if so.
- */
-static ContextRef VerifyContext(ALCcontext *context)
-{
- std::lock_guard<std::recursive_mutex> _{ListLock};
- auto iter = std::lower_bound(ContextList.cbegin(), ContextList.cend(), context);
- if(iter != ContextList.cend() && *iter == context)
- {
- ALCcontext_IncRef(iter->get());
- return ContextRef{iter->get()};
- }
- return ContextRef{};
-}
-
-
-/* GetContextRef
- *
- * Returns a new reference to the currently active context for this thread.
- */
-ContextRef GetContextRef(void)
-{
- ALCcontext *context{LocalContext.get()};
- if(context)
- ALCcontext_IncRef(context);
- else
- {
- std::lock_guard<std::recursive_mutex> _{ListLock};
- context = GlobalContext.load(std::memory_order_acquire);
- if(context) ALCcontext_IncRef(context);
- }
- return ContextRef{context};
-}
-
-
-void AllocateVoices(ALCcontext *context, size_t num_voices)
-{
- ALCdevice *device{context->Device};
- const ALsizei num_sends{device->NumAuxSends};
-
- if(context->Voices && num_voices == context->Voices->size())
- return;
-
- std::unique_ptr<al::FlexArray<ALvoice>> voices;
- {
- void *ptr{al_calloc(16, al::FlexArray<ALvoice>::Sizeof(num_voices))};
- voices.reset(new (ptr) al::FlexArray<ALvoice>{num_voices});
- }
-
- const size_t v_count{minz(context->VoiceCount.load(std::memory_order_relaxed), num_voices)};
- if(context->Voices)
- {
- /* Copy the old voice data to the new storage. */
- auto viter = std::move(context->Voices->begin(), context->Voices->begin()+v_count,
- voices->begin());
-
- /* Clear extraneous property set sends. */
- auto clear_sends = [num_sends](ALvoice &voice) -> void
- {
- std::fill(std::begin(voice.mProps.Send)+num_sends, std::end(voice.mProps.Send),
- ALvoiceProps::SendData{});
-
- std::fill(voice.mSend.begin()+num_sends, voice.mSend.end(), ALvoice::SendData{});
- auto clear_chan_sends = [num_sends](ALvoice::ChannelData &chandata) -> void
- {
- std::fill(chandata.mWetParams.begin()+num_sends, chandata.mWetParams.end(),
- SendParams{});
- };
- std::for_each(voice.mChans.begin(), voice.mChans.end(), clear_chan_sends);
- };
- std::for_each(voices->begin(), viter, clear_sends);
- }
-
- context->Voices = std::move(voices);
- context->VoiceCount.store(static_cast<ALuint>(v_count), std::memory_order_relaxed);
-}
-
-
-/************************************************
- * Standard ALC functions
- ************************************************/
-
-/* alcGetError
- *
- * Return last ALC generated error code for the given device
- */
-ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)
-START_API_FUNC
-{
- DeviceRef dev{VerifyDevice(device)};
- if(dev) return dev->LastError.exchange(ALC_NO_ERROR);
- return LastNullDeviceError.exchange(ALC_NO_ERROR);
-}
-END_API_FUNC
-
-
-/* alcSuspendContext
- *
- * Suspends updates for the given context
- */
-ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *context)
-START_API_FUNC
-{
- if(!SuspendDefers)
- return;
-
- ContextRef ctx{VerifyContext(context)};
- if(!ctx)
- alcSetError(nullptr, ALC_INVALID_CONTEXT);
- else
- ALCcontext_DeferUpdates(ctx.get());
-}
-END_API_FUNC
-
-/* alcProcessContext
- *
- * Resumes processing updates for the given context
- */
-ALC_API ALCvoid ALC_APIENTRY alcProcessContext(ALCcontext *context)
-START_API_FUNC
-{
- if(!SuspendDefers)
- return;
-
- ContextRef ctx{VerifyContext(context)};
- if(!ctx)
- alcSetError(nullptr, ALC_INVALID_CONTEXT);
- else
- ALCcontext_ProcessUpdates(ctx.get());
-}
-END_API_FUNC
-
-
-/* alcGetString
- *
- * Returns information about the device, and error strings
- */
-ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param)
-START_API_FUNC
-{
- 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(DeviceRef dev{VerifyDevice(Device)})
- value = dev->DeviceName.c_str();
- else
- {
- ProbeAllDevicesList();
- value = alcAllDevicesList.c_str();
- }
- break;
-
- case ALC_CAPTURE_DEVICE_SPECIFIER:
- if(DeviceRef dev{VerifyDevice(Device)})
- value = dev->DeviceName.c_str();
- else
- {
- ProbeCaptureDeviceList();
- value = alcCaptureDeviceList.c_str();
- }
- break;
-
- /* Default devices are always first in the list */
- case ALC_DEFAULT_DEVICE_SPECIFIER:
- value = alcDefaultName;
- break;
-
- case ALC_DEFAULT_ALL_DEVICES_SPECIFIER:
- if(alcAllDevicesList.empty())
- ProbeAllDevicesList();
-
- /* Copy first entry as default. */
- alcDefaultAllDevicesSpecifier = alcAllDevicesList.c_str();
- value = alcDefaultAllDevicesSpecifier.c_str();
- break;
-
- case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER:
- if(alcCaptureDeviceList.empty())
- ProbeCaptureDeviceList();
-
- /* Copy first entry as default. */
- alcCaptureDefaultDeviceSpecifier = alcCaptureDeviceList.c_str();
- value = alcCaptureDefaultDeviceSpecifier.c_str();
- break;
-
- case ALC_EXTENSIONS:
- if(VerifyDevice(Device))
- value = alcExtensionList;
- else
- value = alcNoDeviceExtList;
- break;
-
- case ALC_HRTF_SPECIFIER_SOFT:
- if(DeviceRef dev{VerifyDevice(Device)})
- {
- std::lock_guard<std::mutex> _{dev->StateLock};
- value = (dev->mHrtf ? dev->HrtfName.c_str() : "");
- }
- else
- alcSetError(nullptr, ALC_INVALID_DEVICE);
- break;
-
- default:
- alcSetError(VerifyDevice(Device).get(), ALC_INVALID_ENUM);
- break;
- }
-
- return value;
-}
-END_API_FUNC
-
-
-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, const al::span<ALCint> values)
-{
- ALCsizei i;
-
- if(values.empty())
- {
- 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:
- i = 0;
- if(values.size() < static_cast<size_t>(NumAttrsForDevice(device)))
- alcSetError(device, ALC_INVALID_VALUE);
- else
- {
- std::lock_guard<std::mutex> _{device->StateLock};
- values[i++] = ALC_MAJOR_VERSION;
- values[i++] = alcMajorVersion;
- values[i++] = ALC_MINOR_VERSION;
- values[i++] = alcMinorVersion;
- values[i++] = ALC_CAPTURE_SAMPLES;
- values[i++] = device->Backend->availableSamples();
- values[i++] = ALC_CONNECTED;
- values[i++] = device->Connected.load(std::memory_order_relaxed);
- 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:
- { std::lock_guard<std::mutex> _{device->StateLock};
- values[0] = device->Backend->availableSamples();
- }
- return 1;
-
- case ALC_CONNECTED:
- { std::lock_guard<std::mutex> _{device->StateLock};
- values[0] = device->Connected.load(std::memory_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:
- i = 0;
- if(values.size() < static_cast<size_t>(NumAttrsForDevice(device)))
- alcSetError(device, ALC_INVALID_VALUE);
- else
- {
- std::lock_guard<std::mutex> _{device->StateLock};
- 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++] = static_cast<ALCint>(device->mAmbiLayout);
-
- values[i++] = ALC_AMBISONIC_SCALING_SOFT;
- values[i++] = static_cast<ALCint>(device->mAmbiScale);
-
- values[i++] = ALC_AMBISONIC_ORDER_SOFT;
- values[i++] = device->mAmbiOrder;
- }
-
- 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->mHrtf ? 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;
-
- 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;
- }
- { std::lock_guard<std::mutex> _{device->StateLock};
- values[0] = device->Frequency / device->UpdateSize;
- }
- return 1;
-
- case ALC_SYNC:
- if(device->Type == Loopback)
- {
- alcSetError(device, ALC_INVALID_DEVICE);
- return 0;
- }
- values[0] = ALC_FALSE;
- return 1;
-
- case ALC_FORMAT_CHANNELS_SOFT:
- if(device->Type != Loopback)
- {
- alcSetError(device, ALC_INVALID_DEVICE);
- return 0;
- }
- values[0] = device->FmtChans;
- return 1;
-
- case ALC_FORMAT_TYPE_SOFT:
- if(device->Type != Loopback)
- {
- alcSetError(device, ALC_INVALID_DEVICE);
- return 0;
- }
- values[0] = device->FmtType;
- return 1;
-
- case ALC_AMBISONIC_LAYOUT_SOFT:
- if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D)
- {
- alcSetError(device, ALC_INVALID_DEVICE);
- return 0;
- }
- values[0] = static_cast<ALCint>(device->mAmbiLayout);
- return 1;
-
- case ALC_AMBISONIC_SCALING_SOFT:
- if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D)
- {
- alcSetError(device, ALC_INVALID_DEVICE);
- return 0;
- }
- values[0] = static_cast<ALCint>(device->mAmbiScale);
- return 1;
-
- case ALC_AMBISONIC_ORDER_SOFT:
- if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D)
- {
- alcSetError(device, ALC_INVALID_DEVICE);
- return 0;
- }
- values[0] = device->mAmbiOrder;
- 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:
- { std::lock_guard<std::mutex> _{device->StateLock};
- values[0] = device->Connected.load(std::memory_order_acquire);
- }
- return 1;
-
- case ALC_HRTF_SOFT:
- values[0] = (device->mHrtf ? ALC_TRUE : ALC_FALSE);
- return 1;
-
- case ALC_HRTF_STATUS_SOFT:
- values[0] = device->HrtfStatus;
- return 1;
-
- case ALC_NUM_HRTF_SPECIFIERS_SOFT:
- { std::lock_guard<std::mutex> _{device->StateLock};
- device->HrtfList.clear();
- device->HrtfList = EnumerateHrtf(device->DeviceName.c_str());
- values[0] = static_cast<ALCint>(minz(device->HrtfList.size(),
- std::numeric_limits<ALCint>::max()));
- }
- 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)
-START_API_FUNC
-{
- DeviceRef dev{VerifyDevice(device)};
- if(size <= 0 || values == nullptr)
- alcSetError(dev.get(), ALC_INVALID_VALUE);
- else
- GetIntegerv(dev.get(), param, {values, values+size});
-}
-END_API_FUNC
-
-ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALCsizei size, ALCint64SOFT *values)
-START_API_FUNC
-{
- DeviceRef dev{VerifyDevice(device)};
- if(size <= 0 || values == nullptr)
- alcSetError(dev.get(), ALC_INVALID_VALUE);
- else if(!dev || dev->Type == Capture)
- {
- auto ivals = al::vector<ALCint>(size);
- size = GetIntegerv(dev.get(), pname, {ivals.data(), ivals.size()});
- std::copy(ivals.begin(), ivals.begin()+size, values);
- }
- else /* render device */
- {
- switch(pname)
- {
- case ALC_ATTRIBUTES_SIZE:
- *values = NumAttrsForDevice(dev.get())+4;
- break;
-
- case ALC_ALL_ATTRIBUTES:
- if(size < NumAttrsForDevice(dev.get())+4)
- alcSetError(dev.get(), ALC_INVALID_VALUE);
- else
- {
- ALsizei i{0};
- std::lock_guard<std::mutex> _{dev->StateLock};
- values[i++] = ALC_FREQUENCY;
- values[i++] = dev->Frequency;
-
- if(dev->Type != Loopback)
- {
- values[i++] = ALC_REFRESH;
- values[i++] = dev->Frequency / dev->UpdateSize;
-
- values[i++] = ALC_SYNC;
- values[i++] = ALC_FALSE;
- }
- else
- {
- if(dev->FmtChans == DevFmtAmbi3D)
- {
- values[i++] = ALC_AMBISONIC_LAYOUT_SOFT;
- values[i++] = static_cast<ALCint64SOFT>(dev->mAmbiLayout);
-
- values[i++] = ALC_AMBISONIC_SCALING_SOFT;
- values[i++] = static_cast<ALCint64SOFT>(dev->mAmbiScale);
-
- values[i++] = ALC_AMBISONIC_ORDER_SOFT;
- values[i++] = dev->mAmbiOrder;
- }
-
- values[i++] = ALC_FORMAT_CHANNELS_SOFT;
- values[i++] = dev->FmtChans;
-
- values[i++] = ALC_FORMAT_TYPE_SOFT;
- values[i++] = dev->FmtType;
- }
-
- values[i++] = ALC_MONO_SOURCES;
- values[i++] = dev->NumMonoSources;
-
- values[i++] = ALC_STEREO_SOURCES;
- values[i++] = dev->NumStereoSources;
-
- values[i++] = ALC_MAX_AUXILIARY_SENDS;
- values[i++] = dev->NumAuxSends;
-
- values[i++] = ALC_HRTF_SOFT;
- values[i++] = (dev->mHrtf ? ALC_TRUE : ALC_FALSE);
-
- values[i++] = ALC_HRTF_STATUS_SOFT;
- values[i++] = dev->HrtfStatus;
-
- values[i++] = ALC_OUTPUT_LIMITER_SOFT;
- values[i++] = dev->Limiter ? ALC_TRUE : ALC_FALSE;
-
- ClockLatency clock{GetClockLatency(dev.get())};
- values[i++] = ALC_DEVICE_CLOCK_SOFT;
- values[i++] = clock.ClockTime.count();
-
- values[i++] = ALC_DEVICE_LATENCY_SOFT;
- values[i++] = clock.Latency.count();
-
- values[i++] = 0;
- }
- break;
-
- case ALC_DEVICE_CLOCK_SOFT:
- { std::lock_guard<std::mutex> _{dev->StateLock};
- nanoseconds basecount;
- ALuint samplecount;
- ALuint refcount;
- do {
- while(((refcount=ReadRef(&dev->MixCount))&1) != 0)
- std::this_thread::yield();
- basecount = dev->ClockBase;
- samplecount = dev->SamplesDone;
- } while(refcount != ReadRef(&dev->MixCount));
- basecount += nanoseconds{seconds{samplecount}} / dev->Frequency;
- *values = basecount.count();
- }
- break;
-
- case ALC_DEVICE_LATENCY_SOFT:
- { std::lock_guard<std::mutex> _{dev->StateLock};
- ClockLatency clock{GetClockLatency(dev.get())};
- *values = clock.Latency.count();
- }
- break;
-
- case ALC_DEVICE_CLOCK_LATENCY_SOFT:
- if(size < 2)
- alcSetError(dev.get(), ALC_INVALID_VALUE);
- else
- {
- std::lock_guard<std::mutex> _{dev->StateLock};
- ClockLatency clock{GetClockLatency(dev.get())};
- values[0] = clock.ClockTime.count();
- values[1] = clock.Latency.count();
- }
- break;
-
- default:
- auto ivals = al::vector<ALCint>(size);
- size = GetIntegerv(dev.get(), pname, {ivals.data(), ivals.size()});
- std::copy(ivals.begin(), ivals.begin()+size, values);
- break;
- }
- }
-}
-END_API_FUNC
-
-
-/* alcIsExtensionPresent
- *
- * Determines if there is support for a particular extension
- */
-ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extName)
-START_API_FUNC
-{
- DeviceRef dev{VerifyDevice(device)};
- if(!extName)
- alcSetError(dev.get(), ALC_INVALID_VALUE);
- else
- {
- size_t len = strlen(extName);
- const char *ptr = (dev ? alcExtensionList : alcNoDeviceExtList);
- while(ptr && *ptr)
- {
- if(strncasecmp(ptr, extName, len) == 0 &&
- (ptr[len] == '\0' || isspace(ptr[len])))
- return ALC_TRUE;
-
- if((ptr=strchr(ptr, ' ')) != nullptr)
- {
- do {
- ++ptr;
- } while(isspace(*ptr));
- }
- }
- }
- return ALC_FALSE;
-}
-END_API_FUNC
-
-
-/* alcGetProcAddress
- *
- * Retrieves the function address for a particular extension function
- */
-ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName)
-START_API_FUNC
-{
- if(!funcName)
- {
- DeviceRef dev{VerifyDevice(device)};
- alcSetError(dev.get(), ALC_INVALID_VALUE);
- }
- else
- {
- for(const auto &func : alcFunctions)
- {
- if(strcmp(func.funcName, funcName) == 0)
- return func.address;
- }
- }
- return nullptr;
-}
-END_API_FUNC
-
-
-/* alcGetEnumValue
- *
- * Get the value for a particular ALC enumeration name
- */
-ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName)
-START_API_FUNC
-{
- if(!enumName)
- {
- DeviceRef dev{VerifyDevice(device)};
- alcSetError(dev.get(), ALC_INVALID_VALUE);
- }
- else
- {
- for(const auto &enm : alcEnumerations)
- {
- if(strcmp(enm.enumName, enumName) == 0)
- return enm.value;
- }
- }
- return 0;
-}
-END_API_FUNC
-
-
-/* alcCreateContext
- *
- * Create and attach a context to the given device.
- */
-ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList)
-START_API_FUNC
-{
- /* Explicitly hold the list lock while taking the StateLock in case the
- * device is asynchronously destroyed, to ensure this new context is
- * properly cleaned up after being made.
- */
- std::unique_lock<std::recursive_mutex> listlock{ListLock};
- DeviceRef dev{VerifyDevice(device)};
- if(!dev || dev->Type == Capture || !dev->Connected.load(std::memory_order_relaxed))
- {
- listlock.unlock();
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- return nullptr;
- }
- std::unique_lock<std::mutex> statelock{dev->StateLock};
- listlock.unlock();
-
- dev->LastError.store(ALC_NO_ERROR);
-
- ContextRef context{new ALCcontext{dev.get()}};
- ALCdevice_IncRef(context->Device);
-
- ALCenum err{UpdateDeviceParams(dev.get(), attrList)};
- if(err != ALC_NO_ERROR)
- {
- alcSetError(dev.get(), err);
- if(err == ALC_INVALID_DEVICE)
- aluHandleDisconnect(dev.get(), "Device update failure");
- statelock.unlock();
-
- return nullptr;
- }
- AllocateVoices(context.get(), 256);
-
- if(DefaultEffect.type != AL_EFFECT_NULL && dev->Type == Playback)
- {
- void *ptr{al_calloc(16, sizeof(ALeffectslot))};
- context->DefaultSlot = std::unique_ptr<ALeffectslot>{new (ptr) ALeffectslot{}};
- if(InitEffectSlot(context->DefaultSlot.get()) == AL_NO_ERROR)
- aluInitEffectPanning(context->DefaultSlot.get(), dev.get());
- else
- {
- context->DefaultSlot = nullptr;
- ERR("Failed to initialize the default effect slot\n");
- }
- }
-
- InitContext(context.get());
-
- if(auto volopt = ConfigValueFloat(dev->DeviceName.c_str(), nullptr, "volume-adjust"))
- {
- const ALfloat valf{*volopt};
- if(!std::isfinite(valf))
- ERR("volume-adjust must be finite: %f\n", valf);
- else
- {
- const ALfloat db{clampf(valf, -24.0f, 24.0f)};
- if(db != valf)
- WARN("volume-adjust clamped: %f, range: +/-%f\n", valf, 24.0f);
- context->GainBoost = std::pow(10.0f, db/20.0f);
- TRACE("volume-adjust gain: %f\n", context->GainBoost);
- }
- }
- UpdateListenerProps(context.get());
-
- {
- using ContextArray = al::FlexArray<ALCcontext*>;
-
- /* Allocate a new context array, which holds 1 more than the current/
- * old array.
- */
- auto *oldarray = device->mContexts.load();
- const size_t newcount{oldarray->size()+1};
- void *ptr{al_calloc(alignof(ContextArray), ContextArray::Sizeof(newcount))};
- auto *newarray = new (ptr) ContextArray{newcount};
-
- /* Copy the current/old context handles to the new array, appending the
- * new context.
- */
- auto iter = std::copy(oldarray->begin(), oldarray->end(), newarray->begin());
- *iter = context.get();
-
- /* Store the new context array in the device. Wait for any current mix
- * to finish before deleting the old array.
- */
- dev->mContexts.store(newarray);
- if(oldarray != &EmptyContextArray)
- {
- while((dev->MixCount.load(std::memory_order_acquire)&1))
- std::this_thread::yield();
- delete oldarray;
- }
- }
- statelock.unlock();
-
- {
- std::lock_guard<std::recursive_mutex> _{ListLock};
- auto iter = std::lower_bound(ContextList.cbegin(), ContextList.cend(), context.get());
- ALCcontext_IncRef(context.get());
- ContextList.insert(iter, ContextRef{context.get()});
- }
-
- if(context->DefaultSlot)
- {
- if(InitializeEffect(context.get(), context->DefaultSlot.get(), &DefaultEffect) == AL_NO_ERROR)
- UpdateEffectSlotProps(context->DefaultSlot.get(), context.get());
- else
- ERR("Failed to initialize the default effect\n");
- }
-
- TRACE("Created context %p\n", context.get());
- return context.get();
-}
-END_API_FUNC
-
-/* alcDestroyContext
- *
- * Remove a context from its device
- */
-ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context)
-START_API_FUNC
-{
- std::unique_lock<std::recursive_mutex> listlock{ListLock};
- auto iter = std::lower_bound(ContextList.begin(), ContextList.end(), context);
- if(iter == ContextList.end() || *iter != context)
- {
- listlock.unlock();
- alcSetError(nullptr, ALC_INVALID_CONTEXT);
- return;
- }
- /* Hold an extra reference to this context so it remains valid until the
- * ListLock is released.
- */
- ContextRef ctx{std::move(*iter)};
- ContextList.erase(iter);
-
- ALCdevice *Device{ctx->Device};
-
- std::lock_guard<std::mutex> _{Device->StateLock};
- if(!ReleaseContext(ctx.get(), Device) && Device->Flags.get<DeviceRunning>())
- {
- Device->Backend->stop();
- Device->Flags.unset<DeviceRunning>();
- }
-}
-END_API_FUNC
-
-
-/* alcGetCurrentContext
- *
- * Returns the currently active context on the calling thread
- */
-ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void)
-START_API_FUNC
-{
- ALCcontext *Context{LocalContext.get()};
- if(!Context) Context = GlobalContext.load();
- return Context;
-}
-END_API_FUNC
-
-/* alcGetThreadContext
- *
- * Returns the currently active thread-local context
- */
-ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void)
-START_API_FUNC
-{ return LocalContext.get(); }
-END_API_FUNC
-
-/* 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)
-START_API_FUNC
-{
- /* context must be valid or nullptr */
- ContextRef ctx;
- if(context)
- {
- ctx = VerifyContext(context);
- if(!ctx)
- {
- alcSetError(nullptr, ALC_INVALID_CONTEXT);
- return ALC_FALSE;
- }
- }
- /* Release this reference (if any) to store it in the GlobalContext
- * pointer. Take ownership of the reference (if any) that was previously
- * stored there.
- */
- ctx = ContextRef{GlobalContext.exchange(ctx.release())};
-
- /* Reset (decrement) the previous global reference by replacing it with the
- * thread-local context. Take ownership of the thread-local context
- * reference (if any), clearing the storage to null.
- */
- ctx = ContextRef{LocalContext.get()};
- if(ctx) LocalContext.set(nullptr);
- /* Reset (decrement) the previous thread-local reference. */
-
- return ALC_TRUE;
-}
-END_API_FUNC
-
-/* alcSetThreadContext
- *
- * Makes the given context the active context for the current thread
- */
-ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context)
-START_API_FUNC
-{
- /* context must be valid or nullptr */
- ContextRef ctx;
- if(context)
- {
- ctx = VerifyContext(context);
- if(!ctx)
- {
- alcSetError(nullptr, ALC_INVALID_CONTEXT);
- return ALC_FALSE;
- }
- }
- /* context's reference count is already incremented */
- ContextRef old{LocalContext.get()};
- LocalContext.set(ctx.release());
-
- return ALC_TRUE;
-}
-END_API_FUNC
-
-
-/* alcGetContextsDevice
- *
- * Returns the device that a particular context is attached to
- */
-ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context)
-START_API_FUNC
-{
- ContextRef ctx{VerifyContext(Context)};
- if(!ctx)
- {
- alcSetError(nullptr, ALC_INVALID_CONTEXT);
- return nullptr;
- }
- return ctx->Device;
-}
-END_API_FUNC
-
-
-/* alcOpenDevice
- *
- * Opens the named device.
- */
-ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
-START_API_FUNC
-{
- DO_INITCONFIG();
-
- if(!PlaybackFactory)
- {
- 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;
-
- DeviceRef device{new ALCdevice{Playback}};
-
- /* Set output format */
- device->FmtChans = DevFmtChannelsDefault;
- device->FmtType = DevFmtTypeDefault;
- device->Frequency = DEFAULT_OUTPUT_RATE;
- device->UpdateSize = DEFAULT_UPDATE_SIZE;
- device->BufferSize = DEFAULT_UPDATE_SIZE * DEFAULT_NUM_UPDATES;
-
- device->SourcesMax = 256;
- device->AuxiliaryEffectSlotMax = 64;
- device->NumAuxSends = DEFAULT_SENDS;
-
- try {
- /* Create the device backend. */
- device->Backend = PlaybackFactory->createBackend(device.get(), BackendType::Playback);
-
- /* Find a playback device to open */
- ALCenum err{device->Backend->open(deviceName)};
- if(err != ALC_NO_ERROR)
- {
- alcSetError(nullptr, err);
- return nullptr;
- }
- }
- catch(al::backend_exception &e) {
- WARN("Failed to open playback device: %s\n", e.what());
- alcSetError(nullptr, e.errorCode());
- return nullptr;
- }
-
- deviceName = device->DeviceName.c_str();
- if(auto chanopt = ConfigValueStr(deviceName, nullptr, "channels"))
- {
- static constexpr struct ChannelMap {
- const char name[16];
- 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 },
- };
-
- const ALCchar *fmt{chanopt->c_str()};
- auto iter = std::find_if(std::begin(chanlist), std::end(chanlist),
- [fmt](const ChannelMap &entry) -> bool
- { return strcasecmp(entry.name, fmt) == 0; }
- );
- if(iter == std::end(chanlist))
- ERR("Unsupported channels: %s\n", fmt);
- else
- {
- device->FmtChans = iter->chans;
- device->mAmbiOrder = iter->order;
- device->Flags.set<ChannelsRequest>();
- }
- }
- if(auto typeopt = ConfigValueStr(deviceName, nullptr, "sample-type"))
- {
- static constexpr struct TypeMap {
- const char name[16];
- DevFmtType type;
- } typelist[] = {
- { "int8", DevFmtByte },
- { "uint8", DevFmtUByte },
- { "int16", DevFmtShort },
- { "uint16", DevFmtUShort },
- { "int32", DevFmtInt },
- { "uint32", DevFmtUInt },
- { "float32", DevFmtFloat },
- };
-
- const ALCchar *fmt{typeopt->c_str()};
- auto iter = std::find_if(std::begin(typelist), std::end(typelist),
- [fmt](const TypeMap &entry) -> bool
- { return strcasecmp(entry.name, fmt) == 0; }
- );
- if(iter == std::end(typelist))
- ERR("Unsupported sample-type: %s\n", fmt);
- else
- {
- device->FmtType = iter->type;
- device->Flags.set<SampleTypeRequest>();
- }
- }
-
- if(ALuint freq{ConfigValueUInt(deviceName, nullptr, "frequency").value_or(0)})
- {
- if(freq < MIN_OUTPUT_RATE)
- {
- ERR("%uhz request clamped to %uhz minimum\n", freq, MIN_OUTPUT_RATE);
- freq = MIN_OUTPUT_RATE;
- }
- device->UpdateSize = (device->UpdateSize*freq + device->Frequency/2) / device->Frequency;
- device->BufferSize = (device->BufferSize*freq + device->Frequency/2) / device->Frequency;
- device->Frequency = freq;
- device->Flags.set<FrequencyRequest>();
- }
-
- if(auto persizeopt = ConfigValueUInt(deviceName, nullptr, "period_size"))
- device->UpdateSize = clampu(*persizeopt, 64, 8192);
-
- if(auto peropt = ConfigValueUInt(deviceName, nullptr, "periods"))
- device->BufferSize = device->UpdateSize * clampu(*peropt, 2, 16);
- else
- device->BufferSize = maxu(device->BufferSize, device->UpdateSize*2);
-
- if(auto srcsopt = ConfigValueUInt(deviceName, nullptr, "sources"))
- {
- if(*srcsopt > 0) device->SourcesMax = *srcsopt;
- }
-
- if(auto slotsopt = ConfigValueUInt(deviceName, nullptr, "slots"))
- {
- if(*slotsopt > 0)
- device->AuxiliaryEffectSlotMax = minu(*slotsopt, INT_MAX);
- }
-
- if(auto sendsopt = ConfigValueInt(deviceName, nullptr, "sends"))
- device->NumAuxSends = clampi(DEFAULT_SENDS, 0, clampi(*sendsopt, 0, MAX_SENDS));
-
- device->NumStereoSources = 1;
- device->NumMonoSources = device->SourcesMax - device->NumStereoSources;
-
- if(auto ambiopt = ConfigValueStr(deviceName, nullptr, "ambi-format"))
- {
- const ALCchar *fmt{ambiopt->c_str()};
- if(strcasecmp(fmt, "fuma") == 0)
- {
- if(device->mAmbiOrder > 3)
- ERR("FuMa is incompatible with %d%s order ambisonics (up to third-order only)\n",
- device->mAmbiOrder,
- (((device->mAmbiOrder%100)/10) == 1) ? "th" :
- ((device->mAmbiOrder%10) == 1) ? "st" :
- ((device->mAmbiOrder%10) == 2) ? "nd" :
- ((device->mAmbiOrder%10) == 3) ? "rd" : "th");
- else
- {
- device->mAmbiLayout = AmbiLayout::FuMa;
- device->mAmbiScale = AmbiNorm::FuMa;
- }
- }
- else if(strcasecmp(fmt, "ambix") == 0 || strcasecmp(fmt, "acn+sn3d") == 0)
- {
- device->mAmbiLayout = AmbiLayout::ACN;
- device->mAmbiScale = AmbiNorm::SN3D;
- }
- else if(strcasecmp(fmt, "acn+n3d") == 0)
- {
- device->mAmbiLayout = AmbiLayout::ACN;
- device->mAmbiScale = AmbiNorm::N3D;
- }
- else
- ERR("Unsupported ambi-format: %s\n", fmt);
- }
-
- {
- std::lock_guard<std::recursive_mutex> _{ListLock};
- auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get());
- ALCdevice_IncRef(device.get());
- DeviceList.insert(iter, DeviceRef{device.get()});
- }
-
- TRACE("Created device %p, \"%s\"\n", device.get(), device->DeviceName.c_str());
- return device.get();
-}
-END_API_FUNC
-
-/* alcCloseDevice
- *
- * Closes the given device.
- */
-ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
-START_API_FUNC
-{
- std::unique_lock<std::recursive_mutex> listlock{ListLock};
- auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device);
- if(iter == DeviceList.end() || *iter != device)
- {
- alcSetError(nullptr, ALC_INVALID_DEVICE);
- return ALC_FALSE;
- }
- if((*iter)->Type == Capture)
- {
- alcSetError(iter->get(), ALC_INVALID_DEVICE);
- return ALC_FALSE;
- }
-
- /* Erase the device, and any remaining contexts left on it, from their
- * respective lists.
- */
- DeviceRef dev{std::move(*iter)};
- DeviceList.erase(iter);
-
- std::unique_lock<std::mutex> statelock{dev->StateLock};
- al::vector<ContextRef> orphanctxs;
- for(ALCcontext *ctx : *dev->mContexts.load())
- {
- auto iter = std::lower_bound(ContextList.begin(), ContextList.end(), ctx);
- if(iter != ContextList.end() && *iter == ctx)
- {
- orphanctxs.emplace_back(std::move(*iter));
- ContextList.erase(iter);
- }
- }
- listlock.unlock();
-
- for(ContextRef &context : orphanctxs)
- {
- WARN("Releasing context %p\n", context.get());
- ReleaseContext(context.get(), dev.get());
- }
- orphanctxs.clear();
-
- if(dev->Flags.get<DeviceRunning>())
- dev->Backend->stop();
- dev->Flags.unset<DeviceRunning>();
-
- return ALC_TRUE;
-}
-END_API_FUNC
-
-
-/************************************************
- * ALC capture functions
- ************************************************/
-ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples)
-START_API_FUNC
-{
- DO_INITCONFIG();
-
- if(!CaptureFactory)
- {
- 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;
-
- DeviceRef device{new ALCdevice{Capture}};
-
- auto decompfmt = DecomposeDevFormat(format);
- if(!decompfmt)
- {
- alcSetError(nullptr, ALC_INVALID_ENUM);
- return nullptr;
- }
-
- device->Frequency = frequency;
- device->FmtChans = decompfmt->chans;
- device->FmtType = decompfmt->type;
- device->Flags.set<FrequencyRequest, ChannelsRequest, SampleTypeRequest>();
-
- device->UpdateSize = samples;
- device->BufferSize = samples;
-
- try {
- device->Backend = CaptureFactory->createBackend(device.get(), BackendType::Capture);
-
- TRACE("Capture format: %s, %s, %uhz, %u / %u buffer\n",
- DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
- device->Frequency, device->UpdateSize, device->BufferSize);
- ALCenum err{device->Backend->open(deviceName)};
- if(err != ALC_NO_ERROR)
- {
- alcSetError(nullptr, err);
- return nullptr;
- }
- }
- catch(al::backend_exception &e) {
- WARN("Failed to open capture device: %s\n", e.what());
- alcSetError(nullptr, e.errorCode());
- return nullptr;
- }
-
- {
- std::lock_guard<std::recursive_mutex> _{ListLock};
- auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get());
- ALCdevice_IncRef(device.get());
- DeviceList.insert(iter, DeviceRef{device.get()});
- }
-
- TRACE("Created device %p, \"%s\"\n", device.get(), device->DeviceName.c_str());
- return device.get();
-}
-END_API_FUNC
-
-ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
-START_API_FUNC
-{
- std::unique_lock<std::recursive_mutex> listlock{ListLock};
- auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device);
- if(iter == DeviceList.end() || *iter != device)
- {
- alcSetError(nullptr, ALC_INVALID_DEVICE);
- return ALC_FALSE;
- }
- if((*iter)->Type != Capture)
- {
- alcSetError(iter->get(), ALC_INVALID_DEVICE);
- return ALC_FALSE;
- }
-
- DeviceRef dev{std::move(*iter)};
- DeviceList.erase(iter);
- listlock.unlock();
-
- std::lock_guard<std::mutex> _{dev->StateLock};
- if(dev->Flags.get<DeviceRunning>())
- dev->Backend->stop();
- dev->Flags.unset<DeviceRunning>();
-
- return ALC_TRUE;
-}
-END_API_FUNC
-
-ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
-START_API_FUNC
-{
- DeviceRef dev{VerifyDevice(device)};
- if(!dev || dev->Type != Capture)
- {
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- return;
- }
-
- std::lock_guard<std::mutex> _{dev->StateLock};
- if(!dev->Connected.load(std::memory_order_acquire))
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- else if(!dev->Flags.get<DeviceRunning>())
- {
- if(dev->Backend->start())
- dev->Flags.set<DeviceRunning>();
- else
- {
- aluHandleDisconnect(dev.get(), "Device start failure");
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- }
- }
-}
-END_API_FUNC
-
-ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
-START_API_FUNC
-{
- DeviceRef dev{VerifyDevice(device)};
- if(!dev || dev->Type != Capture)
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- else
- {
- std::lock_guard<std::mutex> _{dev->StateLock};
- if(dev->Flags.get<DeviceRunning>())
- dev->Backend->stop();
- dev->Flags.unset<DeviceRunning>();
- }
-}
-END_API_FUNC
-
-ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
-START_API_FUNC
-{
- DeviceRef dev{VerifyDevice(device)};
- if(!dev || dev->Type != Capture)
- {
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- return;
- }
-
- ALCenum err{ALC_INVALID_VALUE};
- { std::lock_guard<std::mutex> _{dev->StateLock};
- BackendBase *backend{dev->Backend.get()};
- if(samples >= 0 && backend->availableSamples() >= static_cast<ALCuint>(samples))
- err = backend->captureSamples(buffer, samples);
- }
- if(err != ALC_NO_ERROR)
- alcSetError(dev.get(), err);
-}
-END_API_FUNC
-
-
-/************************************************
- * ALC loopback functions
- ************************************************/
-
-/* alcLoopbackOpenDeviceSOFT
- *
- * Open a loopback device, for manual rendering.
- */
-ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName)
-START_API_FUNC
-{
- DO_INITCONFIG();
-
- /* Make sure the device name, if specified, is us. */
- if(deviceName && strcmp(deviceName, alcDefaultName) != 0)
- {
- alcSetError(nullptr, ALC_INVALID_VALUE);
- return nullptr;
- }
-
- DeviceRef device{new ALCdevice{Loopback}};
-
- device->SourcesMax = 256;
- device->AuxiliaryEffectSlotMax = 64;
- device->NumAuxSends = DEFAULT_SENDS;
-
- //Set output format
- device->BufferSize = 0;
- device->UpdateSize = 0;
-
- device->Frequency = DEFAULT_OUTPUT_RATE;
- device->FmtChans = DevFmtChannelsDefault;
- device->FmtType = DevFmtTypeDefault;
-
- if(auto srcsopt = ConfigValueUInt(nullptr, nullptr, "sources"))
- {
- if(*srcsopt > 0) device->SourcesMax = *srcsopt;
- }
-
- if(auto slotsopt = ConfigValueUInt(nullptr, nullptr, "slots"))
- {
- if(*slotsopt > 0)
- device->AuxiliaryEffectSlotMax = minu(*slotsopt, INT_MAX);
- }
-
- if(auto sendsopt = ConfigValueInt(nullptr, nullptr, "sends"))
- device->NumAuxSends = clampi(DEFAULT_SENDS, 0, clampi(*sendsopt, 0, MAX_SENDS));
-
- device->NumStereoSources = 1;
- device->NumMonoSources = device->SourcesMax - device->NumStereoSources;
-
- try {
- device->Backend = LoopbackBackendFactory::getFactory().createBackend(device.get(),
- BackendType::Playback);
-
- // Open the "backend"
- device->Backend->open("Loopback");
- }
- catch(al::backend_exception &e) {
- WARN("Failed to open loopback device: %s\n", e.what());
- alcSetError(nullptr, e.errorCode());
- return nullptr;
- }
-
- {
- std::lock_guard<std::recursive_mutex> _{ListLock};
- auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get());
- ALCdevice_IncRef(device.get());
- DeviceList.insert(iter, DeviceRef{device.get()});
- }
-
- TRACE("Created device %p\n", device.get());
- return device.get();
-}
-END_API_FUNC
-
-/* 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)
-START_API_FUNC
-{
- DeviceRef dev{VerifyDevice(device)};
- if(!dev || dev->Type != Loopback)
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- else if(freq <= 0)
- alcSetError(dev.get(), ALC_INVALID_VALUE);
- else
- {
- if(IsValidALCType(type) && IsValidALCChannels(channels) && freq >= MIN_OUTPUT_RATE)
- return ALC_TRUE;
- }
-
- return ALC_FALSE;
-}
-END_API_FUNC
-
-/* 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)
-START_API_FUNC
-{
- DeviceRef dev{VerifyDevice(device)};
- if(!dev || dev->Type != Loopback)
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- else if(samples < 0 || (samples > 0 && buffer == nullptr))
- alcSetError(dev.get(), ALC_INVALID_VALUE);
- else
- {
- BackendLockGuard _{*device->Backend};
- aluMixData(dev.get(), buffer, samples);
- }
-}
-END_API_FUNC
-
-
-/************************************************
- * ALC DSP pause/resume functions
- ************************************************/
-
-/* alcDevicePauseSOFT
- *
- * Pause the DSP to stop audio processing.
- */
-ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device)
-START_API_FUNC
-{
- DeviceRef dev{VerifyDevice(device)};
- if(!dev || dev->Type != Playback)
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- else
- {
- std::lock_guard<std::mutex> _{dev->StateLock};
- if(dev->Flags.get<DeviceRunning>())
- dev->Backend->stop();
- dev->Flags.unset<DeviceRunning>();
- dev->Flags.set<DevicePaused>();
- }
-}
-END_API_FUNC
-
-/* alcDeviceResumeSOFT
- *
- * Resume the DSP to restart audio processing.
- */
-ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device)
-START_API_FUNC
-{
- DeviceRef dev{VerifyDevice(device)};
- if(!dev || dev->Type != Playback)
- {
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- return;
- }
-
- std::lock_guard<std::mutex> _{dev->StateLock};
- if(!dev->Flags.get<DevicePaused>())
- return;
- dev->Flags.unset<DevicePaused>();
- if(dev->mContexts.load()->empty())
- return;
-
- if(dev->Backend->start() == ALC_FALSE)
- {
- aluHandleDisconnect(dev.get(), "Device start failure");
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- return;
- }
- dev->Flags.set<DeviceRunning>();
-}
-END_API_FUNC
-
-
-/************************************************
- * 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)
-START_API_FUNC
-{
- DeviceRef dev{VerifyDevice(device)};
- if(!dev || dev->Type == Capture)
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- else switch(paramName)
- {
- case ALC_HRTF_SPECIFIER_SOFT:
- if(index >= 0 && static_cast<size_t>(index) < dev->HrtfList.size())
- return dev->HrtfList[index].name.c_str();
- alcSetError(dev.get(), ALC_INVALID_VALUE);
- break;
-
- default:
- alcSetError(dev.get(), ALC_INVALID_ENUM);
- break;
- }
-
- return nullptr;
-}
-END_API_FUNC
-
-/* alcResetDeviceSOFT
- *
- * Resets the given device output, using the specified attribute list.
- */
-ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs)
-START_API_FUNC
-{
- std::unique_lock<std::recursive_mutex> listlock{ListLock};
- DeviceRef dev{VerifyDevice(device)};
- if(!dev || dev->Type == Capture)
- {
- listlock.unlock();
- alcSetError(dev.get(), ALC_INVALID_DEVICE);
- return ALC_FALSE;
- }
- std::lock_guard<std::mutex> _{dev->StateLock};
- listlock.unlock();
-
- /* Force the backend to stop mixing first since we're resetting. Also reset
- * the connected state so lost devices can attempt recover.
- */
- if(dev->Flags.get<DeviceRunning>())
- dev->Backend->stop();
- dev->Flags.unset<DeviceRunning>();
- device->Connected.store(true);
-
- ALCenum err{UpdateDeviceParams(dev.get(), attribs)};
- if(LIKELY(err == ALC_NO_ERROR)) return ALC_TRUE;
-
- alcSetError(dev.get(), err);
- if(err == ALC_INVALID_DEVICE)
- aluHandleDisconnect(dev.get(), "Device start failure");
- return ALC_FALSE;
-}
-END_API_FUNC
diff --git a/Alc/alcmain.h b/Alc/alcmain.h
deleted file mode 100644
index a22e0e81..00000000
--- a/Alc/alcmain.h
+++ /dev/null
@@ -1,534 +0,0 @@
-#ifndef ALC_MAIN_H
-#define ALC_MAIN_H
-
-#include <algorithm>
-#include <array>
-#include <atomic>
-#include <chrono>
-#include <cstdint>
-#include <cstddef>
-#include <memory>
-#include <mutex>
-#include <string>
-#include <utility>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "AL/alext.h"
-
-#include "albyte.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-#include "alspan.h"
-#include "ambidefs.h"
-#include "atomic.h"
-#include "hrtf.h"
-#include "inprogext.h"
-#include "vector.h"
-
-class BFormatDec;
-struct ALbuffer;
-struct ALeffect;
-struct ALfilter;
-struct BackendBase;
-struct Compressor;
-struct EffectState;
-struct FrontStablizer;
-struct Uhj2Encoder;
-struct bs2b;
-
-
-#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
-#define IS_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
-#else
-static const union {
- ALuint u;
- ALubyte b[sizeof(ALuint)];
-} EndianTest = { 1 };
-#define IS_LITTLE_ENDIAN (EndianTest.b[0] == 1)
-#endif
-
-
-#define MIN_OUTPUT_RATE 8000
-#define DEFAULT_OUTPUT_RATE 44100
-#define DEFAULT_UPDATE_SIZE 882 /* 20ms */
-#define DEFAULT_NUM_UPDATES 3
-
-
-enum Channel {
- FrontLeft = 0,
- FrontRight,
- FrontCenter,
- LFE,
- BackLeft,
- BackRight,
- BackCenter,
- SideLeft,
- SideRight,
-
- UpperFrontLeft,
- UpperFrontRight,
- UpperBackLeft,
- UpperBackRight,
- LowerFrontLeft,
- LowerFrontRight,
- LowerBackLeft,
- LowerBackRight,
-
- Aux0,
- Aux1,
- Aux2,
- Aux3,
- Aux4,
- Aux5,
- Aux6,
- Aux7,
- Aux8,
- Aux9,
- Aux10,
- Aux11,
- Aux12,
- Aux13,
- Aux14,
- Aux15,
-
- MaxChannels
-};
-
-
-/* Device formats */
-enum DevFmtType : ALenum {
- DevFmtByte = ALC_BYTE_SOFT,
- DevFmtUByte = ALC_UNSIGNED_BYTE_SOFT,
- DevFmtShort = ALC_SHORT_SOFT,
- DevFmtUShort = ALC_UNSIGNED_SHORT_SOFT,
- DevFmtInt = ALC_INT_SOFT,
- DevFmtUInt = ALC_UNSIGNED_INT_SOFT,
- DevFmtFloat = ALC_FLOAT_SOFT,
-
- DevFmtTypeDefault = DevFmtFloat
-};
-enum DevFmtChannels : ALenum {
- DevFmtMono = ALC_MONO_SOFT,
- DevFmtStereo = ALC_STEREO_SOFT,
- DevFmtQuad = ALC_QUAD_SOFT,
- DevFmtX51 = ALC_5POINT1_SOFT,
- DevFmtX61 = ALC_6POINT1_SOFT,
- DevFmtX71 = ALC_7POINT1_SOFT,
- DevFmtAmbi3D = ALC_BFORMAT3D_SOFT,
-
- /* Similar to 5.1, except using rear channels instead of sides */
- DevFmtX51Rear = 0x70000000,
-
- DevFmtChannelsDefault = DevFmtStereo
-};
-#define MAX_OUTPUT_CHANNELS (16)
-
-/* DevFmtType traits, providing the type, etc given a DevFmtType. */
-template<DevFmtType T>
-struct DevFmtTypeTraits { };
-
-template<>
-struct DevFmtTypeTraits<DevFmtByte> { using Type = ALbyte; };
-template<>
-struct DevFmtTypeTraits<DevFmtUByte> { using Type = ALubyte; };
-template<>
-struct DevFmtTypeTraits<DevFmtShort> { using Type = ALshort; };
-template<>
-struct DevFmtTypeTraits<DevFmtUShort> { using Type = ALushort; };
-template<>
-struct DevFmtTypeTraits<DevFmtInt> { using Type = ALint; };
-template<>
-struct DevFmtTypeTraits<DevFmtUInt> { using Type = ALuint; };
-template<>
-struct DevFmtTypeTraits<DevFmtFloat> { using Type = ALfloat; };
-
-
-ALsizei BytesFromDevFmt(DevFmtType type) noexcept;
-ALsizei ChannelsFromDevFmt(DevFmtChannels chans, ALsizei ambiorder) noexcept;
-inline ALsizei FrameSizeFromDevFmt(DevFmtChannels chans, DevFmtType type, ALsizei ambiorder) noexcept
-{ return ChannelsFromDevFmt(chans, ambiorder) * BytesFromDevFmt(type); }
-
-enum class AmbiLayout {
- FuMa = ALC_FUMA_SOFT, /* FuMa channel order */
- ACN = ALC_ACN_SOFT, /* ACN channel order */
-
- Default = ACN
-};
-
-enum class AmbiNorm {
- FuMa = ALC_FUMA_SOFT, /* FuMa normalization */
- SN3D = ALC_SN3D_SOFT, /* SN3D normalization */
- N3D = ALC_N3D_SOFT, /* N3D normalization */
-
- Default = SN3D
-};
-
-
-enum DeviceType {
- Playback,
- Capture,
- Loopback
-};
-
-
-enum RenderMode {
- NormalRender,
- StereoPair,
- HrtfRender
-};
-
-
-struct BufferSubList {
- uint64_t FreeMask{~0_u64};
- ALbuffer *Buffers{nullptr}; /* 64 */
-
- BufferSubList() noexcept = default;
- BufferSubList(const BufferSubList&) = delete;
- BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers}
- { rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; }
- ~BufferSubList();
-
- BufferSubList& operator=(const BufferSubList&) = delete;
- BufferSubList& operator=(BufferSubList&& rhs) noexcept
- { std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; }
-};
-
-struct EffectSubList {
- uint64_t FreeMask{~0_u64};
- ALeffect *Effects{nullptr}; /* 64 */
-
- EffectSubList() noexcept = default;
- EffectSubList(const EffectSubList&) = delete;
- EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects}
- { rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; }
- ~EffectSubList();
-
- EffectSubList& operator=(const EffectSubList&) = delete;
- EffectSubList& operator=(EffectSubList&& rhs) noexcept
- { std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; }
-};
-
-struct FilterSubList {
- uint64_t FreeMask{~0_u64};
- ALfilter *Filters{nullptr}; /* 64 */
-
- FilterSubList() noexcept = default;
- FilterSubList(const FilterSubList&) = delete;
- FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters}
- { rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; }
- ~FilterSubList();
-
- FilterSubList& operator=(const FilterSubList&) = delete;
- FilterSubList& operator=(FilterSubList&& rhs) noexcept
- { std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; }
-};
-
-
-/* Maximum delay in samples for speaker distance compensation. */
-#define MAX_DELAY_LENGTH 1024
-
-class DistanceComp {
-public:
- struct DistData {
- ALfloat Gain{1.0f};
- ALsizei Length{0}; /* Valid range is [0...MAX_DELAY_LENGTH). */
- ALfloat *Buffer{nullptr};
- };
-
-private:
- std::array<DistData,MAX_OUTPUT_CHANNELS> mChannels;
- al::vector<ALfloat,16> mSamples;
-
-public:
- void setSampleCount(size_t new_size) { mSamples.resize(new_size); }
- void clear() noexcept
- {
- for(auto &chan : mChannels)
- {
- chan.Gain = 1.0f;
- chan.Length = 0;
- chan.Buffer = nullptr;
- }
- using SampleVecT = decltype(mSamples);
- SampleVecT{}.swap(mSamples);
- }
-
- ALfloat *getSamples() noexcept { return mSamples.data(); }
-
- al::span<DistData,MAX_OUTPUT_CHANNELS> as_span() { return mChannels; }
-};
-
-struct BFChannelConfig {
- ALfloat Scale;
- ALsizei Index;
-};
-
-/* Size for temporary storage of buffer data, in ALfloats. Larger values need
- * more memory, while smaller values may need more iterations. The value needs
- * to be a sensible size, however, as it constrains the max stepping value used
- * for mixing, as well as the maximum number of samples per mixing iteration.
- */
-#define BUFFERSIZE 1024
-
-using FloatBufferLine = std::array<float,BUFFERSIZE>;
-
-/* Maximum number of samples to pad on either end of a buffer for resampling.
- * Note that both the beginning and end need padding!
- */
-#define MAX_RESAMPLE_PADDING 24
-
-
-struct MixParams {
- /* Coefficient channel mapping for mixing to the buffer. */
- std::array<BFChannelConfig,MAX_OUTPUT_CHANNELS> AmbiMap{};
-
- al::span<FloatBufferLine> Buffer;
-};
-
-struct RealMixParams {
- std::array<ALint,MaxChannels> ChannelIndex{};
-
- al::span<FloatBufferLine> Buffer;
-};
-
-using POSTPROCESS = void(*)(ALCdevice *device, const ALsizei SamplesToDo);
-
-enum {
- // Frequency was requested by the app or config file
- FrequencyRequest,
- // Channel configuration was requested by the config file
- ChannelsRequest,
- // Sample type was requested by the config file
- SampleTypeRequest,
-
- // Specifies if the DSP is paused at user request
- DevicePaused,
- // Specifies if the device is currently running
- DeviceRunning,
-
- DeviceFlagsCount
-};
-
-struct ALCdevice {
- RefCount ref{1u};
-
- std::atomic<bool> Connected{true};
- const DeviceType Type{};
-
- ALuint Frequency{};
- ALuint UpdateSize{};
- ALuint BufferSize{};
-
- DevFmtChannels FmtChans{};
- DevFmtType FmtType{};
- ALboolean IsHeadphones{AL_FALSE};
- ALsizei mAmbiOrder{0};
- /* For DevFmtAmbi* output only, specifies the channel order and
- * normalization.
- */
- AmbiLayout mAmbiLayout{AmbiLayout::Default};
- AmbiNorm mAmbiScale{AmbiNorm::Default};
-
- ALCenum LimiterState{ALC_DONT_CARE_SOFT};
-
- std::string DeviceName;
-
- // Device flags
- al::bitfield<DeviceFlagsCount> Flags{};
-
- std::string HrtfName;
- al::vector<EnumeratedHrtf> HrtfList;
- ALCenum HrtfStatus{ALC_FALSE};
-
- std::atomic<ALCenum> LastError{ALC_NO_ERROR};
-
- // Maximum number of sources that can be created
- ALuint SourcesMax{};
- // Maximum number of slots that can be created
- ALuint AuxiliaryEffectSlotMax{};
-
- ALCuint NumMonoSources{};
- ALCuint NumStereoSources{};
- ALsizei NumAuxSends{};
-
- // Map of Buffers for this device
- std::mutex BufferLock;
- al::vector<BufferSubList> BufferList;
-
- // Map of Effects for this device
- std::mutex EffectLock;
- al::vector<EffectSubList> EffectList;
-
- // Map of Filters for this device
- std::mutex FilterLock;
- al::vector<FilterSubList> FilterList;
-
- /* Rendering mode. */
- RenderMode mRenderMode{NormalRender};
-
- /* The average speaker distance as determined by the ambdec configuration,
- * HRTF data set, or the NFC-HOA reference delay. Only used for NFC.
- */
- ALfloat AvgSpeakerDist{0.0f};
-
- ALuint SamplesDone{0u};
- std::chrono::nanoseconds ClockBase{0};
- std::chrono::nanoseconds FixedLatency{0};
-
- /* Temp storage used for mixer processing. */
- alignas(16) ALfloat SourceData[BUFFERSIZE + MAX_RESAMPLE_PADDING*2];
- alignas(16) ALfloat ResampledData[BUFFERSIZE];
- alignas(16) ALfloat FilteredData[BUFFERSIZE];
- union {
- alignas(16) ALfloat HrtfSourceData[BUFFERSIZE + HRTF_HISTORY_LENGTH];
- alignas(16) ALfloat NfcSampleData[BUFFERSIZE];
- };
- alignas(16) float2 HrtfAccumData[BUFFERSIZE + HRIR_LENGTH];
-
- /* Mixing buffer used by the Dry mix and Real output. */
- al::vector<FloatBufferLine, 16> MixBuffer;
-
- /* The "dry" path corresponds to the main output. */
- MixParams Dry;
- ALuint NumChannelsPerOrder[MAX_AMBI_ORDER+1]{};
-
- /* "Real" output, which will be written to the device buffer. May alias the
- * dry buffer.
- */
- RealMixParams RealOut;
-
- /* HRTF state and info */
- std::unique_ptr<DirectHrtfState> mHrtfState;
- HrtfEntry *mHrtf{nullptr};
-
- /* Ambisonic-to-UHJ encoder */
- std::unique_ptr<Uhj2Encoder> Uhj_Encoder;
-
- /* Ambisonic decoder for speakers */
- std::unique_ptr<BFormatDec> AmbiDecoder;
-
- /* Stereo-to-binaural filter */
- std::unique_ptr<bs2b> Bs2b;
-
- POSTPROCESS PostProcess{};
-
- std::unique_ptr<FrontStablizer> Stablizer;
-
- std::unique_ptr<Compressor> Limiter;
-
- /* Delay buffers used to compensate for speaker distances. */
- DistanceComp ChannelDelay;
-
- /* Dithering control. */
- ALfloat DitherDepth{0.0f};
- ALuint DitherSeed{0u};
-
- /* Running count of the mixer invocations, in 31.1 fixed point. This
- * actually increments *twice* when mixing, first at the start and then at
- * the end, so the bottom bit indicates if the device is currently mixing
- * and the upper bits indicates how many mixes have been done.
- */
- RefCount MixCount{0u};
-
- // Contexts created on this device
- std::atomic<al::FlexArray<ALCcontext*>*> mContexts{nullptr};
-
- /* This lock protects the device state (format, update size, etc) from
- * being from being changed in multiple threads, or being accessed while
- * being changed. It's also used to serialize calls to the backend.
- */
- std::mutex StateLock;
- std::unique_ptr<BackendBase> Backend;
-
-
- ALCdevice(DeviceType type);
- ALCdevice(const ALCdevice&) = delete;
- ALCdevice& operator=(const ALCdevice&) = delete;
- ~ALCdevice();
-
- ALsizei bytesFromFmt() const noexcept { return BytesFromDevFmt(FmtType); }
- ALsizei channelsFromFmt() const noexcept { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); }
- ALsizei frameSizeFromFmt() const noexcept { return bytesFromFmt() * channelsFromFmt(); }
-
- DEF_NEWDEL(ALCdevice)
-};
-
-/* Must be less than 15 characters (16 including terminating null) for
- * compatibility with pthread_setname_np limitations. */
-#define MIXER_THREAD_NAME "alsoft-mixer"
-
-#define RECORD_THREAD_NAME "alsoft-record"
-
-
-enum {
- /* End event thread processing. */
- EventType_KillThread = 0,
-
- /* User event types. */
- EventType_SourceStateChange = 1<<0,
- EventType_BufferCompleted = 1<<1,
- EventType_Error = 1<<2,
- EventType_Performance = 1<<3,
- EventType_Deprecated = 1<<4,
- EventType_Disconnected = 1<<5,
-
- /* Internal events. */
- EventType_ReleaseEffectState = 65536,
-};
-
-struct AsyncEvent {
- unsigned int EnumType{0u};
- union {
- char dummy;
- struct {
- ALuint id;
- ALenum state;
- } srcstate;
- struct {
- ALuint id;
- ALsizei count;
- } bufcomp;
- struct {
- ALenum type;
- ALuint id;
- ALuint param;
- ALchar msg[1008];
- } user;
- EffectState *mEffectState;
- } u{};
-
- AsyncEvent() noexcept = default;
- constexpr AsyncEvent(unsigned int type) noexcept : EnumType{type} { }
-};
-
-
-void AllocateVoices(ALCcontext *context, size_t num_voices);
-
-
-extern ALint RTPrioLevel;
-void SetRTPriority(void);
-
-void SetDefaultChannelOrder(ALCdevice *device);
-void SetDefaultWFXChannelOrder(ALCdevice *device);
-
-const ALCchar *DevFmtTypeString(DevFmtType type) noexcept;
-const ALCchar *DevFmtChannelsString(DevFmtChannels chans) noexcept;
-
-/**
- * GetChannelIdxByName
- *
- * Returns the index for the given channel name (e.g. FrontCenter), or -1 if it
- * doesn't exist.
- */
-inline ALint GetChannelIdxByName(const RealMixParams &real, Channel chan) noexcept
-{ return real.ChannelIndex[chan]; }
-
-
-void StartEventThrd(ALCcontext *ctx);
-void StopEventThrd(ALCcontext *ctx);
-
-
-al::vector<std::string> SearchDataFiles(const char *match, const char *subdir);
-
-#endif
diff --git a/Alc/alconfig.cpp b/Alc/alconfig.cpp
deleted file mode 100644
index b246a91d..00000000
--- a/Alc/alconfig.cpp
+++ /dev/null
@@ -1,545 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#ifdef _WIN32
-#ifdef __MINGW32__
-#define _WIN32_IE 0x501
-#else
-#define _WIN32_IE 0x400
-#endif
-#endif
-
-#include "config.h"
-
-#include "alconfig.h"
-
-#include <cstdlib>
-#include <cctype>
-#include <cstring>
-#ifdef _WIN32_IE
-#include <windows.h>
-#include <shlobj.h>
-#endif
-#ifdef __APPLE__
-#include <CoreFoundation/CoreFoundation.h>
-#endif
-
-#include <vector>
-#include <string>
-#include <algorithm>
-
-#include "alcmain.h"
-#include "logging.h"
-#include "compat.h"
-
-
-namespace {
-
-struct ConfigEntry {
- std::string key;
- std::string value;
-};
-al::vector<ConfigEntry> ConfOpts;
-
-
-std::string &lstrip(std::string &line)
-{
- size_t pos{0};
- while(pos < line.length() && std::isspace(line[pos]))
- ++pos;
- line.erase(0, pos);
- return line;
-}
-
-bool readline(std::istream &f, std::string &output)
-{
- while(f.good() && f.peek() == '\n')
- f.ignore();
-
- return std::getline(f, output) && !output.empty();
-}
-
-std:: string expdup(const char *str)
-{
- std::string output;
-
- while(*str != '\0')
- {
- const char *addstr;
- size_t addstrlen;
-
- if(str[0] != '$')
- {
- const char *next = std::strchr(str, '$');
- addstr = str;
- addstrlen = next ? static_cast<size_t>(next-str) : std::strlen(str);
-
- str += addstrlen;
- }
- else
- {
- str++;
- if(*str == '$')
- {
- const char *next = std::strchr(str+1, '$');
- addstr = str;
- addstrlen = next ? static_cast<size_t>(next-str) : std::strlen(str);
-
- str += addstrlen;
- }
- else
- {
- bool hasbraces{(*str == '{')};
- if(hasbraces) str++;
-
- std::string envname;
- while((std::isalnum(*str) || *str == '_'))
- envname += *(str++);
-
- if(hasbraces && *str != '}')
- continue;
-
- if(hasbraces) str++;
- if((addstr=std::getenv(envname.c_str())) == nullptr)
- continue;
- addstrlen = std::strlen(addstr);
- }
- }
- if(addstrlen == 0)
- continue;
-
- output.append(addstr, addstrlen);
- }
-
- return output;
-}
-
-void LoadConfigFromFile(std::istream &f)
-{
- std::string curSection;
- std::string buffer;
-
- while(readline(f, buffer))
- {
- while(!buffer.empty() && std::isspace(buffer.back()))
- buffer.pop_back();
- if(lstrip(buffer).empty())
- continue;
-
- buffer.push_back(0);
- char *line{&buffer[0]};
-
- if(line[0] == '[')
- {
- char *section = line+1;
- char *endsection;
-
- endsection = std::strchr(section, ']');
- if(!endsection || section == endsection)
- {
- ERR("config parse error: bad line \"%s\"\n", line);
- continue;
- }
- if(endsection[1] != 0)
- {
- char *end = endsection+1;
- while(std::isspace(*end))
- ++end;
- if(*end != 0 && *end != '#')
- {
- ERR("config parse error: bad line \"%s\"\n", line);
- continue;
- }
- }
- *endsection = 0;
-
- curSection.clear();
- if(strcasecmp(section, "general") != 0)
- {
- do {
- char *nextp = std::strchr(section, '%');
- if(!nextp)
- {
- curSection += section;
- break;
- }
-
- curSection.append(section, nextp);
- section = nextp;
-
- if(((section[1] >= '0' && section[1] <= '9') ||
- (section[1] >= 'a' && section[1] <= 'f') ||
- (section[1] >= 'A' && section[1] <= 'F')) &&
- ((section[2] >= '0' && section[2] <= '9') ||
- (section[2] >= 'a' && section[2] <= 'f') ||
- (section[2] >= 'A' && section[2] <= 'F')))
- {
- unsigned char b = 0;
- if(section[1] >= '0' && section[1] <= '9')
- b = (section[1]-'0') << 4;
- else if(section[1] >= 'a' && section[1] <= 'f')
- b = (section[1]-'a'+0xa) << 4;
- else if(section[1] >= 'A' && section[1] <= 'F')
- b = (section[1]-'A'+0x0a) << 4;
- if(section[2] >= '0' && section[2] <= '9')
- b |= (section[2]-'0');
- else if(section[2] >= 'a' && section[2] <= 'f')
- b |= (section[2]-'a'+0xa);
- else if(section[2] >= 'A' && section[2] <= 'F')
- b |= (section[2]-'A'+0x0a);
- curSection += static_cast<char>(b);
- section += 3;
- }
- else if(section[1] == '%')
- {
- curSection += '%';
- section += 2;
- }
- else
- {
- curSection += '%';
- section += 1;
- }
- } while(*section != 0);
- }
-
- continue;
- }
-
- char *comment{std::strchr(line, '#')};
- if(comment) *(comment++) = 0;
- if(!line[0]) continue;
-
- char key[256]{};
- char value[256]{};
- if(std::sscanf(line, "%255[^=] = \"%255[^\"]\"", key, value) == 2 ||
- std::sscanf(line, "%255[^=] = '%255[^\']'", key, value) == 2 ||
- std::sscanf(line, "%255[^=] = %255[^\n]", key, value) == 2)
- {
- /* sscanf doesn't handle '' or "" as empty values, so clip it
- * manually. */
- if(std::strcmp(value, "\"\"") == 0 || std::strcmp(value, "''") == 0)
- value[0] = 0;
- }
- else if(sscanf(line, "%255[^=] %255[=]", key, value) == 2)
- {
- /* Special case for 'key =' */
- value[0] = 0;
- }
- else
- {
- ERR("config parse error: malformed option line: \"%s\"\n\n", line);
- continue;
- }
-
- std::string fullKey;
- if(!curSection.empty())
- {
- fullKey += curSection;
- fullKey += '/';
- }
- fullKey += key;
- while(!fullKey.empty() && std::isspace(fullKey.back()))
- fullKey.pop_back();
-
- /* Check if we already have this option set */
- auto ent = std::find_if(ConfOpts.begin(), ConfOpts.end(),
- [&fullKey](const ConfigEntry &entry) -> bool
- { return entry.key == fullKey; }
- );
- if(ent != ConfOpts.end())
- ent->value = expdup(value);
- else
- {
- ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(value)});
- ent = ConfOpts.end()-1;
- }
-
- TRACE("found '%s' = '%s'\n", ent->key.c_str(), ent->value.c_str());
- }
- ConfOpts.shrink_to_fit();
-}
-
-} // namespace
-
-
-#ifdef _WIN32
-void ReadALConfig()
-{
- WCHAR buffer[MAX_PATH];
- if(SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE) != FALSE)
- {
- std::string filepath{wstr_to_utf8(buffer)};
- filepath += "\\alsoft.ini";
-
- TRACE("Loading config %s...\n", filepath.c_str());
- al::ifstream f{filepath};
- if(f.is_open())
- LoadConfigFromFile(f);
- }
-
- std::string ppath{GetProcBinary().path};
- if(!ppath.empty())
- {
- ppath += "\\alsoft.ini";
- TRACE("Loading config %s...\n", ppath.c_str());
- al::ifstream f{ppath};
- if(f.is_open())
- LoadConfigFromFile(f);
- }
-
- const WCHAR *str{_wgetenv(L"ALSOFT_CONF")};
- if(str != nullptr && *str)
- {
- std::string filepath{wstr_to_utf8(str)};
-
- TRACE("Loading config %s...\n", filepath.c_str());
- al::ifstream f{filepath};
- if(f.is_open())
- LoadConfigFromFile(f);
- }
-}
-#else
-void ReadALConfig()
-{
- const char *str{"/etc/openal/alsoft.conf"};
-
- TRACE("Loading config %s...\n", str);
- al::ifstream f{str};
- if(f.is_open())
- LoadConfigFromFile(f);
- f.close();
-
- if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0)
- str = "/etc/xdg";
- std::string confpaths = str;
- /* Go through the list in reverse, since "the order of base directories
- * denotes their importance; the first directory listed is the most
- * important". Ergo, we need to load the settings from the later dirs
- * first so that the settings in the earlier dirs override them.
- */
- std::string fname;
- while(!confpaths.empty())
- {
- auto next = confpaths.find_last_of(':');
- if(next < confpaths.length())
- {
- fname = confpaths.substr(next+1);
- confpaths.erase(next);
- }
- else
- {
- fname = confpaths;
- confpaths.clear();
- }
-
- if(fname.empty() || fname.front() != '/')
- WARN("Ignoring XDG config dir: %s\n", fname.c_str());
- else
- {
- if(fname.back() != '/') fname += "/alsoft.conf";
- else fname += "alsoft.conf";
-
- TRACE("Loading config %s...\n", fname.c_str());
- al::ifstream f{fname};
- if(f.is_open())
- LoadConfigFromFile(f);
- }
- fname.clear();
- }
-
-#ifdef __APPLE__
- CFBundleRef mainBundle = CFBundleGetMainBundle();
- if(mainBundle)
- {
- unsigned char fileName[PATH_MAX];
- CFURLRef configURL;
-
- if((configURL=CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), nullptr)) &&
- CFURLGetFileSystemRepresentation(configURL, true, fileName, sizeof(fileName)))
- {
- al::ifstream f{reinterpret_cast<char*>(fileName)};
- if(f.is_open())
- LoadConfigFromFile(f);
- }
- }
-#endif
-
- if((str=getenv("HOME")) != nullptr && *str)
- {
- fname = str;
- if(fname.back() != '/') fname += "/.alsoftrc";
- else fname += ".alsoftrc";
-
- TRACE("Loading config %s...\n", fname.c_str());
- al::ifstream f{fname};
- if(f.is_open())
- LoadConfigFromFile(f);
- }
-
- if((str=getenv("XDG_CONFIG_HOME")) != nullptr && str[0] != 0)
- {
- fname = str;
- if(fname.back() != '/') fname += "/alsoft.conf";
- else fname += "alsoft.conf";
- }
- else
- {
- fname.clear();
- if((str=getenv("HOME")) != nullptr && str[0] != 0)
- {
- fname = str;
- if(fname.back() != '/') fname += "/.config/alsoft.conf";
- else fname += ".config/alsoft.conf";
- }
- }
- if(!fname.empty())
- {
- TRACE("Loading config %s...\n", fname.c_str());
- al::ifstream f{fname};
- if(f.is_open())
- LoadConfigFromFile(f);
- }
-
- std::string ppath{GetProcBinary().path};
- if(!ppath.empty())
- {
- if(ppath.back() != '/') ppath += "/alsoft.conf";
- else ppath += "alsoft.conf";
-
- TRACE("Loading config %s...\n", ppath.c_str());
- al::ifstream f{ppath};
- if(f.is_open())
- LoadConfigFromFile(f);
- }
-
- if((str=getenv("ALSOFT_CONF")) != nullptr && *str)
- {
- TRACE("Loading config %s...\n", str);
- al::ifstream f{str};
- if(f.is_open())
- LoadConfigFromFile(f);
- }
-}
-#endif
-
-const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def)
-{
- if(!keyName)
- return def;
-
- std::string key;
- if(blockName && strcasecmp(blockName, "general") != 0)
- {
- key = blockName;
- if(devName)
- {
- key += '/';
- key += devName;
- }
- key += '/';
- key += keyName;
- }
- else
- {
- if(devName)
- {
- key = devName;
- key += '/';
- }
- key += keyName;
- }
-
- auto iter = std::find_if(ConfOpts.cbegin(), ConfOpts.cend(),
- [&key](const ConfigEntry &entry) -> bool
- { return entry.key == key; }
- );
- if(iter != ConfOpts.cend())
- {
- TRACE("Found %s = \"%s\"\n", key.c_str(), iter->value.c_str());
- if(!iter->value.empty())
- return iter->value.c_str();
- return def;
- }
-
- if(!devName)
- {
- TRACE("Key %s not found\n", key.c_str());
- return def;
- }
- return GetConfigValue(nullptr, blockName, keyName, def);
-}
-
-int ConfigValueExists(const char *devName, const char *blockName, const char *keyName)
-{
- const char *val = GetConfigValue(devName, blockName, keyName, "");
- return val[0] != 0;
-}
-
-al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName)
-{
- const char *val = GetConfigValue(devName, blockName, keyName, "");
- if(!val[0]) return al::nullopt;
-
- return al::make_optional<std::string>(val);
-}
-
-al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName)
-{
- const char *val = GetConfigValue(devName, blockName, keyName, "");
- if(!val[0]) return al::nullopt;
-
- return al::make_optional(static_cast<int>(std::strtol(val, nullptr, 0)));
-}
-
-al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName)
-{
- const char *val = GetConfigValue(devName, blockName, keyName, "");
- if(!val[0]) return al::nullopt;
-
- return al::make_optional(static_cast<unsigned int>(std::strtoul(val, nullptr, 0)));
-}
-
-al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName)
-{
- const char *val = GetConfigValue(devName, blockName, keyName, "");
- if(!val[0]) return al::nullopt;
-
- return al::make_optional(std::strtof(val, nullptr));
-}
-
-al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName)
-{
- const char *val = GetConfigValue(devName, blockName, keyName, "");
- if(!val[0]) return al::nullopt;
-
- return al::make_optional(
- strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
- strcasecmp(val, "on") == 0 || atoi(val) != 0);
-}
-
-int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def)
-{
- const char *val = GetConfigValue(devName, blockName, keyName, "");
-
- if(!val[0]) return def != 0;
- return (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
- strcasecmp(val, "on") == 0 || atoi(val) != 0);
-}
diff --git a/Alc/alconfig.h b/Alc/alconfig.h
deleted file mode 100644
index ffc7adad..00000000
--- a/Alc/alconfig.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef ALCONFIG_H
-#define ALCONFIG_H
-
-#include <string>
-
-#include "aloptional.h"
-
-void ReadALConfig();
-
-int ConfigValueExists(const char *devName, const char *blockName, const char *keyName);
-const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def);
-int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def);
-
-al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName);
-al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName);
-al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName);
-al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName);
-al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName);
-
-#endif /* ALCONFIG_H */
diff --git a/Alc/alcontext.h b/Alc/alcontext.h
deleted file mode 100644
index cf956079..00000000
--- a/Alc/alcontext.h
+++ /dev/null
@@ -1,217 +0,0 @@
-#ifndef ALCONTEXT_H
-#define ALCONTEXT_H
-
-#include <mutex>
-#include <atomic>
-#include <memory>
-#include <thread>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "AL/alext.h"
-#include "inprogext.h"
-
-#include "atomic.h"
-#include "vector.h"
-#include "threads.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-
-#include "alListener.h"
-#include "alu.h"
-
-
-struct ALsource;
-struct ALeffectslot;
-struct ALcontextProps;
-struct ALlistenerProps;
-struct ALvoiceProps;
-struct ALeffectslotProps;
-struct RingBuffer;
-
-enum class DistanceModel {
- InverseClamped = AL_INVERSE_DISTANCE_CLAMPED,
- LinearClamped = AL_LINEAR_DISTANCE_CLAMPED,
- ExponentClamped = AL_EXPONENT_DISTANCE_CLAMPED,
- Inverse = AL_INVERSE_DISTANCE,
- Linear = AL_LINEAR_DISTANCE,
- Exponent = AL_EXPONENT_DISTANCE,
- Disable = AL_NONE,
-
- Default = InverseClamped
-};
-
-struct SourceSubList {
- uint64_t FreeMask{~0_u64};
- ALsource *Sources{nullptr}; /* 64 */
-
- SourceSubList() noexcept = default;
- SourceSubList(const SourceSubList&) = delete;
- SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
- { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
- ~SourceSubList();
-
- SourceSubList& operator=(const SourceSubList&) = delete;
- SourceSubList& operator=(SourceSubList&& rhs) noexcept
- { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
-};
-
-struct EffectSlotSubList {
- uint64_t FreeMask{~0_u64};
- ALeffectslot *EffectSlots{nullptr}; /* 64 */
-
- EffectSlotSubList() noexcept = default;
- EffectSlotSubList(const EffectSlotSubList&) = delete;
- EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
- : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
- { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
- ~EffectSlotSubList();
-
- EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
- EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
- { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
-};
-
-struct ALCcontext {
- RefCount ref{1u};
-
- al::vector<SourceSubList> SourceList;
- ALuint NumSources{0};
- std::mutex SourceLock;
-
- al::vector<EffectSlotSubList> EffectSlotList;
- ALuint NumEffectSlots{0u};
- std::mutex EffectSlotLock;
-
- std::atomic<ALenum> LastError{AL_NO_ERROR};
-
- DistanceModel mDistanceModel{DistanceModel::Default};
- ALboolean SourceDistanceModel{AL_FALSE};
-
- ALfloat DopplerFactor{1.0f};
- ALfloat DopplerVelocity{1.0f};
- ALfloat SpeedOfSound{};
- ALfloat MetersPerUnit{1.0f};
-
- std::atomic_flag PropsClean;
- std::atomic<bool> DeferUpdates{false};
-
- std::mutex PropLock;
-
- /* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit
- * indicates if updates are currently happening).
- */
- RefCount UpdateCount{0u};
- std::atomic<bool> HoldUpdates{false};
-
- ALfloat GainBoost{1.0f};
-
- std::atomic<ALcontextProps*> Update{nullptr};
-
- /* Linked lists of unused property containers, free to use for future
- * updates.
- */
- std::atomic<ALcontextProps*> FreeContextProps{nullptr};
- std::atomic<ALlistenerProps*> FreeListenerProps{nullptr};
- std::atomic<ALvoiceProps*> FreeVoiceProps{nullptr};
- std::atomic<ALeffectslotProps*> FreeEffectslotProps{nullptr};
-
- std::unique_ptr<al::FlexArray<ALvoice>> Voices{nullptr};
- std::atomic<ALuint> VoiceCount{0u};
-
- using ALeffectslotArray = al::FlexArray<ALeffectslot*>;
- std::atomic<ALeffectslotArray*> ActiveAuxSlots{nullptr};
-
- std::thread EventThread;
- al::semaphore EventSem;
- std::unique_ptr<RingBuffer> AsyncEvents;
- std::atomic<ALbitfieldSOFT> EnabledEvts{0u};
- std::mutex EventCbLock;
- ALEVENTPROCSOFT EventCb{};
- void *EventParam{nullptr};
-
- /* Default effect slot */
- std::unique_ptr<ALeffectslot> DefaultSlot;
-
- ALCdevice *const Device;
- const ALCchar *ExtensionList{nullptr};
-
- ALlistener Listener{};
-
-
- ALCcontext(ALCdevice *device);
- ALCcontext(const ALCcontext&) = delete;
- ALCcontext& operator=(const ALCcontext&) = delete;
- ~ALCcontext();
-
- DEF_NEWDEL(ALCcontext)
-};
-
-void ALCcontext_DecRef(ALCcontext *context);
-
-void UpdateContextProps(ALCcontext *context);
-
-void ALCcontext_DeferUpdates(ALCcontext *context);
-void ALCcontext_ProcessUpdates(ALCcontext *context);
-
-
-/* Simple RAII context reference. Takes the reference of the provided
- * ALCcontext, and decrements it when leaving scope. Movable (transfer
- * reference) but not copyable (no new references).
- */
-class ContextRef {
- ALCcontext *mCtx{nullptr};
-
- void reset() noexcept
- {
- if(mCtx)
- ALCcontext_DecRef(mCtx);
- mCtx = nullptr;
- }
-
-public:
- ContextRef() noexcept = default;
- ContextRef(ContextRef&& rhs) noexcept : mCtx{rhs.mCtx}
- { rhs.mCtx = nullptr; }
- explicit ContextRef(ALCcontext *ctx) noexcept : mCtx(ctx) { }
- ~ContextRef() { reset(); }
-
- ContextRef& operator=(const ContextRef&) = delete;
- ContextRef& operator=(ContextRef&& rhs) noexcept
- { std::swap(mCtx, rhs.mCtx); return *this; }
-
- operator bool() const noexcept { return mCtx != nullptr; }
-
- ALCcontext* operator->() const noexcept { return mCtx; }
- ALCcontext* get() const noexcept { return mCtx; }
-
- ALCcontext* release() noexcept
- {
- ALCcontext *ret{mCtx};
- mCtx = nullptr;
- return ret;
- }
-};
-
-inline bool operator==(const ContextRef &lhs, const ALCcontext *rhs) noexcept
-{ return lhs.get() == rhs; }
-inline bool operator!=(const ContextRef &lhs, const ALCcontext *rhs) noexcept
-{ return !(lhs == rhs); }
-inline bool operator<(const ContextRef &lhs, const ALCcontext *rhs) noexcept
-{ return lhs.get() < rhs; }
-
-ContextRef GetContextRef(void);
-
-
-struct ALcontextProps {
- ALfloat DopplerFactor;
- ALfloat DopplerVelocity;
- ALfloat SpeedOfSound;
- ALboolean SourceDistanceModel;
- DistanceModel mDistanceModel;
- ALfloat MetersPerUnit;
-
- std::atomic<ALcontextProps*> next;
-};
-
-#endif /* ALCONTEXT_H */
diff --git a/Alc/alu.cpp b/Alc/alu.cpp
deleted file mode 100644
index cc1a5a98..00000000
--- a/Alc/alu.cpp
+++ /dev/null
@@ -1,1798 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "alu.h"
-
-#include <algorithm>
-#include <array>
-#include <atomic>
-#include <cassert>
-#include <chrono>
-#include <climits>
-#include <cmath>
-#include <cstdarg>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <functional>
-#include <iterator>
-#include <limits>
-#include <memory>
-#include <new>
-#include <numeric>
-#include <utility>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "AL/efx.h"
-
-#include "alAuxEffectSlot.h"
-#include "alBuffer.h"
-#include "alcmain.h"
-#include "alEffect.h"
-#include "alListener.h"
-#include "alcontext.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-#include "alspan.h"
-#include "ambidefs.h"
-#include "atomic.h"
-#include "bformatdec.h"
-#include "bs2b.h"
-#include "cpu_caps.h"
-#include "effects/base.h"
-#include "filters/biquad.h"
-#include "filters/nfc.h"
-#include "filters/splitter.h"
-#include "fpu_modes.h"
-#include "hrtf.h"
-#include "inprogext.h"
-#include "mastering.h"
-#include "math_defs.h"
-#include "mixer/defs.h"
-#include "opthelpers.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "uhjfilter.h"
-#include "vecmat.h"
-#include "vector.h"
-
-#include "bsinc_inc.h"
-
-
-namespace {
-
-using namespace std::placeholders;
-
-ALfloat InitConeScale()
-{
- ALfloat ret{1.0f};
- const char *str{getenv("__ALSOFT_HALF_ANGLE_CONES")};
- if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
- ret *= 0.5f;
- return ret;
-}
-
-ALfloat InitZScale()
-{
- ALfloat ret{1.0f};
- const char *str{getenv("__ALSOFT_REVERSE_Z")};
- if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
- ret *= -1.0f;
- return ret;
-}
-
-ALboolean InitReverbSOS()
-{
- ALboolean ret{AL_FALSE};
- const char *str{getenv("__ALSOFT_REVERB_IGNORES_SOUND_SPEED")};
- if(str && (strcasecmp(str, "true") == 0 || strtol(str, nullptr, 0) == 1))
- ret = AL_TRUE;
- return ret;
-}
-
-} // namespace
-
-/* Cone scalar */
-const ALfloat ConeScale{InitConeScale()};
-
-/* Localized Z scalar for mono sources */
-const ALfloat ZScale{InitZScale()};
-
-/* Force default speed of sound for distance-related reverb decay. */
-const ALboolean OverrideReverbSpeedOfSound{InitReverbSOS()};
-
-
-namespace {
-
-void ClearArray(ALfloat (&f)[MAX_OUTPUT_CHANNELS])
-{
- std::fill(std::begin(f), std::end(f), 0.0f);
-}
-
-struct ChanMap {
- Channel channel;
- ALfloat angle;
- ALfloat elevation;
-};
-
-HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_<CTag>;
-inline HrtfDirectMixerFunc SelectHrtfMixer(void)
-{
-#ifdef HAVE_NEON
- if((CPUCapFlags&CPU_CAP_NEON))
- return MixDirectHrtf_<NEONTag>;
-#endif
-#ifdef HAVE_SSE
- if((CPUCapFlags&CPU_CAP_SSE))
- return MixDirectHrtf_<SSETag>;
-#endif
-
- return MixDirectHrtf_<CTag>;
-}
-
-} // namespace
-
-void aluInit(void)
-{
- MixDirectHrtf = SelectHrtfMixer();
-}
-
-
-void ProcessHrtf(ALCdevice *device, const ALsizei SamplesToDo)
-{
- /* HRTF is stereo output only. */
- const int lidx{device->RealOut.ChannelIndex[FrontLeft]};
- const int ridx{device->RealOut.ChannelIndex[FrontRight]};
- ASSUME(lidx >= 0 && ridx >= 0);
-
- DirectHrtfState *state{device->mHrtfState.get()};
- MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx], device->Dry.Buffer,
- device->HrtfAccumData, state, SamplesToDo);
-}
-
-void ProcessAmbiDec(ALCdevice *device, const ALsizei SamplesToDo)
-{
- BFormatDec *ambidec{device->AmbiDecoder.get()};
- ambidec->process(device->RealOut.Buffer, device->Dry.Buffer.data(), SamplesToDo);
-}
-
-void ProcessUhj(ALCdevice *device, const ALsizei SamplesToDo)
-{
- /* UHJ is stereo output only. */
- const int lidx{device->RealOut.ChannelIndex[FrontLeft]};
- const int ridx{device->RealOut.ChannelIndex[FrontRight]};
- ASSUME(lidx >= 0 && ridx >= 0);
-
- /* Encode to stereo-compatible 2-channel UHJ output. */
- Uhj2Encoder *uhj2enc{device->Uhj_Encoder.get()};
- uhj2enc->encode(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
- device->Dry.Buffer.data(), SamplesToDo);
-}
-
-void ProcessBs2b(ALCdevice *device, const ALsizei SamplesToDo)
-{
- /* First, decode the ambisonic mix to the "real" output. */
- BFormatDec *ambidec{device->AmbiDecoder.get()};
- ambidec->process(device->RealOut.Buffer, device->Dry.Buffer.data(), SamplesToDo);
-
- /* BS2B is stereo output only. */
- const int lidx{device->RealOut.ChannelIndex[FrontLeft]};
- const int ridx{device->RealOut.ChannelIndex[FrontRight]};
- ASSUME(lidx >= 0 && ridx >= 0);
-
- /* Now apply the BS2B binaural/crossfeed filter. */
- bs2b_cross_feed(device->Bs2b.get(), device->RealOut.Buffer[lidx].data(),
- device->RealOut.Buffer[ridx].data(), SamplesToDo);
-}
-
-
-/* Prepares the interpolator for a given rate (determined by increment).
- *
- * With a bit of work, and a trade of memory for CPU cost, this could be
- * modified for use with an interpolated increment for buttery-smooth pitch
- * changes.
- */
-void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
-{
- ALsizei si{BSINC_SCALE_COUNT - 1};
- ALfloat sf{0.0f};
-
- if(increment > FRACTIONONE)
- {
- sf = static_cast<ALfloat>FRACTIONONE / increment;
- sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
- si = float2int(sf);
- /* The interpolation factor is fit to this diagonally-symmetric curve
- * to reduce the transition ripple caused by interpolating different
- * scales of the sinc function.
- */
- sf = 1.0f - std::cos(std::asin(sf - si));
- }
-
- state->sf = sf;
- state->m = table->m[si];
- state->l = (state->m/2) - 1;
- state->filter = table->Tab + table->filterOffset[si];
-}
-
-
-namespace {
-
-/* This RNG method was created based on the math found in opusdec. It's quick,
- * and starting with a seed value of 22222, is suitable for generating
- * whitenoise.
- */
-inline ALuint dither_rng(ALuint *seed) noexcept
-{
- *seed = (*seed * 96314165) + 907633515;
- return *seed;
-}
-
-
-inline alu::Vector aluCrossproduct(const alu::Vector &in1, const alu::Vector &in2)
-{
- return alu::Vector{
- in1[1]*in2[2] - in1[2]*in2[1],
- in1[2]*in2[0] - in1[0]*in2[2],
- in1[0]*in2[1] - in1[1]*in2[0],
- 0.0f
- };
-}
-
-inline ALfloat aluDotproduct(const alu::Vector &vec1, const alu::Vector &vec2)
-{
- return vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2];
-}
-
-
-alu::Vector operator*(const alu::Matrix &mtx, const alu::Vector &vec) noexcept
-{
- return alu::Vector{
- vec[0]*mtx[0][0] + vec[1]*mtx[1][0] + vec[2]*mtx[2][0] + vec[3]*mtx[3][0],
- vec[0]*mtx[0][1] + vec[1]*mtx[1][1] + vec[2]*mtx[2][1] + vec[3]*mtx[3][1],
- vec[0]*mtx[0][2] + vec[1]*mtx[1][2] + vec[2]*mtx[2][2] + vec[3]*mtx[3][2],
- vec[0]*mtx[0][3] + vec[1]*mtx[1][3] + vec[2]*mtx[2][3] + vec[3]*mtx[3][3]
- };
-}
-
-
-bool CalcContextParams(ALCcontext *Context)
-{
- ALcontextProps *props{Context->Update.exchange(nullptr, std::memory_order_acq_rel)};
- if(!props) return false;
-
- ALlistener &Listener = Context->Listener;
- Listener.Params.MetersPerUnit = props->MetersPerUnit;
-
- Listener.Params.DopplerFactor = props->DopplerFactor;
- Listener.Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
- if(!OverrideReverbSpeedOfSound)
- Listener.Params.ReverbSpeedOfSound = Listener.Params.SpeedOfSound *
- Listener.Params.MetersPerUnit;
-
- Listener.Params.SourceDistanceModel = props->SourceDistanceModel;
- Listener.Params.mDistanceModel = props->mDistanceModel;
-
- AtomicReplaceHead(Context->FreeContextProps, props);
- return true;
-}
-
-bool CalcListenerParams(ALCcontext *Context)
-{
- ALlistener &Listener = Context->Listener;
-
- ALlistenerProps *props{Listener.Update.exchange(nullptr, std::memory_order_acq_rel)};
- if(!props) return false;
-
- /* AT then UP */
- alu::Vector N{props->OrientAt[0], props->OrientAt[1], props->OrientAt[2], 0.0f};
- N.normalize();
- alu::Vector V{props->OrientUp[0], props->OrientUp[1], props->OrientUp[2], 0.0f};
- V.normalize();
- /* Build and normalize right-vector */
- alu::Vector U{aluCrossproduct(N, V)};
- U.normalize();
-
- Listener.Params.Matrix = alu::Matrix{
- U[0], V[0], -N[0], 0.0f,
- U[1], V[1], -N[1], 0.0f,
- U[2], V[2], -N[2], 0.0f,
- 0.0f, 0.0f, 0.0f, 1.0f
- };
-
- const alu::Vector P{Listener.Params.Matrix *
- alu::Vector{props->Position[0], props->Position[1], props->Position[2], 1.0f}};
- Listener.Params.Matrix.setRow(3, -P[0], -P[1], -P[2], 1.0f);
-
- const alu::Vector vel{props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f};
- Listener.Params.Velocity = Listener.Params.Matrix * vel;
-
- Listener.Params.Gain = props->Gain * Context->GainBoost;
-
- AtomicReplaceHead(Context->FreeListenerProps, props);
- return true;
-}
-
-bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
-{
- ALeffectslotProps *props{slot->Update.exchange(nullptr, std::memory_order_acq_rel)};
- if(!props && !force) return false;
-
- EffectState *state;
- if(!props)
- state = slot->Params.mEffectState;
- else
- {
- slot->Params.Gain = props->Gain;
- slot->Params.AuxSendAuto = props->AuxSendAuto;
- slot->Params.Target = props->Target;
- slot->Params.EffectType = props->Type;
- slot->Params.mEffectProps = props->Props;
- if(IsReverbEffect(props->Type))
- {
- slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
- slot->Params.DecayTime = props->Props.Reverb.DecayTime;
- slot->Params.DecayLFRatio = props->Props.Reverb.DecayLFRatio;
- slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
- slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
- slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
- }
- else
- {
- slot->Params.RoomRolloff = 0.0f;
- slot->Params.DecayTime = 0.0f;
- slot->Params.DecayLFRatio = 0.0f;
- slot->Params.DecayHFRatio = 0.0f;
- slot->Params.DecayHFLimit = AL_FALSE;
- slot->Params.AirAbsorptionGainHF = 1.0f;
- }
-
- state = props->State;
- props->State = nullptr;
- EffectState *oldstate{slot->Params.mEffectState};
- slot->Params.mEffectState = state;
-
- /* Manually decrement the old effect state's refcount if it's greater
- * than 1. We need to be a bit clever here to avoid the refcount
- * reaching 0 since it can't be deleted in the mixer.
- */
- ALuint oldval{oldstate->mRef.load(std::memory_order_acquire)};
- while(oldval > 1 && !oldstate->mRef.compare_exchange_weak(oldval, oldval-1,
- std::memory_order_acq_rel, std::memory_order_acquire))
- {
- /* oldval was updated with the current value on failure, so just
- * try again.
- */
- }
-
- if(oldval < 2)
- {
- /* Otherwise, if it would be deleted, send it off with a release
- * event.
- */
- RingBuffer *ring{context->AsyncEvents.get()};
- auto evt_vec = ring->getWriteVector();
- if(LIKELY(evt_vec.first.len > 0))
- {
- AsyncEvent *evt{new (evt_vec.first.buf) AsyncEvent{EventType_ReleaseEffectState}};
- evt->u.mEffectState = oldstate;
- ring->writeAdvance(1);
- context->EventSem.post();
- }
- else
- {
- /* If writing the event failed, the queue was probably full.
- * Store the old state in the property object where it can
- * eventually be cleaned up sometime later (not ideal, but
- * better than blocking or leaking).
- */
- props->State = oldstate;
- }
- }
-
- AtomicReplaceHead(context->FreeEffectslotProps, props);
- }
-
- EffectTarget output;
- if(ALeffectslot *target{slot->Params.Target})
- output = EffectTarget{&target->Wet, nullptr};
- else
- {
- ALCdevice *device{context->Device};
- output = EffectTarget{&device->Dry, &device->RealOut};
- }
- state->update(context, slot, &slot->Params.mEffectProps, output);
- return true;
-}
-
-
-/* Scales the given azimuth toward the side (+/- pi/2 radians) for positions in
- * front.
- */
-inline float ScaleAzimuthFront(float azimuth, float scale)
-{
- const ALfloat abs_azi{std::fabs(azimuth)};
- if(!(abs_azi > al::MathDefs<float>::Pi()*0.5f))
- return minf(abs_azi*scale, al::MathDefs<float>::Pi()*0.5f) * std::copysign(1.0f, azimuth);
- return azimuth;
-}
-
-void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypos,
- const ALfloat zpos, const ALfloat Distance, const ALfloat Spread, const ALfloat DryGain,
- const ALfloat DryGainHF, const ALfloat DryGainLF, const ALfloat (&WetGain)[MAX_SENDS],
- const ALfloat (&WetGainLF)[MAX_SENDS], const ALfloat (&WetGainHF)[MAX_SENDS],
- ALeffectslot *(&SendSlots)[MAX_SENDS], const ALvoicePropsBase *props,
- const ALlistener &Listener, const ALCdevice *Device)
-{
- static constexpr ChanMap MonoMap[1]{
- { FrontCenter, 0.0f, 0.0f }
- }, RearMap[2]{
- { BackLeft, Deg2Rad(-150.0f), Deg2Rad(0.0f) },
- { BackRight, Deg2Rad( 150.0f), Deg2Rad(0.0f) }
- }, QuadMap[4]{
- { FrontLeft, Deg2Rad( -45.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 45.0f), Deg2Rad(0.0f) },
- { BackLeft, Deg2Rad(-135.0f), Deg2Rad(0.0f) },
- { BackRight, Deg2Rad( 135.0f), Deg2Rad(0.0f) }
- }, X51Map[6]{
- { FrontLeft, Deg2Rad( -30.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) },
- { FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) },
- { LFE, 0.0f, 0.0f },
- { SideLeft, Deg2Rad(-110.0f), Deg2Rad(0.0f) },
- { SideRight, Deg2Rad( 110.0f), Deg2Rad(0.0f) }
- }, X61Map[7]{
- { FrontLeft, Deg2Rad(-30.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) },
- { FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) },
- { LFE, 0.0f, 0.0f },
- { BackCenter, Deg2Rad(180.0f), Deg2Rad(0.0f) },
- { SideLeft, Deg2Rad(-90.0f), Deg2Rad(0.0f) },
- { SideRight, Deg2Rad( 90.0f), Deg2Rad(0.0f) }
- }, X71Map[8]{
- { FrontLeft, Deg2Rad( -30.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) },
- { FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) },
- { LFE, 0.0f, 0.0f },
- { BackLeft, Deg2Rad(-150.0f), Deg2Rad(0.0f) },
- { BackRight, Deg2Rad( 150.0f), Deg2Rad(0.0f) },
- { SideLeft, Deg2Rad( -90.0f), Deg2Rad(0.0f) },
- { SideRight, Deg2Rad( 90.0f), Deg2Rad(0.0f) }
- };
-
- ChanMap StereoMap[2]{
- { FrontLeft, Deg2Rad(-30.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) }
- };
-
- const auto Frequency = static_cast<ALfloat>(Device->Frequency);
- const ALsizei NumSends{Device->NumAuxSends};
- ASSUME(NumSends >= 0);
-
- bool DirectChannels{props->DirectChannels != AL_FALSE};
- const ChanMap *chans{nullptr};
- ALsizei num_channels{0};
- bool isbformat{false};
- ALfloat downmix_gain{1.0f};
- switch(voice->mFmtChannels)
- {
- case FmtMono:
- chans = MonoMap;
- num_channels = 1;
- /* Mono buffers are never played direct. */
- DirectChannels = false;
- break;
-
- case FmtStereo:
- /* Convert counter-clockwise to clockwise. */
- StereoMap[0].angle = -props->StereoPan[0];
- StereoMap[1].angle = -props->StereoPan[1];
-
- chans = StereoMap;
- num_channels = 2;
- downmix_gain = 1.0f / 2.0f;
- break;
-
- case FmtRear:
- chans = RearMap;
- num_channels = 2;
- downmix_gain = 1.0f / 2.0f;
- break;
-
- case FmtQuad:
- chans = QuadMap;
- num_channels = 4;
- downmix_gain = 1.0f / 4.0f;
- break;
-
- case FmtX51:
- chans = X51Map;
- num_channels = 6;
- /* NOTE: Excludes LFE. */
- downmix_gain = 1.0f / 5.0f;
- break;
-
- case FmtX61:
- chans = X61Map;
- num_channels = 7;
- /* NOTE: Excludes LFE. */
- downmix_gain = 1.0f / 6.0f;
- break;
-
- case FmtX71:
- chans = X71Map;
- num_channels = 8;
- /* NOTE: Excludes LFE. */
- downmix_gain = 1.0f / 7.0f;
- break;
-
- case FmtBFormat2D:
- num_channels = 3;
- isbformat = true;
- DirectChannels = false;
- break;
-
- case FmtBFormat3D:
- num_channels = 4;
- isbformat = true;
- DirectChannels = false;
- break;
- }
- ASSUME(num_channels > 0);
-
- std::for_each(voice->mChans.begin(), voice->mChans.begin()+num_channels,
- [NumSends](ALvoice::ChannelData &chandata) -> void
- {
- chandata.mDryParams.Hrtf.Target = HrtfFilter{};
- ClearArray(chandata.mDryParams.Gains.Target);
- std::for_each(chandata.mWetParams.begin(), chandata.mWetParams.begin()+NumSends,
- [](SendParams &params) -> void { ClearArray(params.Gains.Target); });
- });
-
- voice->mFlags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
- if(isbformat)
- {
- /* Special handling for B-Format sources. */
-
- if(Distance > std::numeric_limits<float>::epsilon())
- {
- /* Panning a B-Format sound toward some direction is easy. Just pan
- * the first (W) channel as a normal mono sound and silence the
- * others.
- */
-
- if(Device->AvgSpeakerDist > 0.0f)
- {
- /* Clamp the distance for really close sources, to prevent
- * excessive bass.
- */
- const ALfloat mdist{maxf(Distance, Device->AvgSpeakerDist/4.0f)};
- const ALfloat w0{SPEEDOFSOUNDMETRESPERSEC / (mdist * Frequency)};
-
- /* Only need to adjust the first channel of a B-Format source. */
- voice->mChans[0].mDryParams.NFCtrlFilter.adjust(w0);
-
- voice->mFlags |= VOICE_HAS_NFC;
- }
-
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- if(Device->mRenderMode != StereoPair)
- CalcDirectionCoeffs({xpos, ypos, zpos}, Spread, coeffs);
- else
- {
- /* Clamp Y, in case rounding errors caused it to end up outside
- * of -1...+1.
- */
- const ALfloat ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
- /* Negate Z for right-handed coords with -Z in front. */
- const ALfloat az{std::atan2(xpos, -zpos)};
-
- /* A scalar of 1.5 for plain stereo results in +/-60 degrees
- * being moved to +/-90 degrees for direct right and left
- * speaker responses.
- */
- CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, Spread, coeffs);
- }
-
- /* NOTE: W needs to be scaled due to FuMa normalization. */
- const ALfloat &scale0 = AmbiScale::FromFuMa[0];
- ComputePanGains(&Device->Dry, coeffs, DryGain*scale0,
- voice->mChans[0].mDryParams.Gains.Target);
- for(ALsizei i{0};i < NumSends;i++)
- {
- if(const ALeffectslot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs, WetGain[i]*scale0,
- voice->mChans[0].mWetParams[i].Gains.Target);
- }
- }
- else
- {
- if(Device->AvgSpeakerDist > 0.0f)
- {
- /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
- * is what we want for FOA input. The first channel may have
- * been previously re-adjusted if panned, so reset it.
- */
- voice->mChans[0].mDryParams.NFCtrlFilter.adjust(0.0f);
-
- voice->mFlags |= VOICE_HAS_NFC;
- }
-
- /* Local B-Format sources have their XYZ channels rotated according
- * to the orientation.
- */
- /* AT then UP */
- alu::Vector N{props->OrientAt[0], props->OrientAt[1], props->OrientAt[2], 0.0f};
- N.normalize();
- alu::Vector V{props->OrientUp[0], props->OrientUp[1], props->OrientUp[2], 0.0f};
- V.normalize();
- if(!props->HeadRelative)
- {
- N = Listener.Params.Matrix * N;
- V = Listener.Params.Matrix * V;
- }
- /* Build and normalize right-vector */
- alu::Vector U{aluCrossproduct(N, V)};
- U.normalize();
-
- /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). NOTE: This
- * matrix is transposed, for the inputs to align on the rows and
- * outputs on the columns.
- */
- const ALfloat &wscale = AmbiScale::FromFuMa[0];
- const ALfloat &yscale = AmbiScale::FromFuMa[1];
- const ALfloat &zscale = AmbiScale::FromFuMa[2];
- const ALfloat &xscale = AmbiScale::FromFuMa[3];
- const ALfloat matrix[4][MAX_AMBI_CHANNELS]{
- // ACN0 ACN1 ACN2 ACN3
- { wscale, 0.0f, 0.0f, 0.0f }, // FuMa W
- { 0.0f, -N[0]*xscale, N[1]*xscale, -N[2]*xscale }, // FuMa X
- { 0.0f, U[0]*yscale, -U[1]*yscale, U[2]*yscale }, // FuMa Y
- { 0.0f, -V[0]*zscale, V[1]*zscale, -V[2]*zscale } // FuMa Z
- };
-
- for(ALsizei c{0};c < num_channels;c++)
- {
- ComputePanGains(&Device->Dry, matrix[c], DryGain,
- voice->mChans[c].mDryParams.Gains.Target);
-
- for(ALsizei i{0};i < NumSends;i++)
- {
- if(const ALeffectslot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, matrix[c], WetGain[i],
- voice->mChans[c].mWetParams[i].Gains.Target);
- }
- }
- }
- }
- else if(DirectChannels)
- {
- /* Direct source channels always play local. Skip the virtual channels
- * and write inputs to the matching real outputs.
- */
- voice->mDirect.Buffer = Device->RealOut.Buffer;
-
- for(ALsizei c{0};c < num_channels;c++)
- {
- int idx{GetChannelIdxByName(Device->RealOut, chans[c].channel)};
- if(idx != -1) voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain;
- }
-
- /* Auxiliary sends still use normal channel panning since they mix to
- * B-Format, which can't channel-match.
- */
- for(ALsizei c{0};c < num_channels;c++)
- {
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
-
- for(ALsizei i{0};i < NumSends;i++)
- {
- if(const ALeffectslot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs, WetGain[i],
- voice->mChans[c].mWetParams[i].Gains.Target);
- }
- }
- }
- else if(Device->mRenderMode == HrtfRender)
- {
- /* Full HRTF rendering. Skip the virtual channels and render to the
- * real outputs.
- */
- voice->mDirect.Buffer = Device->RealOut.Buffer;
-
- if(Distance > std::numeric_limits<float>::epsilon())
- {
- const ALfloat ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
- const ALfloat az{std::atan2(xpos, -zpos)};
-
- /* Get the HRIR coefficients and delays just once, for the given
- * source direction.
- */
- GetHrtfCoeffs(Device->mHrtf, ev, az, Distance, Spread,
- voice->mChans[0].mDryParams.Hrtf.Target.Coeffs,
- voice->mChans[0].mDryParams.Hrtf.Target.Delay);
- voice->mChans[0].mDryParams.Hrtf.Target.Gain = DryGain * downmix_gain;
-
- /* Remaining channels use the same results as the first. */
- for(ALsizei c{1};c < num_channels;c++)
- {
- /* Skip LFE */
- if(chans[c].channel == LFE) continue;
- voice->mChans[c].mDryParams.Hrtf.Target = voice->mChans[0].mDryParams.Hrtf.Target;
- }
-
- /* Calculate the directional coefficients once, which apply to all
- * input channels of the source sends.
- */
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- CalcDirectionCoeffs({xpos, ypos, zpos}, Spread, coeffs);
-
- for(ALsizei c{0};c < num_channels;c++)
- {
- /* Skip LFE */
- if(chans[c].channel == LFE)
- continue;
- for(ALsizei i{0};i < NumSends;i++)
- {
- if(const ALeffectslot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs, WetGain[i] * downmix_gain,
- voice->mChans[c].mWetParams[i].Gains.Target);
- }
- }
- }
- else
- {
- /* Local sources on HRTF play with each channel panned to its
- * relative location around the listener, providing "virtual
- * speaker" responses.
- */
- for(ALsizei c{0};c < num_channels;c++)
- {
- /* Skip LFE */
- if(chans[c].channel == LFE)
- continue;
-
- /* Get the HRIR coefficients and delays for this channel
- * position.
- */
- GetHrtfCoeffs(Device->mHrtf, chans[c].elevation, chans[c].angle,
- std::numeric_limits<float>::infinity(), Spread,
- voice->mChans[c].mDryParams.Hrtf.Target.Coeffs,
- voice->mChans[c].mDryParams.Hrtf.Target.Delay);
- voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain;
-
- /* Normal panning for auxiliary sends. */
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
-
- for(ALsizei i{0};i < NumSends;i++)
- {
- if(const ALeffectslot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs, WetGain[i],
- voice->mChans[c].mWetParams[i].Gains.Target);
- }
- }
- }
-
- voice->mFlags |= VOICE_HAS_HRTF;
- }
- else
- {
- /* Non-HRTF rendering. Use normal panning to the output. */
-
- if(Distance > std::numeric_limits<float>::epsilon())
- {
- /* Calculate NFC filter coefficient if needed. */
- if(Device->AvgSpeakerDist > 0.0f)
- {
- /* Clamp the distance for really close sources, to prevent
- * excessive bass.
- */
- const ALfloat mdist{maxf(Distance, Device->AvgSpeakerDist/4.0f)};
- const ALfloat w0{SPEEDOFSOUNDMETRESPERSEC / (mdist * Frequency)};
-
- /* Adjust NFC filters. */
- for(ALsizei c{0};c < num_channels;c++)
- voice->mChans[c].mDryParams.NFCtrlFilter.adjust(w0);
-
- voice->mFlags |= VOICE_HAS_NFC;
- }
-
- /* Calculate the directional coefficients once, which apply to all
- * input channels.
- */
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- if(Device->mRenderMode != StereoPair)
- CalcDirectionCoeffs({xpos, ypos, zpos}, Spread, coeffs);
- else
- {
- const ALfloat ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
- const ALfloat az{std::atan2(xpos, -zpos)};
- CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, Spread, coeffs);
- }
-
- for(ALsizei c{0};c < num_channels;c++)
- {
- /* Special-case LFE */
- if(chans[c].channel == LFE)
- {
- if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data())
- {
- int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
- if(idx != -1) voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain;
- }
- continue;
- }
-
- ComputePanGains(&Device->Dry, coeffs, DryGain * downmix_gain,
- voice->mChans[c].mDryParams.Gains.Target);
- }
-
- for(ALsizei c{0};c < num_channels;c++)
- {
- /* Skip LFE */
- if(chans[c].channel == LFE)
- continue;
- for(ALsizei i{0};i < NumSends;i++)
- {
- if(const ALeffectslot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs, WetGain[i] * downmix_gain,
- voice->mChans[c].mWetParams[i].Gains.Target);
- }
- }
- }
- else
- {
- if(Device->AvgSpeakerDist > 0.0f)
- {
- /* If the source distance is 0, set w0 to w1 to act as a pass-
- * through. We still want to pass the signal through the
- * filters so they keep an appropriate history, in case the
- * source moves away from the listener.
- */
- const ALfloat w0{SPEEDOFSOUNDMETRESPERSEC / (Device->AvgSpeakerDist * Frequency)};
-
- for(ALsizei c{0};c < num_channels;c++)
- voice->mChans[c].mDryParams.NFCtrlFilter.adjust(w0);
-
- voice->mFlags |= VOICE_HAS_NFC;
- }
-
- for(ALsizei c{0};c < num_channels;c++)
- {
- /* Special-case LFE */
- if(chans[c].channel == LFE)
- {
- if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data())
- {
- int idx = GetChannelIdxByName(Device->RealOut, chans[c].channel);
- if(idx != -1) voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain;
- }
- continue;
- }
-
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- CalcAngleCoeffs(
- (Device->mRenderMode==StereoPair) ? ScaleAzimuthFront(chans[c].angle, 3.0f)
- : chans[c].angle,
- chans[c].elevation, Spread, coeffs
- );
-
- ComputePanGains(&Device->Dry, coeffs, DryGain,
- voice->mChans[c].mDryParams.Gains.Target);
- for(ALsizei i{0};i < NumSends;i++)
- {
- if(const ALeffectslot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs, WetGain[i],
- voice->mChans[c].mWetParams[i].Gains.Target);
- }
- }
- }
- }
-
- {
- const ALfloat hfScale{props->Direct.HFReference / Frequency};
- const ALfloat lfScale{props->Direct.LFReference / Frequency};
- const ALfloat gainHF{maxf(DryGainHF, 0.001f)}; /* Limit -60dB */
- const ALfloat gainLF{maxf(DryGainLF, 0.001f)};
-
- voice->mDirect.FilterType = AF_None;
- if(gainHF != 1.0f) voice->mDirect.FilterType |= AF_LowPass;
- if(gainLF != 1.0f) voice->mDirect.FilterType |= AF_HighPass;
- auto &lowpass = voice->mChans[0].mDryParams.LowPass;
- auto &highpass = voice->mChans[0].mDryParams.HighPass;
- lowpass.setParams(BiquadType::HighShelf, gainHF, hfScale,
- lowpass.rcpQFromSlope(gainHF, 1.0f));
- highpass.setParams(BiquadType::LowShelf, gainLF, lfScale,
- highpass.rcpQFromSlope(gainLF, 1.0f));
- for(ALsizei c{1};c < num_channels;c++)
- {
- voice->mChans[c].mDryParams.LowPass.copyParamsFrom(lowpass);
- voice->mChans[c].mDryParams.HighPass.copyParamsFrom(highpass);
- }
- }
- for(ALsizei i{0};i < NumSends;i++)
- {
- const ALfloat hfScale{props->Send[i].HFReference / Frequency};
- const ALfloat lfScale{props->Send[i].LFReference / Frequency};
- const ALfloat gainHF{maxf(WetGainHF[i], 0.001f)};
- const ALfloat gainLF{maxf(WetGainLF[i], 0.001f)};
-
- voice->mSend[i].FilterType = AF_None;
- if(gainHF != 1.0f) voice->mSend[i].FilterType |= AF_LowPass;
- if(gainLF != 1.0f) voice->mSend[i].FilterType |= AF_HighPass;
-
- auto &lowpass = voice->mChans[0].mWetParams[i].LowPass;
- auto &highpass = voice->mChans[0].mWetParams[i].HighPass;
- lowpass.setParams(BiquadType::HighShelf, gainHF, hfScale,
- lowpass.rcpQFromSlope(gainHF, 1.0f));
- highpass.setParams(BiquadType::LowShelf, gainLF, lfScale,
- highpass.rcpQFromSlope(gainLF, 1.0f));
- for(ALsizei c{1};c < num_channels;c++)
- {
- voice->mChans[c].mWetParams[i].LowPass.copyParamsFrom(lowpass);
- voice->mChans[c].mWetParams[i].HighPass.copyParamsFrom(highpass);
- }
- }
-}
-
-void CalcNonAttnSourceParams(ALvoice *voice, const ALvoicePropsBase *props, const ALCcontext *ALContext)
-{
- const ALCdevice *Device{ALContext->Device};
- ALeffectslot *SendSlots[MAX_SENDS];
-
- voice->mDirect.Buffer = Device->Dry.Buffer;
- for(ALsizei i{0};i < Device->NumAuxSends;i++)
- {
- SendSlots[i] = props->Send[i].Slot;
- if(!SendSlots[i] && i == 0)
- SendSlots[i] = ALContext->DefaultSlot.get();
- if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
- {
- SendSlots[i] = nullptr;
- voice->mSend[i].Buffer = {};
- }
- else
- voice->mSend[i].Buffer = SendSlots[i]->Wet.Buffer;
- }
-
- /* Calculate the stepping value */
- const auto Pitch = static_cast<ALfloat>(voice->mFrequency) /
- static_cast<ALfloat>(Device->Frequency) * props->Pitch;
- if(Pitch > static_cast<ALfloat>(MAX_PITCH))
- voice->mStep = MAX_PITCH<<FRACTIONBITS;
- else
- voice->mStep = maxi(fastf2i(Pitch * FRACTIONONE), 1);
- if(props->mResampler == BSinc24Resampler)
- BsincPrepare(voice->mStep, &voice->mResampleState.bsinc, &bsinc24);
- else if(props->mResampler == BSinc12Resampler)
- BsincPrepare(voice->mStep, &voice->mResampleState.bsinc, &bsinc12);
- voice->mResampler = SelectResampler(props->mResampler);
-
- /* Calculate gains */
- const ALlistener &Listener = ALContext->Listener;
- ALfloat DryGain{clampf(props->Gain, props->MinGain, props->MaxGain)};
- DryGain *= props->Direct.Gain * Listener.Params.Gain;
- DryGain = minf(DryGain, GAIN_MIX_MAX);
- ALfloat DryGainHF{props->Direct.GainHF};
- ALfloat DryGainLF{props->Direct.GainLF};
- ALfloat WetGain[MAX_SENDS], WetGainHF[MAX_SENDS], WetGainLF[MAX_SENDS];
- for(ALsizei i{0};i < Device->NumAuxSends;i++)
- {
- WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
- WetGain[i] *= props->Send[i].Gain * Listener.Params.Gain;
- WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
- WetGainHF[i] = props->Send[i].GainHF;
- WetGainLF[i] = props->Send[i].GainLF;
- }
-
- CalcPanningAndFilters(voice, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, DryGain, DryGainHF, DryGainLF,
- WetGain, WetGainLF, WetGainHF, SendSlots, props, Listener, Device);
-}
-
-void CalcAttnSourceParams(ALvoice *voice, const ALvoicePropsBase *props, const ALCcontext *ALContext)
-{
- const ALCdevice *Device{ALContext->Device};
- const ALsizei NumSends{Device->NumAuxSends};
- const ALlistener &Listener = ALContext->Listener;
-
- /* Set mixing buffers and get send parameters. */
- voice->mDirect.Buffer = Device->Dry.Buffer;
- ALeffectslot *SendSlots[MAX_SENDS];
- ALfloat RoomRolloff[MAX_SENDS];
- ALfloat DecayDistance[MAX_SENDS];
- ALfloat DecayLFDistance[MAX_SENDS];
- ALfloat DecayHFDistance[MAX_SENDS];
- for(ALsizei i{0};i < NumSends;i++)
- {
- SendSlots[i] = props->Send[i].Slot;
- if(!SendSlots[i] && i == 0)
- SendSlots[i] = ALContext->DefaultSlot.get();
- if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
- {
- SendSlots[i] = nullptr;
- RoomRolloff[i] = 0.0f;
- DecayDistance[i] = 0.0f;
- DecayLFDistance[i] = 0.0f;
- DecayHFDistance[i] = 0.0f;
- }
- else if(SendSlots[i]->Params.AuxSendAuto)
- {
- RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
- /* Calculate the distances to where this effect's decay reaches
- * -60dB.
- */
- DecayDistance[i] = SendSlots[i]->Params.DecayTime *
- Listener.Params.ReverbSpeedOfSound;
- DecayLFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayLFRatio;
- DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
- if(SendSlots[i]->Params.DecayHFLimit)
- {
- ALfloat airAbsorption{SendSlots[i]->Params.AirAbsorptionGainHF};
- if(airAbsorption < 1.0f)
- {
- /* Calculate the distance to where this effect's air
- * absorption reaches -60dB, and limit the effect's HF
- * decay distance (so it doesn't take any longer to decay
- * than the air would allow).
- */
- ALfloat absorb_dist{std::log10(REVERB_DECAY_GAIN) / std::log10(airAbsorption)};
- DecayHFDistance[i] = minf(absorb_dist, DecayHFDistance[i]);
- }
- }
- }
- else
- {
- /* If the slot's auxiliary send auto is off, the data sent to the
- * effect slot is the same as the dry path, sans filter effects */
- RoomRolloff[i] = props->RolloffFactor;
- DecayDistance[i] = 0.0f;
- DecayLFDistance[i] = 0.0f;
- DecayHFDistance[i] = 0.0f;
- }
-
- if(!SendSlots[i])
- voice->mSend[i].Buffer = {};
- else
- voice->mSend[i].Buffer = SendSlots[i]->Wet.Buffer;
- }
-
- /* Transform source to listener space (convert to head relative) */
- alu::Vector Position{props->Position[0], props->Position[1], props->Position[2], 1.0f};
- alu::Vector Velocity{props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f};
- alu::Vector Direction{props->Direction[0], props->Direction[1], props->Direction[2], 0.0f};
- if(props->HeadRelative == AL_FALSE)
- {
- /* Transform source vectors */
- Position = Listener.Params.Matrix * Position;
- Velocity = Listener.Params.Matrix * Velocity;
- Direction = Listener.Params.Matrix * Direction;
- }
- else
- {
- /* Offset the source velocity to be relative of the listener velocity */
- Velocity += Listener.Params.Velocity;
- }
-
- const bool directional{Direction.normalize() > 0.0f};
- alu::Vector ToSource{Position[0], Position[1], Position[2], 0.0f};
- const ALfloat Distance{ToSource.normalize()};
-
- /* Initial source gain */
- ALfloat DryGain{props->Gain};
- ALfloat DryGainHF{1.0f};
- ALfloat DryGainLF{1.0f};
- ALfloat WetGain[MAX_SENDS], WetGainHF[MAX_SENDS], WetGainLF[MAX_SENDS];
- for(ALsizei i{0};i < NumSends;i++)
- {
- WetGain[i] = props->Gain;
- WetGainHF[i] = 1.0f;
- WetGainLF[i] = 1.0f;
- }
-
- /* Calculate distance attenuation */
- ALfloat ClampedDist{Distance};
-
- switch(Listener.Params.SourceDistanceModel ?
- props->mDistanceModel : Listener.Params.mDistanceModel)
- {
- case DistanceModel::InverseClamped:
- ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
- if(props->MaxDistance < props->RefDistance) break;
- /*fall-through*/
- case DistanceModel::Inverse:
- if(!(props->RefDistance > 0.0f))
- ClampedDist = props->RefDistance;
- else
- {
- ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
- if(dist > 0.0f) DryGain *= props->RefDistance / dist;
- for(ALsizei i{0};i < NumSends;i++)
- {
- dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
- if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
- }
- }
- break;
-
- case DistanceModel::LinearClamped:
- ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
- if(props->MaxDistance < props->RefDistance) break;
- /*fall-through*/
- case DistanceModel::Linear:
- if(!(props->MaxDistance != props->RefDistance))
- ClampedDist = props->RefDistance;
- else
- {
- ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
- (props->MaxDistance-props->RefDistance);
- DryGain *= maxf(1.0f - attn, 0.0f);
- for(ALsizei i{0};i < NumSends;i++)
- {
- attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
- (props->MaxDistance-props->RefDistance);
- WetGain[i] *= maxf(1.0f - attn, 0.0f);
- }
- }
- break;
-
- case DistanceModel::ExponentClamped:
- ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
- if(props->MaxDistance < props->RefDistance) break;
- /*fall-through*/
- case DistanceModel::Exponent:
- if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
- ClampedDist = props->RefDistance;
- else
- {
- DryGain *= std::pow(ClampedDist/props->RefDistance, -props->RolloffFactor);
- for(ALsizei i{0};i < NumSends;i++)
- WetGain[i] *= std::pow(ClampedDist/props->RefDistance, -RoomRolloff[i]);
- }
- break;
-
- case DistanceModel::Disable:
- ClampedDist = props->RefDistance;
- break;
- }
-
- /* Calculate directional soundcones */
- if(directional && props->InnerAngle < 360.0f)
- {
- const ALfloat Angle{Rad2Deg(std::acos(-aluDotproduct(Direction, ToSource)) *
- ConeScale * 2.0f)};
-
- ALfloat ConeVolume, ConeHF;
- if(!(Angle > props->InnerAngle))
- {
- ConeVolume = 1.0f;
- ConeHF = 1.0f;
- }
- else if(Angle < props->OuterAngle)
- {
- ALfloat scale = ( Angle-props->InnerAngle) /
- (props->OuterAngle-props->InnerAngle);
- ConeVolume = lerp(1.0f, props->OuterGain, scale);
- ConeHF = lerp(1.0f, props->OuterGainHF, scale);
- }
- else
- {
- ConeVolume = props->OuterGain;
- ConeHF = props->OuterGainHF;
- }
-
- DryGain *= ConeVolume;
- if(props->DryGainHFAuto)
- DryGainHF *= ConeHF;
- if(props->WetGainAuto)
- std::transform(std::begin(WetGain), std::begin(WetGain)+NumSends, std::begin(WetGain),
- [ConeVolume](ALfloat gain) noexcept -> ALfloat { return gain * ConeVolume; }
- );
- if(props->WetGainHFAuto)
- std::transform(std::begin(WetGainHF), std::begin(WetGainHF)+NumSends,
- std::begin(WetGainHF),
- [ConeHF](ALfloat gain) noexcept -> ALfloat { return gain * ConeHF; }
- );
- }
-
- /* Apply gain and frequency filters */
- DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
- DryGain = minf(DryGain*props->Direct.Gain*Listener.Params.Gain, GAIN_MIX_MAX);
- DryGainHF *= props->Direct.GainHF;
- DryGainLF *= props->Direct.GainLF;
- for(ALsizei i{0};i < NumSends;i++)
- {
- WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
- WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener.Params.Gain, GAIN_MIX_MAX);
- WetGainHF[i] *= props->Send[i].GainHF;
- WetGainLF[i] *= props->Send[i].GainLF;
- }
-
- /* Distance-based air absorption and initial send decay. */
- if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
- {
- ALfloat meters_base{(ClampedDist-props->RefDistance) * props->RolloffFactor *
- Listener.Params.MetersPerUnit};
- if(props->AirAbsorptionFactor > 0.0f)
- {
- ALfloat hfattn{std::pow(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor)};
- DryGainHF *= hfattn;
- std::transform(std::begin(WetGainHF), std::begin(WetGainHF)+NumSends,
- std::begin(WetGainHF),
- [hfattn](ALfloat gain) noexcept -> ALfloat { return gain * hfattn; }
- );
- }
-
- if(props->WetGainAuto)
- {
- /* Apply a decay-time transformation to the wet path, based on the
- * source distance in meters. The initial decay of the reverb
- * effect is calculated and applied to the wet path.
- */
- for(ALsizei i{0};i < NumSends;i++)
- {
- if(!(DecayDistance[i] > 0.0f))
- continue;
-
- const ALfloat gain{std::pow(REVERB_DECAY_GAIN, meters_base/DecayDistance[i])};
- WetGain[i] *= gain;
- /* Yes, the wet path's air absorption is applied with
- * WetGainAuto on, rather than WetGainHFAuto.
- */
- if(gain > 0.0f)
- {
- ALfloat gainhf{std::pow(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i])};
- WetGainHF[i] *= minf(gainhf / gain, 1.0f);
- ALfloat gainlf{std::pow(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i])};
- WetGainLF[i] *= minf(gainlf / gain, 1.0f);
- }
- }
- }
- }
-
-
- /* Initial source pitch */
- ALfloat Pitch{props->Pitch};
-
- /* Calculate velocity-based doppler effect */
- ALfloat DopplerFactor{props->DopplerFactor * Listener.Params.DopplerFactor};
- if(DopplerFactor > 0.0f)
- {
- const alu::Vector &lvelocity = Listener.Params.Velocity;
- ALfloat vss{aluDotproduct(Velocity, ToSource) * -DopplerFactor};
- ALfloat vls{aluDotproduct(lvelocity, ToSource) * -DopplerFactor};
-
- const ALfloat SpeedOfSound{Listener.Params.SpeedOfSound};
- if(!(vls < SpeedOfSound))
- {
- /* Listener moving away from the source at the speed of sound.
- * Sound waves can't catch it.
- */
- Pitch = 0.0f;
- }
- else if(!(vss < SpeedOfSound))
- {
- /* Source moving toward the listener at the speed of sound. Sound
- * waves bunch up to extreme frequencies.
- */
- Pitch = std::numeric_limits<float>::infinity();
- }
- else
- {
- /* Source and listener movement is nominal. Calculate the proper
- * doppler shift.
- */
- Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
- }
- }
-
- /* Adjust pitch based on the buffer and output frequencies, and calculate
- * fixed-point stepping value.
- */
- Pitch *= static_cast<ALfloat>(voice->mFrequency)/static_cast<ALfloat>(Device->Frequency);
- if(Pitch > static_cast<ALfloat>(MAX_PITCH))
- voice->mStep = MAX_PITCH<<FRACTIONBITS;
- else
- voice->mStep = maxi(fastf2i(Pitch * FRACTIONONE), 1);
- if(props->mResampler == BSinc24Resampler)
- BsincPrepare(voice->mStep, &voice->mResampleState.bsinc, &bsinc24);
- else if(props->mResampler == BSinc12Resampler)
- BsincPrepare(voice->mStep, &voice->mResampleState.bsinc, &bsinc12);
- voice->mResampler = SelectResampler(props->mResampler);
-
- ALfloat spread{0.0f};
- if(props->Radius > Distance)
- spread = al::MathDefs<float>::Tau() - Distance/props->Radius*al::MathDefs<float>::Pi();
- else if(Distance > 0.0f)
- spread = std::asin(props->Radius/Distance) * 2.0f;
-
- CalcPanningAndFilters(voice, ToSource[0], ToSource[1], ToSource[2]*ZScale,
- Distance*Listener.Params.MetersPerUnit, spread, DryGain, DryGainHF, DryGainLF, WetGain,
- WetGainLF, WetGainHF, SendSlots, props, Listener, Device);
-}
-
-void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
-{
- ALvoiceProps *props{voice->mUpdate.exchange(nullptr, std::memory_order_acq_rel)};
- if(!props && !force) return;
-
- if(props)
- {
- voice->mProps = *props;
-
- AtomicReplaceHead(context->FreeVoiceProps, props);
- }
-
- if((voice->mProps.mSpatializeMode == SpatializeAuto && voice->mFmtChannels == FmtMono) ||
- voice->mProps.mSpatializeMode == SpatializeOn)
- CalcAttnSourceParams(voice, &voice->mProps, context);
- else
- CalcNonAttnSourceParams(voice, &voice->mProps, context);
-}
-
-
-void ProcessParamUpdates(ALCcontext *ctx, const ALeffectslotArray *slots)
-{
- IncrementRef(&ctx->UpdateCount);
- if(LIKELY(!ctx->HoldUpdates.load(std::memory_order_acquire)))
- {
- bool cforce{CalcContextParams(ctx)};
- bool force{CalcListenerParams(ctx) || cforce};
- force = std::accumulate(slots->begin(), slots->end(), force,
- [ctx,cforce](bool force, ALeffectslot *slot) -> bool
- { return CalcEffectSlotParams(slot, ctx, cforce) | force; }
- );
-
- std::for_each(ctx->Voices->begin(),
- ctx->Voices->begin() + ctx->VoiceCount.load(std::memory_order_acquire),
- [ctx,force](ALvoice &voice) -> void
- {
- ALuint sid{voice.mSourceID.load(std::memory_order_acquire)};
- if(sid) CalcSourceParams(&voice, ctx, force);
- }
- );
- }
- IncrementRef(&ctx->UpdateCount);
-}
-
-void ProcessContext(ALCcontext *ctx, const ALsizei SamplesToDo)
-{
- ASSUME(SamplesToDo > 0);
-
- const ALeffectslotArray *auxslots{ctx->ActiveAuxSlots.load(std::memory_order_acquire)};
-
- /* Process pending propery updates for objects on the context. */
- ProcessParamUpdates(ctx, auxslots);
-
- /* Clear auxiliary effect slot mixing buffers. */
- std::for_each(auxslots->begin(), auxslots->end(),
- [SamplesToDo](ALeffectslot *slot) -> void
- {
- for(auto &buffer : slot->MixBuffer)
- std::fill_n(buffer.begin(), SamplesToDo, 0.0f);
- }
- );
-
- /* Process voices that have a playing source. */
- std::for_each(ctx->Voices->begin(),
- ctx->Voices->begin() + ctx->VoiceCount.load(std::memory_order_acquire),
- [SamplesToDo,ctx](ALvoice &voice) -> void
- {
- const ALvoice::State vstate{voice.mPlayState.load(std::memory_order_acquire)};
- if(vstate == ALvoice::Stopped) return;
- const ALuint sid{voice.mSourceID.load(std::memory_order_relaxed)};
- if(voice.mStep < 1) return;
-
- MixVoice(&voice, vstate, sid, ctx, SamplesToDo);
- }
- );
-
- /* Process effects. */
- if(auxslots->size() < 1) return;
- auto slots = auxslots->data();
- auto slots_end = slots + auxslots->size();
-
- /* First sort the slots into scratch storage, so that effects come before
- * their effect target (or their targets' target).
- */
- auto sorted_slots = const_cast<ALeffectslot**>(slots_end);
- auto sorted_slots_end = sorted_slots;
- auto in_chain = [](const ALeffectslot *slot1, const ALeffectslot *slot2) noexcept -> bool
- {
- while((slot1=slot1->Params.Target) != nullptr) {
- if(slot1 == slot2) return true;
- }
- return false;
- };
-
- *sorted_slots_end = *slots;
- ++sorted_slots_end;
- while(++slots != slots_end)
- {
- /* If this effect slot targets an effect slot already in the list (i.e.
- * slots outputs to something in sorted_slots), directly or indirectly,
- * insert it prior to that element.
- */
- auto checker = sorted_slots;
- do {
- if(in_chain(*slots, *checker)) break;
- } while(++checker != sorted_slots_end);
-
- checker = std::move_backward(checker, sorted_slots_end, sorted_slots_end+1);
- *--checker = *slots;
- ++sorted_slots_end;
- }
-
- std::for_each(sorted_slots, sorted_slots_end,
- [SamplesToDo](const ALeffectslot *slot) -> void
- {
- EffectState *state{slot->Params.mEffectState};
- state->process(SamplesToDo, slot->Wet.Buffer.data(),
- static_cast<ALsizei>(slot->Wet.Buffer.size()), state->mOutTarget);
- }
- );
-}
-
-
-void ApplyStablizer(FrontStablizer *Stablizer, const al::span<FloatBufferLine> Buffer,
- const ALuint lidx, const ALuint ridx, const ALuint cidx, const ALsizei SamplesToDo)
-{
- ASSUME(SamplesToDo > 0);
-
- /* Apply a delay to all channels, except the front-left and front-right, so
- * they maintain correct timing.
- */
- const size_t NumChannels{Buffer.size()};
- for(size_t i{0u};i < NumChannels;i++)
- {
- if(i == lidx || i == ridx)
- continue;
-
- auto &DelayBuf = Stablizer->DelayBuf[i];
- auto buffer_end = Buffer[i].begin() + SamplesToDo;
- if(LIKELY(SamplesToDo >= ALsizei{FrontStablizer::DelayLength}))
- {
- auto delay_end = std::rotate(Buffer[i].begin(),
- buffer_end - FrontStablizer::DelayLength, buffer_end);
- std::swap_ranges(Buffer[i].begin(), delay_end, std::begin(DelayBuf));
- }
- else
- {
- auto delay_start = std::swap_ranges(Buffer[i].begin(), buffer_end,
- std::begin(DelayBuf));
- std::rotate(std::begin(DelayBuf), delay_start, std::end(DelayBuf));
- }
- }
-
- ALfloat (&lsplit)[2][BUFFERSIZE] = Stablizer->LSplit;
- ALfloat (&rsplit)[2][BUFFERSIZE] = Stablizer->RSplit;
- auto &tmpbuf = Stablizer->TempBuf;
-
- /* This applies the band-splitter, preserving phase at the cost of some
- * delay. The shorter the delay, the more error seeps into the result.
- */
- auto apply_splitter = [&tmpbuf,SamplesToDo](const FloatBufferLine &Buffer,
- ALfloat (&DelayBuf)[FrontStablizer::DelayLength], BandSplitter &Filter,
- ALfloat (&splitbuf)[2][BUFFERSIZE]) -> void
- {
- /* Combine the delayed samples and the input samples into the temp
- * buffer, in reverse. Then copy the final samples back into the delay
- * buffer for next time. Note that the delay buffer's samples are
- * stored backwards here.
- */
- auto tmpbuf_end = std::begin(tmpbuf) + SamplesToDo;
- std::copy_n(std::begin(DelayBuf), FrontStablizer::DelayLength, tmpbuf_end);
- std::reverse_copy(Buffer.begin(), Buffer.begin()+SamplesToDo, std::begin(tmpbuf));
- std::copy_n(std::begin(tmpbuf), FrontStablizer::DelayLength, std::begin(DelayBuf));
-
- /* Apply an all-pass on the reversed signal, then reverse the samples
- * to get the forward signal with a reversed phase shift.
- */
- Filter.applyAllpass(tmpbuf, SamplesToDo+FrontStablizer::DelayLength);
- std::reverse(std::begin(tmpbuf), tmpbuf_end+FrontStablizer::DelayLength);
-
- /* Now apply the band-splitter, combining its phase shift with the
- * reversed phase shift, restoring the original phase on the split
- * signal.
- */
- Filter.process(splitbuf[1], splitbuf[0], tmpbuf, SamplesToDo);
- };
- apply_splitter(Buffer[lidx], Stablizer->DelayBuf[lidx], Stablizer->LFilter, lsplit);
- apply_splitter(Buffer[ridx], Stablizer->DelayBuf[ridx], Stablizer->RFilter, rsplit);
-
- for(ALsizei i{0};i < SamplesToDo;i++)
- {
- ALfloat lfsum{lsplit[0][i] + rsplit[0][i]};
- ALfloat hfsum{lsplit[1][i] + rsplit[1][i]};
- ALfloat s{lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i]};
-
- /* This pans the separate low- and high-frequency sums between being on
- * the center channel and the left/right channels. The low-frequency
- * sum is 1/3rd toward center (2/3rds on left/right) and the high-
- * frequency sum is 1/4th toward center (3/4ths on left/right). These
- * values can be tweaked.
- */
- ALfloat m{lfsum*std::cos(1.0f/3.0f * (al::MathDefs<float>::Pi()*0.5f)) +
- hfsum*std::cos(1.0f/4.0f * (al::MathDefs<float>::Pi()*0.5f))};
- ALfloat c{lfsum*std::sin(1.0f/3.0f * (al::MathDefs<float>::Pi()*0.5f)) +
- hfsum*std::sin(1.0f/4.0f * (al::MathDefs<float>::Pi()*0.5f))};
-
- /* The generated center channel signal adds to the existing signal,
- * while the modified left and right channels replace.
- */
- Buffer[lidx][i] = (m + s) * 0.5f;
- Buffer[ridx][i] = (m - s) * 0.5f;
- Buffer[cidx][i] += c * 0.5f;
- }
-}
-
-void ApplyDistanceComp(const al::span<FloatBufferLine> Samples, const ALsizei SamplesToDo,
- const DistanceComp::DistData *distcomp)
-{
- ASSUME(SamplesToDo > 0);
-
- for(auto &chanbuffer : Samples)
- {
- const ALfloat gain{distcomp->Gain};
- const ALsizei base{distcomp->Length};
- ALfloat *distbuf{al::assume_aligned<16>(distcomp->Buffer)};
- ++distcomp;
-
- if(base < 1)
- continue;
-
- ALfloat *inout{al::assume_aligned<16>(chanbuffer.data())};
- auto inout_end = inout + SamplesToDo;
- if(LIKELY(SamplesToDo >= base))
- {
- auto delay_end = std::rotate(inout, inout_end - base, inout_end);
- std::swap_ranges(inout, delay_end, distbuf);
- }
- else
- {
- auto delay_start = std::swap_ranges(inout, inout_end, distbuf);
- std::rotate(distbuf, delay_start, distbuf + base);
- }
- std::transform(inout, inout_end, inout, std::bind(std::multiplies<float>{}, _1, gain));
- }
-}
-
-void ApplyDither(const al::span<FloatBufferLine> Samples, ALuint *dither_seed,
- const ALfloat quant_scale, const ALsizei SamplesToDo)
-{
- /* Dithering. Generate whitenoise (uniform distribution of random values
- * between -1 and +1) and add it to the sample values, after scaling up to
- * the desired quantization depth amd before rounding.
- */
- const ALfloat invscale{1.0f / quant_scale};
- ALuint seed{*dither_seed};
- auto dither_channel = [&seed,invscale,quant_scale,SamplesToDo](FloatBufferLine &input) -> void
- {
- ASSUME(SamplesToDo > 0);
- auto dither_sample = [&seed,invscale,quant_scale](const ALfloat sample) noexcept -> ALfloat
- {
- ALfloat val{sample * quant_scale};
- ALuint rng0{dither_rng(&seed)};
- ALuint rng1{dither_rng(&seed)};
- val += static_cast<ALfloat>(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
- return fast_roundf(val) * invscale;
- };
- std::transform(input.begin(), input.begin()+SamplesToDo, input.begin(), dither_sample);
- };
- std::for_each(Samples.begin(), Samples.end(), dither_channel);
- *dither_seed = seed;
-}
-
-
-/* Base template left undefined. Should be marked =delete, but Clang 3.8.1
- * chokes on that given the inline specializations.
- */
-template<typename T>
-inline T SampleConv(ALfloat) noexcept;
-
-template<> inline ALfloat SampleConv(ALfloat val) noexcept
-{ return val; }
-template<> inline ALint SampleConv(ALfloat val) noexcept
-{
- /* Floats have a 23-bit mantissa, plus an implied 1 bit and a sign bit.
- * This means a normalized float has at most 25 bits of signed precision.
- * When scaling and clamping for a signed 32-bit integer, these following
- * values are the best a float can give.
- */
- return fastf2i(clampf(val*2147483648.0f, -2147483648.0f, 2147483520.0f));
-}
-template<> inline ALshort SampleConv(ALfloat val) noexcept
-{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
-template<> inline ALbyte SampleConv(ALfloat val) noexcept
-{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
-
-/* Define unsigned output variations. */
-template<> inline ALuint SampleConv(ALfloat val) noexcept
-{ return SampleConv<ALint>(val) + 2147483648u; }
-template<> inline ALushort SampleConv(ALfloat val) noexcept
-{ return SampleConv<ALshort>(val) + 32768; }
-template<> inline ALubyte SampleConv(ALfloat val) noexcept
-{ return SampleConv<ALbyte>(val) + 128; }
-
-template<DevFmtType T>
-void Write(const al::span<const FloatBufferLine> InBuffer, ALvoid *OutBuffer, const size_t Offset,
- const ALsizei SamplesToDo)
-{
- using SampleType = typename DevFmtTypeTraits<T>::Type;
-
- const size_t numchans{InBuffer.size()};
- ASSUME(numchans > 0);
-
- SampleType *outbase = static_cast<SampleType*>(OutBuffer) + Offset*numchans;
- auto conv_channel = [&outbase,SamplesToDo,numchans](const FloatBufferLine &inbuf) -> void
- {
- ASSUME(SamplesToDo > 0);
- SampleType *out{outbase++};
- auto conv_sample = [numchans,&out](const ALfloat s) noexcept -> void
- {
- *out = SampleConv<SampleType>(s);
- out += numchans;
- };
- std::for_each(inbuf.begin(), inbuf.begin()+SamplesToDo, conv_sample);
- };
- std::for_each(InBuffer.cbegin(), InBuffer.cend(), conv_channel);
-}
-
-} // namespace
-
-void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
-{
- FPUCtl mixer_mode{};
- for(ALsizei SamplesDone{0};SamplesDone < NumSamples;)
- {
- const ALsizei SamplesToDo{mini(NumSamples-SamplesDone, BUFFERSIZE)};
-
- /* Clear main mixing buffers. */
- std::for_each(device->MixBuffer.begin(), device->MixBuffer.end(),
- [SamplesToDo](std::array<ALfloat,BUFFERSIZE> &buffer) -> void
- { std::fill_n(buffer.begin(), SamplesToDo, 0.0f); }
- );
-
- /* Increment the mix count at the start (lsb should now be 1). */
- IncrementRef(&device->MixCount);
-
- /* For each context on this device, process and mix its sources and
- * effects.
- */
- for(ALCcontext *ctx : *device->mContexts.load(std::memory_order_acquire))
- ProcessContext(ctx, SamplesToDo);
-
- /* Increment the clock time. Every second's worth of samples is
- * converted and added to clock base so that large sample counts don't
- * overflow during conversion. This also guarantees a stable
- * conversion.
- */
- device->SamplesDone += SamplesToDo;
- device->ClockBase += std::chrono::seconds{device->SamplesDone / device->Frequency};
- device->SamplesDone %= device->Frequency;
-
- /* Increment the mix count at the end (lsb should now be 0). */
- IncrementRef(&device->MixCount);
-
- /* Apply any needed post-process for finalizing the Dry mix to the
- * RealOut (Ambisonic decode, UHJ encode, etc).
- */
- if(LIKELY(device->PostProcess))
- device->PostProcess(device, SamplesToDo);
- const al::span<FloatBufferLine> RealOut{device->RealOut.Buffer};
-
- /* Apply front image stablization for surround sound, if applicable. */
- if(device->Stablizer)
- {
- const int lidx{GetChannelIdxByName(device->RealOut, FrontLeft)};
- const int ridx{GetChannelIdxByName(device->RealOut, FrontRight)};
- const int cidx{GetChannelIdxByName(device->RealOut, FrontCenter)};
- assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
-
- ApplyStablizer(device->Stablizer.get(), RealOut, lidx, ridx, cidx, SamplesToDo);
- }
-
- /* Apply compression, limiting sample amplitude if needed or desired. */
- if(Compressor *comp{device->Limiter.get()})
- comp->process(SamplesToDo, RealOut.data());
-
- /* Apply delays and attenuation for mismatched speaker distances. */
- ApplyDistanceComp(RealOut, SamplesToDo, device->ChannelDelay.as_span().cbegin());
-
- /* Apply dithering. The compressor should have left enough headroom for
- * the dither noise to not saturate.
- */
- if(device->DitherDepth > 0.0f)
- ApplyDither(RealOut, &device->DitherSeed, device->DitherDepth, SamplesToDo);
-
- if(LIKELY(OutBuffer))
- {
- /* Finally, interleave and convert samples, writing to the device's
- * output buffer.
- */
- switch(device->FmtType)
- {
-#define HANDLE_WRITE(T) case T: \
- Write<T>(RealOut, OutBuffer, SamplesDone, SamplesToDo); break;
- HANDLE_WRITE(DevFmtByte)
- HANDLE_WRITE(DevFmtUByte)
- HANDLE_WRITE(DevFmtShort)
- HANDLE_WRITE(DevFmtUShort)
- HANDLE_WRITE(DevFmtInt)
- HANDLE_WRITE(DevFmtUInt)
- HANDLE_WRITE(DevFmtFloat)
-#undef HANDLE_WRITE
- }
- }
-
- SamplesDone += SamplesToDo;
- }
-}
-
-
-void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
-{
- if(!device->Connected.exchange(false, std::memory_order_acq_rel))
- return;
-
- AsyncEvent evt{EventType_Disconnected};
- evt.u.user.type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
- evt.u.user.id = 0;
- evt.u.user.param = 0;
-
- va_list args;
- va_start(args, msg);
- int msglen{vsnprintf(evt.u.user.msg, sizeof(evt.u.user.msg), msg, args)};
- va_end(args);
-
- if(msglen < 0 || static_cast<size_t>(msglen) >= sizeof(evt.u.user.msg))
- evt.u.user.msg[sizeof(evt.u.user.msg)-1] = 0;
-
- for(ALCcontext *ctx : *device->mContexts.load())
- {
- const ALbitfieldSOFT enabledevt{ctx->EnabledEvts.load(std::memory_order_acquire)};
- if((enabledevt&EventType_Disconnected))
- {
- RingBuffer *ring{ctx->AsyncEvents.get()};
- auto evt_data = ring->getWriteVector().first;
- if(evt_data.len > 0)
- {
- new (evt_data.buf) AsyncEvent{evt};
- ring->writeAdvance(1);
- ctx->EventSem.post();
- }
- }
-
- auto stop_voice = [](ALvoice &voice) -> void
- {
- voice.mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
- voice.mLoopBuffer.store(nullptr, std::memory_order_relaxed);
- voice.mSourceID.store(0u, std::memory_order_relaxed);
- voice.mPlayState.store(ALvoice::Stopped, std::memory_order_release);
- };
- std::for_each(ctx->Voices->begin(),
- ctx->Voices->begin() + ctx->VoiceCount.load(std::memory_order_acquire),
- stop_voice);
- }
-}
diff --git a/Alc/alu.h b/Alc/alu.h
deleted file mode 100644
index 9acf904a..00000000
--- a/Alc/alu.h
+++ /dev/null
@@ -1,466 +0,0 @@
-#ifndef _ALU_H_
-#define _ALU_H_
-
-#include <array>
-#include <atomic>
-#include <cmath>
-#include <cstddef>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "AL/alext.h"
-
-#include "alBuffer.h"
-#include "alcmain.h"
-#include "almalloc.h"
-#include "alspan.h"
-#include "ambidefs.h"
-#include "filters/biquad.h"
-#include "filters/nfc.h"
-#include "filters/splitter.h"
-#include "hrtf.h"
-#include "logging.h"
-
-struct ALbufferlistitem;
-struct ALeffectslot;
-struct BSincTable;
-
-
-enum class DistanceModel;
-
-#define MAX_PITCH 255
-#define MAX_SENDS 16
-
-
-#define DITHER_RNG_SEED 22222
-
-
-enum SpatializeMode {
- SpatializeOff = AL_FALSE,
- SpatializeOn = AL_TRUE,
- SpatializeAuto = AL_AUTO_SOFT
-};
-
-enum Resampler {
- PointResampler,
- LinearResampler,
- FIR4Resampler,
- BSinc12Resampler,
- BSinc24Resampler,
-
- ResamplerMax = BSinc24Resampler
-};
-extern Resampler ResamplerDefault;
-
-/* The number of distinct scale and phase intervals within the bsinc filter
- * table.
- */
-#define BSINC_SCALE_BITS 4
-#define BSINC_SCALE_COUNT (1<<BSINC_SCALE_BITS)
-#define BSINC_PHASE_BITS 4
-#define BSINC_PHASE_COUNT (1<<BSINC_PHASE_BITS)
-
-/* Interpolator state. Kind of a misnomer since the interpolator itself is
- * stateless. This just keeps it from having to recompute scale-related
- * mappings for every sample.
- */
-struct BsincState {
- ALfloat sf; /* Scale interpolation factor. */
- ALsizei m; /* Coefficient count. */
- ALsizei l; /* Left coefficient offset. */
- /* Filter coefficients, followed by the scale, phase, and scale-phase
- * delta coefficients. Starting at phase index 0, each subsequent phase
- * index follows contiguously.
- */
- const ALfloat *filter;
-};
-
-union InterpState {
- BsincState bsinc;
-};
-
-using ResamplerFunc = const ALfloat*(*)(const InterpState *state,
- const ALfloat *RESTRICT src, ALsizei frac, ALint increment,
- ALfloat *RESTRICT dst, ALsizei dstlen);
-
-void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table);
-
-extern const BSincTable bsinc12;
-extern const BSincTable bsinc24;
-
-
-enum {
- AF_None = 0,
- AF_LowPass = 1,
- AF_HighPass = 2,
- AF_BandPass = AF_LowPass | AF_HighPass
-};
-
-
-struct MixHrtfFilter {
- const HrirArray<ALfloat> *Coeffs;
- ALsizei Delay[2];
- ALfloat Gain;
- ALfloat GainStep;
-};
-
-
-struct DirectParams {
- BiquadFilter LowPass;
- BiquadFilter HighPass;
-
- NfcFilter NFCtrlFilter;
-
- struct {
- HrtfFilter Old;
- HrtfFilter Target;
- HrtfState State;
- } Hrtf;
-
- struct {
- ALfloat Current[MAX_OUTPUT_CHANNELS];
- ALfloat Target[MAX_OUTPUT_CHANNELS];
- } Gains;
-};
-
-struct SendParams {
- BiquadFilter LowPass;
- BiquadFilter HighPass;
-
- struct {
- ALfloat Current[MAX_OUTPUT_CHANNELS];
- ALfloat Target[MAX_OUTPUT_CHANNELS];
- } Gains;
-};
-
-
-struct ALvoicePropsBase {
- ALfloat Pitch;
- ALfloat Gain;
- ALfloat OuterGain;
- ALfloat MinGain;
- ALfloat MaxGain;
- ALfloat InnerAngle;
- ALfloat OuterAngle;
- ALfloat RefDistance;
- ALfloat MaxDistance;
- ALfloat RolloffFactor;
- std::array<ALfloat,3> Position;
- std::array<ALfloat,3> Velocity;
- std::array<ALfloat,3> Direction;
- std::array<ALfloat,3> OrientAt;
- std::array<ALfloat,3> OrientUp;
- ALboolean HeadRelative;
- DistanceModel mDistanceModel;
- Resampler mResampler;
- ALboolean DirectChannels;
- SpatializeMode mSpatializeMode;
-
- ALboolean DryGainHFAuto;
- ALboolean WetGainAuto;
- ALboolean WetGainHFAuto;
- ALfloat OuterGainHF;
-
- ALfloat AirAbsorptionFactor;
- ALfloat RoomRolloffFactor;
- ALfloat DopplerFactor;
-
- std::array<ALfloat,2> StereoPan;
-
- ALfloat Radius;
-
- /** Direct filter and auxiliary send info. */
- struct {
- ALfloat Gain;
- ALfloat GainHF;
- ALfloat HFReference;
- ALfloat GainLF;
- ALfloat LFReference;
- } Direct;
- struct SendData {
- ALeffectslot *Slot;
- ALfloat Gain;
- ALfloat GainHF;
- ALfloat HFReference;
- ALfloat GainLF;
- ALfloat LFReference;
- } Send[MAX_SENDS];
-};
-
-struct ALvoiceProps : public ALvoicePropsBase {
- std::atomic<ALvoiceProps*> next{nullptr};
-
- DEF_NEWDEL(ALvoiceProps)
-};
-
-#define VOICE_IS_STATIC (1u<<0)
-#define VOICE_IS_FADING (1u<<1) /* Fading sources use gain stepping for smooth transitions. */
-#define VOICE_IS_AMBISONIC (1u<<2) /* Voice needs HF scaling for ambisonic upsampling. */
-#define VOICE_HAS_HRTF (1u<<3)
-#define VOICE_HAS_NFC (1u<<4)
-
-struct ALvoice {
- enum State {
- Stopped = 0,
- Playing = 1,
- Stopping = 2
- };
-
- std::atomic<ALvoiceProps*> mUpdate{nullptr};
-
- std::atomic<ALuint> mSourceID{0u};
- std::atomic<State> mPlayState{Stopped};
-
- ALvoicePropsBase mProps;
-
- /**
- * Source offset in samples, relative to the currently playing buffer, NOT
- * the whole queue.
- */
- std::atomic<ALuint> mPosition;
- /** Fractional (fixed-point) offset to the next sample. */
- std::atomic<ALsizei> mPositionFrac;
-
- /* Current buffer queue item being played. */
- std::atomic<ALbufferlistitem*> mCurrentBuffer;
-
- /* Buffer queue item to loop to at end of queue (will be NULL for non-
- * looping voices).
- */
- std::atomic<ALbufferlistitem*> mLoopBuffer;
-
- /* Properties for the attached buffer(s). */
- FmtChannels mFmtChannels;
- ALuint mFrequency;
- ALsizei mNumChannels;
- ALsizei mSampleSize;
-
- /** Current target parameters used for mixing. */
- ALint mStep;
-
- ResamplerFunc mResampler;
-
- InterpState mResampleState;
-
- ALuint mFlags;
-
- struct DirectData {
- int FilterType;
- al::span<FloatBufferLine> Buffer;
- };
- DirectData mDirect;
-
- struct SendData {
- int FilterType;
- al::span<FloatBufferLine> Buffer;
- };
- std::array<SendData,MAX_SENDS> mSend;
-
- struct ChannelData {
- alignas(16) std::array<ALfloat,MAX_RESAMPLE_PADDING*2> mPrevSamples;
-
- ALfloat mAmbiScale;
- BandSplitter mAmbiSplitter;
-
- DirectParams mDryParams;
- std::array<SendParams,MAX_SENDS> mWetParams;
- };
- std::array<ChannelData,MAX_INPUT_CHANNELS> mChans;
-
- ALvoice() = default;
- ALvoice(const ALvoice&) = delete;
- ~ALvoice() { delete mUpdate.exchange(nullptr, std::memory_order_acq_rel); }
- ALvoice& operator=(const ALvoice&) = delete;
- ALvoice& operator=(ALvoice&& rhs) noexcept
- {
- ALvoiceProps *old_update{mUpdate.load(std::memory_order_relaxed)};
- mUpdate.store(rhs.mUpdate.exchange(old_update, std::memory_order_relaxed),
- std::memory_order_relaxed);
-
- mSourceID.store(rhs.mSourceID.load(std::memory_order_relaxed), std::memory_order_relaxed);
- mPlayState.store(rhs.mPlayState.load(std::memory_order_relaxed),
- std::memory_order_relaxed);
-
- mProps = rhs.mProps;
-
- mPosition.store(rhs.mPosition.load(std::memory_order_relaxed), std::memory_order_relaxed);
- mPositionFrac.store(rhs.mPositionFrac.load(std::memory_order_relaxed),
- std::memory_order_relaxed);
-
- mCurrentBuffer.store(rhs.mCurrentBuffer.load(std::memory_order_relaxed),
- std::memory_order_relaxed);
- mLoopBuffer.store(rhs.mLoopBuffer.load(std::memory_order_relaxed),
- std::memory_order_relaxed);
-
- mFmtChannels = rhs.mFmtChannels;
- mFrequency = rhs.mFrequency;
- mNumChannels = rhs.mNumChannels;
- mSampleSize = rhs.mSampleSize;
-
- mStep = rhs.mStep;
- mResampler = rhs.mResampler;
-
- mResampleState = rhs.mResampleState;
-
- mFlags = rhs.mFlags;
-
- mDirect = rhs.mDirect;
- mSend = rhs.mSend;
- mChans = rhs.mChans;
-
- return *this;
- }
-};
-
-
-using MixerFunc = void(*)(const ALfloat *data, const al::span<FloatBufferLine> OutBuffer,
- ALfloat *CurrentGains, const ALfloat *TargetGains, const ALsizei Counter, const ALsizei OutPos,
- const ALsizei BufferSize);
-using RowMixerFunc = void(*)(FloatBufferLine &OutBuffer, const ALfloat *gains,
- const al::span<const FloatBufferLine> InSamples, const ALsizei InPos,
- const ALsizei BufferSize);
-using HrtfMixerFunc = void(*)(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const ALfloat *InSamples, float2 *AccumSamples, const ALsizei OutPos, const ALsizei IrSize,
- MixHrtfFilter *hrtfparams, const ALsizei BufferSize);
-using HrtfMixerBlendFunc = void(*)(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const ALfloat *InSamples, float2 *AccumSamples, const ALsizei OutPos, const ALsizei IrSize,
- const HrtfFilter *oldparams, MixHrtfFilter *newparams, const ALsizei BufferSize);
-using HrtfDirectMixerFunc = void(*)(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples, DirectHrtfState *State,
- const ALsizei BufferSize);
-
-
-#define GAIN_MIX_MAX (1000.0f) /* +60dB */
-
-#define GAIN_SILENCE_THRESHOLD (0.00001f) /* -100dB */
-
-#define SPEEDOFSOUNDMETRESPERSEC (343.3f)
-#define AIRABSORBGAINHF (0.99426f) /* -0.05dB */
-
-/* Target gain for the reverb decay feedback reaching the decay time. */
-#define REVERB_DECAY_GAIN (0.001f) /* -60 dB */
-
-#define FRACTIONBITS (12)
-#define FRACTIONONE (1<<FRACTIONBITS)
-#define FRACTIONMASK (FRACTIONONE-1)
-
-
-inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu) noexcept
-{ return val1 + (val2-val1)*mu; }
-inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu) noexcept
-{
- ALfloat mu2 = mu*mu, mu3 = mu2*mu;
- ALfloat a0 = -0.5f*mu3 + mu2 + -0.5f*mu;
- ALfloat a1 = 1.5f*mu3 + -2.5f*mu2 + 1.0f;
- ALfloat a2 = -1.5f*mu3 + 2.0f*mu2 + 0.5f*mu;
- ALfloat a3 = 0.5f*mu3 + -0.5f*mu2;
- return val1*a0 + val2*a1 + val3*a2 + val4*a3;
-}
-
-
-enum HrtfRequestMode {
- Hrtf_Default = 0,
- Hrtf_Enable = 1,
- Hrtf_Disable = 2,
-};
-
-void aluInit(void);
-
-void aluInitMixer(void);
-
-ResamplerFunc SelectResampler(Resampler resampler);
-
-/* aluInitRenderer
- *
- * Set up the appropriate panning method and mixing method given the device
- * properties.
- */
-void aluInitRenderer(ALCdevice *device, ALint hrtf_id, HrtfRequestMode hrtf_appreq, HrtfRequestMode hrtf_userreq);
-
-void aluInitEffectPanning(ALeffectslot *slot, ALCdevice *device);
-
-void ProcessHrtf(ALCdevice *device, const ALsizei SamplesToDo);
-void ProcessAmbiDec(ALCdevice *device, const ALsizei SamplesToDo);
-void ProcessUhj(ALCdevice *device, const ALsizei SamplesToDo);
-void ProcessBs2b(ALCdevice *device, const ALsizei SamplesToDo);
-
-/**
- * Calculates ambisonic encoder coefficients using the X, Y, and Z direction
- * components, which must represent a normalized (unit length) vector, and the
- * spread is the angular width of the sound (0...tau).
- *
- * NOTE: The components use ambisonic coordinates. As a result:
- *
- * Ambisonic Y = OpenAL -X
- * Ambisonic Z = OpenAL Y
- * Ambisonic X = OpenAL -Z
- *
- * The components are ordered such that OpenAL's X, Y, and Z are the first,
- * second, and third parameters respectively -- simply negate X and Z.
- */
-void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
- ALfloat (&coeffs)[MAX_AMBI_CHANNELS]);
-
-/**
- * CalcDirectionCoeffs
- *
- * Calculates ambisonic coefficients based on an OpenAL direction vector. The
- * vector must be normalized (unit length), and the spread is the angular width
- * of the sound (0...tau).
- */
-inline void CalcDirectionCoeffs(const ALfloat (&dir)[3], ALfloat spread, ALfloat (&coeffs)[MAX_AMBI_CHANNELS])
-{
- /* Convert from OpenAL coords to Ambisonics. */
- CalcAmbiCoeffs(-dir[0], dir[1], -dir[2], spread, coeffs);
-}
-
-/**
- * CalcAngleCoeffs
- *
- * Calculates ambisonic coefficients based on azimuth and elevation. The
- * azimuth and elevation parameters are in radians, going right and up
- * respectively.
- */
-inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat (&coeffs)[MAX_AMBI_CHANNELS])
-{
- ALfloat x = -std::sin(azimuth) * std::cos(elevation);
- ALfloat y = std::sin(elevation);
- ALfloat z = std::cos(azimuth) * std::cos(elevation);
-
- CalcAmbiCoeffs(x, y, z, spread, coeffs);
-}
-
-
-/**
- * ComputePanGains
- *
- * Computes panning gains using the given channel decoder coefficients and the
- * pre-calculated direction or angle coefficients. For B-Format sources, the
- * coeffs are a 'slice' of a transform matrix for the input channel, used to
- * scale and orient the sound samples.
- */
-void ComputePanGains(const MixParams *mix, const ALfloat*RESTRICT coeffs, ALfloat ingain, ALfloat (&gains)[MAX_OUTPUT_CHANNELS]);
-
-
-inline std::array<ALfloat,MAX_AMBI_CHANNELS> GetAmbiIdentityRow(size_t i) noexcept
-{
- std::array<ALfloat,MAX_AMBI_CHANNELS> ret{};
- ret[i] = 1.0f;
- return ret;
-}
-
-
-void MixVoice(ALvoice *voice, ALvoice::State vstate, const ALuint SourceID, ALCcontext *Context, const ALsizei SamplesToDo);
-
-void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples);
-/* Caller must lock the device state, and the mixer must not be running. */
-void aluHandleDisconnect(ALCdevice *device, const char *msg, ...) DECL_FORMAT(printf, 2, 3);
-
-extern MixerFunc MixSamples;
-extern RowMixerFunc MixRowSamples;
-
-extern const ALfloat ConeScale;
-extern const ALfloat ZScale;
-extern const ALboolean OverrideReverbSpeedOfSound;
-
-#endif
diff --git a/Alc/ambdec.cpp b/Alc/ambdec.cpp
deleted file mode 100644
index 0991cfc5..00000000
--- a/Alc/ambdec.cpp
+++ /dev/null
@@ -1,436 +0,0 @@
-
-#include "config.h"
-
-#include "ambdec.h"
-
-#include <cctype>
-#include <cstring>
-#include <algorithm>
-
-#include <limits>
-#include <string>
-#include <fstream>
-#include <sstream>
-
-#include "logging.h"
-#include "compat.h"
-
-
-namespace {
-
-template<typename T, std::size_t N>
-constexpr inline std::size_t size(const T(&)[N]) noexcept
-{ return N; }
-
-int readline(std::istream &f, std::string &output)
-{
- while(f.good() && f.peek() == '\n')
- f.ignore();
-
- return std::getline(f, output) && !output.empty();
-}
-
-bool read_clipped_line(std::istream &f, std::string &buffer)
-{
- while(readline(f, buffer))
- {
- std::size_t pos{0};
- while(pos < buffer.length() && std::isspace(buffer[pos]))
- pos++;
- buffer.erase(0, pos);
-
- std::size_t cmtpos{buffer.find_first_of('#')};
- if(cmtpos < buffer.length())
- buffer.resize(cmtpos);
- while(!buffer.empty() && std::isspace(buffer.back()))
- buffer.pop_back();
-
- if(!buffer.empty())
- return true;
- }
- return false;
-}
-
-
-std::string read_word(std::istream &f)
-{
- std::string ret;
- f >> ret;
- return ret;
-}
-
-bool is_at_end(const std::string &buffer, std::size_t endpos)
-{
- while(endpos < buffer.length() && std::isspace(buffer[endpos]))
- ++endpos;
- return !(endpos < buffer.length());
-}
-
-
-bool load_ambdec_speakers(al::vector<AmbDecConf::SpeakerConf> &spkrs, const std::size_t num_speakers, std::istream &f, std::string &buffer)
-{
- while(spkrs.size() < num_speakers)
- {
- std::istringstream istr{buffer};
-
- std::string cmd{read_word(istr)};
- if(cmd.empty())
- {
- if(!read_clipped_line(f, buffer))
- {
- ERR("Unexpected end of file\n");
- return false;
- }
- continue;
- }
-
- if(cmd == "add_spkr")
- {
- spkrs.emplace_back();
- AmbDecConf::SpeakerConf &spkr = spkrs.back();
- const size_t spkr_num{spkrs.size()};
-
- istr >> spkr.Name;
- if(istr.fail()) WARN("Name not specified for speaker %zu\n", spkr_num);
- istr >> spkr.Distance;
- if(istr.fail()) WARN("Distance not specified for speaker %zu\n", spkr_num);
- istr >> spkr.Azimuth;
- if(istr.fail()) WARN("Azimuth not specified for speaker %zu\n", spkr_num);
- istr >> spkr.Elevation;
- if(istr.fail()) WARN("Elevation not specified for speaker %zu\n", spkr_num);
- istr >> spkr.Connection;
- if(istr.fail()) TRACE("Connection not specified for speaker %zu\n", spkr_num);
- }
- else
- {
- ERR("Unexpected speakers command: %s\n", cmd.c_str());
- return false;
- }
-
- istr.clear();
- const auto endpos = static_cast<std::size_t>(istr.tellg());
- if(!is_at_end(buffer, endpos))
- {
- ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
- return false;
- }
- buffer.clear();
- }
-
- return true;
-}
-
-bool load_ambdec_matrix(float (&gains)[MAX_AMBI_ORDER+1], al::vector<AmbDecConf::CoeffArray> &matrix, const std::size_t maxrow, std::istream &f, std::string &buffer)
-{
- bool gotgains{false};
- std::size_t cur{0u};
- while(cur < maxrow)
- {
- std::istringstream istr{buffer};
-
- std::string cmd{read_word(istr)};
- if(cmd.empty())
- {
- if(!read_clipped_line(f, buffer))
- {
- ERR("Unexpected end of file\n");
- return false;
- }
- continue;
- }
-
- if(cmd == "order_gain")
- {
- std::size_t curgain{0u};
- float value;
- while(istr.good())
- {
- istr >> value;
- if(istr.fail()) break;
- if(!istr.eof() && !std::isspace(istr.peek()))
- {
- ERR("Extra junk on gain %zu: %s\n", curgain+1,
- buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
- return false;
- }
- if(curgain < size(gains))
- gains[curgain++] = value;
- }
- std::fill(std::begin(gains)+curgain, std::end(gains), 0.0f);
- gotgains = true;
- }
- else if(cmd == "add_row")
- {
- matrix.emplace_back();
- AmbDecConf::CoeffArray &mtxrow = matrix.back();
- std::size_t curidx{0u};
- float value{};
- while(istr.good())
- {
- istr >> value;
- if(istr.fail()) break;
- if(!istr.eof() && !std::isspace(istr.peek()))
- {
- ERR("Extra junk on matrix element %zux%zu: %s\n", curidx,
- matrix.size(), buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
- matrix.pop_back();
- return false;
- }
- if(curidx < mtxrow.size())
- mtxrow[curidx++] = value;
- }
- std::fill(mtxrow.begin()+curidx, mtxrow.end(), 0.0f);
- cur++;
- }
- else
- {
- ERR("Unexpected matrix command: %s\n", cmd.c_str());
- return false;
- }
-
- istr.clear();
- const auto endpos = static_cast<std::size_t>(istr.tellg());
- if(!is_at_end(buffer, endpos))
- {
- ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
- return false;
- }
- buffer.clear();
- }
-
- if(!gotgains)
- {
- ERR("Matrix order_gain not specified\n");
- return false;
- }
-
- return true;
-}
-
-} // namespace
-
-int AmbDecConf::load(const char *fname) noexcept
-{
- al::ifstream f{fname};
- if(!f.is_open())
- {
- ERR("Failed to open: %s\n", fname);
- return 0;
- }
-
- std::size_t num_speakers{0u};
- std::string buffer;
- while(read_clipped_line(f, buffer))
- {
- std::istringstream istr{buffer};
-
- std::string command{read_word(istr)};
- if(command.empty())
- {
- ERR("Malformed line: %s\n", buffer.c_str());
- return 0;
- }
-
- if(command == "/description")
- istr >> Description;
- else if(command == "/version")
- {
- istr >> Version;
- if(!istr.eof() && !std::isspace(istr.peek()))
- {
- ERR("Extra junk after version: %s\n",
- buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
- return 0;
- }
- if(Version != 3)
- {
- ERR("Unsupported version: %u\n", Version);
- return 0;
- }
- }
- else if(command == "/dec/chan_mask")
- {
- istr >> std::hex >> ChanMask >> std::dec;
- if(!istr.eof() && !std::isspace(istr.peek()))
- {
- ERR("Extra junk after mask: %s\n",
- buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
- return 0;
- }
- }
- else if(command == "/dec/freq_bands")
- {
- istr >> FreqBands;
- if(!istr.eof() && !std::isspace(istr.peek()))
- {
- ERR("Extra junk after freq_bands: %s\n",
- buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
- return 0;
- }
- if(FreqBands != 1 && FreqBands != 2)
- {
- ERR("Invalid freq_bands value: %u\n", FreqBands);
- return 0;
- }
- }
- else if(command == "/dec/speakers")
- {
- istr >> num_speakers;
- if(!istr.eof() && !std::isspace(istr.peek()))
- {
- ERR("Extra junk after speakers: %s\n",
- buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
- return 0;
- }
- Speakers.reserve(num_speakers);
- LFMatrix.reserve(num_speakers);
- HFMatrix.reserve(num_speakers);
- }
- else if(command == "/dec/coeff_scale")
- {
- std::string scale = read_word(istr);
- if(scale == "n3d") CoeffScale = AmbDecScale::N3D;
- else if(scale == "sn3d") CoeffScale = AmbDecScale::SN3D;
- else if(scale == "fuma") CoeffScale = AmbDecScale::FuMa;
- else
- {
- ERR("Unsupported coeff scale: %s\n", scale.c_str());
- return 0;
- }
- }
- else if(command == "/opt/xover_freq")
- {
- istr >> XOverFreq;
- if(!istr.eof() && !std::isspace(istr.peek()))
- {
- ERR("Extra junk after xover_freq: %s\n",
- buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
- return 0;
- }
- }
- else if(command == "/opt/xover_ratio")
- {
- istr >> XOverRatio;
- if(!istr.eof() && !std::isspace(istr.peek()))
- {
- ERR("Extra junk after xover_ratio: %s\n",
- buffer.c_str()+static_cast<std::size_t>(istr.tellg()));
- return 0;
- }
- }
- else if(command == "/opt/input_scale" || command == "/opt/nfeff_comp" ||
- command == "/opt/delay_comp" || command == "/opt/level_comp")
- {
- /* Unused */
- read_word(istr);
- }
- else if(command == "/speakers/{")
- {
- const auto endpos = static_cast<std::size_t>(istr.tellg());
- if(!is_at_end(buffer, endpos))
- {
- ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
- return 0;
- }
- buffer.clear();
-
- if(!load_ambdec_speakers(Speakers, num_speakers, f, buffer))
- return 0;
-
- if(!read_clipped_line(f, buffer))
- {
- ERR("Unexpected end of file\n");
- return 0;
- }
- std::istringstream istr2{buffer};
- std::string endmark{read_word(istr2)};
- if(endmark != "/}")
- {
- ERR("Expected /} after speaker definitions, got %s\n", endmark.c_str());
- return 0;
- }
- istr.swap(istr2);
- }
- else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{")
- {
- const auto endpos = static_cast<std::size_t>(istr.tellg());
- if(!is_at_end(buffer, endpos))
- {
- ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
- return 0;
- }
- buffer.clear();
-
- if(FreqBands == 1)
- {
- if(command != "/matrix/{")
- {
- ERR("Unexpected \"%s\" type for a single-band decoder\n", command.c_str());
- return 0;
- }
- if(!load_ambdec_matrix(HFOrderGain, HFMatrix, num_speakers, f, buffer))
- return 0;
- }
- else
- {
- if(command == "/lfmatrix/{")
- {
- if(!load_ambdec_matrix(LFOrderGain, LFMatrix, num_speakers, f, buffer))
- return 0;
- }
- else if(command == "/hfmatrix/{")
- {
- if(!load_ambdec_matrix(HFOrderGain, HFMatrix, num_speakers, f, buffer))
- return 0;
- }
- else
- {
- ERR("Unexpected \"%s\" type for a dual-band decoder\n", command.c_str());
- return 0;
- }
- }
-
- if(!read_clipped_line(f, buffer))
- {
- ERR("Unexpected end of file\n");
- return 0;
- }
- std::istringstream istr2{buffer};
- std::string endmark{read_word(istr2)};
- if(endmark != "/}")
- {
- ERR("Expected /} after matrix definitions, got %s\n", endmark.c_str());
- return 0;
- }
- istr.swap(istr2);
- }
- else if(command == "/end")
- {
- const auto endpos = static_cast<std::size_t>(istr.tellg());
- if(!is_at_end(buffer, endpos))
- {
- ERR("Unexpected junk on end: %s\n", buffer.c_str()+endpos);
- return 0;
- }
-
- return 1;
- }
- else
- {
- ERR("Unexpected command: %s\n", command.c_str());
- return 0;
- }
-
- istr.clear();
- const auto endpos = static_cast<std::size_t>(istr.tellg());
- if(!is_at_end(buffer, endpos))
- {
- ERR("Unexpected junk on line: %s\n", buffer.c_str()+endpos);
- return 0;
- }
- buffer.clear();
- }
- ERR("Unexpected end of file\n");
-
- return 0;
-}
diff --git a/Alc/ambdec.h b/Alc/ambdec.h
deleted file mode 100644
index ff7b71ee..00000000
--- a/Alc/ambdec.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef AMBDEC_H
-#define AMBDEC_H
-
-#include <array>
-#include <string>
-
-#include "ambidefs.h"
-#include "vector.h"
-
-/* Helpers to read .ambdec configuration files. */
-
-enum class AmbDecScale {
- N3D,
- SN3D,
- FuMa,
-};
-struct AmbDecConf {
- std::string Description;
- int Version{0}; /* Must be 3 */
-
- unsigned int ChanMask{0u};
- unsigned int FreqBands{0u}; /* Must be 1 or 2 */
- AmbDecScale CoeffScale{};
-
- float XOverFreq{0.0f};
- float XOverRatio{0.0f};
-
- struct SpeakerConf {
- std::string Name;
- float Distance{0.0f};
- float Azimuth{0.0f};
- float Elevation{0.0f};
- std::string Connection;
- };
- al::vector<SpeakerConf> Speakers;
-
- using CoeffArray = std::array<float,MAX_AMBI_CHANNELS>;
- /* Unused when FreqBands == 1 */
- float LFOrderGain[MAX_AMBI_ORDER+1]{};
- al::vector<CoeffArray> LFMatrix;
-
- float HFOrderGain[MAX_AMBI_ORDER+1]{};
- al::vector<CoeffArray> HFMatrix;
-
- int load(const char *fname) noexcept;
-};
-
-#endif /* AMBDEC_H */
diff --git a/Alc/ambidefs.h b/Alc/ambidefs.h
deleted file mode 100644
index 17a9815b..00000000
--- a/Alc/ambidefs.h
+++ /dev/null
@@ -1,119 +0,0 @@
-#ifndef AMBIDEFS_H
-#define AMBIDEFS_H
-
-#include <array>
-
-/* The maximum number of Ambisonics channels. For a given order (o), the size
- * needed will be (o+1)**2, thus zero-order has 1, first-order has 4, second-
- * order has 9, third-order has 16, and fourth-order has 25.
- */
-#define MAX_AMBI_ORDER 3
-constexpr inline size_t AmbiChannelsFromOrder(size_t order) noexcept
-{ return (order+1) * (order+1); }
-#define MAX_AMBI_CHANNELS AmbiChannelsFromOrder(MAX_AMBI_ORDER)
-
-/* A bitmask of ambisonic channels for 0 to 4th order. This only specifies up
- * to 4th order, which is the highest order a 32-bit mask value can specify (a
- * 64-bit mask could handle up to 7th order).
- */
-#define AMBI_0ORDER_MASK 0x00000001
-#define AMBI_1ORDER_MASK 0x0000000f
-#define AMBI_2ORDER_MASK 0x000001ff
-#define AMBI_3ORDER_MASK 0x0000ffff
-#define AMBI_4ORDER_MASK 0x01ffffff
-
-/* A bitmask of ambisonic channels with height information. If none of these
- * channels are used/needed, there's no height (e.g. with most surround sound
- * speaker setups). This is ACN ordering, with bit 0 being ACN 0, etc.
- */
-#define AMBI_PERIPHONIC_MASK (0xfe7ce4)
-
-/* The maximum number of ambisonic channels for 2D (non-periphonic)
- * representation. This is 2 per each order above zero-order, plus 1 for zero-
- * order. Or simply, o*2 + 1.
- */
-constexpr inline size_t Ambi2DChannelsFromOrder(size_t order) noexcept
-{ return order*2 + 1; }
-#define MAX_AMBI2D_CHANNELS Ambi2DChannelsFromOrder(MAX_AMBI_ORDER)
-
-
-/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
- * coefficients should be divided by these values to get proper scalings.
- */
-struct AmbiScale {
- static constexpr std::array<float,MAX_AMBI_CHANNELS> FromN3D{{
- 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
- 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
- }};
- static constexpr std::array<float,MAX_AMBI_CHANNELS> FromSN3D{{
- 1.000000000f, /* ACN 0, sqrt(1) */
- 1.732050808f, /* ACN 1, sqrt(3) */
- 1.732050808f, /* ACN 2, sqrt(3) */
- 1.732050808f, /* ACN 3, sqrt(3) */
- 2.236067978f, /* ACN 4, sqrt(5) */
- 2.236067978f, /* ACN 5, sqrt(5) */
- 2.236067978f, /* ACN 6, sqrt(5) */
- 2.236067978f, /* ACN 7, sqrt(5) */
- 2.236067978f, /* ACN 8, sqrt(5) */
- 2.645751311f, /* ACN 9, sqrt(7) */
- 2.645751311f, /* ACN 10, sqrt(7) */
- 2.645751311f, /* ACN 11, sqrt(7) */
- 2.645751311f, /* ACN 12, sqrt(7) */
- 2.645751311f, /* ACN 13, sqrt(7) */
- 2.645751311f, /* ACN 14, sqrt(7) */
- 2.645751311f, /* ACN 15, sqrt(7) */
- }};
- static constexpr std::array<float,MAX_AMBI_CHANNELS> FromFuMa{{
- 1.414213562f, /* ACN 0 (W), sqrt(2) */
- 1.732050808f, /* ACN 1 (Y), sqrt(3) */
- 1.732050808f, /* ACN 2 (Z), sqrt(3) */
- 1.732050808f, /* ACN 3 (X), sqrt(3) */
- 1.936491673f, /* ACN 4 (V), sqrt(15)/2 */
- 1.936491673f, /* ACN 5 (T), sqrt(15)/2 */
- 2.236067978f, /* ACN 6 (R), sqrt(5) */
- 1.936491673f, /* ACN 7 (S), sqrt(15)/2 */
- 1.936491673f, /* ACN 8 (U), sqrt(15)/2 */
- 2.091650066f, /* ACN 9 (Q), sqrt(35/8) */
- 1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
- 2.231093404f, /* ACN 11 (M), sqrt(224/45) */
- 2.645751311f, /* ACN 12 (K), sqrt(7) */
- 2.231093404f, /* ACN 13 (L), sqrt(224/45) */
- 1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
- 2.091650066f, /* ACN 15 (P), sqrt(35/8) */
- }};
-};
-
-struct AmbiIndex {
- static constexpr std::array<int,MAX_AMBI_CHANNELS> FromFuMa{{
- 0, /* W */
- 3, /* X */
- 1, /* Y */
- 2, /* Z */
- 6, /* R */
- 7, /* S */
- 5, /* T */
- 8, /* U */
- 4, /* V */
- 12, /* K */
- 13, /* L */
- 11, /* M */
- 14, /* N */
- 10, /* O */
- 15, /* P */
- 9, /* Q */
- }};
- static constexpr std::array<int,MAX_AMBI_CHANNELS> FromACN{{
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15
- }};
-
- static constexpr std::array<int,MAX_AMBI2D_CHANNELS> From2D{{
- 0, 1,3, 4,8, 9,15
- }};
- static constexpr std::array<int,MAX_AMBI_CHANNELS> From3D{{
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15
- }};
-};
-
-#endif /* AMBIDEFS_H */
diff --git a/Alc/backends/alsa.cpp b/Alc/backends/alsa.cpp
deleted file mode 100644
index c133df68..00000000
--- a/Alc/backends/alsa.cpp
+++ /dev/null
@@ -1,1288 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/alsa.h"
-
-#include <algorithm>
-#include <atomic>
-#include <cassert>
-#include <cerrno>
-#include <chrono>
-#include <cstring>
-#include <exception>
-#include <functional>
-#include <memory>
-#include <string>
-#include <thread>
-#include <utility>
-
-#include "AL/al.h"
-
-#include "albyte.h"
-#include "alcmain.h"
-#include "alconfig.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-#include "aloptional.h"
-#include "alu.h"
-#include "compat.h"
-#include "logging.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "vector.h"
-
-#include <alsa/asoundlib.h>
-
-
-namespace {
-
-constexpr ALCchar alsaDevice[] = "ALSA Default";
-
-
-#ifdef HAVE_DYNLOAD
-#define ALSA_FUNCS(MAGIC) \
- MAGIC(snd_strerror); \
- MAGIC(snd_pcm_open); \
- MAGIC(snd_pcm_close); \
- MAGIC(snd_pcm_nonblock); \
- MAGIC(snd_pcm_frames_to_bytes); \
- MAGIC(snd_pcm_bytes_to_frames); \
- MAGIC(snd_pcm_hw_params_malloc); \
- MAGIC(snd_pcm_hw_params_free); \
- MAGIC(snd_pcm_hw_params_any); \
- MAGIC(snd_pcm_hw_params_current); \
- MAGIC(snd_pcm_hw_params_set_access); \
- MAGIC(snd_pcm_hw_params_set_format); \
- MAGIC(snd_pcm_hw_params_set_channels); \
- MAGIC(snd_pcm_hw_params_set_periods_near); \
- MAGIC(snd_pcm_hw_params_set_rate_near); \
- MAGIC(snd_pcm_hw_params_set_rate); \
- MAGIC(snd_pcm_hw_params_set_rate_resample); \
- MAGIC(snd_pcm_hw_params_set_buffer_time_near); \
- MAGIC(snd_pcm_hw_params_set_period_time_near); \
- MAGIC(snd_pcm_hw_params_set_buffer_size_near); \
- MAGIC(snd_pcm_hw_params_set_period_size_near); \
- MAGIC(snd_pcm_hw_params_set_buffer_size_min); \
- MAGIC(snd_pcm_hw_params_get_buffer_time_min); \
- MAGIC(snd_pcm_hw_params_get_buffer_time_max); \
- MAGIC(snd_pcm_hw_params_get_period_time_min); \
- MAGIC(snd_pcm_hw_params_get_period_time_max); \
- MAGIC(snd_pcm_hw_params_get_buffer_size); \
- MAGIC(snd_pcm_hw_params_get_period_size); \
- MAGIC(snd_pcm_hw_params_get_access); \
- MAGIC(snd_pcm_hw_params_get_periods); \
- MAGIC(snd_pcm_hw_params_test_format); \
- MAGIC(snd_pcm_hw_params_test_channels); \
- MAGIC(snd_pcm_hw_params); \
- MAGIC(snd_pcm_sw_params_malloc); \
- MAGIC(snd_pcm_sw_params_current); \
- MAGIC(snd_pcm_sw_params_set_avail_min); \
- MAGIC(snd_pcm_sw_params_set_stop_threshold); \
- MAGIC(snd_pcm_sw_params); \
- MAGIC(snd_pcm_sw_params_free); \
- MAGIC(snd_pcm_prepare); \
- MAGIC(snd_pcm_start); \
- MAGIC(snd_pcm_resume); \
- MAGIC(snd_pcm_reset); \
- MAGIC(snd_pcm_wait); \
- MAGIC(snd_pcm_delay); \
- MAGIC(snd_pcm_state); \
- MAGIC(snd_pcm_avail_update); \
- MAGIC(snd_pcm_areas_silence); \
- MAGIC(snd_pcm_mmap_begin); \
- MAGIC(snd_pcm_mmap_commit); \
- MAGIC(snd_pcm_readi); \
- MAGIC(snd_pcm_writei); \
- MAGIC(snd_pcm_drain); \
- MAGIC(snd_pcm_drop); \
- MAGIC(snd_pcm_recover); \
- MAGIC(snd_pcm_info_malloc); \
- MAGIC(snd_pcm_info_free); \
- MAGIC(snd_pcm_info_set_device); \
- MAGIC(snd_pcm_info_set_subdevice); \
- MAGIC(snd_pcm_info_set_stream); \
- MAGIC(snd_pcm_info_get_name); \
- MAGIC(snd_ctl_pcm_next_device); \
- MAGIC(snd_ctl_pcm_info); \
- MAGIC(snd_ctl_open); \
- MAGIC(snd_ctl_close); \
- MAGIC(snd_ctl_card_info_malloc); \
- MAGIC(snd_ctl_card_info_free); \
- MAGIC(snd_ctl_card_info); \
- MAGIC(snd_ctl_card_info_get_name); \
- MAGIC(snd_ctl_card_info_get_id); \
- MAGIC(snd_card_next); \
- MAGIC(snd_config_update_free_global)
-
-static void *alsa_handle;
-#define MAKE_FUNC(f) decltype(f) * p##f
-ALSA_FUNCS(MAKE_FUNC);
-#undef MAKE_FUNC
-
-#ifndef IN_IDE_PARSER
-#define snd_strerror psnd_strerror
-#define snd_pcm_open psnd_pcm_open
-#define snd_pcm_close psnd_pcm_close
-#define snd_pcm_nonblock psnd_pcm_nonblock
-#define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes
-#define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames
-#define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc
-#define snd_pcm_hw_params_free psnd_pcm_hw_params_free
-#define snd_pcm_hw_params_any psnd_pcm_hw_params_any
-#define snd_pcm_hw_params_current psnd_pcm_hw_params_current
-#define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access
-#define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format
-#define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels
-#define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near
-#define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near
-#define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate
-#define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample
-#define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near
-#define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near
-#define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near
-#define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near
-#define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min
-#define snd_pcm_hw_params_get_buffer_time_min psnd_pcm_hw_params_get_buffer_time_min
-#define snd_pcm_hw_params_get_buffer_time_max psnd_pcm_hw_params_get_buffer_time_max
-#define snd_pcm_hw_params_get_period_time_min psnd_pcm_hw_params_get_period_time_min
-#define snd_pcm_hw_params_get_period_time_max psnd_pcm_hw_params_get_period_time_max
-#define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size
-#define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size
-#define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access
-#define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods
-#define snd_pcm_hw_params_test_format psnd_pcm_hw_params_test_format
-#define snd_pcm_hw_params_test_channels psnd_pcm_hw_params_test_channels
-#define snd_pcm_hw_params psnd_pcm_hw_params
-#define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc
-#define snd_pcm_sw_params_current psnd_pcm_sw_params_current
-#define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min
-#define snd_pcm_sw_params_set_stop_threshold psnd_pcm_sw_params_set_stop_threshold
-#define snd_pcm_sw_params psnd_pcm_sw_params
-#define snd_pcm_sw_params_free psnd_pcm_sw_params_free
-#define snd_pcm_prepare psnd_pcm_prepare
-#define snd_pcm_start psnd_pcm_start
-#define snd_pcm_resume psnd_pcm_resume
-#define snd_pcm_reset psnd_pcm_reset
-#define snd_pcm_wait psnd_pcm_wait
-#define snd_pcm_delay psnd_pcm_delay
-#define snd_pcm_state psnd_pcm_state
-#define snd_pcm_avail_update psnd_pcm_avail_update
-#define snd_pcm_areas_silence psnd_pcm_areas_silence
-#define snd_pcm_mmap_begin psnd_pcm_mmap_begin
-#define snd_pcm_mmap_commit psnd_pcm_mmap_commit
-#define snd_pcm_readi psnd_pcm_readi
-#define snd_pcm_writei psnd_pcm_writei
-#define snd_pcm_drain psnd_pcm_drain
-#define snd_pcm_drop psnd_pcm_drop
-#define snd_pcm_recover psnd_pcm_recover
-#define snd_pcm_info_malloc psnd_pcm_info_malloc
-#define snd_pcm_info_free psnd_pcm_info_free
-#define snd_pcm_info_set_device psnd_pcm_info_set_device
-#define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice
-#define snd_pcm_info_set_stream psnd_pcm_info_set_stream
-#define snd_pcm_info_get_name psnd_pcm_info_get_name
-#define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device
-#define snd_ctl_pcm_info psnd_ctl_pcm_info
-#define snd_ctl_open psnd_ctl_open
-#define snd_ctl_close psnd_ctl_close
-#define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc
-#define snd_ctl_card_info_free psnd_ctl_card_info_free
-#define snd_ctl_card_info psnd_ctl_card_info
-#define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name
-#define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id
-#define snd_card_next psnd_card_next
-#define snd_config_update_free_global psnd_config_update_free_global
-#endif
-#endif
-
-
-struct DevMap {
- std::string name;
- std::string device_name;
-};
-
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
-
-
-const char *prefix_name(snd_pcm_stream_t stream)
-{
- assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE);
- return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix";
-}
-
-al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
-{
- al::vector<DevMap> devlist;
-
- snd_ctl_card_info_t *info;
- snd_ctl_card_info_malloc(&info);
- snd_pcm_info_t *pcminfo;
- snd_pcm_info_malloc(&pcminfo);
-
- devlist.emplace_back(DevMap{alsaDevice,
- GetConfigValue(nullptr, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? "device" : "capture",
- "default")});
-
- if(stream == SND_PCM_STREAM_PLAYBACK)
- {
- const char *customdevs;
- const char *next{GetConfigValue(nullptr, "alsa", "custom-devices", "")};
- while((customdevs=next) != nullptr && customdevs[0])
- {
- next = strchr(customdevs, ';');
- const char *sep{strchr(customdevs, '=')};
- if(!sep)
- {
- std::string spec{next ? std::string(customdevs, next++) : std::string(customdevs)};
- ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str());
- continue;
- }
-
- const char *oldsep{sep++};
- devlist.emplace_back(DevMap{std::string(customdevs, oldsep),
- next ? std::string(sep, next++) : std::string(sep)});
- const auto &entry = devlist.back();
- TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
- }
- }
-
- const std::string main_prefix{
- ConfigValueStr(nullptr, "alsa", prefix_name(stream)).value_or("plughw:")};
-
- int card{-1};
- int err{snd_card_next(&card)};
- for(;err >= 0 && card >= 0;err = snd_card_next(&card))
- {
- std::string name{"hw:" + std::to_string(card)};
-
- snd_ctl_t *handle;
- if((err=snd_ctl_open(&handle, name.c_str(), 0)) < 0)
- {
- ERR("control open (hw:%d): %s\n", card, snd_strerror(err));
- continue;
- }
- if((err=snd_ctl_card_info(handle, info)) < 0)
- {
- ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err));
- snd_ctl_close(handle);
- continue;
- }
-
- const char *cardname{snd_ctl_card_info_get_name(info)};
- const char *cardid{snd_ctl_card_info_get_id(info)};
- name = prefix_name(stream);
- name += '-';
- name += cardid;
- const std::string card_prefix{
- ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(main_prefix)};
-
- int dev{-1};
- while(1)
- {
- if(snd_ctl_pcm_next_device(handle, &dev) < 0)
- ERR("snd_ctl_pcm_next_device failed\n");
- if(dev < 0) break;
-
- snd_pcm_info_set_device(pcminfo, dev);
- snd_pcm_info_set_subdevice(pcminfo, 0);
- snd_pcm_info_set_stream(pcminfo, stream);
- if((err=snd_ctl_pcm_info(handle, pcminfo)) < 0)
- {
- if(err != -ENOENT)
- ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
- continue;
- }
-
- /* "prefix-cardid-dev" */
- name = prefix_name(stream);
- name += '-';
- name += cardid;
- name += '-';
- name += std::to_string(dev);
- const std::string device_prefix{
- ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(card_prefix)};
-
- /* "CardName, PcmName (CARD=cardid,DEV=dev)" */
- name = cardname;
- name += ", ";
- name += snd_pcm_info_get_name(pcminfo);
- name += " (CARD=";
- name += cardid;
- name += ",DEV=";
- name += std::to_string(dev);
- name += ')';
-
- /* "devprefixCARD=cardid,DEV=dev" */
- std::string device{device_prefix};
- device += "CARD=";
- device += cardid;
- device += ",DEV=";
- device += std::to_string(dev);
-
- devlist.emplace_back(DevMap{std::move(name), std::move(device)});
- const auto &entry = devlist.back();
- TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
- }
- snd_ctl_close(handle);
- }
- if(err < 0)
- ERR("snd_card_next failed: %s\n", snd_strerror(err));
-
- snd_pcm_info_free(pcminfo);
- snd_ctl_card_info_free(info);
-
- return devlist;
-}
-
-
-int verify_state(snd_pcm_t *handle)
-{
- snd_pcm_state_t state{snd_pcm_state(handle)};
-
- int err;
- switch(state)
- {
- case SND_PCM_STATE_OPEN:
- case SND_PCM_STATE_SETUP:
- case SND_PCM_STATE_PREPARED:
- case SND_PCM_STATE_RUNNING:
- case SND_PCM_STATE_DRAINING:
- case SND_PCM_STATE_PAUSED:
- /* All Okay */
- break;
-
- case SND_PCM_STATE_XRUN:
- if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0)
- return err;
- break;
- case SND_PCM_STATE_SUSPENDED:
- if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0)
- return err;
- break;
- case SND_PCM_STATE_DISCONNECTED:
- return -ENODEV;
- }
-
- return state;
-}
-
-
-struct AlsaPlayback final : public BackendBase {
- AlsaPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~AlsaPlayback() override;
-
- int mixerProc();
- int mixerNoMMapProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- ClockLatency getClockLatency() override;
-
- snd_pcm_t *mPcmHandle{nullptr};
-
- al::vector<char> mBuffer;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(AlsaPlayback)
-};
-
-AlsaPlayback::~AlsaPlayback()
-{
- if(mPcmHandle)
- snd_pcm_close(mPcmHandle);
- mPcmHandle = nullptr;
-}
-
-
-int AlsaPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
- const snd_pcm_uframes_t num_updates{mDevice->BufferSize / update_size};
- while(!mKillNow.load(std::memory_order_acquire))
- {
- int state{verify_state(mPcmHandle)};
- if(state < 0)
- {
- ERR("Invalid state detected: %s\n", snd_strerror(state));
- aluHandleDisconnect(mDevice, "Bad state: %s", snd_strerror(state));
- break;
- }
-
- snd_pcm_sframes_t avail{snd_pcm_avail_update(mPcmHandle)};
- if(avail < 0)
- {
- ERR("available update failed: %s\n", snd_strerror(avail));
- continue;
- }
-
- if(static_cast<snd_pcm_uframes_t>(avail) > update_size*(num_updates+1))
- {
- WARN("available samples exceeds the buffer size\n");
- snd_pcm_reset(mPcmHandle);
- continue;
- }
-
- // make sure there's frames to process
- if(static_cast<snd_pcm_uframes_t>(avail) < update_size)
- {
- if(state != SND_PCM_STATE_RUNNING)
- {
- int err{snd_pcm_start(mPcmHandle)};
- if(err < 0)
- {
- ERR("start failed: %s\n", snd_strerror(err));
- continue;
- }
- }
- if(snd_pcm_wait(mPcmHandle, 1000) == 0)
- ERR("Wait timeout... buffer size too low?\n");
- continue;
- }
- avail -= avail%update_size;
-
- // it is possible that contiguous areas are smaller, thus we use a loop
- lock();
- while(avail > 0)
- {
- snd_pcm_uframes_t frames{static_cast<snd_pcm_uframes_t>(avail)};
-
- const snd_pcm_channel_area_t *areas{};
- snd_pcm_uframes_t offset{};
- int err{snd_pcm_mmap_begin(mPcmHandle, &areas, &offset, &frames)};
- if(err < 0)
- {
- ERR("mmap begin error: %s\n", snd_strerror(err));
- break;
- }
-
- char *WritePtr{static_cast<char*>(areas->addr) + (offset * areas->step / 8)};
- aluMixData(mDevice, WritePtr, frames);
-
- snd_pcm_sframes_t commitres{snd_pcm_mmap_commit(mPcmHandle, offset, frames)};
- if(commitres < 0 || (commitres-frames) != 0)
- {
- ERR("mmap commit error: %s\n",
- snd_strerror(commitres >= 0 ? -EPIPE : commitres));
- break;
- }
-
- avail -= frames;
- }
- unlock();
- }
-
- return 0;
-}
-
-int AlsaPlayback::mixerNoMMapProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
- const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
- while(!mKillNow.load(std::memory_order_acquire))
- {
- int state{verify_state(mPcmHandle)};
- if(state < 0)
- {
- ERR("Invalid state detected: %s\n", snd_strerror(state));
- aluHandleDisconnect(mDevice, "Bad state: %s", snd_strerror(state));
- break;
- }
-
- snd_pcm_sframes_t avail{snd_pcm_avail_update(mPcmHandle)};
- if(avail < 0)
- {
- ERR("available update failed: %s\n", snd_strerror(avail));
- continue;
- }
-
- if(static_cast<snd_pcm_uframes_t>(avail) > buffer_size)
- {
- WARN("available samples exceeds the buffer size\n");
- snd_pcm_reset(mPcmHandle);
- continue;
- }
-
- if(static_cast<snd_pcm_uframes_t>(avail) < update_size)
- {
- if(state != SND_PCM_STATE_RUNNING)
- {
- int err{snd_pcm_start(mPcmHandle)};
- if(err < 0)
- {
- ERR("start failed: %s\n", snd_strerror(err));
- continue;
- }
- }
- if(snd_pcm_wait(mPcmHandle, 1000) == 0)
- ERR("Wait timeout... buffer size too low?\n");
- continue;
- }
-
- lock();
- char *WritePtr{mBuffer.data()};
- avail = snd_pcm_bytes_to_frames(mPcmHandle, mBuffer.size());
- aluMixData(mDevice, WritePtr, avail);
- while(avail > 0)
- {
- snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, WritePtr, avail)};
- switch(ret)
- {
- case -EAGAIN:
- continue;
-#if ESTRPIPE != EPIPE
- case -ESTRPIPE:
-#endif
- case -EPIPE:
- case -EINTR:
- ret = snd_pcm_recover(mPcmHandle, ret, 1);
- if(ret < 0)
- avail = 0;
- break;
- default:
- if(ret >= 0)
- {
- WritePtr += snd_pcm_frames_to_bytes(mPcmHandle, ret);
- avail -= ret;
- }
- break;
- }
- if(ret < 0)
- {
- ret = snd_pcm_prepare(mPcmHandle);
- if(ret < 0) break;
- }
- }
- unlock();
- }
-
- return 0;
-}
-
-
-ALCenum AlsaPlayback::open(const ALCchar *name)
-{
- const char *driver{};
- if(name)
- {
- if(PlaybackDevices.empty())
- PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
-
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == PlaybackDevices.cend())
- return ALC_INVALID_VALUE;
- driver = iter->device_name.c_str();
- }
- else
- {
- name = alsaDevice;
- driver = GetConfigValue(nullptr, "alsa", "device", "default");
- }
-
- TRACE("Opening device \"%s\"\n", driver);
- int err{snd_pcm_open(&mPcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)};
- if(err < 0)
- {
- ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(err));
- return ALC_OUT_OF_MEMORY;
- }
-
- /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
- snd_config_update_free_global();
-
- mDevice->DeviceName = name;
-
- return ALC_NO_ERROR;
-}
-
-ALCboolean AlsaPlayback::reset()
-{
- snd_pcm_format_t format{SND_PCM_FORMAT_UNKNOWN};
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- format = SND_PCM_FORMAT_S8;
- break;
- case DevFmtUByte:
- format = SND_PCM_FORMAT_U8;
- break;
- case DevFmtShort:
- format = SND_PCM_FORMAT_S16;
- break;
- case DevFmtUShort:
- format = SND_PCM_FORMAT_U16;
- break;
- case DevFmtInt:
- format = SND_PCM_FORMAT_S32;
- break;
- case DevFmtUInt:
- format = SND_PCM_FORMAT_U32;
- break;
- case DevFmtFloat:
- format = SND_PCM_FORMAT_FLOAT;
- break;
- }
-
- bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", 1)};
- ALuint periodLen{static_cast<ALuint>(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)};
- ALuint bufferLen{static_cast<ALuint>(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)};
- ALuint rate{mDevice->Frequency};
-
- snd_pcm_uframes_t periodSizeInFrames{};
- snd_pcm_uframes_t bufferSizeInFrames{};
- snd_pcm_sw_params_t *sp{};
- snd_pcm_hw_params_t *hp{};
- snd_pcm_access_t access{};
- const char *funcerr{};
- int err{};
-
- snd_pcm_hw_params_malloc(&hp);
-#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
- CHECK(snd_pcm_hw_params_any(mPcmHandle, hp));
- /* set interleaved access */
- if(!allowmmap || snd_pcm_hw_params_set_access(mPcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
- {
- /* No mmap */
- CHECK(snd_pcm_hw_params_set_access(mPcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
- }
- /* test and set format (implicitly sets sample bits) */
- if(snd_pcm_hw_params_test_format(mPcmHandle, hp, format) < 0)
- {
- static const struct {
- snd_pcm_format_t format;
- DevFmtType fmttype;
- } formatlist[] = {
- { SND_PCM_FORMAT_FLOAT, DevFmtFloat },
- { SND_PCM_FORMAT_S32, DevFmtInt },
- { SND_PCM_FORMAT_U32, DevFmtUInt },
- { SND_PCM_FORMAT_S16, DevFmtShort },
- { SND_PCM_FORMAT_U16, DevFmtUShort },
- { SND_PCM_FORMAT_S8, DevFmtByte },
- { SND_PCM_FORMAT_U8, DevFmtUByte },
- };
-
- for(const auto &fmt : formatlist)
- {
- format = fmt.format;
- if(snd_pcm_hw_params_test_format(mPcmHandle, hp, format) >= 0)
- {
- mDevice->FmtType = fmt.fmttype;
- break;
- }
- }
- }
- CHECK(snd_pcm_hw_params_set_format(mPcmHandle, hp, format));
- /* test and set channels (implicitly sets frame bits) */
- if(snd_pcm_hw_params_test_channels(mPcmHandle, hp, mDevice->channelsFromFmt()) < 0)
- {
- static const DevFmtChannels channellist[] = {
- DevFmtStereo,
- DevFmtQuad,
- DevFmtX51,
- DevFmtX71,
- DevFmtMono,
- };
-
- for(const auto &chan : channellist)
- {
- if(snd_pcm_hw_params_test_channels(mPcmHandle, hp, ChannelsFromDevFmt(chan, 0)) >= 0)
- {
- mDevice->FmtChans = chan;
- mDevice->mAmbiOrder = 0;
- break;
- }
- }
- }
- CHECK(snd_pcm_hw_params_set_channels(mPcmHandle, hp, mDevice->channelsFromFmt()));
- /* set rate (implicitly constrains period/buffer parameters) */
- if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "allow-resampler", 0) ||
- !mDevice->Flags.get<FrequencyRequest>())
- {
- if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp, 0) < 0)
- ERR("Failed to disable ALSA resampler\n");
- }
- else if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp, 1) < 0)
- ERR("Failed to enable ALSA resampler\n");
- CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp, &rate, nullptr));
- /* set period time (implicitly constrains period/buffer parameters) */
- if((err=snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp, &periodLen, nullptr)) < 0)
- ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
- /* set buffer time (implicitly sets buffer size/bytes/time and period size/bytes) */
- if((err=snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp, &bufferLen, nullptr)) < 0)
- ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err));
- /* install and prepare hardware configuration */
- CHECK(snd_pcm_hw_params(mPcmHandle, hp));
-
- /* retrieve configuration info */
- CHECK(snd_pcm_hw_params_get_access(hp, &access));
- CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, nullptr));
- CHECK(snd_pcm_hw_params_get_buffer_size(hp, &bufferSizeInFrames));
- snd_pcm_hw_params_free(hp);
- hp = nullptr;
-
- snd_pcm_sw_params_malloc(&sp);
- CHECK(snd_pcm_sw_params_current(mPcmHandle, sp));
- CHECK(snd_pcm_sw_params_set_avail_min(mPcmHandle, sp, periodSizeInFrames));
- CHECK(snd_pcm_sw_params_set_stop_threshold(mPcmHandle, sp, bufferSizeInFrames));
- CHECK(snd_pcm_sw_params(mPcmHandle, sp));
-#undef CHECK
- snd_pcm_sw_params_free(sp);
- sp = nullptr;
-
- mDevice->BufferSize = bufferSizeInFrames;
- mDevice->UpdateSize = periodSizeInFrames;
- mDevice->Frequency = rate;
-
- SetDefaultChannelOrder(mDevice);
-
- return ALC_TRUE;
-
-error:
- ERR("%s failed: %s\n", funcerr, snd_strerror(err));
- if(hp) snd_pcm_hw_params_free(hp);
- if(sp) snd_pcm_sw_params_free(sp);
- return ALC_FALSE;
-}
-
-ALCboolean AlsaPlayback::start()
-{
- snd_pcm_hw_params_t *hp{};
- snd_pcm_access_t access;
- const char *funcerr;
- int err;
-
- snd_pcm_hw_params_malloc(&hp);
-#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
- CHECK(snd_pcm_hw_params_current(mPcmHandle, hp));
- /* retrieve configuration info */
- CHECK(snd_pcm_hw_params_get_access(hp, &access));
-#undef CHECK
- if(0)
- {
- error:
- ERR("%s failed: %s\n", funcerr, snd_strerror(err));
- if(hp) snd_pcm_hw_params_free(hp);
- return ALC_FALSE;
- }
- snd_pcm_hw_params_free(hp);
- hp = nullptr;
-
- int (AlsaPlayback::*thread_func)(){};
- if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
- {
- mBuffer.resize(snd_pcm_frames_to_bytes(mPcmHandle, mDevice->UpdateSize));
- thread_func = &AlsaPlayback::mixerNoMMapProc;
- }
- else
- {
- err = snd_pcm_prepare(mPcmHandle);
- if(err < 0)
- {
- ERR("snd_pcm_prepare(data->mPcmHandle) failed: %s\n", snd_strerror(err));
- return ALC_FALSE;
- }
- thread_func = &AlsaPlayback::mixerProc;
- }
-
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(thread_func), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- mBuffer.clear();
- return ALC_FALSE;
-}
-
-void AlsaPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- mBuffer.clear();
-}
-
-ClockLatency AlsaPlayback::getClockLatency()
-{
- ClockLatency ret;
-
- lock();
- ret.ClockTime = GetDeviceClockTime(mDevice);
- snd_pcm_sframes_t delay{};
- int err{snd_pcm_delay(mPcmHandle, &delay)};
- if(err < 0)
- {
- ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
- delay = 0;
- }
- ret.Latency = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
- ret.Latency /= mDevice->Frequency;
- unlock();
-
- return ret;
-}
-
-
-struct AlsaCapture final : public BackendBase {
- AlsaCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~AlsaCapture() override;
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
- ClockLatency getClockLatency() override;
-
- snd_pcm_t *mPcmHandle{nullptr};
-
- al::vector<char> mBuffer;
-
- bool mDoCapture{false};
- RingBufferPtr mRing{nullptr};
-
- snd_pcm_sframes_t mLastAvail{0};
-
- DEF_NEWDEL(AlsaCapture)
-};
-
-AlsaCapture::~AlsaCapture()
-{
- if(mPcmHandle)
- snd_pcm_close(mPcmHandle);
- mPcmHandle = nullptr;
-}
-
-
-ALCenum AlsaCapture::open(const ALCchar *name)
-{
- const char *driver{};
- if(name)
- {
- if(CaptureDevices.empty())
- CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
-
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == CaptureDevices.cend())
- return ALC_INVALID_VALUE;
- driver = iter->device_name.c_str();
- }
- else
- {
- name = alsaDevice;
- driver = GetConfigValue(nullptr, "alsa", "capture", "default");
- }
-
- TRACE("Opening device \"%s\"\n", driver);
- int err{snd_pcm_open(&mPcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)};
- if(err < 0)
- {
- ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(err));
- return ALC_INVALID_VALUE;
- }
-
- /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
- snd_config_update_free_global();
-
- snd_pcm_format_t format{SND_PCM_FORMAT_UNKNOWN};
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- format = SND_PCM_FORMAT_S8;
- break;
- case DevFmtUByte:
- format = SND_PCM_FORMAT_U8;
- break;
- case DevFmtShort:
- format = SND_PCM_FORMAT_S16;
- break;
- case DevFmtUShort:
- format = SND_PCM_FORMAT_U16;
- break;
- case DevFmtInt:
- format = SND_PCM_FORMAT_S32;
- break;
- case DevFmtUInt:
- format = SND_PCM_FORMAT_U32;
- break;
- case DevFmtFloat:
- format = SND_PCM_FORMAT_FLOAT;
- break;
- }
-
- snd_pcm_uframes_t bufferSizeInFrames{maxu(mDevice->BufferSize, 100*mDevice->Frequency/1000)};
- snd_pcm_uframes_t periodSizeInFrames{minu(bufferSizeInFrames, 25*mDevice->Frequency/1000)};
-
- bool needring{false};
- const char *funcerr{};
- snd_pcm_hw_params_t *hp{};
- snd_pcm_hw_params_malloc(&hp);
-#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
- CHECK(snd_pcm_hw_params_any(mPcmHandle, hp));
- /* set interleaved access */
- CHECK(snd_pcm_hw_params_set_access(mPcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
- /* set format (implicitly sets sample bits) */
- CHECK(snd_pcm_hw_params_set_format(mPcmHandle, hp, format));
- /* set channels (implicitly sets frame bits) */
- CHECK(snd_pcm_hw_params_set_channels(mPcmHandle, hp, mDevice->channelsFromFmt()));
- /* set rate (implicitly constrains period/buffer parameters) */
- CHECK(snd_pcm_hw_params_set_rate(mPcmHandle, hp, mDevice->Frequency, 0));
- /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
- if(snd_pcm_hw_params_set_buffer_size_min(mPcmHandle, hp, &bufferSizeInFrames) < 0)
- {
- TRACE("Buffer too large, using intermediate ring buffer\n");
- needring = true;
- CHECK(snd_pcm_hw_params_set_buffer_size_near(mPcmHandle, hp, &bufferSizeInFrames));
- }
- /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
- CHECK(snd_pcm_hw_params_set_period_size_near(mPcmHandle, hp, &periodSizeInFrames, nullptr));
- /* install and prepare hardware configuration */
- CHECK(snd_pcm_hw_params(mPcmHandle, hp));
- /* retrieve configuration info */
- CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, nullptr));
-#undef CHECK
- snd_pcm_hw_params_free(hp);
- hp = nullptr;
-
- if(needring)
- {
- mRing = CreateRingBuffer(mDevice->BufferSize, mDevice->frameSizeFromFmt(), false);
- if(!mRing)
- {
- ERR("ring buffer create failed\n");
- goto error2;
- }
- }
-
- mDevice->DeviceName = name;
-
- return ALC_NO_ERROR;
-
-error:
- ERR("%s failed: %s\n", funcerr, snd_strerror(err));
- if(hp) snd_pcm_hw_params_free(hp);
-
-error2:
- mRing = nullptr;
- snd_pcm_close(mPcmHandle);
- mPcmHandle = nullptr;
-
- return ALC_INVALID_VALUE;
-}
-
-
-ALCboolean AlsaCapture::start()
-{
- int err{snd_pcm_prepare(mPcmHandle)};
- if(err < 0)
- ERR("prepare failed: %s\n", snd_strerror(err));
- else
- {
- err = snd_pcm_start(mPcmHandle);
- if(err < 0)
- ERR("start failed: %s\n", snd_strerror(err));
- }
- if(err < 0)
- {
- aluHandleDisconnect(mDevice, "Capture state failure: %s", snd_strerror(err));
- return ALC_FALSE;
- }
-
- mDoCapture = true;
- return ALC_TRUE;
-}
-
-void AlsaCapture::stop()
-{
- /* OpenAL requires access to unread audio after stopping, but ALSA's
- * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's
- * available now so it'll be available later after the drop.
- */
- ALCuint avail{availableSamples()};
- if(!mRing && avail > 0)
- {
- /* The ring buffer implicitly captures when checking availability.
- * Direct access needs to explicitly capture it into temp storage. */
- al::vector<char> temp(snd_pcm_frames_to_bytes(mPcmHandle, avail));
- captureSamples(temp.data(), avail);
- mBuffer = std::move(temp);
- }
- int err{snd_pcm_drop(mPcmHandle)};
- if(err < 0)
- ERR("drop failed: %s\n", snd_strerror(err));
- mDoCapture = false;
-}
-
-ALCenum AlsaCapture::captureSamples(ALCvoid *buffer, ALCuint samples)
-{
- if(mRing)
- {
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
- }
-
- mLastAvail -= samples;
- while(mDevice->Connected.load(std::memory_order_acquire) && samples > 0)
- {
- snd_pcm_sframes_t amt{0};
-
- if(!mBuffer.empty())
- {
- /* First get any data stored from the last stop */
- amt = snd_pcm_bytes_to_frames(mPcmHandle, mBuffer.size());
- if(static_cast<snd_pcm_uframes_t>(amt) > samples) amt = samples;
-
- amt = snd_pcm_frames_to_bytes(mPcmHandle, amt);
- memcpy(buffer, mBuffer.data(), amt);
-
- mBuffer.erase(mBuffer.begin(), mBuffer.begin()+amt);
- amt = snd_pcm_bytes_to_frames(mPcmHandle, amt);
- }
- else if(mDoCapture)
- amt = snd_pcm_readi(mPcmHandle, buffer, samples);
- if(amt < 0)
- {
- ERR("read error: %s\n", snd_strerror(amt));
-
- if(amt == -EAGAIN)
- continue;
- if((amt=snd_pcm_recover(mPcmHandle, amt, 1)) >= 0)
- {
- amt = snd_pcm_start(mPcmHandle);
- if(amt >= 0)
- amt = snd_pcm_avail_update(mPcmHandle);
- }
- if(amt < 0)
- {
- ERR("restore error: %s\n", snd_strerror(amt));
- aluHandleDisconnect(mDevice, "Capture recovery failure: %s", snd_strerror(amt));
- break;
- }
- /* If the amount available is less than what's asked, we lost it
- * during recovery. So just give silence instead. */
- if(static_cast<snd_pcm_uframes_t>(amt) < samples)
- break;
- continue;
- }
-
- buffer = static_cast<ALbyte*>(buffer) + amt;
- samples -= amt;
- }
- if(samples > 0)
- memset(buffer, ((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0),
- snd_pcm_frames_to_bytes(mPcmHandle, samples));
-
- return ALC_NO_ERROR;
-}
-
-ALCuint AlsaCapture::availableSamples()
-{
- snd_pcm_sframes_t avail{0};
- if(mDevice->Connected.load(std::memory_order_acquire) && mDoCapture)
- avail = snd_pcm_avail_update(mPcmHandle);
- if(avail < 0)
- {
- ERR("avail update failed: %s\n", snd_strerror(avail));
-
- if((avail=snd_pcm_recover(mPcmHandle, avail, 1)) >= 0)
- {
- if(mDoCapture)
- avail = snd_pcm_start(mPcmHandle);
- if(avail >= 0)
- avail = snd_pcm_avail_update(mPcmHandle);
- }
- if(avail < 0)
- {
- ERR("restore error: %s\n", snd_strerror(avail));
- aluHandleDisconnect(mDevice, "Capture recovery failure: %s", snd_strerror(avail));
- }
- }
-
- if(!mRing)
- {
- if(avail < 0) avail = 0;
- avail += snd_pcm_bytes_to_frames(mPcmHandle, mBuffer.size());
- if(avail > mLastAvail) mLastAvail = avail;
- return mLastAvail;
- }
-
- while(avail > 0)
- {
- auto vec = mRing->getWriteVector();
- if(vec.first.len == 0) break;
-
- snd_pcm_sframes_t amt{std::min<snd_pcm_sframes_t>(vec.first.len, avail)};
- amt = snd_pcm_readi(mPcmHandle, vec.first.buf, amt);
- if(amt < 0)
- {
- ERR("read error: %s\n", snd_strerror(amt));
-
- if(amt == -EAGAIN)
- continue;
- if((amt=snd_pcm_recover(mPcmHandle, amt, 1)) >= 0)
- {
- if(mDoCapture)
- amt = snd_pcm_start(mPcmHandle);
- if(amt >= 0)
- amt = snd_pcm_avail_update(mPcmHandle);
- }
- if(amt < 0)
- {
- ERR("restore error: %s\n", snd_strerror(amt));
- aluHandleDisconnect(mDevice, "Capture recovery failure: %s", snd_strerror(amt));
- break;
- }
- avail = amt;
- continue;
- }
-
- mRing->writeAdvance(amt);
- avail -= amt;
- }
-
- return mRing->readSpace();
-}
-
-ClockLatency AlsaCapture::getClockLatency()
-{
- ClockLatency ret;
-
- lock();
- ret.ClockTime = GetDeviceClockTime(mDevice);
- snd_pcm_sframes_t delay{};
- int err{snd_pcm_delay(mPcmHandle, &delay)};
- if(err < 0)
- {
- ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
- delay = 0;
- }
- ret.Latency = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
- ret.Latency /= mDevice->Frequency;
- unlock();
-
- return ret;
-}
-
-} // namespace
-
-
-bool AlsaBackendFactory::init()
-{
- bool error{false};
-
-#ifdef HAVE_DYNLOAD
- if(!alsa_handle)
- {
- std::string missing_funcs;
-
- alsa_handle = LoadLib("libasound.so.2");
- if(!alsa_handle)
- {
- WARN("Failed to load %s\n", "libasound.so.2");
- return ALC_FALSE;
- }
-
- error = ALC_FALSE;
-#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \
- if(p##f == nullptr) { \
- error = true; \
- missing_funcs += "\n" #f; \
- } \
-} while(0)
- ALSA_FUNCS(LOAD_FUNC);
-#undef LOAD_FUNC
-
- if(error)
- {
- WARN("Missing expected functions:%s\n", missing_funcs.c_str());
- CloseLib(alsa_handle);
- alsa_handle = nullptr;
- }
- }
-#endif
-
- return !error;
-}
-
-bool AlsaBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void AlsaBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const DevMap &entry) -> void
- {
- /* +1 to also append the null char (to ensure a null-separated list and
- * double-null terminated list).
- */
- outnames->append(entry.name.c_str(), entry.name.length()+1);
- };
- switch(type)
- {
- case DevProbe::Playback:
- PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
-}
-
-BackendPtr AlsaBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new AlsaPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new AlsaCapture{device}};
- return nullptr;
-}
-
-BackendFactory &AlsaBackendFactory::getFactory()
-{
- static AlsaBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/alsa.h b/Alc/backends/alsa.h
deleted file mode 100644
index fb9de006..00000000
--- a/Alc/backends/alsa.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_ALSA_H
-#define BACKENDS_ALSA_H
-
-#include "backends/base.h"
-
-struct AlsaBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_ALSA_H */
diff --git a/Alc/backends/base.cpp b/Alc/backends/base.cpp
deleted file mode 100644
index a7d47c6d..00000000
--- a/Alc/backends/base.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-
-#include "config.h"
-
-#include <cstdlib>
-
-#include <thread>
-
-#include "alcmain.h"
-#include "alu.h"
-
-#include "backends/base.h"
-
-
-ClockLatency GetClockLatency(ALCdevice *device)
-{
- BackendBase *backend{device->Backend.get()};
- ClockLatency ret{backend->getClockLatency()};
- ret.Latency += device->FixedLatency;
- return ret;
-}
-
-
-/* BackendBase method implementations. */
-BackendBase::BackendBase(ALCdevice *device) noexcept : mDevice{device}
-{ }
-
-BackendBase::~BackendBase() = default;
-
-ALCboolean BackendBase::reset()
-{ return ALC_FALSE; }
-
-ALCenum BackendBase::captureSamples(void*, ALCuint)
-{ return ALC_INVALID_DEVICE; }
-
-ALCuint BackendBase::availableSamples()
-{ return 0; }
-
-ClockLatency BackendBase::getClockLatency()
-{
- ClockLatency ret;
-
- ALuint refcount;
- do {
- while(((refcount=mDevice->MixCount.load(std::memory_order_acquire))&1))
- std::this_thread::yield();
- ret.ClockTime = GetDeviceClockTime(mDevice);
- std::atomic_thread_fence(std::memory_order_acquire);
- } while(refcount != mDevice->MixCount.load(std::memory_order_relaxed));
-
- /* NOTE: The device will generally have about all but one periods filled at
- * any given time during playback. Without a more accurate measurement from
- * the output, this is an okay approximation.
- */
- ret.Latency = std::chrono::seconds{maxi(mDevice->BufferSize-mDevice->UpdateSize, 0)};
- ret.Latency /= mDevice->Frequency;
-
- return ret;
-}
diff --git a/Alc/backends/base.h b/Alc/backends/base.h
deleted file mode 100644
index 437e31d9..00000000
--- a/Alc/backends/base.h
+++ /dev/null
@@ -1,78 +0,0 @@
-#ifndef ALC_BACKENDS_BASE_H
-#define ALC_BACKENDS_BASE_H
-
-#include <memory>
-#include <chrono>
-#include <string>
-#include <mutex>
-
-#include "alcmain.h"
-
-
-struct ClockLatency {
- std::chrono::nanoseconds ClockTime;
- std::chrono::nanoseconds Latency;
-};
-
-/* Helper to get the current clock time from the device's ClockBase, and
- * SamplesDone converted from the sample rate.
- */
-inline std::chrono::nanoseconds GetDeviceClockTime(ALCdevice *device)
-{
- using std::chrono::seconds;
- using std::chrono::nanoseconds;
-
- auto ns = nanoseconds{seconds{device->SamplesDone}} / device->Frequency;
- return device->ClockBase + ns;
-}
-
-ClockLatency GetClockLatency(ALCdevice *device);
-
-struct BackendBase {
- virtual ALCenum open(const ALCchar *name) = 0;
-
- virtual ALCboolean reset();
- virtual ALCboolean start() = 0;
- virtual void stop() = 0;
-
- virtual ALCenum captureSamples(void *buffer, ALCuint samples);
- virtual ALCuint availableSamples();
-
- virtual ClockLatency getClockLatency();
-
- virtual void lock() { mMutex.lock(); }
- virtual void unlock() { mMutex.unlock(); }
-
- ALCdevice *mDevice;
-
- std::recursive_mutex mMutex;
-
- BackendBase(ALCdevice *device) noexcept;
- virtual ~BackendBase();
-};
-using BackendPtr = std::unique_ptr<BackendBase>;
-using BackendUniqueLock = std::unique_lock<BackendBase>;
-using BackendLockGuard = std::lock_guard<BackendBase>;
-
-enum class BackendType {
- Playback,
- Capture
-};
-
-enum class DevProbe {
- Playback,
- Capture
-};
-
-
-struct BackendFactory {
- virtual bool init() = 0;
-
- virtual bool querySupport(BackendType type) = 0;
-
- virtual void probe(DevProbe type, std::string *outnames) = 0;
-
- virtual BackendPtr createBackend(ALCdevice *device, BackendType type) = 0;
-};
-
-#endif /* ALC_BACKENDS_BASE_H */
diff --git a/Alc/backends/coreaudio.cpp b/Alc/backends/coreaudio.cpp
deleted file mode 100644
index b4b46382..00000000
--- a/Alc/backends/coreaudio.cpp
+++ /dev/null
@@ -1,709 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/coreaudio.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "ringbuffer.h"
-#include "converter.h"
-#include "backends/base.h"
-
-#include <unistd.h>
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/AudioToolbox.h>
-
-
-namespace {
-
-static const ALCchar ca_device[] = "CoreAudio Default";
-
-
-struct CoreAudioPlayback final : public BackendBase {
- CoreAudioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~CoreAudioPlayback() override;
-
- static OSStatus MixerProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
- AudioBufferList *ioData);
- OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
- AudioBufferList *ioData);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- AudioUnit mAudioUnit;
-
- ALuint mFrameSize{0u};
- AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
-
- DEF_NEWDEL(CoreAudioPlayback)
-};
-
-CoreAudioPlayback::~CoreAudioPlayback()
-{
- AudioUnitUninitialize(mAudioUnit);
- AudioComponentInstanceDispose(mAudioUnit);
-}
-
-
-OSStatus CoreAudioPlayback::MixerProcC(void *inRefCon,
- AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
- UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp,
- inBusNumber, inNumberFrames, ioData);
-}
-
-OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*,
- const AudioTimeStamp*, UInt32, UInt32, AudioBufferList *ioData)
-{
- lock();
- aluMixData(mDevice, ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize/mFrameSize);
- unlock();
- return noErr;
-}
-
-
-ALCenum CoreAudioPlayback::open(const ALCchar *name)
-{
- if(!name)
- name = ca_device;
- else if(strcmp(name, ca_device) != 0)
- return ALC_INVALID_VALUE;
-
- /* open the default output unit */
- AudioComponentDescription desc{};
- desc.componentType = kAudioUnitType_Output;
-#if TARGET_OS_IOS
- desc.componentSubType = kAudioUnitSubType_RemoteIO;
-#else
- desc.componentSubType = kAudioUnitSubType_DefaultOutput;
-#endif
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
-
- AudioComponent comp{AudioComponentFindNext(NULL, &desc)};
- if(comp == nullptr)
- {
- ERR("AudioComponentFindNext failed\n");
- return ALC_INVALID_VALUE;
- }
-
- OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
- if(err != noErr)
- {
- ERR("AudioComponentInstanceNew failed\n");
- return ALC_INVALID_VALUE;
- }
-
- /* init and start the default audio unit... */
- err = AudioUnitInitialize(mAudioUnit);
- if(err != noErr)
- {
- ERR("AudioUnitInitialize failed\n");
- AudioComponentInstanceDispose(mAudioUnit);
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean CoreAudioPlayback::reset()
-{
- OSStatus err{AudioUnitUninitialize(mAudioUnit)};
- if(err != noErr)
- ERR("-- AudioUnitUninitialize failed.\n");
-
- /* retrieve default output unit's properties (output side) */
- AudioStreamBasicDescription streamFormat{};
- auto size = static_cast<UInt32>(sizeof(AudioStreamBasicDescription));
- err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
- 0, &streamFormat, &size);
- if(err != noErr || size != sizeof(AudioStreamBasicDescription))
- {
- ERR("AudioUnitGetProperty failed\n");
- return ALC_FALSE;
- }
-
-#if 0
- TRACE("Output streamFormat of default output unit -\n");
- TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
- TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
- TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
- TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
- TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
- TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
-#endif
-
- /* set default output unit's input side to match output side */
- err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
- 0, &streamFormat, size);
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_FALSE;
- }
-
- if(mDevice->Frequency != streamFormat.mSampleRate)
- {
- mDevice->BufferSize = static_cast<ALuint>(uint64_t{mDevice->BufferSize} *
- streamFormat.mSampleRate / mDevice->Frequency);
- mDevice->Frequency = streamFormat.mSampleRate;
- }
-
- /* FIXME: How to tell what channels are what in the output device, and how
- * to specify what we're giving? eg, 6.0 vs 5.1 */
- switch(streamFormat.mChannelsPerFrame)
- {
- case 1:
- mDevice->FmtChans = DevFmtMono;
- break;
- case 2:
- mDevice->FmtChans = DevFmtStereo;
- break;
- case 4:
- mDevice->FmtChans = DevFmtQuad;
- break;
- case 6:
- mDevice->FmtChans = DevFmtX51;
- break;
- case 7:
- mDevice->FmtChans = DevFmtX61;
- break;
- case 8:
- mDevice->FmtChans = DevFmtX71;
- break;
- default:
- ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
- mDevice->FmtChans = DevFmtStereo;
- streamFormat.mChannelsPerFrame = 2;
- break;
- }
- SetDefaultWFXChannelOrder(mDevice);
-
- /* use channel count and sample rate from the default output unit's current
- * parameters, but reset everything else */
- streamFormat.mFramesPerPacket = 1;
- streamFormat.mFormatFlags = 0;
- switch(mDevice->FmtType)
- {
- case DevFmtUByte:
- mDevice->FmtType = DevFmtByte;
- /* fall-through */
- case DevFmtByte:
- streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
- streamFormat.mBitsPerChannel = 8;
- break;
- case DevFmtUShort:
- mDevice->FmtType = DevFmtShort;
- /* fall-through */
- case DevFmtShort:
- streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
- streamFormat.mBitsPerChannel = 16;
- break;
- case DevFmtUInt:
- mDevice->FmtType = DevFmtInt;
- /* fall-through */
- case DevFmtInt:
- streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
- streamFormat.mBitsPerChannel = 32;
- break;
- case DevFmtFloat:
- streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
- streamFormat.mBitsPerChannel = 32;
- break;
- }
- streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame *
- streamFormat.mBitsPerChannel / 8;
- streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame;
- streamFormat.mFormatID = kAudioFormatLinearPCM;
- streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
- kLinearPCMFormatFlagIsPacked;
-
- err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
- 0, &streamFormat, sizeof(AudioStreamBasicDescription));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_FALSE;
- }
-
- /* setup callback */
- mFrameSize = mDevice->frameSizeFromFmt();
- AURenderCallbackStruct input{};
- input.inputProc = CoreAudioPlayback::MixerProcC;
- input.inputProcRefCon = this;
-
- err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
- kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_FALSE;
- }
-
- /* init the default audio unit... */
- err = AudioUnitInitialize(mAudioUnit);
- if(err != noErr)
- {
- ERR("AudioUnitInitialize failed\n");
- return ALC_FALSE;
- }
-
- return ALC_TRUE;
-}
-
-ALCboolean CoreAudioPlayback::start()
-{
- OSStatus err{AudioOutputUnitStart(mAudioUnit)};
- if(err != noErr)
- {
- ERR("AudioOutputUnitStart failed\n");
- return ALC_FALSE;
- }
- return ALC_TRUE;
-}
-
-void CoreAudioPlayback::stop()
-{
- OSStatus err{AudioOutputUnitStop(mAudioUnit)};
- if(err != noErr)
- ERR("AudioOutputUnitStop failed\n");
-}
-
-
-struct CoreAudioCapture final : public BackendBase {
- CoreAudioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~CoreAudioCapture() override;
-
- static OSStatus RecordProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
- AudioBufferList *ioData);
- OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
- UInt32 inNumberFrames, AudioBufferList *ioData);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- AudioUnit mAudioUnit{0};
-
- ALuint mFrameSize{0u};
- AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
-
- SampleConverterPtr mConverter;
-
- RingBufferPtr mRing{nullptr};
-
- DEF_NEWDEL(CoreAudioCapture)
-};
-
-CoreAudioCapture::~CoreAudioCapture()
-{
- if(mAudioUnit)
- AudioComponentInstanceDispose(mAudioUnit);
- mAudioUnit = 0;
-}
-
-
-OSStatus CoreAudioCapture::RecordProcC(void *inRefCon,
- AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
- UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp,
- inBusNumber, inNumberFrames, ioData);
-}
-
-OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags*,
- const AudioTimeStamp *inTimeStamp, UInt32, UInt32 inNumberFrames,
- AudioBufferList*)
-{
- AudioUnitRenderActionFlags flags = 0;
- union {
- ALbyte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)*2];
- AudioBufferList list;
- } audiobuf = { { 0 } };
-
- auto rec_vec = mRing->getWriteVector();
- inNumberFrames = minz(inNumberFrames, rec_vec.first.len+rec_vec.second.len);
-
- // Fill the ringbuffer's two segments with data from the input device
- if(rec_vec.first.len >= inNumberFrames)
- {
- audiobuf.list.mNumberBuffers = 1;
- audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame;
- audiobuf.list.mBuffers[0].mData = rec_vec.first.buf;
- audiobuf.list.mBuffers[0].mDataByteSize = inNumberFrames * mFormat.mBytesPerFrame;
- }
- else
- {
- const size_t remaining{inNumberFrames-rec_vec.first.len};
- audiobuf.list.mNumberBuffers = 2;
- audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame;
- audiobuf.list.mBuffers[0].mData = rec_vec.first.buf;
- audiobuf.list.mBuffers[0].mDataByteSize = rec_vec.first.len * mFormat.mBytesPerFrame;
- audiobuf.list.mBuffers[1].mNumberChannels = mFormat.mChannelsPerFrame;
- audiobuf.list.mBuffers[1].mData = rec_vec.second.buf;
- audiobuf.list.mBuffers[1].mDataByteSize = remaining * mFormat.mBytesPerFrame;
- }
- OSStatus err{AudioUnitRender(mAudioUnit, &flags, inTimeStamp, audiobuf.list.mNumberBuffers,
- inNumberFrames, &audiobuf.list)};
- if(err != noErr)
- {
- ERR("AudioUnitRender error: %d\n", err);
- return err;
- }
-
- mRing->writeAdvance(inNumberFrames);
- return noErr;
-}
-
-
-ALCenum CoreAudioCapture::open(const ALCchar *name)
-{
- AudioStreamBasicDescription requestedFormat; // The application requested format
- AudioStreamBasicDescription hardwareFormat; // The hardware format
- AudioStreamBasicDescription outputFormat; // The AudioUnit output format
- AURenderCallbackStruct input;
- AudioComponentDescription desc;
- UInt32 outputFrameCount;
- UInt32 propertySize;
- AudioObjectPropertyAddress propertyAddress;
- UInt32 enableIO;
- AudioComponent comp;
- OSStatus err;
-
- if(!name)
- name = ca_device;
- else if(strcmp(name, ca_device) != 0)
- return ALC_INVALID_VALUE;
-
- desc.componentType = kAudioUnitType_Output;
-#if TARGET_OS_IOS
- desc.componentSubType = kAudioUnitSubType_RemoteIO;
-#else
- desc.componentSubType = kAudioUnitSubType_HALOutput;
-#endif
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
-
- // Search for component with given description
- comp = AudioComponentFindNext(NULL, &desc);
- if(comp == NULL)
- {
- ERR("AudioComponentFindNext failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Open the component
- err = AudioComponentInstanceNew(comp, &mAudioUnit);
- if(err != noErr)
- {
- ERR("AudioComponentInstanceNew failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Turn off AudioUnit output
- enableIO = 0;
- err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
- kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Turn on AudioUnit input
- enableIO = 1;
- err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
- kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_INVALID_VALUE;
- }
-
-#if !TARGET_OS_IOS
- {
- // Get the default input device
- AudioDeviceID inputDevice = kAudioDeviceUnknown;
-
- propertySize = sizeof(AudioDeviceID);
- propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
-
- err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &inputDevice);
- if(err != noErr)
- {
- ERR("AudioObjectGetPropertyData failed\n");
- return ALC_INVALID_VALUE;
- }
- if(inputDevice == kAudioDeviceUnknown)
- {
- ERR("No input device found\n");
- return ALC_INVALID_VALUE;
- }
-
- // Track the input device
- err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
- kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_INVALID_VALUE;
- }
- }
-#endif
-
- // set capture callback
- input.inputProc = CoreAudioCapture::RecordProcC;
- input.inputProcRefCon = this;
-
- err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
- kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Initialize the device
- err = AudioUnitInitialize(mAudioUnit);
- if(err != noErr)
- {
- ERR("AudioUnitInitialize failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Get the hardware format
- propertySize = sizeof(AudioStreamBasicDescription);
- err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
- 1, &hardwareFormat, &propertySize);
- if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
- {
- ERR("AudioUnitGetProperty failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Set up the requested format description
- switch(mDevice->FmtType)
- {
- case DevFmtUByte:
- requestedFormat.mBitsPerChannel = 8;
- requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
- break;
- case DevFmtShort:
- requestedFormat.mBitsPerChannel = 16;
- requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
- break;
- case DevFmtInt:
- requestedFormat.mBitsPerChannel = 32;
- requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
- break;
- case DevFmtFloat:
- requestedFormat.mBitsPerChannel = 32;
- requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
- break;
- case DevFmtByte:
- case DevFmtUShort:
- case DevFmtUInt:
- ERR("%s samples not supported\n", DevFmtTypeString(mDevice->FmtType));
- return ALC_INVALID_VALUE;
- }
-
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- requestedFormat.mChannelsPerFrame = 1;
- break;
- case DevFmtStereo:
- requestedFormat.mChannelsPerFrame = 2;
- break;
-
- case DevFmtQuad:
- case DevFmtX51:
- case DevFmtX51Rear:
- case DevFmtX61:
- case DevFmtX71:
- case DevFmtAmbi3D:
- ERR("%s not supported\n", DevFmtChannelsString(mDevice->FmtChans));
- return ALC_INVALID_VALUE;
- }
-
- requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
- requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
- requestedFormat.mSampleRate = mDevice->Frequency;
- requestedFormat.mFormatID = kAudioFormatLinearPCM;
- requestedFormat.mReserved = 0;
- requestedFormat.mFramesPerPacket = 1;
-
- // save requested format description for later use
- mFormat = requestedFormat;
- mFrameSize = mDevice->frameSizeFromFmt();
-
- // Use intermediate format for sample rate conversion (outputFormat)
- // Set sample rate to the same as hardware for resampling later
- outputFormat = requestedFormat;
- outputFormat.mSampleRate = hardwareFormat.mSampleRate;
-
- // The output format should be the requested format, but using the hardware sample rate
- // This is because the AudioUnit will automatically scale other properties, except for sample rate
- err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
- 1, (void*)&outputFormat, sizeof(outputFormat));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Set the AudioUnit output format frame count
- uint64_t FrameCount64{mDevice->UpdateSize};
- FrameCount64 = (FrameCount64*outputFormat.mSampleRate + mDevice->Frequency-1) /
- mDevice->Frequency;
- FrameCount64 += MAX_RESAMPLE_PADDING*2;
- if(FrameCount64 > std::numeric_limits<uint32_t>::max()/2)
- {
- ERR("FrameCount too large\n");
- return ALC_INVALID_VALUE;
- }
-
- outputFrameCount = static_cast<uint32_t>(FrameCount64);
- err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
- kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed: %d\n", err);
- return ALC_INVALID_VALUE;
- }
-
- // Set up sample converter if needed
- if(outputFormat.mSampleRate != mDevice->Frequency)
- mConverter = CreateSampleConverter(mDevice->FmtType, mDevice->FmtType,
- mFormat.mChannelsPerFrame, hardwareFormat.mSampleRate, mDevice->Frequency,
- BSinc24Resampler);
-
- mRing = CreateRingBuffer(outputFrameCount, mFrameSize, false);
- if(!mRing) return ALC_INVALID_VALUE;
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-
-ALCboolean CoreAudioCapture::start()
-{
- OSStatus err{AudioOutputUnitStart(mAudioUnit)};
- if(err != noErr)
- {
- ERR("AudioOutputUnitStart failed\n");
- return ALC_FALSE;
- }
- return ALC_TRUE;
-}
-
-void CoreAudioCapture::stop()
-{
- OSStatus err{AudioOutputUnitStop(mAudioUnit)};
- if(err != noErr)
- ERR("AudioOutputUnitStop failed\n");
-}
-
-ALCenum CoreAudioCapture::captureSamples(void *buffer, ALCuint samples)
-{
- if(!mConverter)
- {
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
- }
-
- auto rec_vec = mRing->getReadVector();
- const void *src0{rec_vec.first.buf};
- auto src0len = static_cast<ALsizei>(rec_vec.first.len);
- auto got = static_cast<ALuint>(mConverter->convert(&src0, &src0len, buffer, samples));
- size_t total_read{rec_vec.first.len - src0len};
- if(got < samples && !src0len && rec_vec.second.len > 0)
- {
- const void *src1{rec_vec.second.buf};
- auto src1len = static_cast<ALsizei>(rec_vec.second.len);
- got += static_cast<ALuint>(mConverter->convert(&src1, &src1len,
- static_cast<char*>(buffer)+got, samples-got));
- total_read += rec_vec.second.len - src1len;
- }
-
- mRing->readAdvance(total_read);
- return ALC_NO_ERROR;
-}
-
-ALCuint CoreAudioCapture::availableSamples()
-{
- if(!mConverter) return mRing->readSpace();
- return mConverter->availableOut(mRing->readSpace());
-}
-
-} // namespace
-
-BackendFactory &CoreAudioBackendFactory::getFactory()
-{
- static CoreAudioBackendFactory factory{};
- return factory;
-}
-
-bool CoreAudioBackendFactory::init() { return true; }
-
-bool CoreAudioBackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback || type == BackendType::Capture; }
-
-void CoreAudioBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- case DevProbe::Capture:
- /* Includes null char. */
- outnames->append(ca_device, sizeof(ca_device));
- break;
- }
-}
-
-BackendPtr CoreAudioBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new CoreAudioPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new CoreAudioCapture{device}};
- return nullptr;
-}
diff --git a/Alc/backends/coreaudio.h b/Alc/backends/coreaudio.h
deleted file mode 100644
index 37b9ebe5..00000000
--- a/Alc/backends/coreaudio.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_COREAUDIO_H
-#define BACKENDS_COREAUDIO_H
-
-#include "backends/base.h"
-
-struct CoreAudioBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_COREAUDIO_H */
diff --git a/Alc/backends/dsound.cpp b/Alc/backends/dsound.cpp
deleted file mode 100644
index 5a156d54..00000000
--- a/Alc/backends/dsound.cpp
+++ /dev/null
@@ -1,938 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/dsound.h"
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <memory.h>
-
-#include <cguid.h>
-#include <mmreg.h>
-#ifndef _WAVEFORMATEXTENSIBLE_
-#include <ks.h>
-#include <ksmedia.h>
-#endif
-
-#include <atomic>
-#include <cassert>
-#include <thread>
-#include <string>
-#include <vector>
-#include <algorithm>
-#include <functional>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "ringbuffer.h"
-#include "compat.h"
-#include "threads.h"
-
-/* MinGW-w64 needs this for some unknown reason now. */
-using LPCWAVEFORMATEX = const WAVEFORMATEX*;
-#include <dsound.h>
-
-
-#ifndef DSSPEAKER_5POINT1
-# define DSSPEAKER_5POINT1 0x00000006
-#endif
-#ifndef DSSPEAKER_5POINT1_BACK
-# define DSSPEAKER_5POINT1_BACK 0x00000006
-#endif
-#ifndef DSSPEAKER_7POINT1
-# define DSSPEAKER_7POINT1 0x00000007
-#endif
-#ifndef DSSPEAKER_7POINT1_SURROUND
-# define DSSPEAKER_7POINT1_SURROUND 0x00000008
-#endif
-#ifndef DSSPEAKER_5POINT1_SURROUND
-# define DSSPEAKER_5POINT1_SURROUND 0x00000009
-#endif
-
-
-/* Some headers seem to define these as macros for __uuidof, which is annoying
- * since some headers don't declare them at all. Hopefully the ifdef is enough
- * to tell if they need to be declared.
- */
-#ifndef KSDATAFORMAT_SUBTYPE_PCM
-DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
-#endif
-#ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
-DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
-#endif
-
-namespace {
-
-#define DEVNAME_HEAD "OpenAL Soft on "
-
-
-#ifdef HAVE_DYNLOAD
-void *ds_handle;
-HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
-HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
-HRESULT (WINAPI *pDirectSoundCaptureCreate)(const GUID *pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter);
-HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
-
-#ifndef IN_IDE_PARSER
-#define DirectSoundCreate pDirectSoundCreate
-#define DirectSoundEnumerateW pDirectSoundEnumerateW
-#define DirectSoundCaptureCreate pDirectSoundCaptureCreate
-#define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
-#endif
-#endif
-
-
-#define MAX_UPDATES 128
-
-struct DevMap {
- std::string name;
- GUID guid;
-
- template<typename T0, typename T1>
- DevMap(T0&& name_, T1&& guid_)
- : name{std::forward<T0>(name_)}, guid{std::forward<T1>(guid_)}
- { }
-};
-
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
-
-bool checkName(const al::vector<DevMap> &list, const std::string &name)
-{
- return std::find_if(list.cbegin(), list.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- ) != list.cend();
-}
-
-BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, void *data)
-{
- if(!guid)
- return TRUE;
-
- auto& devices = *static_cast<al::vector<DevMap>*>(data);
- const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)};
-
- int count{1};
- std::string newname{basename};
- while(checkName(devices, newname))
- {
- newname = basename;
- newname += " #";
- newname += std::to_string(++count);
- }
- devices.emplace_back(std::move(newname), *guid);
- const DevMap &newentry = devices.back();
-
- OLECHAR *guidstr{nullptr};
- HRESULT hr{StringFromCLSID(*guid, &guidstr)};
- if(SUCCEEDED(hr))
- {
- TRACE("Got device \"%s\", GUID \"%ls\"\n", newentry.name.c_str(), guidstr);
- CoTaskMemFree(guidstr);
- }
-
- return TRUE;
-}
-
-
-struct DSoundPlayback final : public BackendBase {
- DSoundPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~DSoundPlayback() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- IDirectSound *mDS{nullptr};
- IDirectSoundBuffer *mPrimaryBuffer{nullptr};
- IDirectSoundBuffer *mBuffer{nullptr};
- IDirectSoundNotify *mNotifies{nullptr};
- HANDLE mNotifyEvent{nullptr};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(DSoundPlayback)
-};
-
-DSoundPlayback::~DSoundPlayback()
-{
- if(mNotifies)
- mNotifies->Release();
- mNotifies = nullptr;
- if(mBuffer)
- mBuffer->Release();
- mBuffer = nullptr;
- if(mPrimaryBuffer)
- mPrimaryBuffer->Release();
- mPrimaryBuffer = nullptr;
-
- if(mDS)
- mDS->Release();
- mDS = nullptr;
- if(mNotifyEvent)
- CloseHandle(mNotifyEvent);
- mNotifyEvent = nullptr;
-}
-
-
-FORCE_ALIGN int DSoundPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- DSBCAPS DSBCaps{};
- DSBCaps.dwSize = sizeof(DSBCaps);
- HRESULT err{mBuffer->GetCaps(&DSBCaps)};
- if(FAILED(err))
- {
- ERR("Failed to get buffer caps: 0x%lx\n", err);
- aluHandleDisconnect(mDevice, "Failure retrieving playback buffer info: 0x%lx", err);
- return 1;
- }
-
- ALsizei FrameSize{mDevice->frameSizeFromFmt()};
- DWORD FragSize{mDevice->UpdateSize * FrameSize};
-
- bool Playing{false};
- DWORD LastCursor{0u};
- mBuffer->GetCurrentPosition(&LastCursor, nullptr);
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- // Get current play cursor
- DWORD PlayCursor;
- mBuffer->GetCurrentPosition(&PlayCursor, nullptr);
- DWORD avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
-
- if(avail < FragSize)
- {
- if(!Playing)
- {
- err = mBuffer->Play(0, 0, DSBPLAY_LOOPING);
- if(FAILED(err))
- {
- ERR("Failed to play buffer: 0x%lx\n", err);
- aluHandleDisconnect(mDevice, "Failure starting playback: 0x%lx", err);
- return 1;
- }
- Playing = true;
- }
-
- avail = WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE);
- if(avail != WAIT_OBJECT_0)
- ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
- continue;
- }
- avail -= avail%FragSize;
-
- // Lock output buffer
- void *WritePtr1, *WritePtr2;
- DWORD WriteCnt1{0u}, WriteCnt2{0u};
- err = mBuffer->Lock(LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
-
- // If the buffer is lost, restore it and lock
- if(err == DSERR_BUFFERLOST)
- {
- WARN("Buffer lost, restoring...\n");
- err = mBuffer->Restore();
- if(SUCCEEDED(err))
- {
- Playing = false;
- LastCursor = 0;
- err = mBuffer->Lock(0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1,
- &WritePtr2, &WriteCnt2, 0);
- }
- }
-
- if(SUCCEEDED(err))
- {
- lock();
- aluMixData(mDevice, WritePtr1, WriteCnt1/FrameSize);
- if(WriteCnt2 > 0)
- aluMixData(mDevice, WritePtr2, WriteCnt2/FrameSize);
- unlock();
-
- mBuffer->Unlock(WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
- }
- else
- {
- ERR("Buffer lock error: %#lx\n", err);
- aluHandleDisconnect(mDevice, "Failed to lock output buffer: 0x%lx", err);
- return 1;
- }
-
- // Update old write cursor location
- LastCursor += WriteCnt1+WriteCnt2;
- LastCursor %= DSBCaps.dwBufferBytes;
- }
-
- return 0;
-}
-
-ALCenum DSoundPlayback::open(const ALCchar *name)
-{
- HRESULT hr;
- if(PlaybackDevices.empty())
- {
- /* Initialize COM to prevent name truncation */
- HRESULT hrcom{CoInitialize(nullptr)};
- hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
- if(FAILED(hr))
- ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
- if(SUCCEEDED(hrcom))
- CoUninitialize();
- }
-
- const GUID *guid{nullptr};
- if(!name && !PlaybackDevices.empty())
- {
- name = PlaybackDevices[0].name.c_str();
- guid = &PlaybackDevices[0].guid;
- }
- else
- {
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == PlaybackDevices.cend())
- return ALC_INVALID_VALUE;
- guid = &iter->guid;
- }
-
- hr = DS_OK;
- mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
- if(!mNotifyEvent) hr = E_FAIL;
-
- //DirectSound Init code
- if(SUCCEEDED(hr))
- hr = DirectSoundCreate(guid, &mDS, nullptr);
- if(SUCCEEDED(hr))
- hr = mDS->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
- if(FAILED(hr))
- {
- ERR("Device init failed: 0x%08lx\n", hr);
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean DSoundPlayback::reset()
-{
- if(mNotifies)
- mNotifies->Release();
- mNotifies = nullptr;
- if(mBuffer)
- mBuffer->Release();
- mBuffer = nullptr;
- if(mPrimaryBuffer)
- mPrimaryBuffer->Release();
- mPrimaryBuffer = nullptr;
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mDevice->FmtType = DevFmtUByte;
- break;
- case DevFmtFloat:
- if(mDevice->Flags.get<SampleTypeRequest>())
- break;
- /* fall-through */
- case DevFmtUShort:
- mDevice->FmtType = DevFmtShort;
- break;
- case DevFmtUInt:
- mDevice->FmtType = DevFmtInt;
- break;
- case DevFmtUByte:
- case DevFmtShort:
- case DevFmtInt:
- break;
- }
-
- WAVEFORMATEXTENSIBLE OutputType{};
- DWORD speakers;
- HRESULT hr{mDS->GetSpeakerConfig(&speakers)};
- if(SUCCEEDED(hr))
- {
- speakers = DSSPEAKER_CONFIG(speakers);
- if(!mDevice->Flags.get<ChannelsRequest>())
- {
- if(speakers == DSSPEAKER_MONO)
- mDevice->FmtChans = DevFmtMono;
- else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
- mDevice->FmtChans = DevFmtStereo;
- else if(speakers == DSSPEAKER_QUAD)
- mDevice->FmtChans = DevFmtQuad;
- else if(speakers == DSSPEAKER_5POINT1_SURROUND)
- mDevice->FmtChans = DevFmtX51;
- else if(speakers == DSSPEAKER_5POINT1_BACK)
- mDevice->FmtChans = DevFmtX51Rear;
- else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
- mDevice->FmtChans = DevFmtX71;
- else
- ERR("Unknown system speaker config: 0x%lx\n", speakers);
- }
- mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo &&
- speakers == DSSPEAKER_HEADPHONE);
-
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
- break;
- case DevFmtAmbi3D:
- mDevice->FmtChans = DevFmtStereo;
- /*fall-through*/
- case DevFmtStereo:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT;
- break;
- case DevFmtQuad:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX51:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX51Rear:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX61:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_CENTER |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX71:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- }
-
-retry_open:
- hr = S_OK;
- OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- OutputType.Format.nChannels = mDevice->channelsFromFmt();
- OutputType.Format.wBitsPerSample = mDevice->bytesFromFmt() * 8;
- OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
- OutputType.Format.nSamplesPerSec = mDevice->Frequency;
- OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
- OutputType.Format.cbSize = 0;
- }
-
- if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
- {
- OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
- OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- if(mDevice->FmtType == DevFmtFloat)
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- else
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
-
- if(mPrimaryBuffer)
- mPrimaryBuffer->Release();
- mPrimaryBuffer = nullptr;
- }
- else
- {
- if(SUCCEEDED(hr) && !mPrimaryBuffer)
- {
- DSBUFFERDESC DSBDescription{};
- DSBDescription.dwSize = sizeof(DSBDescription);
- DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
- hr = mDS->CreateSoundBuffer(&DSBDescription, &mPrimaryBuffer, nullptr);
- }
- if(SUCCEEDED(hr))
- hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
- }
-
- if(SUCCEEDED(hr))
- {
- ALuint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
- if(num_updates > MAX_UPDATES)
- num_updates = MAX_UPDATES;
- mDevice->BufferSize = mDevice->UpdateSize * num_updates;
-
- DSBUFFERDESC DSBDescription{};
- DSBDescription.dwSize = sizeof(DSBDescription);
- DSBDescription.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 |
- DSBCAPS_GLOBALFOCUS;
- DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
- DSBDescription.lpwfxFormat = &OutputType.Format;
-
- hr = mDS->CreateSoundBuffer(&DSBDescription, &mBuffer, nullptr);
- if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
- {
- mDevice->FmtType = DevFmtShort;
- goto retry_open;
- }
- }
-
- if(SUCCEEDED(hr))
- {
- void *ptr;
- hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
- if(SUCCEEDED(hr))
- {
- auto Notifies = static_cast<IDirectSoundNotify*>(ptr);
- mNotifies = Notifies;
-
- ALuint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
- assert(num_updates <= MAX_UPDATES);
-
- std::array<DSBPOSITIONNOTIFY,MAX_UPDATES> nots;
- for(ALuint i{0};i < num_updates;++i)
- {
- nots[i].dwOffset = i * mDevice->UpdateSize * OutputType.Format.nBlockAlign;
- nots[i].hEventNotify = mNotifyEvent;
- }
- if(Notifies->SetNotificationPositions(num_updates, nots.data()) != DS_OK)
- hr = E_FAIL;
- }
- }
-
- if(FAILED(hr))
- {
- if(mNotifies)
- mNotifies->Release();
- mNotifies = nullptr;
- if(mBuffer)
- mBuffer->Release();
- mBuffer = nullptr;
- if(mPrimaryBuffer)
- mPrimaryBuffer->Release();
- mPrimaryBuffer = nullptr;
- return ALC_FALSE;
- }
-
- ResetEvent(mNotifyEvent);
- SetDefaultWFXChannelOrder(mDevice);
-
- return ALC_TRUE;
-}
-
-ALCboolean DSoundPlayback::start()
-{
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&DSoundPlayback::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Failed to start mixing thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void DSoundPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- mBuffer->Stop();
-}
-
-
-struct DSoundCapture final : public BackendBase {
- DSoundCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~DSoundCapture() override;
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- IDirectSoundCapture *mDSC{nullptr};
- IDirectSoundCaptureBuffer *mDSCbuffer{nullptr};
- DWORD mBufferBytes{0u};
- DWORD mCursor{0u};
-
- RingBufferPtr mRing;
-
- DEF_NEWDEL(DSoundCapture)
-};
-
-DSoundCapture::~DSoundCapture()
-{
- if(mDSCbuffer)
- {
- mDSCbuffer->Stop();
- mDSCbuffer->Release();
- mDSCbuffer = nullptr;
- }
-
- if(mDSC)
- mDSC->Release();
- mDSC = nullptr;
-}
-
-
-ALCenum DSoundCapture::open(const ALCchar *name)
-{
- HRESULT hr;
- if(CaptureDevices.empty())
- {
- /* Initialize COM to prevent name truncation */
- HRESULT hrcom{CoInitialize(nullptr)};
- hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
- if(FAILED(hr))
- ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
- if(SUCCEEDED(hrcom))
- CoUninitialize();
- }
-
- const GUID *guid{nullptr};
- if(!name && !CaptureDevices.empty())
- {
- name = CaptureDevices[0].name.c_str();
- guid = &CaptureDevices[0].guid;
- }
- else
- {
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == CaptureDevices.cend())
- return ALC_INVALID_VALUE;
- guid = &iter->guid;
- }
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- case DevFmtUShort:
- case DevFmtUInt:
- WARN("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
- return ALC_INVALID_ENUM;
-
- case DevFmtUByte:
- case DevFmtShort:
- case DevFmtInt:
- case DevFmtFloat:
- break;
- }
-
- WAVEFORMATEXTENSIBLE InputType{};
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
- break;
- case DevFmtStereo:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT;
- break;
- case DevFmtQuad:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX51:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX51Rear:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX61:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_CENTER |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX71:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtAmbi3D:
- WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans));
- return ALC_INVALID_ENUM;
- }
-
- InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- InputType.Format.nChannels = mDevice->channelsFromFmt();
- InputType.Format.wBitsPerSample = mDevice->bytesFromFmt() * 8;
- InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
- InputType.Format.nSamplesPerSec = mDevice->Frequency;
- InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
- InputType.Format.cbSize = 0;
- InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
- if(mDevice->FmtType == DevFmtFloat)
- InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- else
- InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
-
- if(InputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
- {
- InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- }
-
- ALuint samples{mDevice->BufferSize};
- samples = maxu(samples, 100 * mDevice->Frequency / 1000);
-
- DSCBUFFERDESC DSCBDescription{};
- DSCBDescription.dwSize = sizeof(DSCBDescription);
- DSCBDescription.dwFlags = 0;
- DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
- DSCBDescription.lpwfxFormat = &InputType.Format;
-
- //DirectSoundCapture Init code
- hr = DirectSoundCaptureCreate(guid, &mDSC, nullptr);
- if(SUCCEEDED(hr))
- mDSC->CreateCaptureBuffer(&DSCBDescription, &mDSCbuffer, nullptr);
- if(SUCCEEDED(hr))
- {
- mRing = CreateRingBuffer(mDevice->BufferSize, InputType.Format.nBlockAlign, false);
- if(!mRing) hr = DSERR_OUTOFMEMORY;
- }
-
- if(FAILED(hr))
- {
- ERR("Device init failed: 0x%08lx\n", hr);
-
- mRing = nullptr;
- if(mDSCbuffer)
- mDSCbuffer->Release();
- mDSCbuffer = nullptr;
- if(mDSC)
- mDSC->Release();
- mDSC = nullptr;
-
- return ALC_INVALID_VALUE;
- }
-
- mBufferBytes = DSCBDescription.dwBufferBytes;
- SetDefaultWFXChannelOrder(mDevice);
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean DSoundCapture::start()
-{
- HRESULT hr{mDSCbuffer->Start(DSCBSTART_LOOPING)};
- if(FAILED(hr))
- {
- ERR("start failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failure starting capture: 0x%lx", hr);
- return ALC_FALSE;
- }
- return ALC_TRUE;
-}
-
-void DSoundCapture::stop()
-{
- HRESULT hr{mDSCbuffer->Stop()};
- if(FAILED(hr))
- {
- ERR("stop failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failure stopping capture: 0x%lx", hr);
- }
-}
-
-ALCenum DSoundCapture::captureSamples(void *buffer, ALCuint samples)
-{
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
-}
-
-ALCuint DSoundCapture::availableSamples()
-{
- if(!mDevice->Connected.load(std::memory_order_acquire))
- return static_cast<ALCuint>(mRing->readSpace());
-
- ALsizei FrameSize{mDevice->frameSizeFromFmt()};
- DWORD BufferBytes{mBufferBytes};
- DWORD LastCursor{mCursor};
-
- DWORD ReadCursor;
- void *ReadPtr1, *ReadPtr2;
- DWORD ReadCnt1, ReadCnt2;
- HRESULT hr{mDSCbuffer->GetCurrentPosition(nullptr, &ReadCursor)};
- if(SUCCEEDED(hr))
- {
- DWORD NumBytes{(ReadCursor-LastCursor + BufferBytes) % BufferBytes};
- if(!NumBytes) return static_cast<ALCubyte>(mRing->readSpace());
- hr = mDSCbuffer->Lock(LastCursor, NumBytes, &ReadPtr1, &ReadCnt1, &ReadPtr2, &ReadCnt2, 0);
- }
- if(SUCCEEDED(hr))
- {
- mRing->write(ReadPtr1, ReadCnt1/FrameSize);
- if(ReadPtr2 != nullptr && ReadCnt2 > 0)
- mRing->write(ReadPtr2, ReadCnt2/FrameSize);
- hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2);
- mCursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
- }
-
- if(FAILED(hr))
- {
- ERR("update failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failure retrieving capture data: 0x%lx", hr);
- }
-
- return static_cast<ALCuint>(mRing->readSpace());
-}
-
-} // namespace
-
-
-BackendFactory &DSoundBackendFactory::getFactory()
-{
- static DSoundBackendFactory factory{};
- return factory;
-}
-
-bool DSoundBackendFactory::init()
-{
-#ifdef HAVE_DYNLOAD
- if(!ds_handle)
- {
- ds_handle = LoadLib("dsound.dll");
- if(!ds_handle)
- {
- ERR("Failed to load dsound.dll\n");
- return false;
- }
-
-#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \
- if(!p##f) \
- { \
- CloseLib(ds_handle); \
- ds_handle = nullptr; \
- return false; \
- } \
-} while(0)
- LOAD_FUNC(DirectSoundCreate);
- LOAD_FUNC(DirectSoundEnumerateW);
- LOAD_FUNC(DirectSoundCaptureCreate);
- LOAD_FUNC(DirectSoundCaptureEnumerateW);
-#undef LOAD_FUNC
- }
-#endif
- return true;
-}
-
-bool DSoundBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void DSoundBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const DevMap &entry) -> void
- {
- /* +1 to also append the null char (to ensure a null-separated list and
- * double-null terminated list).
- */
- outnames->append(entry.name.c_str(), entry.name.length()+1);
- };
-
- /* Initialize COM to prevent name truncation */
- HRESULT hr;
- HRESULT hrcom{CoInitialize(nullptr)};
- switch(type)
- {
- case DevProbe::Playback:
- PlaybackDevices.clear();
- hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
- if(FAILED(hr))
- ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- CaptureDevices.clear();
- hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
- if(FAILED(hr))
- ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
- if(SUCCEEDED(hrcom))
- CoUninitialize();
-}
-
-BackendPtr DSoundBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new DSoundPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new DSoundCapture{device}};
- return nullptr;
-}
diff --git a/Alc/backends/dsound.h b/Alc/backends/dsound.h
deleted file mode 100644
index 6bef0bfc..00000000
--- a/Alc/backends/dsound.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_DSOUND_H
-#define BACKENDS_DSOUND_H
-
-#include "backends/base.h"
-
-struct DSoundBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_DSOUND_H */
diff --git a/Alc/backends/jack.cpp b/Alc/backends/jack.cpp
deleted file mode 100644
index 3f81d08c..00000000
--- a/Alc/backends/jack.cpp
+++ /dev/null
@@ -1,562 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/jack.h"
-
-#include <cstdlib>
-#include <cstdio>
-#include <memory.h>
-
-#include <thread>
-#include <functional>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "alconfig.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "compat.h"
-
-#include <jack/jack.h>
-#include <jack/ringbuffer.h>
-
-
-namespace {
-
-constexpr ALCchar jackDevice[] = "JACK Default";
-
-
-#ifdef HAVE_DYNLOAD
-#define JACK_FUNCS(MAGIC) \
- MAGIC(jack_client_open); \
- MAGIC(jack_client_close); \
- MAGIC(jack_client_name_size); \
- MAGIC(jack_get_client_name); \
- MAGIC(jack_connect); \
- MAGIC(jack_activate); \
- MAGIC(jack_deactivate); \
- MAGIC(jack_port_register); \
- MAGIC(jack_port_unregister); \
- MAGIC(jack_port_get_buffer); \
- MAGIC(jack_port_name); \
- MAGIC(jack_get_ports); \
- MAGIC(jack_free); \
- MAGIC(jack_get_sample_rate); \
- MAGIC(jack_set_error_function); \
- MAGIC(jack_set_process_callback); \
- MAGIC(jack_set_buffer_size_callback); \
- MAGIC(jack_set_buffer_size); \
- MAGIC(jack_get_buffer_size);
-
-void *jack_handle;
-#define MAKE_FUNC(f) decltype(f) * p##f
-JACK_FUNCS(MAKE_FUNC);
-decltype(jack_error_callback) * pjack_error_callback;
-#undef MAKE_FUNC
-
-#ifndef IN_IDE_PARSER
-#define jack_client_open pjack_client_open
-#define jack_client_close pjack_client_close
-#define jack_client_name_size pjack_client_name_size
-#define jack_get_client_name pjack_get_client_name
-#define jack_connect pjack_connect
-#define jack_activate pjack_activate
-#define jack_deactivate pjack_deactivate
-#define jack_port_register pjack_port_register
-#define jack_port_unregister pjack_port_unregister
-#define jack_port_get_buffer pjack_port_get_buffer
-#define jack_port_name pjack_port_name
-#define jack_get_ports pjack_get_ports
-#define jack_free pjack_free
-#define jack_get_sample_rate pjack_get_sample_rate
-#define jack_set_error_function pjack_set_error_function
-#define jack_set_process_callback pjack_set_process_callback
-#define jack_set_buffer_size_callback pjack_set_buffer_size_callback
-#define jack_set_buffer_size pjack_set_buffer_size
-#define jack_get_buffer_size pjack_get_buffer_size
-#define jack_error_callback (*pjack_error_callback)
-#endif
-#endif
-
-
-jack_options_t ClientOptions = JackNullOption;
-
-ALCboolean jack_load()
-{
- ALCboolean error = ALC_FALSE;
-
-#ifdef HAVE_DYNLOAD
- if(!jack_handle)
- {
- std::string missing_funcs;
-
-#ifdef _WIN32
-#define JACKLIB "libjack.dll"
-#else
-#define JACKLIB "libjack.so.0"
-#endif
- jack_handle = LoadLib(JACKLIB);
- if(!jack_handle)
- {
- WARN("Failed to load %s\n", JACKLIB);
- return ALC_FALSE;
- }
-
- error = ALC_FALSE;
-#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \
- if(p##f == nullptr) { \
- error = ALC_TRUE; \
- missing_funcs += "\n" #f; \
- } \
-} while(0)
- JACK_FUNCS(LOAD_FUNC);
-#undef LOAD_FUNC
- /* Optional symbols. These don't exist in all versions of JACK. */
-#define LOAD_SYM(f) p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f))
- LOAD_SYM(jack_error_callback);
-#undef LOAD_SYM
-
- if(error)
- {
- WARN("Missing expected functions:%s\n", missing_funcs.c_str());
- CloseLib(jack_handle);
- jack_handle = nullptr;
- }
- }
-#endif
-
- return !error;
-}
-
-
-struct JackPlayback final : public BackendBase {
- JackPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~JackPlayback() override;
-
- static int bufferSizeNotifyC(jack_nframes_t numframes, void *arg);
- int bufferSizeNotify(jack_nframes_t numframes);
-
- static int processC(jack_nframes_t numframes, void *arg);
- int process(jack_nframes_t numframes);
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
- ClockLatency getClockLatency() override;
-
- jack_client_t *mClient{nullptr};
- jack_port_t *mPort[MAX_OUTPUT_CHANNELS]{};
-
- RingBufferPtr mRing;
- al::semaphore mSem;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(JackPlayback)
-};
-
-JackPlayback::~JackPlayback()
-{
- if(!mClient)
- return;
-
- std::for_each(std::begin(mPort), std::end(mPort),
- [this](jack_port_t *port) -> void
- { if(port) jack_port_unregister(mClient, port); }
- );
- std::fill(std::begin(mPort), std::end(mPort), nullptr);
- jack_client_close(mClient);
- mClient = nullptr;
-}
-
-
-int JackPlayback::bufferSizeNotifyC(jack_nframes_t numframes, void *arg)
-{ return static_cast<JackPlayback*>(arg)->bufferSizeNotify(numframes); }
-
-int JackPlayback::bufferSizeNotify(jack_nframes_t numframes)
-{
- std::lock_guard<std::mutex> _{mDevice->StateLock};
- mDevice->UpdateSize = numframes;
- mDevice->BufferSize = numframes*2;
-
- const char *devname{mDevice->DeviceName.c_str()};
- ALuint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
- bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
- mDevice->BufferSize = bufsize + mDevice->UpdateSize;
-
- TRACE("%u / %u buffer\n", mDevice->UpdateSize, mDevice->BufferSize);
-
- mRing = nullptr;
- mRing = CreateRingBuffer(bufsize, mDevice->frameSizeFromFmt(), true);
- if(!mRing)
- {
- ERR("Failed to reallocate ringbuffer\n");
- aluHandleDisconnect(mDevice, "Failed to reallocate %u-sample buffer", bufsize);
- }
- return 0;
-}
-
-
-int JackPlayback::processC(jack_nframes_t numframes, void *arg)
-{ return static_cast<JackPlayback*>(arg)->process(numframes); }
-
-int JackPlayback::process(jack_nframes_t numframes)
-{
- jack_default_audio_sample_t *out[MAX_OUTPUT_CHANNELS];
- ALsizei numchans{0};
- for(auto port : mPort)
- {
- if(!port) break;
- out[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
- }
-
- auto data = mRing->getReadVector();
- jack_nframes_t todo{minu(numframes, data.first.len)};
- std::transform(out, out+numchans, out,
- [&data,numchans,todo](ALfloat *outbuf) -> ALfloat*
- {
- const ALfloat *RESTRICT in = reinterpret_cast<ALfloat*>(data.first.buf);
- std::generate_n(outbuf, todo,
- [&in,numchans]() noexcept -> ALfloat
- {
- ALfloat ret{*in};
- in += numchans;
- return ret;
- }
- );
- data.first.buf += sizeof(ALfloat);
- return outbuf + todo;
- }
- );
- jack_nframes_t total{todo};
-
- todo = minu(numframes-total, data.second.len);
- if(todo > 0)
- {
- std::transform(out, out+numchans, out,
- [&data,numchans,todo](ALfloat *outbuf) -> ALfloat*
- {
- const ALfloat *RESTRICT in = reinterpret_cast<ALfloat*>(data.second.buf);
- std::generate_n(outbuf, todo,
- [&in,numchans]() noexcept -> ALfloat
- {
- ALfloat ret{*in};
- in += numchans;
- return ret;
- }
- );
- data.second.buf += sizeof(ALfloat);
- return outbuf + todo;
- }
- );
- total += todo;
- }
-
- mRing->readAdvance(total);
- mSem.post();
-
- if(numframes > total)
- {
- todo = numframes-total;
- std::transform(out, out+numchans, out,
- [todo](ALfloat *outbuf) -> ALfloat*
- {
- std::fill_n(outbuf, todo, 0.0f);
- return outbuf + todo;
- }
- );
- }
-
- return 0;
-}
-
-int JackPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- lock();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- if(mRing->writeSpace() < mDevice->UpdateSize)
- {
- unlock();
- mSem.wait();
- lock();
- continue;
- }
-
- auto data = mRing->getWriteVector();
- auto todo = static_cast<ALuint>(data.first.len + data.second.len);
- todo -= todo%mDevice->UpdateSize;
-
- ALuint len1{minu(data.first.len, todo)};
- ALuint len2{minu(data.second.len, todo-len1)};
-
- aluMixData(mDevice, data.first.buf, len1);
- if(len2 > 0)
- aluMixData(mDevice, data.second.buf, len2);
- mRing->writeAdvance(todo);
- }
- unlock();
-
- return 0;
-}
-
-
-ALCenum JackPlayback::open(const ALCchar *name)
-{
- if(!name)
- name = jackDevice;
- else if(strcmp(name, jackDevice) != 0)
- return ALC_INVALID_VALUE;
-
- const char *client_name{"alsoft"};
- jack_status_t status;
- mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
- if(mClient == nullptr)
- {
- ERR("jack_client_open() failed, status = 0x%02x\n", status);
- return ALC_INVALID_VALUE;
- }
- if((status&JackServerStarted))
- TRACE("JACK server started\n");
- if((status&JackNameNotUnique))
- {
- client_name = jack_get_client_name(mClient);
- TRACE("Client name not unique, got `%s' instead\n", client_name);
- }
-
- jack_set_process_callback(mClient, &JackPlayback::processC, this);
- jack_set_buffer_size_callback(mClient, &JackPlayback::bufferSizeNotifyC, this);
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean JackPlayback::reset()
-{
- std::for_each(std::begin(mPort), std::end(mPort),
- [this](jack_port_t *port) -> void
- { if(port) jack_port_unregister(mClient, port); }
- );
- std::fill(std::begin(mPort), std::end(mPort), nullptr);
-
- /* Ignore the requested buffer metrics and just keep one JACK-sized buffer
- * ready for when requested.
- */
- mDevice->Frequency = jack_get_sample_rate(mClient);
- mDevice->UpdateSize = jack_get_buffer_size(mClient);
- mDevice->BufferSize = mDevice->UpdateSize * 2;
-
- const char *devname{mDevice->DeviceName.c_str()};
- ALuint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
- bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
- mDevice->BufferSize = bufsize + mDevice->UpdateSize;
-
- /* Force 32-bit float output. */
- mDevice->FmtType = DevFmtFloat;
-
- ALsizei numchans{mDevice->channelsFromFmt()};
- auto ports_end = std::begin(mPort) + numchans;
- auto bad_port = std::find_if_not(std::begin(mPort), ports_end,
- [this](jack_port_t *&port) -> bool
- {
- std::string name{"channel_" + std::to_string(&port - mPort + 1)};
- port = jack_port_register(mClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE,
- JackPortIsOutput, 0);
- return port != nullptr;
- }
- );
- if(bad_port != ports_end)
- {
- ERR("Not enough JACK ports available for %s output\n", DevFmtChannelsString(mDevice->FmtChans));
- if(bad_port == std::begin(mPort)) return ALC_FALSE;
-
- if(bad_port == std::begin(mPort)+1)
- mDevice->FmtChans = DevFmtMono;
- else
- {
- ports_end = mPort+2;
- while(bad_port != ports_end)
- {
- jack_port_unregister(mClient, *(--bad_port));
- *bad_port = nullptr;
- }
- mDevice->FmtChans = DevFmtStereo;
- }
- numchans = std::distance(std::begin(mPort), bad_port);
- }
-
- mRing = nullptr;
- mRing = CreateRingBuffer(bufsize, mDevice->frameSizeFromFmt(), true);
- if(!mRing)
- {
- ERR("Failed to allocate ringbuffer\n");
- return ALC_FALSE;
- }
-
- SetDefaultChannelOrder(mDevice);
-
- return ALC_TRUE;
-}
-
-ALCboolean JackPlayback::start()
-{
- if(jack_activate(mClient))
- {
- ERR("Failed to activate client\n");
- return ALC_FALSE;
- }
-
- const char **ports{jack_get_ports(mClient, nullptr, nullptr,
- JackPortIsPhysical|JackPortIsInput)};
- if(ports == nullptr)
- {
- ERR("No physical playback ports found\n");
- jack_deactivate(mClient);
- return ALC_FALSE;
- }
- std::mismatch(std::begin(mPort), std::end(mPort), ports,
- [this](const jack_port_t *port, const char *pname) -> bool
- {
- if(!port) return false;
- if(!pname)
- {
- ERR("No physical playback port for \"%s\"\n", jack_port_name(port));
- return false;
- }
- if(jack_connect(mClient, jack_port_name(port), pname))
- ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(port),
- pname);
- return true;
- }
- );
- jack_free(ports);
-
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&JackPlayback::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- jack_deactivate(mClient);
- return ALC_FALSE;
-}
-
-void JackPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
-
- mSem.post();
- mThread.join();
-
- jack_deactivate(mClient);
-}
-
-
-ClockLatency JackPlayback::getClockLatency()
-{
- ClockLatency ret;
-
- lock();
- ret.ClockTime = GetDeviceClockTime(mDevice);
- ret.Latency = std::chrono::seconds{mRing->readSpace()};
- ret.Latency /= mDevice->Frequency;
- unlock();
-
- return ret;
-}
-
-
-void jack_msg_handler(const char *message)
-{
- WARN("%s\n", message);
-}
-
-} // namespace
-
-bool JackBackendFactory::init()
-{
- if(!jack_load())
- return false;
-
- if(!GetConfigValueBool(nullptr, "jack", "spawn-server", 0))
- ClientOptions = static_cast<jack_options_t>(ClientOptions | JackNoStartServer);
-
- void (*old_error_cb)(const char*){&jack_error_callback ? jack_error_callback : nullptr};
- jack_set_error_function(jack_msg_handler);
- jack_status_t status;
- jack_client_t *client{jack_client_open("alsoft", ClientOptions, &status, nullptr)};
- jack_set_error_function(old_error_cb);
- if(!client)
- {
- WARN("jack_client_open() failed, 0x%02x\n", status);
- if((status&JackServerFailed) && !(ClientOptions&JackNoStartServer))
- ERR("Unable to connect to JACK server\n");
- return false;
- }
-
- jack_client_close(client);
- return true;
-}
-
-bool JackBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback); }
-
-void JackBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- /* Includes null char. */
- outnames->append(jackDevice, sizeof(jackDevice));
- break;
-
- case DevProbe::Capture:
- break;
- }
-}
-
-BackendPtr JackBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new JackPlayback{device}};
- return nullptr;
-}
-
-BackendFactory &JackBackendFactory::getFactory()
-{
- static JackBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/jack.h b/Alc/backends/jack.h
deleted file mode 100644
index 10beebfb..00000000
--- a/Alc/backends/jack.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_JACK_H
-#define BACKENDS_JACK_H
-
-#include "backends/base.h"
-
-struct JackBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_JACK_H */
diff --git a/Alc/backends/loopback.cpp b/Alc/backends/loopback.cpp
deleted file mode 100644
index 4a1c641a..00000000
--- a/Alc/backends/loopback.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2011 by Chris Robinson
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/loopback.h"
-
-#include "alcmain.h"
-#include "alu.h"
-
-
-namespace {
-
-struct LoopbackBackend final : public BackendBase {
- LoopbackBackend(ALCdevice *device) noexcept : BackendBase{device} { }
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- DEF_NEWDEL(LoopbackBackend)
-};
-
-
-ALCenum LoopbackBackend::open(const ALCchar *name)
-{
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean LoopbackBackend::reset()
-{
- SetDefaultWFXChannelOrder(mDevice);
- return ALC_TRUE;
-}
-
-ALCboolean LoopbackBackend::start()
-{ return ALC_TRUE; }
-
-void LoopbackBackend::stop()
-{ }
-
-} // namespace
-
-
-bool LoopbackBackendFactory::init()
-{ return true; }
-
-bool LoopbackBackendFactory::querySupport(BackendType)
-{ return true; }
-
-void LoopbackBackendFactory::probe(DevProbe, std::string*)
-{ }
-
-BackendPtr LoopbackBackendFactory::createBackend(ALCdevice *device, BackendType)
-{ return BackendPtr{new LoopbackBackend{device}}; }
-
-BackendFactory &LoopbackBackendFactory::getFactory()
-{
- static LoopbackBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/loopback.h b/Alc/backends/loopback.h
deleted file mode 100644
index 09c085b8..00000000
--- a/Alc/backends/loopback.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_LOOPBACK_H
-#define BACKENDS_LOOPBACK_H
-
-#include "backends/base.h"
-
-struct LoopbackBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_LOOPBACK_H */
diff --git a/Alc/backends/null.cpp b/Alc/backends/null.cpp
deleted file mode 100644
index ae58cb8b..00000000
--- a/Alc/backends/null.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2010 by Chris Robinson
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/null.h"
-
-#include <exception>
-#include <atomic>
-#include <chrono>
-#include <cstdint>
-#include <cstring>
-#include <functional>
-#include <thread>
-
-#include "alcmain.h"
-#include "almalloc.h"
-#include "alu.h"
-#include "logging.h"
-#include "threads.h"
-
-
-namespace {
-
-using std::chrono::seconds;
-using std::chrono::milliseconds;
-using std::chrono::nanoseconds;
-
-constexpr ALCchar nullDevice[] = "No Output";
-
-
-struct NullBackend final : public BackendBase {
- NullBackend(ALCdevice *device) noexcept : BackendBase{device} { }
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(NullBackend)
-};
-
-int NullBackend::mixerProc()
-{
- const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
-
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- int64_t done{0};
- auto start = std::chrono::steady_clock::now();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- auto now = std::chrono::steady_clock::now();
-
- /* This converts from nanoseconds to nanosamples, then to samples. */
- int64_t avail{std::chrono::duration_cast<seconds>((now-start) * mDevice->Frequency).count()};
- if(avail-done < mDevice->UpdateSize)
- {
- std::this_thread::sleep_for(restTime);
- continue;
- }
- while(avail-done >= mDevice->UpdateSize)
- {
- lock();
- aluMixData(mDevice, nullptr, mDevice->UpdateSize);
- unlock();
- done += mDevice->UpdateSize;
- }
-
- /* For every completed second, increment the start time and reduce the
- * samples done. This prevents the difference between the start time
- * and current time from growing too large, while maintaining the
- * correct number of samples to render.
- */
- if(done >= mDevice->Frequency)
- {
- seconds s{done/mDevice->Frequency};
- start += s;
- done -= mDevice->Frequency*s.count();
- }
- }
-
- return 0;
-}
-
-
-ALCenum NullBackend::open(const ALCchar *name)
-{
- if(!name)
- name = nullDevice;
- else if(strcmp(name, nullDevice) != 0)
- return ALC_INVALID_VALUE;
-
- mDevice->DeviceName = name;
-
- return ALC_NO_ERROR;
-}
-
-ALCboolean NullBackend::reset()
-{
- SetDefaultWFXChannelOrder(mDevice);
- return ALC_TRUE;
-}
-
-ALCboolean NullBackend::start()
-{
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&NullBackend::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Failed to start mixing thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void NullBackend::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-}
-
-} // namespace
-
-
-bool NullBackendFactory::init()
-{ return true; }
-
-bool NullBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback); }
-
-void NullBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- /* Includes null char. */
- outnames->append(nullDevice, sizeof(nullDevice));
- break;
- case DevProbe::Capture:
- break;
- }
-}
-
-BackendPtr NullBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new NullBackend{device}};
- return nullptr;
-}
-
-BackendFactory &NullBackendFactory::getFactory()
-{
- static NullBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/null.h b/Alc/backends/null.h
deleted file mode 100644
index f19d5b4d..00000000
--- a/Alc/backends/null.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_NULL_H
-#define BACKENDS_NULL_H
-
-#include "backends/base.h"
-
-struct NullBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_NULL_H */
diff --git a/Alc/backends/opensl.cpp b/Alc/backends/opensl.cpp
deleted file mode 100644
index b34dc0cb..00000000
--- a/Alc/backends/opensl.cpp
+++ /dev/null
@@ -1,936 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* This is an OpenAL backend for Android using the native audio APIs based on
- * OpenSL ES 1.0.1. It is based on source code for the native-audio sample app
- * bundled with NDK.
- */
-
-#include "config.h"
-
-#include "backends/opensl.h"
-
-#include <stdlib.h>
-#include <jni.h>
-
-#include <new>
-#include <array>
-#include <thread>
-#include <functional>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "compat.h"
-
-#include <SLES/OpenSLES.h>
-#include <SLES/OpenSLES_Android.h>
-#include <SLES/OpenSLES_AndroidConfiguration.h>
-
-
-namespace {
-
-/* Helper macros */
-#define EXTRACT_VCALL_ARGS(...) __VA_ARGS__))
-#define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
-#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
-
-
-constexpr ALCchar opensl_device[] = "OpenSL";
-
-
-SLuint32 GetChannelMask(DevFmtChannels chans)
-{
- switch(chans)
- {
- case DevFmtMono: return SL_SPEAKER_FRONT_CENTER;
- case DevFmtStereo: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT;
- case DevFmtQuad: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
- SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
- case DevFmtX51: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
- SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
- SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
- case DevFmtX51Rear: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
- SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
- SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
- case DevFmtX61: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
- SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
- SL_SPEAKER_BACK_CENTER|
- SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
- case DevFmtX71: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
- SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
- SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT|
- SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
- case DevFmtAmbi3D:
- break;
- }
- return 0;
-}
-
-#ifdef SL_ANDROID_DATAFORMAT_PCM_EX
-SLuint32 GetTypeRepresentation(DevFmtType type)
-{
- switch(type)
- {
- case DevFmtUByte:
- case DevFmtUShort:
- case DevFmtUInt:
- return SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT;
- case DevFmtByte:
- case DevFmtShort:
- case DevFmtInt:
- return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
- case DevFmtFloat:
- return SL_ANDROID_PCM_REPRESENTATION_FLOAT;
- }
- return 0;
-}
-#endif
-
-const char *res_str(SLresult result)
-{
- switch(result)
- {
- case SL_RESULT_SUCCESS: return "Success";
- case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated";
- case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid";
- case SL_RESULT_MEMORY_FAILURE: return "Memory failure";
- case SL_RESULT_RESOURCE_ERROR: return "Resource error";
- case SL_RESULT_RESOURCE_LOST: return "Resource lost";
- case SL_RESULT_IO_ERROR: return "I/O error";
- case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient";
- case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted";
- case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported";
- case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found";
- case SL_RESULT_PERMISSION_DENIED: return "Permission denied";
- case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported";
- case SL_RESULT_INTERNAL_ERROR: return "Internal error";
- case SL_RESULT_UNKNOWN_ERROR: return "Unknown error";
- case SL_RESULT_OPERATION_ABORTED: return "Operation aborted";
- case SL_RESULT_CONTROL_LOST: return "Control lost";
-#ifdef SL_RESULT_READONLY
- case SL_RESULT_READONLY: return "ReadOnly";
-#endif
-#ifdef SL_RESULT_ENGINEOPTION_UNSUPPORTED
- case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported";
-#endif
-#ifdef SL_RESULT_SOURCE_SINK_INCOMPATIBLE
- case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible";
-#endif
- }
- return "Unknown error code";
-}
-
-#define PRINTERR(x, s) do { \
- if(UNLIKELY((x) != SL_RESULT_SUCCESS)) \
- ERR("%s: %s\n", (s), res_str((x))); \
-} while(0)
-
-
-struct OpenSLPlayback final : public BackendBase {
- OpenSLPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~OpenSLPlayback() override;
-
- static void processC(SLAndroidSimpleBufferQueueItf bq, void *context);
- void process(SLAndroidSimpleBufferQueueItf bq);
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
- ClockLatency getClockLatency() override;
-
- /* engine interfaces */
- SLObjectItf mEngineObj{nullptr};
- SLEngineItf mEngine{nullptr};
-
- /* output mix interfaces */
- SLObjectItf mOutputMix{nullptr};
-
- /* buffer queue player interfaces */
- SLObjectItf mBufferQueueObj{nullptr};
-
- RingBufferPtr mRing{nullptr};
- al::semaphore mSem;
-
- ALsizei mFrameSize{0};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(OpenSLPlayback)
-};
-
-OpenSLPlayback::~OpenSLPlayback()
-{
- if(mBufferQueueObj)
- VCALL0(mBufferQueueObj,Destroy)();
- mBufferQueueObj = nullptr;
-
- if(mOutputMix)
- VCALL0(mOutputMix,Destroy)();
- mOutputMix = nullptr;
-
- if(mEngineObj)
- VCALL0(mEngineObj,Destroy)();
- mEngineObj = nullptr;
- mEngine = nullptr;
-}
-
-
-/* this callback handler is called every time a buffer finishes playing */
-void OpenSLPlayback::processC(SLAndroidSimpleBufferQueueItf bq, void *context)
-{ static_cast<OpenSLPlayback*>(context)->process(bq); }
-
-void OpenSLPlayback::process(SLAndroidSimpleBufferQueueItf)
-{
- /* A note on the ringbuffer usage: The buffer queue seems to hold on to the
- * pointer passed to the Enqueue method, rather than copying the audio.
- * Consequently, the ringbuffer contains the audio that is currently queued
- * and waiting to play. This process() callback is called when a buffer is
- * finished, so we simply move the read pointer up to indicate the space is
- * available for writing again, and wake up the mixer thread to mix and
- * queue more audio.
- */
- mRing->readAdvance(1);
-
- mSem.post();
-}
-
-int OpenSLPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- SLPlayItf player;
- SLAndroidSimpleBufferQueueItf bufferQueue;
- SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
- &bufferQueue)};
- PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
- PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY");
- }
-
- lock();
- if(SL_RESULT_SUCCESS != result)
- aluHandleDisconnect(mDevice, "Failed to get playback buffer: 0x%08x", result);
-
- while(SL_RESULT_SUCCESS == result && !mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- if(mRing->writeSpace() == 0)
- {
- SLuint32 state{0};
-
- result = VCALL(player,GetPlayState)(&state);
- PRINTERR(result, "player->GetPlayState");
- if(SL_RESULT_SUCCESS == result && state != SL_PLAYSTATE_PLAYING)
- {
- result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
- PRINTERR(result, "player->SetPlayState");
- }
- if(SL_RESULT_SUCCESS != result)
- {
- aluHandleDisconnect(mDevice, "Failed to start platback: 0x%08x", result);
- break;
- }
-
- if(mRing->writeSpace() == 0)
- {
- unlock();
- mSem.wait();
- lock();
- continue;
- }
- }
-
- auto data = mRing->getWriteVector();
- aluMixData(mDevice, data.first.buf, data.first.len*mDevice->UpdateSize);
- if(data.second.len > 0)
- aluMixData(mDevice, data.second.buf, data.second.len*mDevice->UpdateSize);
-
- size_t todo{data.first.len + data.second.len};
- mRing->writeAdvance(todo);
-
- for(size_t i{0};i < todo;i++)
- {
- if(!data.first.len)
- {
- data.first = data.second;
- data.second.buf = nullptr;
- data.second.len = 0;
- }
-
- result = VCALL(bufferQueue,Enqueue)(data.first.buf, mDevice->UpdateSize*mFrameSize);
- PRINTERR(result, "bufferQueue->Enqueue");
- if(SL_RESULT_SUCCESS != result)
- {
- aluHandleDisconnect(mDevice, "Failed to queue audio: 0x%08x", result);
- break;
- }
-
- data.first.len--;
- data.first.buf += mDevice->UpdateSize*mFrameSize;
- }
- }
- unlock();
-
- return 0;
-}
-
-
-ALCenum OpenSLPlayback::open(const ALCchar *name)
-{
- if(!name)
- name = opensl_device;
- else if(strcmp(name, opensl_device) != 0)
- return ALC_INVALID_VALUE;
-
- // create engine
- SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
- PRINTERR(result, "slCreateEngine");
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mEngineObj,Realize)(SL_BOOLEAN_FALSE);
- PRINTERR(result, "engine->Realize");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mEngineObj,GetInterface)(SL_IID_ENGINE, &mEngine);
- PRINTERR(result, "engine->GetInterface");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mEngine,CreateOutputMix)(&mOutputMix, 0, nullptr, nullptr);
- PRINTERR(result, "engine->CreateOutputMix");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mOutputMix,Realize)(SL_BOOLEAN_FALSE);
- PRINTERR(result, "outputMix->Realize");
- }
-
- if(SL_RESULT_SUCCESS != result)
- {
- if(mOutputMix)
- VCALL0(mOutputMix,Destroy)();
- mOutputMix = nullptr;
-
- if(mEngineObj)
- VCALL0(mEngineObj,Destroy)();
- mEngineObj = nullptr;
- mEngine = nullptr;
-
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean OpenSLPlayback::reset()
-{
- SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
- SLDataLocator_OutputMix loc_outmix;
- SLDataSource audioSrc;
- SLDataSink audioSnk;
- SLresult result;
-
- if(mBufferQueueObj)
- VCALL0(mBufferQueueObj,Destroy)();
- mBufferQueueObj = nullptr;
-
- mRing = nullptr;
-
-#if 0
- if(!mDevice->Flags.get<FrequencyRequest>())
- {
- /* FIXME: Disabled until I figure out how to get the Context needed for
- * the getSystemService call.
- */
- JNIEnv *env = Android_GetJNIEnv();
- jobject jctx = Android_GetContext();
-
- /* Get necessary stuff for using java.lang.Integer,
- * android.content.Context, and android.media.AudioManager.
- */
- jclass int_cls = JCALL(env,FindClass)("java/lang/Integer");
- jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls,
- "parseInt", "(Ljava/lang/String;)I"
- );
- TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint);
-
- jclass ctx_cls = JCALL(env,FindClass)("android/content/Context");
- jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls,
- "AUDIO_SERVICE", "Ljava/lang/String;"
- );
- jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls,
- "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"
- );
- TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n",
- ctx_cls, ctx_audsvc, ctx_getSysSvc);
-
- jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager");
- jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls,
- "PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;"
- );
- jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls,
- "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"
- );
- TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n",
- audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty);
-
- const char *strchars;
- jstring strobj;
-
- /* Now make the calls. */
- //AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
- strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc);
- jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj);
- strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
- TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
- JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
-
- //String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
- strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate);
- jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj);
- strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
- TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr);
- JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
-
- //int sampleRate = Integer.parseInt(srateStr);
- sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr);
-
- strchars = JCALL(env,GetStringUTFChars)(srateStr, nullptr);
- TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars);
- JCALL(env,ReleaseStringUTFChars)(srateStr, strchars);
-
- if(!sampleRate) sampleRate = device->Frequency;
- else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
- }
-#endif
-
- mDevice->FmtChans = DevFmtStereo;
- mDevice->FmtType = DevFmtShort;
-
- SetDefaultWFXChannelOrder(mDevice);
- mFrameSize = mDevice->frameSizeFromFmt();
-
-
- const std::array<SLInterfaceID,2> ids{{ SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }};
- const std::array<SLboolean,2> reqs{{ SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE }};
-
- loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
- loc_bufq.numBuffers = mDevice->BufferSize / mDevice->UpdateSize;
-
-#ifdef SL_ANDROID_DATAFORMAT_PCM_EX
- SLAndroidDataFormat_PCM_EX format_pcm{};
- format_pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
- format_pcm.numChannels = mDevice->channelsFromFmt();
- format_pcm.sampleRate = mDevice->Frequency * 1000;
- format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
- format_pcm.containerSize = format_pcm.bitsPerSample;
- format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
- format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
- SL_BYTEORDER_BIGENDIAN;
- format_pcm.representation = GetTypeRepresentation(mDevice->FmtType);
-#else
- SLDataFormat_PCM format_pcm{};
- format_pcm.formatType = SL_DATAFORMAT_PCM;
- format_pcm.numChannels = mDevice->channelsFromFmt();
- format_pcm.samplesPerSec = mDevice->Frequency * 1000;
- format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
- format_pcm.containerSize = format_pcm.bitsPerSample;
- format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
- format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
- SL_BYTEORDER_BIGENDIAN;
-#endif
-
- audioSrc.pLocator = &loc_bufq;
- audioSrc.pFormat = &format_pcm;
-
- loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
- loc_outmix.outputMix = mOutputMix;
- audioSnk.pLocator = &loc_outmix;
- audioSnk.pFormat = nullptr;
-
-
- result = VCALL(mEngine,CreateAudioPlayer)(&mBufferQueueObj, &audioSrc, &audioSnk, ids.size(),
- ids.data(), reqs.data());
- PRINTERR(result, "engine->CreateAudioPlayer");
- if(SL_RESULT_SUCCESS == result)
- {
- /* Set the stream type to "media" (games, music, etc), if possible. */
- SLAndroidConfigurationItf config;
- result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
- PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDCONFIGURATION");
- if(SL_RESULT_SUCCESS == result)
- {
- SLint32 streamType = SL_ANDROID_STREAM_MEDIA;
- result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_STREAM_TYPE, &streamType,
- sizeof(streamType));
- PRINTERR(result, "config->SetConfiguration");
- }
-
- /* Clear any error since this was optional. */
- result = SL_RESULT_SUCCESS;
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mBufferQueueObj,Realize)(SL_BOOLEAN_FALSE);
- PRINTERR(result, "bufferQueue->Realize");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- const ALuint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
- try {
- mRing = CreateRingBuffer(num_updates, mFrameSize*mDevice->UpdateSize, true);
- }
- catch(std::exception& e) {
- ERR("Failed allocating ring buffer %ux%ux%u: %s\n", mDevice->UpdateSize,
- num_updates, mFrameSize, e.what());
- result = SL_RESULT_MEMORY_FAILURE;
- }
- }
-
- if(SL_RESULT_SUCCESS != result)
- {
- if(mBufferQueueObj)
- VCALL0(mBufferQueueObj,Destroy)();
- mBufferQueueObj = nullptr;
-
- return ALC_FALSE;
- }
-
- return ALC_TRUE;
-}
-
-ALCboolean OpenSLPlayback::start()
-{
- mRing->reset();
-
- SLAndroidSimpleBufferQueueItf bufferQueue;
- SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
- &bufferQueue)};
- PRINTERR(result, "bufferQueue->GetInterface");
- if(SL_RESULT_SUCCESS != result)
- return ALC_FALSE;
-
- result = VCALL(bufferQueue,RegisterCallback)(&OpenSLPlayback::processC, this);
- PRINTERR(result, "bufferQueue->RegisterCallback");
- if(SL_RESULT_SUCCESS != result) return ALC_FALSE;
-
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread(std::mem_fn(&OpenSLPlayback::mixerProc), this);
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void OpenSLPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
-
- mSem.post();
- mThread.join();
-
- SLPlayItf player;
- SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player)};
- PRINTERR(result, "bufferQueue->GetInterface");
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
- PRINTERR(result, "player->SetPlayState");
- }
-
- SLAndroidSimpleBufferQueueItf bufferQueue;
- result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
- PRINTERR(result, "bufferQueue->GetInterface");
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL0(bufferQueue,Clear)();
- PRINTERR(result, "bufferQueue->Clear");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(bufferQueue,RegisterCallback)(nullptr, nullptr);
- PRINTERR(result, "bufferQueue->RegisterCallback");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- SLAndroidSimpleBufferQueueState state;
- do {
- std::this_thread::yield();
- result = VCALL(bufferQueue,GetState)(&state);
- } while(SL_RESULT_SUCCESS == result && state.count > 0);
- PRINTERR(result, "bufferQueue->GetState");
- }
-}
-
-ClockLatency OpenSLPlayback::getClockLatency()
-{
- ClockLatency ret;
-
- lock();
- ret.ClockTime = GetDeviceClockTime(mDevice);
- ret.Latency = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize};
- ret.Latency /= mDevice->Frequency;
- unlock();
-
- return ret;
-}
-
-
-struct OpenSLCapture final : public BackendBase {
- OpenSLCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~OpenSLCapture() override;
-
- static void processC(SLAndroidSimpleBufferQueueItf bq, void *context);
- void process(SLAndroidSimpleBufferQueueItf bq);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- /* engine interfaces */
- SLObjectItf mEngineObj{nullptr};
- SLEngineItf mEngine;
-
- /* recording interfaces */
- SLObjectItf mRecordObj{nullptr};
-
- RingBufferPtr mRing{nullptr};
- ALCuint mSplOffset{0u};
-
- ALsizei mFrameSize{0};
-
- DEF_NEWDEL(OpenSLCapture)
-};
-
-OpenSLCapture::~OpenSLCapture()
-{
- if(mRecordObj)
- VCALL0(mRecordObj,Destroy)();
- mRecordObj = nullptr;
-
- if(mEngineObj)
- VCALL0(mEngineObj,Destroy)();
- mEngineObj = nullptr;
- mEngine = nullptr;
-}
-
-
-void OpenSLCapture::processC(SLAndroidSimpleBufferQueueItf bq, void *context)
-{ static_cast<OpenSLCapture*>(context)->process(bq); }
-
-void OpenSLCapture::process(SLAndroidSimpleBufferQueueItf)
-{
- /* A new chunk has been written into the ring buffer, advance it. */
- mRing->writeAdvance(1);
-}
-
-
-ALCenum OpenSLCapture::open(const ALCchar* name)
-{
- if(!name)
- name = opensl_device;
- else if(strcmp(name, opensl_device) != 0)
- return ALC_INVALID_VALUE;
-
- SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
- PRINTERR(result, "slCreateEngine");
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mEngineObj,Realize)(SL_BOOLEAN_FALSE);
- PRINTERR(result, "engine->Realize");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mEngineObj,GetInterface)(SL_IID_ENGINE, &mEngine);
- PRINTERR(result, "engine->GetInterface");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- mFrameSize = mDevice->frameSizeFromFmt();
- /* Ensure the total length is at least 100ms */
- ALsizei length{maxi(mDevice->BufferSize, mDevice->Frequency/10)};
- /* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
- ALsizei update_len{clampi(mDevice->BufferSize/3, mDevice->Frequency/100,
- mDevice->Frequency/100*5)};
- ALsizei num_updates{(length+update_len-1) / update_len};
-
- try {
- mRing = CreateRingBuffer(num_updates, update_len*mFrameSize, false);
-
- mDevice->UpdateSize = update_len;
- mDevice->BufferSize = mRing->writeSpace() * update_len;
- }
- catch(std::exception& e) {
- ERR("Failed to allocate ring buffer: %s\n", e.what());
- result = SL_RESULT_MEMORY_FAILURE;
- }
- }
- if(SL_RESULT_SUCCESS == result)
- {
- const std::array<SLInterfaceID,2> ids{{ SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }};
- const std::array<SLboolean,2> reqs{{ SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE }};
-
- SLDataLocator_IODevice loc_dev{};
- loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
- loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
- loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
- loc_dev.device = nullptr;
-
- SLDataSource audioSrc{};
- audioSrc.pLocator = &loc_dev;
- audioSrc.pFormat = nullptr;
-
- SLDataLocator_AndroidSimpleBufferQueue loc_bq{};
- loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
- loc_bq.numBuffers = mDevice->BufferSize / mDevice->UpdateSize;
-
-#ifdef SL_ANDROID_DATAFORMAT_PCM_EX
- SLAndroidDataFormat_PCM_EX format_pcm{};
- format_pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
- format_pcm.numChannels = mDevice->channelsFromFmt();
- format_pcm.sampleRate = mDevice->Frequency * 1000;
- format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
- format_pcm.containerSize = format_pcm.bitsPerSample;
- format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
- format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
- format_pcm.representation = GetTypeRepresentation(mDevice->FmtType);
-#else
- SLDataFormat_PCM format_pcm{};
- format_pcm.formatType = SL_DATAFORMAT_PCM;
- format_pcm.numChannels = mDevice->channelsFromFmt();
- format_pcm.samplesPerSec = mDevice->Frequency * 1000;
- format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
- format_pcm.containerSize = format_pcm.bitsPerSample;
- format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
- format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
-#endif
-
- SLDataSink audioSnk{};
- audioSnk.pLocator = &loc_bq;
- audioSnk.pFormat = &format_pcm;
-
- result = VCALL(mEngine,CreateAudioRecorder)(&mRecordObj, &audioSrc, &audioSnk,
- ids.size(), ids.data(), reqs.data());
- PRINTERR(result, "engine->CreateAudioRecorder");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- /* Set the record preset to "generic", if possible. */
- SLAndroidConfigurationItf config;
- result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
- PRINTERR(result, "recordObj->GetInterface SL_IID_ANDROIDCONFIGURATION");
- if(SL_RESULT_SUCCESS == result)
- {
- SLuint32 preset = SL_ANDROID_RECORDING_PRESET_GENERIC;
- result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_RECORDING_PRESET, &preset,
- sizeof(preset));
- PRINTERR(result, "config->SetConfiguration");
- }
-
- /* Clear any error since this was optional. */
- result = SL_RESULT_SUCCESS;
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mRecordObj,Realize)(SL_BOOLEAN_FALSE);
- PRINTERR(result, "recordObj->Realize");
- }
-
- SLAndroidSimpleBufferQueueItf bufferQueue;
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
- PRINTERR(result, "recordObj->GetInterface");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(bufferQueue,RegisterCallback)(&OpenSLCapture::processC, this);
- PRINTERR(result, "bufferQueue->RegisterCallback");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- const ALuint chunk_size{mDevice->UpdateSize * mFrameSize};
-
- auto data = mRing->getWriteVector();
- for(size_t i{0u};i < data.first.len && SL_RESULT_SUCCESS == result;i++)
- {
- result = VCALL(bufferQueue,Enqueue)(data.first.buf + chunk_size*i, chunk_size);
- PRINTERR(result, "bufferQueue->Enqueue");
- }
- for(size_t i{0u};i < data.second.len && SL_RESULT_SUCCESS == result;i++)
- {
- result = VCALL(bufferQueue,Enqueue)(data.second.buf + chunk_size*i, chunk_size);
- PRINTERR(result, "bufferQueue->Enqueue");
- }
- }
-
- if(SL_RESULT_SUCCESS != result)
- {
- if(mRecordObj)
- VCALL0(mRecordObj,Destroy)();
- mRecordObj = nullptr;
-
- if(mEngineObj)
- VCALL0(mEngineObj,Destroy)();
- mEngineObj = nullptr;
- mEngine = nullptr;
-
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean OpenSLCapture::start()
-{
- SLRecordItf record;
- SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_RECORD, &record)};
- PRINTERR(result, "recordObj->GetInterface");
-
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(record,SetRecordState)(SL_RECORDSTATE_RECORDING);
- PRINTERR(result, "record->SetRecordState");
- }
-
- if(SL_RESULT_SUCCESS != result)
- {
- aluHandleDisconnect(mDevice, "Failed to start capture: 0x%08x", result);
- return ALC_FALSE;
- }
-
- return ALC_TRUE;
-}
-
-void OpenSLCapture::stop()
-{
- SLRecordItf record;
- SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_RECORD, &record)};
- PRINTERR(result, "recordObj->GetInterface");
-
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(record,SetRecordState)(SL_RECORDSTATE_PAUSED);
- PRINTERR(result, "record->SetRecordState");
- }
-}
-
-ALCenum OpenSLCapture::captureSamples(void* buffer, ALCuint samples)
-{
- ALsizei chunk_size = mDevice->UpdateSize * mFrameSize;
- SLAndroidSimpleBufferQueueItf bufferQueue;
- SLresult result;
- ALCuint i;
-
- result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
- PRINTERR(result, "recordObj->GetInterface");
-
- /* Read the desired samples from the ring buffer then advance its read
- * pointer.
- */
- auto data = mRing->getReadVector();
- for(i = 0;i < samples;)
- {
- ALCuint rem{minu(samples - i, mDevice->UpdateSize - mSplOffset)};
- memcpy((ALCbyte*)buffer + i*mFrameSize, data.first.buf + mSplOffset*mFrameSize,
- rem * mFrameSize);
-
- mSplOffset += rem;
- if(mSplOffset == mDevice->UpdateSize)
- {
- /* Finished a chunk, reset the offset and advance the read pointer. */
- mSplOffset = 0;
-
- mRing->readAdvance(1);
- result = VCALL(bufferQueue,Enqueue)(data.first.buf, chunk_size);
- PRINTERR(result, "bufferQueue->Enqueue");
- if(SL_RESULT_SUCCESS != result) break;
-
- data.first.len--;
- if(!data.first.len)
- data.first = data.second;
- else
- data.first.buf += chunk_size;
- }
-
- i += rem;
- }
-
- if(SL_RESULT_SUCCESS != result)
- {
- aluHandleDisconnect(mDevice, "Failed to update capture buffer: 0x%08x", result);
- return ALC_INVALID_DEVICE;
- }
-
- return ALC_NO_ERROR;
-}
-
-ALCuint OpenSLCapture::availableSamples()
-{ return mRing->readSpace()*mDevice->UpdateSize - mSplOffset; }
-
-} // namespace
-
-bool OSLBackendFactory::init() { return true; }
-
-bool OSLBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void OSLBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- case DevProbe::Capture:
- /* Includes null char. */
- outnames->append(opensl_device, sizeof(opensl_device));
- break;
- }
-}
-
-BackendPtr OSLBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new OpenSLPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new OpenSLCapture{device}};
- return nullptr;
-}
-
-BackendFactory &OSLBackendFactory::getFactory()
-{
- static OSLBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/opensl.h b/Alc/backends/opensl.h
deleted file mode 100644
index 809aa339..00000000
--- a/Alc/backends/opensl.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_OSL_H
-#define BACKENDS_OSL_H
-
-#include "backends/base.h"
-
-struct OSLBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_OSL_H */
diff --git a/Alc/backends/oss.cpp b/Alc/backends/oss.cpp
deleted file mode 100644
index 8cfe9e96..00000000
--- a/Alc/backends/oss.cpp
+++ /dev/null
@@ -1,751 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/oss.h"
-
-#include <fcntl.h>
-#include <poll.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <atomic>
-#include <cerrno>
-#include <cstdio>
-#include <cstring>
-#include <exception>
-#include <functional>
-#include <memory>
-#include <new>
-#include <string>
-#include <thread>
-#include <utility>
-
-#include "AL/al.h"
-
-#include "alcmain.h"
-#include "alconfig.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-#include "aloptional.h"
-#include "alu.h"
-#include "logging.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "vector.h"
-
-#include <sys/soundcard.h>
-
-/*
- * The OSS documentation talks about SOUND_MIXER_READ, but the header
- * only contains MIXER_READ. Play safe. Same for WRITE.
- */
-#ifndef SOUND_MIXER_READ
-#define SOUND_MIXER_READ MIXER_READ
-#endif
-#ifndef SOUND_MIXER_WRITE
-#define SOUND_MIXER_WRITE MIXER_WRITE
-#endif
-
-#if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
-#define ALC_OSS_COMPAT
-#endif
-#ifndef SNDCTL_AUDIOINFO
-#define ALC_OSS_COMPAT
-#endif
-
-/*
- * FreeBSD strongly discourages the use of specific devices,
- * such as those returned in oss_audioinfo.devnode
- */
-#ifdef __FreeBSD__
-#define ALC_OSS_DEVNODE_TRUC
-#endif
-
-namespace {
-
-constexpr char DefaultName[] = "OSS Default";
-std::string DefaultPlayback{"/dev/dsp"};
-std::string DefaultCapture{"/dev/dsp"};
-
-struct DevMap {
- std::string name;
- std::string device_name;
-};
-
-bool checkName(const al::vector<DevMap> &list, const std::string &name)
-{
- return std::find_if(list.cbegin(), list.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- ) != list.cend();
-}
-
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
-
-
-#ifdef ALC_OSS_COMPAT
-
-#define DSP_CAP_OUTPUT 0x00020000
-#define DSP_CAP_INPUT 0x00010000
-void ALCossListPopulate(al::vector<DevMap> *devlist, int type)
-{
- devlist->emplace_back(DevMap{DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback});
-}
-
-#else
-
-void ALCossListAppend(al::vector<DevMap> *list, const char *handle, size_t hlen, const char *path, size_t plen)
-{
-#ifdef ALC_OSS_DEVNODE_TRUC
- for(size_t i{0};i < plen;i++)
- {
- if(path[i] == '.')
- {
- if(strncmp(path + i, handle + hlen + i - plen, plen - i) == 0)
- hlen = hlen + i - plen;
- plen = i;
- }
- }
-#endif
- if(handle[0] == '\0')
- {
- handle = path;
- hlen = plen;
- }
-
- std::string basename{handle, hlen};
- basename.erase(std::find(basename.begin(), basename.end(), '\0'), basename.end());
- std::string devname{path, plen};
- devname.erase(std::find(devname.begin(), devname.end(), '\0'), devname.end());
-
- auto iter = std::find_if(list->cbegin(), list->cend(),
- [&devname](const DevMap &entry) -> bool
- { return entry.device_name == devname; }
- );
- if(iter != list->cend())
- return;
-
- int count{1};
- std::string newname{basename};
- while(checkName(PlaybackDevices, newname))
- {
- newname = basename;
- newname += " #";
- newname += std::to_string(++count);
- }
-
- list->emplace_back(DevMap{std::move(newname), std::move(devname)});
- const DevMap &entry = list->back();
-
- TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
-}
-
-void ALCossListPopulate(al::vector<DevMap> *devlist, int type_flag)
-{
- int fd{open("/dev/mixer", O_RDONLY)};
- if(fd < 0)
- {
- TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
- goto done;
- }
-
- oss_sysinfo si;
- if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
- {
- TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
- goto done;
- }
-
- for(int i{0};i < si.numaudios;i++)
- {
- oss_audioinfo ai;
- ai.dev = i;
- if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
- {
- ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
- continue;
- }
- if(!(ai.caps&type_flag) || ai.devnode[0] == '\0')
- continue;
-
- const char *handle;
- size_t len;
- if(ai.handle[0] != '\0')
- {
- len = strnlen(ai.handle, sizeof(ai.handle));
- handle = ai.handle;
- }
- else
- {
- len = strnlen(ai.name, sizeof(ai.name));
- handle = ai.name;
- }
-
- ALCossListAppend(devlist, handle, len, ai.devnode,
- strnlen(ai.devnode, sizeof(ai.devnode)));
- }
-
-done:
- if(fd >= 0)
- close(fd);
- fd = -1;
-
- const char *defdev{((type_flag==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback).c_str()};
- auto iter = std::find_if(devlist->cbegin(), devlist->cend(),
- [defdev](const DevMap &entry) -> bool
- { return entry.device_name == defdev; }
- );
- if(iter == devlist->cend())
- devlist->insert(devlist->begin(), DevMap{DefaultName, defdev});
- else
- {
- DevMap entry{std::move(*iter)};
- devlist->erase(iter);
- devlist->insert(devlist->begin(), std::move(entry));
- }
- devlist->shrink_to_fit();
-}
-
-#endif
-
-int log2i(ALCuint x)
-{
- int y = 0;
- while (x > 1)
- {
- x >>= 1;
- y++;
- }
- return y;
-}
-
-
-struct OSSPlayback final : public BackendBase {
- OSSPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~OSSPlayback() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- int mFd{-1};
-
- al::vector<ALubyte> mMixData;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(OSSPlayback)
-};
-
-OSSPlayback::~OSSPlayback()
-{
- if(mFd != -1)
- close(mFd);
- mFd = -1;
-}
-
-
-int OSSPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- const int frame_size{mDevice->frameSizeFromFmt()};
-
- lock();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- pollfd pollitem{};
- pollitem.fd = mFd;
- pollitem.events = POLLOUT;
-
- unlock();
- int pret{poll(&pollitem, 1, 1000)};
- lock();
- if(pret < 0)
- {
- if(errno == EINTR || errno == EAGAIN)
- continue;
- ERR("poll failed: %s\n", strerror(errno));
- aluHandleDisconnect(mDevice, "Failed waiting for playback buffer: %s", strerror(errno));
- break;
- }
- else if(pret == 0)
- {
- WARN("poll timeout\n");
- continue;
- }
-
- ALubyte *write_ptr{mMixData.data()};
- size_t to_write{mMixData.size()};
- aluMixData(mDevice, write_ptr, to_write/frame_size);
- while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
- {
- ssize_t wrote{write(mFd, write_ptr, to_write)};
- if(wrote < 0)
- {
- if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
- continue;
- ERR("write failed: %s\n", strerror(errno));
- aluHandleDisconnect(mDevice, "Failed writing playback samples: %s",
- strerror(errno));
- break;
- }
-
- to_write -= wrote;
- write_ptr += wrote;
- }
- }
- unlock();
-
- return 0;
-}
-
-
-ALCenum OSSPlayback::open(const ALCchar *name)
-{
- const char *devname{DefaultPlayback.c_str()};
- if(!name)
- name = DefaultName;
- else
- {
- if(PlaybackDevices.empty())
- ALCossListPopulate(&PlaybackDevices, DSP_CAP_OUTPUT);
-
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == PlaybackDevices.cend())
- return ALC_INVALID_VALUE;
- devname = iter->device_name.c_str();
- }
-
- mFd = ::open(devname, O_WRONLY);
- if(mFd == -1)
- {
- ERR("Could not open %s: %s\n", devname, strerror(errno));
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean OSSPlayback::reset()
-{
- int numFragmentsLogSize;
- int log2FragmentSize;
- unsigned int periods;
- audio_buf_info info;
- ALuint frameSize;
- int numChannels;
- int ossFormat;
- int ossSpeed;
- const char *err;
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- ossFormat = AFMT_S8;
- break;
- case DevFmtUByte:
- ossFormat = AFMT_U8;
- break;
- case DevFmtUShort:
- case DevFmtInt:
- case DevFmtUInt:
- case DevFmtFloat:
- mDevice->FmtType = DevFmtShort;
- /* fall-through */
- case DevFmtShort:
- ossFormat = AFMT_S16_NE;
- break;
- }
-
- periods = mDevice->BufferSize / mDevice->UpdateSize;
- numChannels = mDevice->channelsFromFmt();
- ossSpeed = mDevice->Frequency;
- frameSize = numChannels * mDevice->bytesFromFmt();
- /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
- log2FragmentSize = maxi(log2i(mDevice->UpdateSize*frameSize), 4);
- numFragmentsLogSize = (periods << 16) | log2FragmentSize;
-
-#define CHECKERR(func) if((func) < 0) { \
- err = #func; \
- goto err; \
-}
- /* Don't fail if SETFRAGMENT fails. We can handle just about anything
- * that's reported back via GETOSPACE */
- ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
- CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_GETOSPACE, &info));
- if(0)
- {
- err:
- ERR("%s failed: %s\n", err, strerror(errno));
- return ALC_FALSE;
- }
-#undef CHECKERR
-
- if(mDevice->channelsFromFmt() != numChannels)
- {
- ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
- numChannels);
- return ALC_FALSE;
- }
-
- if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte) ||
- (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
- (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
- {
- ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType),
- ossFormat);
- return ALC_FALSE;
- }
-
- mDevice->Frequency = ossSpeed;
- mDevice->UpdateSize = info.fragsize / frameSize;
- mDevice->BufferSize = info.fragments * mDevice->UpdateSize;
-
- SetDefaultChannelOrder(mDevice);
-
- mMixData.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
-
- return ALC_TRUE;
-}
-
-ALCboolean OSSPlayback::start()
-{
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&OSSPlayback::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void OSSPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
- ERR("Error resetting device: %s\n", strerror(errno));
-}
-
-
-struct OSScapture final : public BackendBase {
- OSScapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~OSScapture() override;
-
- int recordProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- int mFd{-1};
-
- RingBufferPtr mRing{nullptr};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(OSScapture)
-};
-
-OSScapture::~OSScapture()
-{
- if(mFd != -1)
- close(mFd);
- mFd = -1;
-}
-
-
-int OSScapture::recordProc()
-{
- SetRTPriority();
- althrd_setname(RECORD_THREAD_NAME);
-
- const int frame_size{mDevice->frameSizeFromFmt()};
- while(!mKillNow.load(std::memory_order_acquire))
- {
- pollfd pollitem{};
- pollitem.fd = mFd;
- pollitem.events = POLLIN;
-
- int sret{poll(&pollitem, 1, 1000)};
- if(sret < 0)
- {
- if(errno == EINTR || errno == EAGAIN)
- continue;
- ERR("poll failed: %s\n", strerror(errno));
- aluHandleDisconnect(mDevice, "Failed to check capture samples: %s", strerror(errno));
- break;
- }
- else if(sret == 0)
- {
- WARN("poll timeout\n");
- continue;
- }
-
- auto vec = mRing->getWriteVector();
- if(vec.first.len > 0)
- {
- ssize_t amt{read(mFd, vec.first.buf, vec.first.len*frame_size)};
- if(amt < 0)
- {
- ERR("read failed: %s\n", strerror(errno));
- aluHandleDisconnect(mDevice, "Failed reading capture samples: %s",
- strerror(errno));
- break;
- }
- mRing->writeAdvance(amt/frame_size);
- }
- }
-
- return 0;
-}
-
-
-ALCenum OSScapture::open(const ALCchar *name)
-{
- const char *devname{DefaultCapture.c_str()};
- if(!name)
- name = DefaultName;
- else
- {
- if(CaptureDevices.empty())
- ALCossListPopulate(&CaptureDevices, DSP_CAP_INPUT);
-
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == CaptureDevices.cend())
- return ALC_INVALID_VALUE;
- devname = iter->device_name.c_str();
- }
-
- mFd = ::open(devname, O_RDONLY);
- if(mFd == -1)
- {
- ERR("Could not open %s: %s\n", devname, strerror(errno));
- return ALC_INVALID_VALUE;
- }
-
- int ossFormat{};
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- ossFormat = AFMT_S8;
- break;
- case DevFmtUByte:
- ossFormat = AFMT_U8;
- break;
- case DevFmtShort:
- ossFormat = AFMT_S16_NE;
- break;
- case DevFmtUShort:
- case DevFmtInt:
- case DevFmtUInt:
- case DevFmtFloat:
- ERR("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
- return ALC_INVALID_VALUE;
- }
-
- int periods{4};
- int numChannels{mDevice->channelsFromFmt()};
- int frameSize{numChannels * mDevice->bytesFromFmt()};
- int ossSpeed{static_cast<int>(mDevice->Frequency)};
- int log2FragmentSize{log2i(mDevice->BufferSize * frameSize / periods)};
-
- /* according to the OSS spec, 16 bytes are the minimum */
- log2FragmentSize = std::max(log2FragmentSize, 4);
- int numFragmentsLogSize{(periods << 16) | log2FragmentSize};
-
- audio_buf_info info;
- const char *err;
-#define CHECKERR(func) if((func) < 0) { \
- err = #func; \
- goto err; \
-}
- CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_GETISPACE, &info));
- if(0)
- {
- err:
- ERR("%s failed: %s\n", err, strerror(errno));
- close(mFd);
- mFd = -1;
- return ALC_INVALID_VALUE;
- }
-#undef CHECKERR
-
- if(mDevice->channelsFromFmt() != numChannels)
- {
- ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
- numChannels);
- close(mFd);
- mFd = -1;
- return ALC_INVALID_VALUE;
- }
-
- if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte) ||
- (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
- (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
- {
- ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType), ossFormat);
- close(mFd);
- mFd = -1;
- return ALC_INVALID_VALUE;
- }
-
- mRing = CreateRingBuffer(mDevice->BufferSize, frameSize, false);
- if(!mRing)
- {
- ERR("Ring buffer create failed\n");
- close(mFd);
- mFd = -1;
- return ALC_OUT_OF_MEMORY;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean OSScapture::start()
-{
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&OSScapture::recordProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create record thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void OSScapture::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
- ERR("Error resetting device: %s\n", strerror(errno));
-}
-
-ALCenum OSScapture::captureSamples(ALCvoid *buffer, ALCuint samples)
-{
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
-}
-
-ALCuint OSScapture::availableSamples()
-{ return mRing->readSpace(); }
-
-} // namespace
-
-
-BackendFactory &OSSBackendFactory::getFactory()
-{
- static OSSBackendFactory factory{};
- return factory;
-}
-
-bool OSSBackendFactory::init()
-{
- if(auto devopt = ConfigValueStr(nullptr, "oss", "device"))
- DefaultPlayback = std::move(*devopt);
- if(auto capopt = ConfigValueStr(nullptr, "oss", "capture"))
- DefaultCapture = std::move(*capopt);
-
- return true;
-}
-
-bool OSSBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void OSSBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const DevMap &entry) -> void
- {
-#ifdef HAVE_STAT
- struct stat buf;
- if(stat(entry.device_name.c_str(), &buf) == 0)
-#endif
- {
- /* Includes null char. */
- outnames->append(entry.name.c_str(), entry.name.length()+1);
- }
- };
-
- switch(type)
- {
- case DevProbe::Playback:
- PlaybackDevices.clear();
- ALCossListPopulate(&PlaybackDevices, DSP_CAP_OUTPUT);
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- CaptureDevices.clear();
- ALCossListPopulate(&CaptureDevices, DSP_CAP_INPUT);
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
-}
-
-BackendPtr OSSBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new OSSPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new OSScapture{device}};
- return nullptr;
-}
diff --git a/Alc/backends/oss.h b/Alc/backends/oss.h
deleted file mode 100644
index 9e63d7b6..00000000
--- a/Alc/backends/oss.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_OSS_H
-#define BACKENDS_OSS_H
-
-#include "backends/base.h"
-
-struct OSSBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_OSS_H */
diff --git a/Alc/backends/portaudio.cpp b/Alc/backends/portaudio.cpp
deleted file mode 100644
index 73e972c5..00000000
--- a/Alc/backends/portaudio.cpp
+++ /dev/null
@@ -1,463 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/portaudio.h"
-
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "alconfig.h"
-#include "ringbuffer.h"
-#include "compat.h"
-
-#include <portaudio.h>
-
-
-namespace {
-
-constexpr ALCchar pa_device[] = "PortAudio Default";
-
-
-#ifdef HAVE_DYNLOAD
-void *pa_handle;
-#define MAKE_FUNC(x) decltype(x) * p##x
-MAKE_FUNC(Pa_Initialize);
-MAKE_FUNC(Pa_Terminate);
-MAKE_FUNC(Pa_GetErrorText);
-MAKE_FUNC(Pa_StartStream);
-MAKE_FUNC(Pa_StopStream);
-MAKE_FUNC(Pa_OpenStream);
-MAKE_FUNC(Pa_CloseStream);
-MAKE_FUNC(Pa_GetDefaultOutputDevice);
-MAKE_FUNC(Pa_GetDefaultInputDevice);
-MAKE_FUNC(Pa_GetStreamInfo);
-#undef MAKE_FUNC
-
-#ifndef IN_IDE_PARSER
-#define Pa_Initialize pPa_Initialize
-#define Pa_Terminate pPa_Terminate
-#define Pa_GetErrorText pPa_GetErrorText
-#define Pa_StartStream pPa_StartStream
-#define Pa_StopStream pPa_StopStream
-#define Pa_OpenStream pPa_OpenStream
-#define Pa_CloseStream pPa_CloseStream
-#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
-#define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
-#define Pa_GetStreamInfo pPa_GetStreamInfo
-#endif
-#endif
-
-
-struct PortPlayback final : public BackendBase {
- PortPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~PortPlayback() override;
-
- static int writeCallbackC(const void *inputBuffer, void *outputBuffer,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
- const PaStreamCallbackFlags statusFlags, void *userData);
- int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
- const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- PaStream *mStream{nullptr};
- PaStreamParameters mParams{};
- ALuint mUpdateSize{0u};
-
- DEF_NEWDEL(PortPlayback)
-};
-
-PortPlayback::~PortPlayback()
-{
- PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
- if(err != paNoError)
- ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
- mStream = nullptr;
-}
-
-
-int PortPlayback::writeCallbackC(const void *inputBuffer, void *outputBuffer,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
- const PaStreamCallbackFlags statusFlags, void *userData)
-{
- return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
- framesPerBuffer, timeInfo, statusFlags);
-}
-
-int PortPlayback::writeCallback(const void*, void *outputBuffer,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo*,
- const PaStreamCallbackFlags)
-{
- lock();
- aluMixData(mDevice, outputBuffer, framesPerBuffer);
- unlock();
- return 0;
-}
-
-
-ALCenum PortPlayback::open(const ALCchar *name)
-{
- if(!name)
- name = pa_device;
- else if(strcmp(name, pa_device) != 0)
- return ALC_INVALID_VALUE;
-
- mUpdateSize = mDevice->UpdateSize;
-
- auto devidopt = ConfigValueInt(nullptr, "port", "device");
- if(devidopt && *devidopt >= 0) mParams.device = *devidopt;
- else mParams.device = Pa_GetDefaultOutputDevice();
- mParams.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
- mParams.hostApiSpecificStreamInfo = nullptr;
-
- mParams.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mParams.sampleFormat = paInt8;
- break;
- case DevFmtUByte:
- mParams.sampleFormat = paUInt8;
- break;
- case DevFmtUShort:
- /* fall-through */
- case DevFmtShort:
- mParams.sampleFormat = paInt16;
- break;
- case DevFmtUInt:
- /* fall-through */
- case DevFmtInt:
- mParams.sampleFormat = paInt32;
- break;
- case DevFmtFloat:
- mParams.sampleFormat = paFloat32;
- break;
- }
-
-retry_open:
- PaError err{Pa_OpenStream(&mStream, nullptr, &mParams, mDevice->Frequency, mDevice->UpdateSize,
- paNoFlag, &PortPlayback::writeCallbackC, this)};
- if(err != paNoError)
- {
- if(mParams.sampleFormat == paFloat32)
- {
- mParams.sampleFormat = paInt16;
- goto retry_open;
- }
- ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-
-}
-
-ALCboolean PortPlayback::reset()
-{
- const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)};
- mDevice->Frequency = streamInfo->sampleRate;
- mDevice->UpdateSize = mUpdateSize;
-
- if(mParams.sampleFormat == paInt8)
- mDevice->FmtType = DevFmtByte;
- else if(mParams.sampleFormat == paUInt8)
- mDevice->FmtType = DevFmtUByte;
- else if(mParams.sampleFormat == paInt16)
- mDevice->FmtType = DevFmtShort;
- else if(mParams.sampleFormat == paInt32)
- mDevice->FmtType = DevFmtInt;
- else if(mParams.sampleFormat == paFloat32)
- mDevice->FmtType = DevFmtFloat;
- else
- {
- ERR("Unexpected sample format: 0x%lx\n", mParams.sampleFormat);
- return ALC_FALSE;
- }
-
- if(mParams.channelCount == 2)
- mDevice->FmtChans = DevFmtStereo;
- else if(mParams.channelCount == 1)
- mDevice->FmtChans = DevFmtMono;
- else
- {
- ERR("Unexpected channel count: %u\n", mParams.channelCount);
- return ALC_FALSE;
- }
- SetDefaultChannelOrder(mDevice);
-
- return ALC_TRUE;
-}
-
-ALCboolean PortPlayback::start()
-{
- PaError err{Pa_StartStream(mStream)};
- if(err != paNoError)
- {
- ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
- return ALC_FALSE;
- }
- return ALC_TRUE;
-}
-
-void PortPlayback::stop()
-{
- PaError err{Pa_StopStream(mStream)};
- if(err != paNoError)
- ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
-}
-
-
-struct PortCapture final : public BackendBase {
- PortCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~PortCapture() override;
-
- static int readCallbackC(const void *inputBuffer, void *outputBuffer,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
- const PaStreamCallbackFlags statusFlags, void *userData);
- int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
- const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- PaStream *mStream{nullptr};
- PaStreamParameters mParams;
-
- RingBufferPtr mRing{nullptr};
-
- DEF_NEWDEL(PortCapture)
-};
-
-PortCapture::~PortCapture()
-{
- PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
- if(err != paNoError)
- ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
- mStream = nullptr;
-}
-
-
-int PortCapture::readCallbackC(const void *inputBuffer, void *outputBuffer,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
- const PaStreamCallbackFlags statusFlags, void* userData)
-{
- return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
- framesPerBuffer, timeInfo, statusFlags);
-}
-
-int PortCapture::readCallback(const void *inputBuffer, void*,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo*,
- const PaStreamCallbackFlags)
-{
- mRing->write(inputBuffer, framesPerBuffer);
- return 0;
-}
-
-
-ALCenum PortCapture::open(const ALCchar *name)
-{
- if(!name)
- name = pa_device;
- else if(strcmp(name, pa_device) != 0)
- return ALC_INVALID_VALUE;
-
- ALuint samples{mDevice->BufferSize};
- samples = maxu(samples, 100 * mDevice->Frequency / 1000);
- ALsizei frame_size{mDevice->frameSizeFromFmt()};
-
- mRing = CreateRingBuffer(samples, frame_size, false);
- if(!mRing) return ALC_INVALID_VALUE;
-
- auto devidopt = ConfigValueInt(nullptr, "port", "capture");
- if(devidopt && *devidopt >= 0) mParams.device = *devidopt;
- else mParams.device = Pa_GetDefaultOutputDevice();
- mParams.suggestedLatency = 0.0f;
- mParams.hostApiSpecificStreamInfo = nullptr;
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mParams.sampleFormat = paInt8;
- break;
- case DevFmtUByte:
- mParams.sampleFormat = paUInt8;
- break;
- case DevFmtShort:
- mParams.sampleFormat = paInt16;
- break;
- case DevFmtInt:
- mParams.sampleFormat = paInt32;
- break;
- case DevFmtFloat:
- mParams.sampleFormat = paFloat32;
- break;
- case DevFmtUInt:
- case DevFmtUShort:
- ERR("%s samples not supported\n", DevFmtTypeString(mDevice->FmtType));
- return ALC_INVALID_VALUE;
- }
- mParams.channelCount = mDevice->channelsFromFmt();
-
- PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->Frequency,
- paFramesPerBufferUnspecified, paNoFlag, &PortCapture::readCallbackC, this)};
- if(err != paNoError)
- {
- ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-
-ALCboolean PortCapture::start()
-{
- PaError err{Pa_StartStream(mStream)};
- if(err != paNoError)
- {
- ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
- return ALC_FALSE;
- }
- return ALC_TRUE;
-}
-
-void PortCapture::stop()
-{
- PaError err{Pa_StopStream(mStream)};
- if(err != paNoError)
- ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
-}
-
-
-ALCuint PortCapture::availableSamples()
-{ return mRing->readSpace(); }
-
-ALCenum PortCapture::captureSamples(ALCvoid *buffer, ALCuint samples)
-{
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
-}
-
-} // namespace
-
-
-bool PortBackendFactory::init()
-{
- PaError err;
-
-#ifdef HAVE_DYNLOAD
- if(!pa_handle)
- {
-#ifdef _WIN32
-# define PALIB "portaudio.dll"
-#elif defined(__APPLE__) && defined(__MACH__)
-# define PALIB "libportaudio.2.dylib"
-#elif defined(__OpenBSD__)
-# define PALIB "libportaudio.so"
-#else
-# define PALIB "libportaudio.so.2"
-#endif
-
- pa_handle = LoadLib(PALIB);
- if(!pa_handle)
- return false;
-
-#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pa_handle, #f)); \
- if(p##f == nullptr) \
- { \
- CloseLib(pa_handle); \
- pa_handle = nullptr; \
- return false; \
- } \
-} while(0)
- LOAD_FUNC(Pa_Initialize);
- LOAD_FUNC(Pa_Terminate);
- LOAD_FUNC(Pa_GetErrorText);
- LOAD_FUNC(Pa_StartStream);
- LOAD_FUNC(Pa_StopStream);
- LOAD_FUNC(Pa_OpenStream);
- LOAD_FUNC(Pa_CloseStream);
- LOAD_FUNC(Pa_GetDefaultOutputDevice);
- LOAD_FUNC(Pa_GetDefaultInputDevice);
- LOAD_FUNC(Pa_GetStreamInfo);
-#undef LOAD_FUNC
-
- if((err=Pa_Initialize()) != paNoError)
- {
- ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
- CloseLib(pa_handle);
- pa_handle = nullptr;
- return false;
- }
- }
-#else
- if((err=Pa_Initialize()) != paNoError)
- {
- ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
- return false;
- }
-#endif
- return true;
-}
-
-bool PortBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void PortBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- case DevProbe::Capture:
- /* Includes null char. */
- outnames->append(pa_device, sizeof(pa_device));
- break;
- }
-}
-
-BackendPtr PortBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new PortPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new PortCapture{device}};
- return nullptr;
-}
-
-BackendFactory &PortBackendFactory::getFactory()
-{
- static PortBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/portaudio.h b/Alc/backends/portaudio.h
deleted file mode 100644
index 082e9020..00000000
--- a/Alc/backends/portaudio.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_PORTAUDIO_H
-#define BACKENDS_PORTAUDIO_H
-
-#include "backends/base.h"
-
-struct PortBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_PORTAUDIO_H */
diff --git a/Alc/backends/pulseaudio.cpp b/Alc/backends/pulseaudio.cpp
deleted file mode 100644
index da209c8d..00000000
--- a/Alc/backends/pulseaudio.cpp
+++ /dev/null
@@ -1,1532 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2009 by Konstantinos Natsakis <[email protected]>
- * Copyright (C) 2010 by Chris Robinson <[email protected]>
- * 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 "backends/pulseaudio.h"
-
-#include <poll.h>
-#include <cstring>
-
-#include <array>
-#include <string>
-#include <vector>
-#include <atomic>
-#include <thread>
-#include <algorithm>
-#include <condition_variable>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "alconfig.h"
-#include "compat.h"
-#include "alexcpt.h"
-
-#include <pulse/pulseaudio.h>
-
-
-namespace {
-
-#ifdef HAVE_DYNLOAD
-#define PULSE_FUNCS(MAGIC) \
- MAGIC(pa_mainloop_new); \
- MAGIC(pa_mainloop_free); \
- MAGIC(pa_mainloop_set_poll_func); \
- MAGIC(pa_mainloop_run); \
- MAGIC(pa_mainloop_get_api); \
- MAGIC(pa_context_new); \
- MAGIC(pa_context_unref); \
- MAGIC(pa_context_get_state); \
- MAGIC(pa_context_disconnect); \
- MAGIC(pa_context_set_state_callback); \
- MAGIC(pa_context_errno); \
- MAGIC(pa_context_connect); \
- MAGIC(pa_context_get_server_info); \
- MAGIC(pa_context_get_sink_info_by_name); \
- MAGIC(pa_context_get_sink_info_list); \
- MAGIC(pa_context_get_source_info_by_name); \
- MAGIC(pa_context_get_source_info_list); \
- MAGIC(pa_stream_new); \
- MAGIC(pa_stream_unref); \
- MAGIC(pa_stream_drop); \
- MAGIC(pa_stream_get_state); \
- MAGIC(pa_stream_peek); \
- MAGIC(pa_stream_write); \
- MAGIC(pa_stream_connect_record); \
- MAGIC(pa_stream_connect_playback); \
- MAGIC(pa_stream_readable_size); \
- MAGIC(pa_stream_writable_size); \
- MAGIC(pa_stream_is_corked); \
- MAGIC(pa_stream_cork); \
- MAGIC(pa_stream_is_suspended); \
- MAGIC(pa_stream_get_device_name); \
- MAGIC(pa_stream_get_latency); \
- MAGIC(pa_stream_set_write_callback); \
- MAGIC(pa_stream_set_buffer_attr); \
- MAGIC(pa_stream_get_buffer_attr); \
- MAGIC(pa_stream_get_sample_spec); \
- MAGIC(pa_stream_get_time); \
- MAGIC(pa_stream_set_read_callback); \
- MAGIC(pa_stream_set_state_callback); \
- MAGIC(pa_stream_set_moved_callback); \
- MAGIC(pa_stream_set_underflow_callback); \
- MAGIC(pa_stream_new_with_proplist); \
- MAGIC(pa_stream_disconnect); \
- MAGIC(pa_stream_set_buffer_attr_callback); \
- MAGIC(pa_stream_begin_write); \
- MAGIC(pa_channel_map_init_auto); \
- MAGIC(pa_channel_map_parse); \
- MAGIC(pa_channel_map_snprint); \
- MAGIC(pa_channel_map_equal); \
- MAGIC(pa_channel_map_superset); \
- MAGIC(pa_operation_get_state); \
- MAGIC(pa_operation_unref); \
- MAGIC(pa_sample_spec_valid); \
- MAGIC(pa_frame_size); \
- MAGIC(pa_strerror); \
- MAGIC(pa_path_get_filename); \
- MAGIC(pa_get_binary_name); \
- MAGIC(pa_xmalloc); \
- MAGIC(pa_xfree);
-
-void *pulse_handle;
-#define MAKE_FUNC(x) decltype(x) * p##x
-PULSE_FUNCS(MAKE_FUNC)
-#undef MAKE_FUNC
-
-#ifndef IN_IDE_PARSER
-#define pa_mainloop_new ppa_mainloop_new
-#define pa_mainloop_free ppa_mainloop_free
-#define pa_mainloop_set_poll_func ppa_mainloop_set_poll_func
-#define pa_mainloop_run ppa_mainloop_run
-#define pa_mainloop_get_api ppa_mainloop_get_api
-#define pa_context_new ppa_context_new
-#define pa_context_unref ppa_context_unref
-#define pa_context_get_state ppa_context_get_state
-#define pa_context_disconnect ppa_context_disconnect
-#define pa_context_set_state_callback ppa_context_set_state_callback
-#define pa_context_errno ppa_context_errno
-#define pa_context_connect ppa_context_connect
-#define pa_context_get_server_info ppa_context_get_server_info
-#define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name
-#define pa_context_get_sink_info_list ppa_context_get_sink_info_list
-#define pa_context_get_source_info_by_name ppa_context_get_source_info_by_name
-#define pa_context_get_source_info_list ppa_context_get_source_info_list
-#define pa_stream_new ppa_stream_new
-#define pa_stream_unref ppa_stream_unref
-#define pa_stream_disconnect ppa_stream_disconnect
-#define pa_stream_drop ppa_stream_drop
-#define pa_stream_set_write_callback ppa_stream_set_write_callback
-#define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr
-#define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr
-#define pa_stream_get_sample_spec ppa_stream_get_sample_spec
-#define pa_stream_get_time ppa_stream_get_time
-#define pa_stream_set_read_callback ppa_stream_set_read_callback
-#define pa_stream_set_state_callback ppa_stream_set_state_callback
-#define pa_stream_set_moved_callback ppa_stream_set_moved_callback
-#define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback
-#define pa_stream_connect_record ppa_stream_connect_record
-#define pa_stream_connect_playback ppa_stream_connect_playback
-#define pa_stream_readable_size ppa_stream_readable_size
-#define pa_stream_writable_size ppa_stream_writable_size
-#define pa_stream_is_corked ppa_stream_is_corked
-#define pa_stream_cork ppa_stream_cork
-#define pa_stream_is_suspended ppa_stream_is_suspended
-#define pa_stream_get_device_name ppa_stream_get_device_name
-#define pa_stream_get_latency ppa_stream_get_latency
-#define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback
-#define pa_stream_begin_write ppa_stream_begin_write*/
-#define pa_channel_map_init_auto ppa_channel_map_init_auto
-#define pa_channel_map_parse ppa_channel_map_parse
-#define pa_channel_map_snprint ppa_channel_map_snprint
-#define pa_channel_map_equal ppa_channel_map_equal
-#define pa_channel_map_superset ppa_channel_map_superset
-#define pa_operation_get_state ppa_operation_get_state
-#define pa_operation_unref ppa_operation_unref
-#define pa_sample_spec_valid ppa_sample_spec_valid
-#define pa_frame_size ppa_frame_size
-#define pa_strerror ppa_strerror
-#define pa_stream_get_state ppa_stream_get_state
-#define pa_stream_peek ppa_stream_peek
-#define pa_stream_write ppa_stream_write
-#define pa_xfree ppa_xfree
-#define pa_path_get_filename ppa_path_get_filename
-#define pa_get_binary_name ppa_get_binary_name
-#define pa_xmalloc ppa_xmalloc
-#endif /* IN_IDE_PARSER */
-
-#endif
-
-
-constexpr pa_channel_map MonoChanMap{
- 1, {PA_CHANNEL_POSITION_MONO}
-}, StereoChanMap{
- 2, {PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT}
-}, QuadChanMap{
- 4, {
- PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
- }
-}, X51ChanMap{
- 6, {
- PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
- PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
- }
-}, X51RearChanMap{
- 6, {
- PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
- PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
- }
-}, X61ChanMap{
- 7, {
- PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
- PA_CHANNEL_POSITION_REAR_CENTER,
- PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
- }
-}, X71ChanMap{
- 8, {
- PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
- PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
- PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
- }
-};
-
-size_t ChannelFromPulse(pa_channel_position_t chan)
-{
- switch(chan)
- {
- case PA_CHANNEL_POSITION_INVALID: break;
- case PA_CHANNEL_POSITION_MONO: return FrontCenter;
- case PA_CHANNEL_POSITION_FRONT_LEFT: return FrontLeft;
- case PA_CHANNEL_POSITION_FRONT_RIGHT: return FrontRight;
- case PA_CHANNEL_POSITION_FRONT_CENTER: return FrontCenter;
- case PA_CHANNEL_POSITION_REAR_CENTER: return BackCenter;
- case PA_CHANNEL_POSITION_REAR_LEFT: return BackLeft;
- case PA_CHANNEL_POSITION_REAR_RIGHT: return BackRight;
- case PA_CHANNEL_POSITION_LFE: return LFE;
- case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: break;
- case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: break;
- case PA_CHANNEL_POSITION_SIDE_LEFT: return SideLeft;
- case PA_CHANNEL_POSITION_SIDE_RIGHT: return SideRight;
- case PA_CHANNEL_POSITION_AUX0: return Aux0;
- case PA_CHANNEL_POSITION_AUX1: return Aux1;
- case PA_CHANNEL_POSITION_AUX2: return Aux2;
- case PA_CHANNEL_POSITION_AUX3: return Aux3;
- case PA_CHANNEL_POSITION_AUX4: return Aux4;
- case PA_CHANNEL_POSITION_AUX5: return Aux5;
- case PA_CHANNEL_POSITION_AUX6: return Aux6;
- case PA_CHANNEL_POSITION_AUX7: return Aux7;
- case PA_CHANNEL_POSITION_AUX8: return Aux8;
- case PA_CHANNEL_POSITION_AUX9: return Aux9;
- case PA_CHANNEL_POSITION_AUX10: return Aux10;
- case PA_CHANNEL_POSITION_AUX11: return Aux11;
- case PA_CHANNEL_POSITION_AUX12: return Aux12;
- case PA_CHANNEL_POSITION_AUX13: return Aux13;
- case PA_CHANNEL_POSITION_AUX14: return Aux14;
- case PA_CHANNEL_POSITION_AUX15: return Aux15;
- case PA_CHANNEL_POSITION_AUX16: break;
- case PA_CHANNEL_POSITION_AUX17: break;
- case PA_CHANNEL_POSITION_AUX18: break;
- case PA_CHANNEL_POSITION_AUX19: break;
- case PA_CHANNEL_POSITION_AUX20: break;
- case PA_CHANNEL_POSITION_AUX21: break;
- case PA_CHANNEL_POSITION_AUX22: break;
- case PA_CHANNEL_POSITION_AUX23: break;
- case PA_CHANNEL_POSITION_AUX24: break;
- case PA_CHANNEL_POSITION_AUX25: break;
- case PA_CHANNEL_POSITION_AUX26: break;
- case PA_CHANNEL_POSITION_AUX27: break;
- case PA_CHANNEL_POSITION_AUX28: break;
- case PA_CHANNEL_POSITION_AUX29: break;
- case PA_CHANNEL_POSITION_AUX30: break;
- case PA_CHANNEL_POSITION_AUX31: break;
- case PA_CHANNEL_POSITION_TOP_CENTER: break;
- case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return UpperFrontLeft;
- case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return UpperFrontRight;
- case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: break;
- case PA_CHANNEL_POSITION_TOP_REAR_LEFT: return UpperBackLeft;
- case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return UpperBackRight;
- case PA_CHANNEL_POSITION_TOP_REAR_CENTER: break;
- case PA_CHANNEL_POSITION_MAX: break;
- }
- throw al::backend_exception{ALC_INVALID_VALUE, "Unexpected channel enum %d", chan};
-}
-
-void SetChannelOrderFromMap(ALCdevice *device, const pa_channel_map &chanmap)
-{
- device->RealOut.ChannelIndex.fill(-1);
- for(int i{0};i < chanmap.channels;++i)
- device->RealOut.ChannelIndex[ChannelFromPulse(chanmap.map[i])] = i;
-}
-
-
-/* *grumble* Don't use enums for bitflags. */
-inline pa_stream_flags_t operator|(pa_stream_flags_t lhs, pa_stream_flags_t rhs)
-{ return pa_stream_flags_t(int(lhs) | int(rhs)); }
-inline pa_stream_flags_t& operator|=(pa_stream_flags_t &lhs, pa_stream_flags_t rhs)
-{
- lhs = pa_stream_flags_t(int(lhs) | int(rhs));
- return lhs;
-}
-inline pa_context_flags_t& operator|=(pa_context_flags_t &lhs, pa_context_flags_t rhs)
-{
- lhs = pa_context_flags_t(int(lhs) | int(rhs));
- return lhs;
-}
-
-inline pa_stream_flags_t& operator&=(pa_stream_flags_t &lhs, int rhs)
-{
- lhs = pa_stream_flags_t(int(lhs) & rhs);
- return lhs;
-}
-
-
-/* Global flags and properties */
-pa_context_flags_t pulse_ctx_flags;
-
-pa_mainloop *pulse_mainloop{nullptr};
-
-std::mutex pulse_lock;
-std::condition_variable pulse_condvar;
-
-int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata)
-{
- auto plock = static_cast<std::unique_lock<std::mutex>*>(userdata);
- plock->unlock();
- int r{poll(ufds, nfds, timeout)};
- plock->lock();
- return r;
-}
-
-int pulse_mainloop_thread()
-{
- SetRTPriority();
-
- std::unique_lock<std::mutex> plock{pulse_lock};
- pulse_mainloop = pa_mainloop_new();
-
- pa_mainloop_set_poll_func(pulse_mainloop, pulse_poll_func, &plock);
- pulse_condvar.notify_all();
-
- int ret{};
- pa_mainloop_run(pulse_mainloop, &ret);
-
- pa_mainloop_free(pulse_mainloop);
- pulse_mainloop = nullptr;
-
- return ret;
-}
-
-
-/* PulseAudio Event Callbacks */
-void context_state_callback(pa_context *context, void* /*pdata*/)
-{
- pa_context_state_t state{pa_context_get_state(context)};
- if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))
- pulse_condvar.notify_all();
-}
-
-void stream_state_callback(pa_stream *stream, void* /*pdata*/)
-{
- pa_stream_state_t state{pa_stream_get_state(stream)};
- if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
- pulse_condvar.notify_all();
-}
-
-void stream_success_callback(pa_stream* /*stream*/, int /*success*/, void* /*pdata*/)
-{
- pulse_condvar.notify_all();
-}
-
-void wait_for_operation(pa_operation *op, std::unique_lock<std::mutex> &plock)
-{
- if(op)
- {
- while(pa_operation_get_state(op) == PA_OPERATION_RUNNING)
- pulse_condvar.wait(plock);
- pa_operation_unref(op);
- }
-}
-
-
-pa_context *connect_context(std::unique_lock<std::mutex> &plock)
-{
- const char *name{"OpenAL Soft"};
-
- const PathNamePair &binname = GetProcBinary();
- if(!binname.fname.empty())
- name = binname.fname.c_str();
-
- if(UNLIKELY(!pulse_mainloop))
- {
- std::thread{pulse_mainloop_thread}.detach();
- while(!pulse_mainloop)
- pulse_condvar.wait(plock);
- }
-
- pa_context *context{pa_context_new(pa_mainloop_get_api(pulse_mainloop), name)};
- if(!context) throw al::backend_exception{ALC_OUT_OF_MEMORY, "pa_context_new() failed"};
-
- pa_context_set_state_callback(context, context_state_callback, nullptr);
-
- int err;
- if((err=pa_context_connect(context, nullptr, pulse_ctx_flags, nullptr)) >= 0)
- {
- pa_context_state_t state;
- while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)
- {
- if(!PA_CONTEXT_IS_GOOD(state))
- {
- err = pa_context_errno(context);
- if(err > 0) err = -err;
- break;
- }
-
- pulse_condvar.wait(plock);
- }
- }
- pa_context_set_state_callback(context, nullptr, nullptr);
-
- if(err < 0)
- {
- pa_context_unref(context);
- throw al::backend_exception{ALC_INVALID_VALUE, "Context did not connect (%s)",
- pa_strerror(err)};
- }
-
- return context;
-}
-
-
-void pulse_close(pa_context *context, pa_stream *stream)
-{
- std::lock_guard<std::mutex> _{pulse_lock};
- if(stream)
- {
- pa_stream_set_state_callback(stream, nullptr, nullptr);
- pa_stream_set_moved_callback(stream, nullptr, nullptr);
- pa_stream_set_write_callback(stream, nullptr, nullptr);
- pa_stream_set_buffer_attr_callback(stream, nullptr, nullptr);
- pa_stream_disconnect(stream);
- pa_stream_unref(stream);
- }
-
- pa_context_disconnect(context);
- pa_context_unref(context);
-}
-
-
-struct DevMap {
- std::string name;
- std::string device_name;
-};
-
-bool checkName(const al::vector<DevMap> &list, const std::string &name)
-{
- return std::find_if(list.cbegin(), list.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- ) != list.cend();
-}
-
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
-
-
-pa_stream *pulse_connect_stream(const char *device_name, std::unique_lock<std::mutex> &plock,
- pa_context *context, pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
- pa_channel_map *chanmap, BackendType type)
-{
- const char *stream_id{(type==BackendType::Playback) ? "Playback Stream" : "Capture Stream"};
- pa_stream *stream{pa_stream_new(context, stream_id, spec, chanmap)};
- if(!stream)
- throw al::backend_exception{ALC_OUT_OF_MEMORY, "pa_stream_new() failed (%s)",
- pa_strerror(pa_context_errno(context))};
-
- pa_stream_set_state_callback(stream, stream_state_callback, nullptr);
-
- int err{(type==BackendType::Playback) ?
- pa_stream_connect_playback(stream, device_name, attr, flags, nullptr, nullptr) :
- pa_stream_connect_record(stream, device_name, attr, flags)};
- if(err < 0)
- {
- pa_stream_unref(stream);
- throw al::backend_exception{ALC_INVALID_VALUE, "%s did not connect (%s)", stream_id,
- pa_strerror(err)};
- }
-
- pa_stream_state_t state;
- while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
- {
- if(!PA_STREAM_IS_GOOD(state))
- {
- int err{pa_context_errno(context)};
- pa_stream_unref(stream);
- throw al::backend_exception{ALC_INVALID_VALUE, "%s did not get ready (%s)", stream_id,
- pa_strerror(err)};
- }
-
- pulse_condvar.wait(plock);
- }
- pa_stream_set_state_callback(stream, nullptr, nullptr);
-
- return stream;
-}
-
-
-void device_sink_callback(pa_context*, const pa_sink_info *info, int eol, void*)
-{
- if(eol)
- {
- pulse_condvar.notify_all();
- return;
- }
-
- /* Skip this device is if it's already in the list. */
- if(std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [info](const DevMap &entry) -> bool
- { return entry.device_name == info->name; }
- ) != PlaybackDevices.cend())
- return;
-
- /* Make sure the display name (description) is unique. Append a number
- * counter as needed.
- */
- int count{1};
- std::string newname{info->description};
- while(checkName(PlaybackDevices, newname))
- {
- newname = info->description;
- newname += " #";
- newname += std::to_string(++count);
- }
- PlaybackDevices.emplace_back(DevMap{std::move(newname), info->name});
- DevMap &newentry = PlaybackDevices.back();
-
- TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
-}
-
-void probePlaybackDevices()
-{
- PlaybackDevices.clear();
-
- try {
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- pa_context *context{connect_context(plock)};
-
- const pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
- PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE};
-
- pa_sample_spec spec{};
- spec.format = PA_SAMPLE_S16NE;
- spec.rate = 44100;
- spec.channels = 2;
-
- pa_stream *stream{pulse_connect_stream(nullptr, plock, context, flags, nullptr, &spec,
- nullptr, BackendType::Playback)};
- pa_operation *op{pa_context_get_sink_info_by_name(context,
- pa_stream_get_device_name(stream), device_sink_callback, nullptr)};
- wait_for_operation(op, plock);
-
- pa_stream_disconnect(stream);
- pa_stream_unref(stream);
- stream = nullptr;
-
- op = pa_context_get_sink_info_list(context, device_sink_callback, nullptr);
- wait_for_operation(op, plock);
-
- pa_context_disconnect(context);
- pa_context_unref(context);
- }
- catch(std::exception &e) {
- ERR("Error enumerating devices: %s\n", e.what());
- }
-}
-
-
-void device_source_callback(pa_context*, const pa_source_info *info, int eol, void*)
-{
- if(eol)
- {
- pulse_condvar.notify_all();
- return;
- }
-
- /* Skip this device is if it's already in the list. */
- if(std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [info](const DevMap &entry) -> bool
- { return entry.device_name == info->name; }
- ) != CaptureDevices.cend())
- return;
-
- /* Make sure the display name (description) is unique. Append a number
- * counter as needed.
- */
- int count{1};
- std::string newname{info->description};
- while(checkName(CaptureDevices, newname))
- {
- newname = info->description;
- newname += " #";
- newname += std::to_string(++count);
- }
- CaptureDevices.emplace_back(DevMap{std::move(newname), info->name});
- DevMap &newentry = CaptureDevices.back();
-
- TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
-}
-
-void probeCaptureDevices()
-{
- CaptureDevices.clear();
-
- try {
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- pa_context *context{connect_context(plock)};
-
- const pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
- PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE};
-
- pa_sample_spec spec{};
- spec.format = PA_SAMPLE_S16NE;
- spec.rate = 44100;
- spec.channels = 1;
-
- pa_stream *stream{pulse_connect_stream(nullptr, plock, context, flags, nullptr, &spec, nullptr,
- BackendType::Capture)};
- pa_operation *op{pa_context_get_source_info_by_name(context,
- pa_stream_get_device_name(stream), device_source_callback, nullptr)};
- wait_for_operation(op, plock);
-
- pa_stream_disconnect(stream);
- pa_stream_unref(stream);
- stream = nullptr;
-
- op = pa_context_get_source_info_list(context, device_source_callback, nullptr);
- wait_for_operation(op, plock);
-
- pa_context_disconnect(context);
- pa_context_unref(context);
- }
- catch(std::exception &e) {
- ERR("Error enumerating devices: %s\n", e.what());
- }
-}
-
-
-struct PulsePlayback final : public BackendBase {
- PulsePlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~PulsePlayback() override;
-
- static void bufferAttrCallbackC(pa_stream *stream, void *pdata);
- void bufferAttrCallback(pa_stream *stream);
-
- static void contextStateCallbackC(pa_context *context, void *pdata);
- void contextStateCallback(pa_context *context);
-
- static void streamStateCallbackC(pa_stream *stream, void *pdata);
- void streamStateCallback(pa_stream *stream);
-
- static void streamWriteCallbackC(pa_stream *stream, size_t nbytes, void *pdata);
- void streamWriteCallback(pa_stream *stream, size_t nbytes);
-
- static void sinkInfoCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
- void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int eol);
-
- static void sinkNameCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
- void sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol);
-
- static void streamMovedCallbackC(pa_stream *stream, void *pdata);
- void streamMovedCallback(pa_stream *stream);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
- ClockLatency getClockLatency() override;
- void lock() override;
- void unlock() override;
-
- std::string mDeviceName;
-
- pa_buffer_attr mAttr;
- pa_sample_spec mSpec;
-
- pa_stream *mStream{nullptr};
- pa_context *mContext{nullptr};
-
- ALuint mFrameSize{0u};
-
- DEF_NEWDEL(PulsePlayback)
-};
-
-PulsePlayback::~PulsePlayback()
-{
- if(!mContext)
- return;
-
- pulse_close(mContext, mStream);
- mContext = nullptr;
- mStream = nullptr;
-}
-
-
-void PulsePlayback::bufferAttrCallbackC(pa_stream *stream, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->bufferAttrCallback(stream); }
-
-void PulsePlayback::bufferAttrCallback(pa_stream *stream)
-{
- /* FIXME: Update the device's UpdateSize (and/or BufferSize) using the new
- * buffer attributes? Changing UpdateSize will change the ALC_REFRESH
- * property, which probably shouldn't change between device resets. But
- * leaving it alone means ALC_REFRESH will be off.
- */
- mAttr = *(pa_stream_get_buffer_attr(stream));
- TRACE("minreq=%d, tlength=%d, prebuf=%d\n", mAttr.minreq, mAttr.tlength, mAttr.prebuf);
-}
-
-void PulsePlayback::contextStateCallbackC(pa_context *context, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->contextStateCallback(context); }
-
-void PulsePlayback::contextStateCallback(pa_context *context)
-{
- if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
- {
- ERR("Received context failure!\n");
- aluHandleDisconnect(mDevice, "Playback state failure");
- }
- pulse_condvar.notify_all();
-}
-
-void PulsePlayback::streamStateCallbackC(pa_stream *stream, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->streamStateCallback(stream); }
-
-void PulsePlayback::streamStateCallback(pa_stream *stream)
-{
- if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
- {
- ERR("Received stream failure!\n");
- aluHandleDisconnect(mDevice, "Playback stream failure");
- }
- pulse_condvar.notify_all();
-}
-
-void PulsePlayback::streamWriteCallbackC(pa_stream *stream, size_t nbytes, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->streamWriteCallback(stream, nbytes); }
-
-void PulsePlayback::streamWriteCallback(pa_stream *stream, size_t nbytes)
-{
- void *buf{pa_xmalloc(nbytes)};
- aluMixData(mDevice, buf, nbytes/mFrameSize);
-
- int ret{pa_stream_write(stream, buf, nbytes, pa_xfree, 0, PA_SEEK_RELATIVE)};
- if(UNLIKELY(ret != PA_OK))
- ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
-}
-
-void PulsePlayback::sinkInfoCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->sinkInfoCallback(context, info, eol); }
-
-void PulsePlayback::sinkInfoCallback(pa_context*, const pa_sink_info *info, int eol)
-{
- struct ChannelMap {
- DevFmtChannels chans;
- pa_channel_map map;
- };
- static constexpr std::array<ChannelMap,7> chanmaps{{
- { DevFmtX71, X71ChanMap },
- { DevFmtX61, X61ChanMap },
- { DevFmtX51, X51ChanMap },
- { DevFmtX51Rear, X51RearChanMap },
- { DevFmtQuad, QuadChanMap },
- { DevFmtStereo, StereoChanMap },
- { DevFmtMono, MonoChanMap }
- }};
-
- if(eol)
- {
- pulse_condvar.notify_all();
- return;
- }
-
- auto chanmap = std::find_if(chanmaps.cbegin(), chanmaps.cend(),
- [info](const ChannelMap &chanmap) -> bool
- { return pa_channel_map_superset(&info->channel_map, &chanmap.map); }
- );
- if(chanmap != chanmaps.cend())
- {
- if(!mDevice->Flags.get<ChannelsRequest>())
- mDevice->FmtChans = chanmap->chans;
- }
- else
- {
- char chanmap_str[PA_CHANNEL_MAP_SNPRINT_MAX]{};
- pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
- WARN("Failed to find format for channel map:\n %s\n", chanmap_str);
- }
-
- if(info->active_port)
- TRACE("Active port: %s (%s)\n", info->active_port->name, info->active_port->description);
- mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo &&
- info->active_port && strcmp(info->active_port->name, "analog-output-headphones") == 0);
-}
-
-void PulsePlayback::sinkNameCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->sinkNameCallback(context, info, eol); }
-
-void PulsePlayback::sinkNameCallback(pa_context*, const pa_sink_info *info, int eol)
-{
- if(eol)
- {
- pulse_condvar.notify_all();
- return;
- }
- mDevice->DeviceName = info->description;
-}
-
-void PulsePlayback::streamMovedCallbackC(pa_stream *stream, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->streamMovedCallback(stream); }
-
-void PulsePlayback::streamMovedCallback(pa_stream *stream)
-{
- mDeviceName = pa_stream_get_device_name(stream);
- TRACE("Stream moved to %s\n", mDeviceName.c_str());
-}
-
-
-ALCenum PulsePlayback::open(const ALCchar *name)
-{
- const char *pulse_name{nullptr};
- const char *dev_name{nullptr};
-
- if(name)
- {
- if(PlaybackDevices.empty())
- probePlaybackDevices();
-
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == PlaybackDevices.cend())
- throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
- pulse_name = iter->device_name.c_str();
- dev_name = iter->name.c_str();
- }
-
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- mContext = connect_context(plock);
- pa_context_set_state_callback(mContext, &PulsePlayback::contextStateCallbackC, this);
-
- pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS};
- if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
- flags |= PA_STREAM_DONT_MOVE;
-
- pa_sample_spec spec{};
- spec.format = PA_SAMPLE_S16NE;
- spec.rate = 44100;
- spec.channels = 2;
-
- if(!pulse_name)
- {
- pulse_name = getenv("ALSOFT_PULSE_DEFAULT");
- if(pulse_name && !pulse_name[0]) pulse_name = nullptr;
- }
- TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
- mStream = pulse_connect_stream(pulse_name, plock, mContext, flags, nullptr, &spec, nullptr,
- BackendType::Playback);
-
- pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
- mFrameSize = pa_frame_size(pa_stream_get_sample_spec(mStream));
-
- mDeviceName = pa_stream_get_device_name(mStream);
- if(!dev_name)
- {
- pa_operation *op{pa_context_get_sink_info_by_name(mContext, mDeviceName.c_str(),
- &PulsePlayback::sinkNameCallbackC, this)};
- wait_for_operation(op, plock);
- }
- else
- mDevice->DeviceName = dev_name;
-
- return ALC_NO_ERROR;
-}
-
-ALCboolean PulsePlayback::reset()
-{
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- if(mStream)
- {
- pa_stream_set_state_callback(mStream, nullptr, nullptr);
- pa_stream_set_moved_callback(mStream, nullptr, nullptr);
- pa_stream_set_write_callback(mStream, nullptr, nullptr);
- pa_stream_set_buffer_attr_callback(mStream, nullptr, nullptr);
- pa_stream_disconnect(mStream);
- pa_stream_unref(mStream);
- mStream = nullptr;
- }
-
- pa_operation *op{pa_context_get_sink_info_by_name(mContext, mDeviceName.c_str(),
- &PulsePlayback::sinkInfoCallbackC, this)};
- wait_for_operation(op, plock);
-
- pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING |
- PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS};
- if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
- flags |= PA_STREAM_DONT_MOVE;
- if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "adjust-latency", 0))
- {
- /* ADJUST_LATENCY can't be specified with EARLY_REQUESTS, for some
- * reason. So if the user wants to adjust the overall device latency,
- * we can't ask to get write signals as soon as minreq is reached.
- */
- flags &= ~PA_STREAM_EARLY_REQUESTS;
- flags |= PA_STREAM_ADJUST_LATENCY;
- }
- if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "fix-rate", 0) ||
- !mDevice->Flags.get<FrequencyRequest>())
- flags |= PA_STREAM_FIX_RATE;
-
- pa_channel_map chanmap{};
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- chanmap = MonoChanMap;
- break;
- case DevFmtAmbi3D:
- mDevice->FmtChans = DevFmtStereo;
- /*fall-through*/
- case DevFmtStereo:
- chanmap = StereoChanMap;
- break;
- case DevFmtQuad:
- chanmap = QuadChanMap;
- break;
- case DevFmtX51:
- chanmap = X51ChanMap;
- break;
- case DevFmtX51Rear:
- chanmap = X51RearChanMap;
- break;
- case DevFmtX61:
- chanmap = X61ChanMap;
- break;
- case DevFmtX71:
- chanmap = X71ChanMap;
- break;
- }
- SetChannelOrderFromMap(mDevice, chanmap);
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mDevice->FmtType = DevFmtUByte;
- /* fall-through */
- case DevFmtUByte:
- mSpec.format = PA_SAMPLE_U8;
- break;
- case DevFmtUShort:
- mDevice->FmtType = DevFmtShort;
- /* fall-through */
- case DevFmtShort:
- mSpec.format = PA_SAMPLE_S16NE;
- break;
- case DevFmtUInt:
- mDevice->FmtType = DevFmtInt;
- /* fall-through */
- case DevFmtInt:
- mSpec.format = PA_SAMPLE_S32NE;
- break;
- case DevFmtFloat:
- mSpec.format = PA_SAMPLE_FLOAT32NE;
- break;
- }
- mSpec.rate = mDevice->Frequency;
- mSpec.channels = mDevice->channelsFromFmt();
- if(pa_sample_spec_valid(&mSpec) == 0)
- throw al::backend_exception{ALC_INVALID_VALUE, "Invalid sample spec"};
-
- mAttr.maxlength = -1;
- mAttr.tlength = mDevice->BufferSize * pa_frame_size(&mSpec);
- mAttr.prebuf = 0;
- mAttr.minreq = mDevice->UpdateSize * pa_frame_size(&mSpec);
- mAttr.fragsize = -1;
-
- mStream = pulse_connect_stream(mDeviceName.c_str(), plock, mContext, flags, &mAttr, &mSpec,
- &chanmap, BackendType::Playback);
-
- pa_stream_set_state_callback(mStream, &PulsePlayback::streamStateCallbackC, this);
- pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
-
- mSpec = *(pa_stream_get_sample_spec(mStream));
- mFrameSize = pa_frame_size(&mSpec);
-
- if(mDevice->Frequency != mSpec.rate)
- {
- /* Server updated our playback rate, so modify the buffer attribs
- * accordingly.
- */
- const auto scale = static_cast<double>(mSpec.rate) / mDevice->Frequency;
- const ALuint perlen{static_cast<ALuint>(clampd(scale*mDevice->UpdateSize + 0.5, 64.0,
- 8192.0))};
- const ALuint buflen{static_cast<ALuint>(clampd(scale*mDevice->BufferSize + 0.5, perlen*2,
- std::numeric_limits<int>::max()/mFrameSize))};
-
- mAttr.maxlength = -1;
- mAttr.tlength = buflen * mFrameSize;
- mAttr.prebuf = 0;
- mAttr.minreq = perlen * mFrameSize;
-
- op = pa_stream_set_buffer_attr(mStream, &mAttr, stream_success_callback, nullptr);
- wait_for_operation(op, plock);
-
- mDevice->Frequency = mSpec.rate;
- }
-
- pa_stream_set_buffer_attr_callback(mStream, &PulsePlayback::bufferAttrCallbackC, this);
- bufferAttrCallback(mStream);
-
- mDevice->BufferSize = mAttr.tlength / mFrameSize;
- mDevice->UpdateSize = mAttr.minreq / mFrameSize;
-
- /* HACK: prebuf should be 0 as that's what we set it to. However on some
- * systems it comes back as non-0, so we have to make sure the device will
- * write enough audio to start playback. The lack of manual start control
- * may have unintended consequences, but it's better than not starting at
- * all.
- */
- if(mAttr.prebuf != 0)
- {
- ALuint len{mAttr.prebuf / mFrameSize};
- if(len <= mDevice->BufferSize)
- ERR("Non-0 prebuf, %u samples (%u bytes), device has %u samples\n",
- len, mAttr.prebuf, mDevice->BufferSize);
- }
-
- return ALC_TRUE;
-}
-
-ALCboolean PulsePlayback::start()
-{
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- pa_stream_set_write_callback(mStream, &PulsePlayback::streamWriteCallbackC, this);
- pa_operation *op{pa_stream_cork(mStream, 0, stream_success_callback, nullptr)};
- wait_for_operation(op, plock);
-
- return ALC_TRUE;
-}
-
-void PulsePlayback::stop()
-{
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- pa_stream_set_write_callback(mStream, nullptr, nullptr);
- pa_operation *op{pa_stream_cork(mStream, 1, stream_success_callback, nullptr)};
- wait_for_operation(op, plock);
-}
-
-
-ClockLatency PulsePlayback::getClockLatency()
-{
- ClockLatency ret;
- pa_usec_t latency;
- int neg, err;
-
- { std::lock_guard<std::mutex> _{pulse_lock};
- ret.ClockTime = GetDeviceClockTime(mDevice);
- err = pa_stream_get_latency(mStream, &latency, &neg);
- }
-
- if(UNLIKELY(err != 0))
- {
- /* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon
- * after starting the stream and no timing info has been received from
- * the server yet. Should we wait, possibly stalling the app, or give a
- * dummy value? Either way, it shouldn't be 0. */
- if(err != -PA_ERR_NODATA)
- ERR("Failed to get stream latency: 0x%x\n", err);
- latency = 0;
- neg = 0;
- }
- else if(UNLIKELY(neg))
- latency = 0;
- ret.Latency = std::chrono::microseconds{latency};
-
- return ret;
-}
-
-
-void PulsePlayback::lock()
-{ pulse_lock.lock(); }
-
-void PulsePlayback::unlock()
-{ pulse_lock.unlock(); }
-
-
-struct PulseCapture final : public BackendBase {
- PulseCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~PulseCapture() override;
-
- static void contextStateCallbackC(pa_context *context, void *pdata);
- void contextStateCallback(pa_context *context);
-
- static void streamStateCallbackC(pa_stream *stream, void *pdata);
- void streamStateCallback(pa_stream *stream);
-
- static void sourceNameCallbackC(pa_context *context, const pa_source_info *info, int eol, void *pdata);
- void sourceNameCallback(pa_context *context, const pa_source_info *info, int eol);
-
- static void streamMovedCallbackC(pa_stream *stream, void *pdata);
- void streamMovedCallback(pa_stream *stream);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
- ClockLatency getClockLatency() override;
- void lock() override;
- void unlock() override;
-
- std::string mDeviceName;
-
- ALCuint mLastReadable{0u};
- al::byte mSilentVal{};
-
- al::span<const al::byte> mCapBuffer;
- ssize_t mCapLen{0};
-
- pa_buffer_attr mAttr{};
- pa_sample_spec mSpec{};
-
- pa_stream *mStream{nullptr};
- pa_context *mContext{nullptr};
-
- DEF_NEWDEL(PulseCapture)
-};
-
-PulseCapture::~PulseCapture()
-{
- if(!mContext)
- return;
-
- pulse_close(mContext, mStream);
- mContext = nullptr;
- mStream = nullptr;
-}
-
-void PulseCapture::contextStateCallbackC(pa_context *context, void *pdata)
-{ static_cast<PulseCapture*>(pdata)->contextStateCallback(context); }
-
-void PulseCapture::contextStateCallback(pa_context *context)
-{
- if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
- {
- ERR("Received context failure!\n");
- aluHandleDisconnect(mDevice, "Capture state failure");
- }
- pulse_condvar.notify_all();
-}
-
-void PulseCapture::streamStateCallbackC(pa_stream *stream, void *pdata)
-{ static_cast<PulseCapture*>(pdata)->streamStateCallback(stream); }
-
-void PulseCapture::streamStateCallback(pa_stream *stream)
-{
- if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
- {
- ERR("Received stream failure!\n");
- aluHandleDisconnect(mDevice, "Capture stream failure");
- }
- pulse_condvar.notify_all();
-}
-
-void PulseCapture::sourceNameCallbackC(pa_context *context, const pa_source_info *info, int eol, void *pdata)
-{ static_cast<PulseCapture*>(pdata)->sourceNameCallback(context, info, eol); }
-
-void PulseCapture::sourceNameCallback(pa_context*, const pa_source_info *info, int eol)
-{
- if(eol)
- {
- pulse_condvar.notify_all();
- return;
- }
- mDevice->DeviceName = info->description;
-}
-
-void PulseCapture::streamMovedCallbackC(pa_stream *stream, void *pdata)
-{ static_cast<PulseCapture*>(pdata)->streamMovedCallback(stream); }
-
-void PulseCapture::streamMovedCallback(pa_stream *stream)
-{
- mDeviceName = pa_stream_get_device_name(stream);
- TRACE("Stream moved to %s\n", mDeviceName.c_str());
-}
-
-
-ALCenum PulseCapture::open(const ALCchar *name)
-{
- const char *pulse_name{nullptr};
- if(name)
- {
- if(CaptureDevices.empty())
- probeCaptureDevices();
-
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == CaptureDevices.cend())
- throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
- pulse_name = iter->device_name.c_str();
- mDevice->DeviceName = iter->name;
- }
-
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- mContext = connect_context(plock);
- pa_context_set_state_callback(mContext, &PulseCapture::contextStateCallbackC, this);
-
- pa_channel_map chanmap{};
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- chanmap = MonoChanMap;
- break;
- case DevFmtStereo:
- chanmap = StereoChanMap;
- break;
- case DevFmtQuad:
- chanmap = QuadChanMap;
- break;
- case DevFmtX51:
- chanmap = X51ChanMap;
- break;
- case DevFmtX51Rear:
- chanmap = X51RearChanMap;
- break;
- case DevFmtX61:
- chanmap = X61ChanMap;
- break;
- case DevFmtX71:
- chanmap = X71ChanMap;
- break;
- case DevFmtAmbi3D:
- throw al::backend_exception{ALC_INVALID_VALUE, "%s capture samples not supported",
- DevFmtChannelsString(mDevice->FmtChans)};
- }
- SetChannelOrderFromMap(mDevice, chanmap);
-
- switch(mDevice->FmtType)
- {
- case DevFmtUByte:
- mSilentVal = al::byte(0x80);
- mSpec.format = PA_SAMPLE_U8;
- break;
- case DevFmtShort:
- mSpec.format = PA_SAMPLE_S16NE;
- break;
- case DevFmtInt:
- mSpec.format = PA_SAMPLE_S32NE;
- break;
- case DevFmtFloat:
- mSpec.format = PA_SAMPLE_FLOAT32NE;
- break;
- case DevFmtByte:
- case DevFmtUShort:
- case DevFmtUInt:
- throw al::backend_exception{ALC_INVALID_VALUE, "%s capture samples not supported",
- DevFmtTypeString(mDevice->FmtType)};
- }
- mSpec.rate = mDevice->Frequency;
- mSpec.channels = mDevice->channelsFromFmt();
- if(pa_sample_spec_valid(&mSpec) == 0)
- throw al::backend_exception{ALC_INVALID_VALUE, "Invalid sample format"};
-
- ALuint samples{mDevice->BufferSize};
- samples = maxu(samples, 100 * mDevice->Frequency / 1000);
-
- mAttr.minreq = -1;
- mAttr.prebuf = -1;
- mAttr.maxlength = samples * pa_frame_size(&mSpec);
- mAttr.tlength = -1;
- mAttr.fragsize = minu(samples, 50*mDevice->Frequency/1000) * pa_frame_size(&mSpec);
-
- pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY};
- if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
- flags |= PA_STREAM_DONT_MOVE;
-
- TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
- mStream = pulse_connect_stream(pulse_name, plock, mContext, flags, &mAttr, &mSpec, &chanmap,
- BackendType::Capture);
-
- pa_stream_set_moved_callback(mStream, &PulseCapture::streamMovedCallbackC, this);
- pa_stream_set_state_callback(mStream, &PulseCapture::streamStateCallbackC, this);
-
- mDeviceName = pa_stream_get_device_name(mStream);
- if(mDevice->DeviceName.empty())
- {
- pa_operation *op{pa_context_get_source_info_by_name(mContext, mDeviceName.c_str(),
- &PulseCapture::sourceNameCallbackC, this)};
- wait_for_operation(op, plock);
- }
-
- return ALC_NO_ERROR;
-}
-
-ALCboolean PulseCapture::start()
-{
- std::unique_lock<std::mutex> plock{pulse_lock};
- pa_operation *op{pa_stream_cork(mStream, 0, stream_success_callback, nullptr)};
- wait_for_operation(op, plock);
- return ALC_TRUE;
-}
-
-void PulseCapture::stop()
-{
- std::unique_lock<std::mutex> plock{pulse_lock};
- pa_operation *op{pa_stream_cork(mStream, 1, stream_success_callback, nullptr)};
- wait_for_operation(op, plock);
-}
-
-ALCenum PulseCapture::captureSamples(ALCvoid *buffer, ALCuint samples)
-{
- al::span<al::byte> dstbuf{static_cast<al::byte*>(buffer), samples * pa_frame_size(&mSpec)};
-
- /* Capture is done in fragment-sized chunks, so we loop until we get all
- * that's available */
- mLastReadable -= dstbuf.size();
- std::lock_guard<std::mutex> _{pulse_lock};
- while(!dstbuf.empty())
- {
- if(mCapBuffer.empty())
- {
- if(UNLIKELY(!mDevice->Connected.load(std::memory_order_acquire)))
- break;
- const pa_stream_state_t state{pa_stream_get_state(mStream)};
- if(UNLIKELY(!PA_STREAM_IS_GOOD(state)))
- {
- aluHandleDisconnect(mDevice, "Bad capture state: %u", state);
- break;
- }
- const void *capbuf;
- size_t caplen;
- if(UNLIKELY(pa_stream_peek(mStream, &capbuf, &caplen) < 0))
- {
- aluHandleDisconnect(mDevice, "Failed retrieving capture samples: %s",
- pa_strerror(pa_context_errno(mContext)));
- break;
- }
- if(caplen == 0) break;
- if(UNLIKELY(!capbuf))
- mCapLen = -static_cast<ssize_t>(caplen);
- else
- mCapLen = static_cast<ssize_t>(caplen);
- mCapBuffer = {static_cast<const al::byte*>(capbuf), caplen};
- }
-
- const size_t rem{minz(dstbuf.size(), mCapBuffer.size())};
- if(UNLIKELY(mCapLen < 0))
- std::fill_n(dstbuf.begin(), rem, mSilentVal);
- else
- std::copy_n(mCapBuffer.begin(), rem, dstbuf.begin());
- dstbuf = dstbuf.subspan(rem);
- mCapBuffer = mCapBuffer.subspan(rem);
-
- if(mCapBuffer.empty())
- {
- pa_stream_drop(mStream);
- mCapLen = 0;
- }
- }
- if(!dstbuf.empty())
- std::fill(dstbuf.begin(), dstbuf.end(), mSilentVal);
-
- return ALC_NO_ERROR;
-}
-
-ALCuint PulseCapture::availableSamples()
-{
- size_t readable{mCapBuffer.size()};
-
- if(mDevice->Connected.load(std::memory_order_acquire))
- {
- std::lock_guard<std::mutex> _{pulse_lock};
- size_t got{pa_stream_readable_size(mStream)};
- if(static_cast<ssize_t>(got) < 0)
- {
- ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got));
- aluHandleDisconnect(mDevice, "Failed getting readable size: %s", pa_strerror(got));
- }
- else
- {
- const auto caplen = static_cast<size_t>(std::abs(mCapLen));
- if(got > caplen) readable += got - caplen;
- }
- }
-
- readable = std::min<size_t>(readable, std::numeric_limits<ALCuint>::max());
- mLastReadable = std::max(mLastReadable, static_cast<ALCuint>(readable));
- return mLastReadable / pa_frame_size(&mSpec);
-}
-
-
-ClockLatency PulseCapture::getClockLatency()
-{
- ClockLatency ret;
- pa_usec_t latency;
- int neg, err;
-
- { std::lock_guard<std::mutex> _{pulse_lock};
- ret.ClockTime = GetDeviceClockTime(mDevice);
- err = pa_stream_get_latency(mStream, &latency, &neg);
- }
-
- if(UNLIKELY(err != 0))
- {
- ERR("Failed to get stream latency: 0x%x\n", err);
- latency = 0;
- neg = 0;
- }
- else if(UNLIKELY(neg))
- latency = 0;
- ret.Latency = std::chrono::microseconds{latency};
-
- return ret;
-}
-
-
-void PulseCapture::lock()
-{ pulse_lock.lock(); }
-
-void PulseCapture::unlock()
-{ pulse_lock.unlock(); }
-
-} // namespace
-
-
-bool PulseBackendFactory::init()
-{
-#ifdef HAVE_DYNLOAD
- if(!pulse_handle)
- {
- bool ret{true};
- std::string missing_funcs;
-
-#ifdef _WIN32
-#define PALIB "libpulse-0.dll"
-#elif defined(__APPLE__) && defined(__MACH__)
-#define PALIB "libpulse.0.dylib"
-#else
-#define PALIB "libpulse.so.0"
-#endif
- pulse_handle = LoadLib(PALIB);
- if(!pulse_handle)
- {
- WARN("Failed to load %s\n", PALIB);
- return false;
- }
-
-#define LOAD_FUNC(x) do { \
- p##x = reinterpret_cast<decltype(p##x)>(GetSymbol(pulse_handle, #x)); \
- if(!(p##x)) { \
- ret = false; \
- missing_funcs += "\n" #x; \
- } \
-} while(0)
- PULSE_FUNCS(LOAD_FUNC)
-#undef LOAD_FUNC
-
- if(!ret)
- {
- WARN("Missing expected functions:%s\n", missing_funcs.c_str());
- CloseLib(pulse_handle);
- pulse_handle = nullptr;
- return false;
- }
- }
-#endif /* HAVE_DYNLOAD */
-
- pulse_ctx_flags = PA_CONTEXT_NOFLAGS;
- if(!GetConfigValueBool(nullptr, "pulse", "spawn-server", 1))
- pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;
-
- try {
- std::unique_lock<std::mutex> plock{pulse_lock};
- pa_context *context{connect_context(plock)};
- pa_context_disconnect(context);
- pa_context_unref(context);
- return true;
- }
- catch(...) {
- return false;
- }
-}
-
-bool PulseBackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback || type == BackendType::Capture; }
-
-void PulseBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const DevMap &entry) -> void
- {
- /* +1 to also append the null char (to ensure a null-separated list and
- * double-null terminated list).
- */
- outnames->append(entry.name.c_str(), entry.name.length()+1);
- };
- switch(type)
- {
- case DevProbe::Playback:
- probePlaybackDevices();
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- probeCaptureDevices();
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
-}
-
-BackendPtr PulseBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new PulsePlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new PulseCapture{device}};
- return nullptr;
-}
-
-BackendFactory &PulseBackendFactory::getFactory()
-{
- static PulseBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/pulseaudio.h b/Alc/backends/pulseaudio.h
deleted file mode 100644
index 40f3e305..00000000
--- a/Alc/backends/pulseaudio.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_PULSEAUDIO_H
-#define BACKENDS_PULSEAUDIO_H
-
-#include "backends/base.h"
-
-class PulseBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_PULSEAUDIO_H */
diff --git a/Alc/backends/qsa.cpp b/Alc/backends/qsa.cpp
deleted file mode 100644
index 64ed53aa..00000000
--- a/Alc/backends/qsa.cpp
+++ /dev/null
@@ -1,953 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2011-2013 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/qsa.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <sched.h>
-#include <errno.h>
-#include <memory.h>
-#include <poll.h>
-
-#include <thread>
-#include <memory>
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "threads.h"
-
-#include <sys/asoundlib.h>
-#include <sys/neutrino.h>
-
-
-namespace {
-
-struct qsa_data {
- snd_pcm_t* pcmHandle{nullptr};
- int audio_fd{-1};
-
- snd_pcm_channel_setup_t csetup{};
- snd_pcm_channel_params_t cparams{};
-
- ALvoid* buffer{nullptr};
- ALsizei size{0};
-
- std::atomic<ALenum> mKillNow{AL_TRUE};
- std::thread mThread;
-};
-
-struct DevMap {
- ALCchar* name;
- int card;
- int dev;
-};
-
-al::vector<DevMap> DeviceNameMap;
-al::vector<DevMap> CaptureNameMap;
-
-constexpr ALCchar qsaDevice[] = "QSA Default";
-
-constexpr struct {
- int32_t format;
-} formatlist[] = {
- {SND_PCM_SFMT_FLOAT_LE},
- {SND_PCM_SFMT_S32_LE},
- {SND_PCM_SFMT_U32_LE},
- {SND_PCM_SFMT_S16_LE},
- {SND_PCM_SFMT_U16_LE},
- {SND_PCM_SFMT_S8},
- {SND_PCM_SFMT_U8},
- {0},
-};
-
-constexpr struct {
- int32_t rate;
-} ratelist[] = {
- {192000},
- {176400},
- {96000},
- {88200},
- {48000},
- {44100},
- {32000},
- {24000},
- {22050},
- {16000},
- {12000},
- {11025},
- {8000},
- {0},
-};
-
-constexpr struct {
- int32_t channels;
-} channellist[] = {
- {8},
- {7},
- {6},
- {4},
- {2},
- {1},
- {0},
-};
-
-void deviceList(int type, al::vector<DevMap> *devmap)
-{
- snd_ctl_t* handle;
- snd_pcm_info_t pcminfo;
- int max_cards, card, err, dev;
- DevMap entry;
- char name[1024];
- snd_ctl_hw_info info;
-
- max_cards = snd_cards();
- if(max_cards < 0)
- return;
-
- std::for_each(devmap->begin(), devmap->end(),
- [](const DevMap &entry) -> void
- { free(entry.name); }
- );
- devmap->clear();
-
- entry.name = strdup(qsaDevice);
- entry.card = 0;
- entry.dev = 0;
- devmap->push_back(entry);
-
- for(card = 0;card < max_cards;card++)
- {
- if((err=snd_ctl_open(&handle, card)) < 0)
- continue;
-
- if((err=snd_ctl_hw_info(handle, &info)) < 0)
- {
- snd_ctl_close(handle);
- continue;
- }
-
- for(dev = 0;dev < (int)info.pcmdevs;dev++)
- {
- if((err=snd_ctl_pcm_info(handle, dev, &pcminfo)) < 0)
- continue;
-
- if((type==SND_PCM_CHANNEL_PLAYBACK && (pcminfo.flags&SND_PCM_INFO_PLAYBACK)) ||
- (type==SND_PCM_CHANNEL_CAPTURE && (pcminfo.flags&SND_PCM_INFO_CAPTURE)))
- {
- snprintf(name, sizeof(name), "%s [%s] (hw:%d,%d)", info.name, pcminfo.name, card, dev);
- entry.name = strdup(name);
- entry.card = card;
- entry.dev = dev;
-
- devmap->push_back(entry);
- TRACE("Got device \"%s\", card %d, dev %d\n", name, card, dev);
- }
- }
- snd_ctl_close(handle);
- }
-}
-
-
-/* Wrappers to use an old-style backend with the new interface. */
-struct PlaybackWrapper final : public BackendBase {
- PlaybackWrapper(ALCdevice *device) noexcept : BackendBase{device} { }
- ~PlaybackWrapper() override;
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- std::unique_ptr<qsa_data> mExtraData;
-
- DEF_NEWDEL(PlaybackWrapper)
-};
-
-
-FORCE_ALIGN static int qsa_proc_playback(void *ptr)
-{
- PlaybackWrapper *self = static_cast<PlaybackWrapper*>(ptr);
- ALCdevice *device = self->mDevice;
- qsa_data *data = self->mExtraData.get();
- snd_pcm_channel_status_t status;
- sched_param param;
- char* write_ptr;
- ALint len;
- int sret;
-
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- /* Increase default 10 priority to 11 to avoid jerky sound */
- SchedGet(0, 0, &param);
- param.sched_priority=param.sched_curpriority+1;
- SchedSet(0, 0, SCHED_NOCHANGE, &param);
-
- const ALint frame_size = device->frameSizeFromFmt();
-
- self->lock();
- while(!data->mKillNow.load(std::memory_order_acquire))
- {
- pollfd pollitem{};
- pollitem.fd = data->audio_fd;
- pollitem.events = POLLOUT;
-
- /* Select also works like time slice to OS */
- self->unlock();
- sret = poll(&pollitem, 1, 2000);
- self->lock();
- if(sret == -1)
- {
- if(errno == EINTR || errno == EAGAIN)
- continue;
- ERR("poll error: %s\n", strerror(errno));
- aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
- break;
- }
- if(sret == 0)
- {
- ERR("poll timeout\n");
- continue;
- }
-
- len = data->size;
- write_ptr = static_cast<char*>(data->buffer);
- aluMixData(device, write_ptr, len/frame_size);
- while(len>0 && !data->mKillNow.load(std::memory_order_acquire))
- {
- int wrote = snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
- if(wrote <= 0)
- {
- if(errno==EAGAIN || errno==EWOULDBLOCK)
- continue;
-
- memset(&status, 0, sizeof(status));
- status.channel = SND_PCM_CHANNEL_PLAYBACK;
-
- snd_pcm_plugin_status(data->pcmHandle, &status);
-
- /* we need to reinitialize the sound channel if we've underrun the buffer */
- if(status.status == SND_PCM_STATUS_UNDERRUN ||
- status.status == SND_PCM_STATUS_READY)
- {
- if(snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK) < 0)
- {
- aluHandleDisconnect(device, "Playback recovery failed");
- break;
- }
- }
- }
- else
- {
- write_ptr += wrote;
- len -= wrote;
- }
- }
- }
- self->unlock();
-
- return 0;
-}
-
-/************/
-/* Playback */
-/************/
-
-static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName)
-{
- ALCdevice *device = self->mDevice;
- int card, dev;
- int status;
-
- std::unique_ptr<qsa_data> data{new qsa_data{}};
- data->mKillNow.store(AL_TRUE, std::memory_order_relaxed);
-
- if(!deviceName)
- deviceName = qsaDevice;
-
- if(strcmp(deviceName, qsaDevice) == 0)
- status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK);
- else
- {
- if(DeviceNameMap.empty())
- deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
-
- auto iter = std::find_if(DeviceNameMap.begin(), DeviceNameMap.end(),
- [deviceName](const DevMap &entry) -> bool
- { return entry.name && strcmp(deviceName, entry.name) == 0; }
- );
- if(iter == DeviceNameMap.cend())
- return ALC_INVALID_DEVICE;
-
- status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_PLAYBACK);
- }
-
- if(status < 0)
- return ALC_INVALID_DEVICE;
-
- data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
- if(data->audio_fd < 0)
- {
- snd_pcm_close(data->pcmHandle);
- return ALC_INVALID_DEVICE;
- }
-
- device->DeviceName = deviceName;
- self->mExtraData = std::move(data);
-
- return ALC_NO_ERROR;
-}
-
-static void qsa_close_playback(PlaybackWrapper *self)
-{
- qsa_data *data = self->mExtraData.get();
-
- if (data->buffer!=NULL)
- {
- free(data->buffer);
- data->buffer=NULL;
- }
-
- snd_pcm_close(data->pcmHandle);
-
- self->mExtraData = nullptr;
-}
-
-static ALCboolean qsa_reset_playback(PlaybackWrapper *self)
-{
- ALCdevice *device = self->mDevice;
- qsa_data *data = self->mExtraData.get();
- int32_t format=-1;
-
- switch(device->FmtType)
- {
- case DevFmtByte:
- format=SND_PCM_SFMT_S8;
- break;
- case DevFmtUByte:
- format=SND_PCM_SFMT_U8;
- break;
- case DevFmtShort:
- format=SND_PCM_SFMT_S16_LE;
- break;
- case DevFmtUShort:
- format=SND_PCM_SFMT_U16_LE;
- break;
- case DevFmtInt:
- format=SND_PCM_SFMT_S32_LE;
- break;
- case DevFmtUInt:
- format=SND_PCM_SFMT_U32_LE;
- break;
- case DevFmtFloat:
- format=SND_PCM_SFMT_FLOAT_LE;
- break;
- }
-
- /* we actually don't want to block on writes */
- snd_pcm_nonblock_mode(data->pcmHandle, 1);
- /* Disable mmap to control data transfer to the audio device */
- snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
- snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_BUFFER_PARTIAL_BLOCKS);
-
- // configure a sound channel
- memset(&data->cparams, 0, sizeof(data->cparams));
- data->cparams.channel=SND_PCM_CHANNEL_PLAYBACK;
- data->cparams.mode=SND_PCM_MODE_BLOCK;
- data->cparams.start_mode=SND_PCM_START_FULL;
- data->cparams.stop_mode=SND_PCM_STOP_STOP;
-
- data->cparams.buf.block.frag_size=device->UpdateSize * device->frameSizeFromFmt();
- data->cparams.buf.block.frags_max=device->BufferSize / device->UpdateSize;
- data->cparams.buf.block.frags_min=data->cparams.buf.block.frags_max;
-
- data->cparams.format.interleave=1;
- data->cparams.format.rate=device->Frequency;
- data->cparams.format.voices=device->channelsFromFmt();
- data->cparams.format.format=format;
-
- if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
- {
- int original_rate=data->cparams.format.rate;
- int original_voices=data->cparams.format.voices;
- int original_format=data->cparams.format.format;
- int it;
- int jt;
-
- for (it=0; it<1; it++)
- {
- /* Check for second pass */
- if (it==1)
- {
- original_rate=ratelist[0].rate;
- original_voices=channellist[0].channels;
- original_format=formatlist[0].format;
- }
-
- do {
- /* At first downgrade sample format */
- jt=0;
- do {
- if (formatlist[jt].format==data->cparams.format.format)
- {
- data->cparams.format.format=formatlist[jt+1].format;
- break;
- }
- if (formatlist[jt].format==0)
- {
- data->cparams.format.format=0;
- break;
- }
- jt++;
- } while(1);
-
- if (data->cparams.format.format==0)
- {
- data->cparams.format.format=original_format;
-
- /* At secod downgrade sample rate */
- jt=0;
- do {
- if (ratelist[jt].rate==data->cparams.format.rate)
- {
- data->cparams.format.rate=ratelist[jt+1].rate;
- break;
- }
- if (ratelist[jt].rate==0)
- {
- data->cparams.format.rate=0;
- break;
- }
- jt++;
- } while(1);
-
- if (data->cparams.format.rate==0)
- {
- data->cparams.format.rate=original_rate;
- data->cparams.format.format=original_format;
-
- /* At third downgrade channels number */
- jt=0;
- do {
- if(channellist[jt].channels==data->cparams.format.voices)
- {
- data->cparams.format.voices=channellist[jt+1].channels;
- break;
- }
- if (channellist[jt].channels==0)
- {
- data->cparams.format.voices=0;
- break;
- }
- jt++;
- } while(1);
- }
-
- if (data->cparams.format.voices==0)
- {
- break;
- }
- }
-
- data->cparams.buf.block.frag_size=device->UpdateSize*
- data->cparams.format.voices*
- snd_pcm_format_width(data->cparams.format.format)/8;
- data->cparams.buf.block.frags_max=device->NumUpdates;
- data->cparams.buf.block.frags_min=device->NumUpdates;
- if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
- {
- continue;
- }
- else
- {
- break;
- }
- } while(1);
-
- if (data->cparams.format.voices!=0)
- {
- break;
- }
- }
-
- if (data->cparams.format.voices==0)
- {
- return ALC_FALSE;
- }
- }
-
- if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
- {
- return ALC_FALSE;
- }
-
- memset(&data->csetup, 0, sizeof(data->csetup));
- data->csetup.channel=SND_PCM_CHANNEL_PLAYBACK;
- if (snd_pcm_plugin_setup(data->pcmHandle, &data->csetup)<0)
- {
- return ALC_FALSE;
- }
-
- /* now fill back to the our AL device */
- device->Frequency=data->cparams.format.rate;
-
- switch (data->cparams.format.voices)
- {
- case 1:
- device->FmtChans=DevFmtMono;
- break;
- case 2:
- device->FmtChans=DevFmtStereo;
- break;
- case 4:
- device->FmtChans=DevFmtQuad;
- break;
- case 6:
- device->FmtChans=DevFmtX51;
- break;
- case 7:
- device->FmtChans=DevFmtX61;
- break;
- case 8:
- device->FmtChans=DevFmtX71;
- break;
- default:
- device->FmtChans=DevFmtMono;
- break;
- }
-
- switch (data->cparams.format.format)
- {
- case SND_PCM_SFMT_S8:
- device->FmtType=DevFmtByte;
- break;
- case SND_PCM_SFMT_U8:
- device->FmtType=DevFmtUByte;
- break;
- case SND_PCM_SFMT_S16_LE:
- device->FmtType=DevFmtShort;
- break;
- case SND_PCM_SFMT_U16_LE:
- device->FmtType=DevFmtUShort;
- break;
- case SND_PCM_SFMT_S32_LE:
- device->FmtType=DevFmtInt;
- break;
- case SND_PCM_SFMT_U32_LE:
- device->FmtType=DevFmtUInt;
- break;
- case SND_PCM_SFMT_FLOAT_LE:
- device->FmtType=DevFmtFloat;
- break;
- default:
- device->FmtType=DevFmtShort;
- break;
- }
-
- SetDefaultChannelOrder(device);
-
- device->UpdateSize=data->csetup.buf.block.frag_size / device->frameSizeFromFmt();
- device->NumUpdates=data->csetup.buf.block.frags;
-
- data->size=data->csetup.buf.block.frag_size;
- data->buffer=malloc(data->size);
- if (!data->buffer)
- {
- return ALC_FALSE;
- }
-
- return ALC_TRUE;
-}
-
-static ALCboolean qsa_start_playback(PlaybackWrapper *self)
-{
- qsa_data *data = self->mExtraData.get();
-
- try {
- data->mKillNow.store(AL_FALSE, std::memory_order_release);
- data->mThread = std::thread(qsa_proc_playback, self);
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-static void qsa_stop_playback(PlaybackWrapper *self)
-{
- qsa_data *data = self->mExtraData.get();
-
- if(data->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !data->mThread.joinable())
- return;
- data->mThread.join();
-}
-
-
-PlaybackWrapper::~PlaybackWrapper()
-{
- if(mExtraData)
- qsa_close_playback(this);
-}
-
-ALCenum PlaybackWrapper::open(const ALCchar *name)
-{ return qsa_open_playback(this, name); }
-
-ALCboolean PlaybackWrapper::reset()
-{ return qsa_reset_playback(this); }
-
-ALCboolean PlaybackWrapper::start()
-{ return qsa_start_playback(this); }
-
-void PlaybackWrapper::stop()
-{ qsa_stop_playback(this); }
-
-
-/***********/
-/* Capture */
-/***********/
-
-struct CaptureWrapper final : public BackendBase {
- CaptureWrapper(ALCdevice *device) noexcept : BackendBase{device} { }
- ~CaptureWrapper() override;
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- std::unique_ptr<qsa_data> mExtraData;
-
- DEF_NEWDEL(CaptureWrapper)
-};
-
-static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName)
-{
- ALCdevice *device = self->mDevice;
- int card, dev;
- int format=-1;
- int status;
-
- std::unique_ptr<qsa_data> data{new qsa_data{}};
-
- if(!deviceName)
- deviceName = qsaDevice;
-
- if(strcmp(deviceName, qsaDevice) == 0)
- status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE);
- else
- {
- if(CaptureNameMap.empty())
- deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
-
- auto iter = std::find_if(CaptureNameMap.cbegin(), CaptureNameMap.cend(),
- [deviceName](const DevMap &entry) -> bool
- { return entry.name && strcmp(deviceName, entry.name) == 0; }
- );
- if(iter == CaptureNameMap.cend())
- return ALC_INVALID_DEVICE;
-
- status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_CAPTURE);
- }
-
- if(status < 0)
- return ALC_INVALID_DEVICE;
-
- data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE);
- if(data->audio_fd < 0)
- {
- snd_pcm_close(data->pcmHandle);
- return ALC_INVALID_DEVICE;
- }
-
- device->DeviceName = deviceName;
-
- switch (device->FmtType)
- {
- case DevFmtByte:
- format=SND_PCM_SFMT_S8;
- break;
- case DevFmtUByte:
- format=SND_PCM_SFMT_U8;
- break;
- case DevFmtShort:
- format=SND_PCM_SFMT_S16_LE;
- break;
- case DevFmtUShort:
- format=SND_PCM_SFMT_U16_LE;
- break;
- case DevFmtInt:
- format=SND_PCM_SFMT_S32_LE;
- break;
- case DevFmtUInt:
- format=SND_PCM_SFMT_U32_LE;
- break;
- case DevFmtFloat:
- format=SND_PCM_SFMT_FLOAT_LE;
- break;
- }
-
- /* we actually don't want to block on reads */
- snd_pcm_nonblock_mode(data->pcmHandle, 1);
- /* Disable mmap to control data transfer to the audio device */
- snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
-
- /* configure a sound channel */
- memset(&data->cparams, 0, sizeof(data->cparams));
- data->cparams.mode=SND_PCM_MODE_BLOCK;
- data->cparams.channel=SND_PCM_CHANNEL_CAPTURE;
- data->cparams.start_mode=SND_PCM_START_GO;
- data->cparams.stop_mode=SND_PCM_STOP_STOP;
-
- data->cparams.buf.block.frag_size=device->UpdateSize * device->frameSizeFromFmt();
- data->cparams.buf.block.frags_max=device->NumUpdates;
- data->cparams.buf.block.frags_min=device->NumUpdates;
-
- data->cparams.format.interleave=1;
- data->cparams.format.rate=device->Frequency;
- data->cparams.format.voices=device->channelsFromFmt();
- data->cparams.format.format=format;
-
- if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0)
- {
- snd_pcm_close(data->pcmHandle);
- return ALC_INVALID_VALUE;
- }
-
- self->mExtraData = std::move(data);
-
- return ALC_NO_ERROR;
-}
-
-static void qsa_close_capture(CaptureWrapper *self)
-{
- qsa_data *data = self->mExtraData.get();
-
- if (data->pcmHandle!=nullptr)
- snd_pcm_close(data->pcmHandle);
- data->pcmHandle = nullptr;
-
- self->mExtraData = nullptr;
-}
-
-static void qsa_start_capture(CaptureWrapper *self)
-{
- qsa_data *data = self->mExtraData.get();
- int rstatus;
-
- if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
- {
- ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
- return;
- }
-
- memset(&data->csetup, 0, sizeof(data->csetup));
- data->csetup.channel=SND_PCM_CHANNEL_CAPTURE;
- if ((rstatus=snd_pcm_plugin_setup(data->pcmHandle, &data->csetup))<0)
- {
- ERR("capture setup failed: %s\n", snd_strerror(rstatus));
- return;
- }
-
- snd_pcm_capture_go(data->pcmHandle);
-}
-
-static void qsa_stop_capture(CaptureWrapper *self)
-{
- qsa_data *data = self->mExtraData.get();
- snd_pcm_capture_flush(data->pcmHandle);
-}
-
-static ALCuint qsa_available_samples(CaptureWrapper *self)
-{
- ALCdevice *device = self->mDevice;
- qsa_data *data = self->mExtraData.get();
- snd_pcm_channel_status_t status;
- ALint frame_size = device->frameSizeFromFmt();
- ALint free_size;
- int rstatus;
-
- memset(&status, 0, sizeof (status));
- status.channel=SND_PCM_CHANNEL_CAPTURE;
- snd_pcm_plugin_status(data->pcmHandle, &status);
- if ((status.status==SND_PCM_STATUS_OVERRUN) ||
- (status.status==SND_PCM_STATUS_READY))
- {
- if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
- {
- ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
- aluHandleDisconnect(device, "Failed capture recovery: %s", snd_strerror(rstatus));
- return 0;
- }
-
- snd_pcm_capture_go(data->pcmHandle);
- return 0;
- }
-
- free_size=data->csetup.buf.block.frag_size*data->csetup.buf.block.frags;
- free_size-=status.free;
-
- return free_size/frame_size;
-}
-
-static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples)
-{
- ALCdevice *device = self->mDevice;
- qsa_data *data = self->mExtraData.get();
- char* read_ptr;
- snd_pcm_channel_status_t status;
- int selectret;
- int bytes_read;
- ALint frame_size=device->frameSizeFromFmt();
- ALint len=samples*frame_size;
- int rstatus;
-
- read_ptr = static_cast<char*>(buffer);
-
- while (len>0)
- {
- pollfd pollitem{};
- pollitem.fd = data->audio_fd;
- pollitem.events = POLLOUT;
-
- /* Select also works like time slice to OS */
- bytes_read=0;
- selectret = poll(&pollitem, 1, 2000);
- switch (selectret)
- {
- case -1:
- aluHandleDisconnect(device, "Failed to check capture samples");
- return ALC_INVALID_DEVICE;
- case 0:
- break;
- default:
- bytes_read=snd_pcm_plugin_read(data->pcmHandle, read_ptr, len);
- break;
- }
-
- if (bytes_read<=0)
- {
- if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
- {
- continue;
- }
-
- memset(&status, 0, sizeof (status));
- status.channel=SND_PCM_CHANNEL_CAPTURE;
- snd_pcm_plugin_status(data->pcmHandle, &status);
-
- /* we need to reinitialize the sound channel if we've overrun the buffer */
- if ((status.status==SND_PCM_STATUS_OVERRUN) ||
- (status.status==SND_PCM_STATUS_READY))
- {
- if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
- {
- ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
- aluHandleDisconnect(device, "Failed capture recovery: %s",
- snd_strerror(rstatus));
- return ALC_INVALID_DEVICE;
- }
- snd_pcm_capture_go(data->pcmHandle);
- }
- }
- else
- {
- read_ptr+=bytes_read;
- len-=bytes_read;
- }
- }
-
- return ALC_NO_ERROR;
-}
-
-
-CaptureWrapper::~CaptureWrapper()
-{
- if(mExtraData)
- qsa_close_capture(this);
-}
-
-ALCenum CaptureWrapper::open(const ALCchar *name)
-{ return qsa_open_capture(this, name); }
-
-ALCboolean CaptureWrapper::start()
-{ qsa_start_capture(this); return ALC_TRUE; }
-
-void CaptureWrapper::stop()
-{ qsa_stop_capture(this); }
-
-ALCenum CaptureWrapper::captureSamples(void *buffer, ALCuint samples)
-{ return qsa_capture_samples(this, buffer, samples); }
-
-ALCuint CaptureWrapper::availableSamples()
-{ return qsa_available_samples(this); }
-
-} // namespace
-
-
-bool QSABackendFactory::init()
-{ return true; }
-
-bool QSABackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void QSABackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const DevMap &entry) -> void
- {
- const char *n = entry.name;
- if(n && n[0])
- outnames->append(n, strlen(n)+1);
- };
-
- switch (type)
- {
- case DevProbe::Playback:
- deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
- std::for_each(DeviceNameMap.cbegin(), DeviceNameMap.cend(), add_device);
- break;
- case DevProbe::Capture:
- deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
- std::for_each(CaptureNameMap.cbegin(), CaptureNameMap.cend(), add_device);
- break;
- }
-}
-
-BackendPtr QSABackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new PlaybackWrapper{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new CaptureWrapper{device}};
- return nullptr;
-}
-
-BackendFactory &QSABackendFactory::getFactory()
-{
- static QSABackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/qsa.h b/Alc/backends/qsa.h
deleted file mode 100644
index da548bba..00000000
--- a/Alc/backends/qsa.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_QSA_H
-#define BACKENDS_QSA_H
-
-#include "backends/base.h"
-
-struct QSABackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_QSA_H */
diff --git a/Alc/backends/sdl2.cpp b/Alc/backends/sdl2.cpp
deleted file mode 100644
index 97547959..00000000
--- a/Alc/backends/sdl2.cpp
+++ /dev/null
@@ -1,223 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2018 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 "backends/sdl2.h"
-
-#include <stdlib.h>
-#include <SDL2/SDL.h>
-
-#include <string>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "threads.h"
-#include "compat.h"
-
-
-namespace {
-
-#ifdef _WIN32
-#define DEVNAME_PREFIX "OpenAL Soft on "
-#else
-#define DEVNAME_PREFIX ""
-#endif
-
-constexpr ALCchar defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
-
-struct Sdl2Backend final : public BackendBase {
- Sdl2Backend(ALCdevice *device) noexcept : BackendBase{device} { }
- ~Sdl2Backend() override;
-
- static void audioCallbackC(void *ptr, Uint8 *stream, int len);
- void audioCallback(Uint8 *stream, int len);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
- void lock() override;
- void unlock() override;
-
- SDL_AudioDeviceID mDeviceID{0u};
- ALsizei mFrameSize{0};
-
- ALuint mFrequency{0u};
- DevFmtChannels mFmtChans{};
- DevFmtType mFmtType{};
- ALuint mUpdateSize{0u};
-
- DEF_NEWDEL(Sdl2Backend)
-};
-
-Sdl2Backend::~Sdl2Backend()
-{
- if(mDeviceID)
- SDL_CloseAudioDevice(mDeviceID);
- mDeviceID = 0;
-}
-
-void Sdl2Backend::audioCallbackC(void *ptr, Uint8 *stream, int len)
-{ static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); }
-
-void Sdl2Backend::audioCallback(Uint8 *stream, int len)
-{
- assert((len % mFrameSize) == 0);
- aluMixData(mDevice, stream, len / mFrameSize);
-}
-
-ALCenum Sdl2Backend::open(const ALCchar *name)
-{
- SDL_AudioSpec want{}, have{};
- want.freq = mDevice->Frequency;
- switch(mDevice->FmtType)
- {
- case DevFmtUByte: want.format = AUDIO_U8; break;
- case DevFmtByte: want.format = AUDIO_S8; break;
- case DevFmtUShort: want.format = AUDIO_U16SYS; break;
- case DevFmtShort: want.format = AUDIO_S16SYS; break;
- case DevFmtUInt: /* fall-through */
- case DevFmtInt: want.format = AUDIO_S32SYS; break;
- case DevFmtFloat: want.format = AUDIO_F32; break;
- }
- want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2;
- want.samples = mDevice->UpdateSize;
- want.callback = &Sdl2Backend::audioCallbackC;
- want.userdata = this;
-
- /* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't
- * necessarily the first in the list.
- */
- if(!name || strcmp(name, defaultDeviceName) == 0)
- mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have,
- SDL_AUDIO_ALLOW_ANY_CHANGE);
- else
- {
- const size_t prefix_len = strlen(DEVNAME_PREFIX);
- if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
- mDeviceID = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
- SDL_AUDIO_ALLOW_ANY_CHANGE);
- else
- mDeviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have,
- SDL_AUDIO_ALLOW_ANY_CHANGE);
- }
- if(mDeviceID == 0)
- return ALC_INVALID_VALUE;
-
- mDevice->Frequency = have.freq;
- if(have.channels == 1)
- mDevice->FmtChans = DevFmtMono;
- else if(have.channels == 2)
- mDevice->FmtChans = DevFmtStereo;
- else
- {
- ERR("Got unhandled SDL channel count: %d\n", (int)have.channels);
- return ALC_INVALID_VALUE;
- }
- switch(have.format)
- {
- case AUDIO_U8: mDevice->FmtType = DevFmtUByte; break;
- case AUDIO_S8: mDevice->FmtType = DevFmtByte; break;
- case AUDIO_U16SYS: mDevice->FmtType = DevFmtUShort; break;
- case AUDIO_S16SYS: mDevice->FmtType = DevFmtShort; break;
- case AUDIO_S32SYS: mDevice->FmtType = DevFmtInt; break;
- case AUDIO_F32SYS: mDevice->FmtType = DevFmtFloat; break;
- default:
- ERR("Got unsupported SDL format: 0x%04x\n", have.format);
- return ALC_INVALID_VALUE;
- }
- mDevice->UpdateSize = have.samples;
- mDevice->BufferSize = have.samples * 2; /* SDL always (tries to) use two periods. */
-
- mFrameSize = mDevice->frameSizeFromFmt();
- mFrequency = mDevice->Frequency;
- mFmtChans = mDevice->FmtChans;
- mFmtType = mDevice->FmtType;
- mUpdateSize = mDevice->UpdateSize;
-
- mDevice->DeviceName = name ? name : defaultDeviceName;
- return ALC_NO_ERROR;
-}
-
-ALCboolean Sdl2Backend::reset()
-{
- mDevice->Frequency = mFrequency;
- mDevice->FmtChans = mFmtChans;
- mDevice->FmtType = mFmtType;
- mDevice->UpdateSize = mUpdateSize;
- mDevice->BufferSize = mUpdateSize * 2;
- SetDefaultWFXChannelOrder(mDevice);
- return ALC_TRUE;
-}
-
-ALCboolean Sdl2Backend::start()
-{
- SDL_PauseAudioDevice(mDeviceID, 0);
- return ALC_TRUE;
-}
-
-void Sdl2Backend::stop()
-{ SDL_PauseAudioDevice(mDeviceID, 1); }
-
-void Sdl2Backend::lock()
-{ SDL_LockAudioDevice(mDeviceID); }
-
-void Sdl2Backend::unlock()
-{ SDL_UnlockAudioDevice(mDeviceID); }
-
-} // namespace
-
-BackendFactory &SDL2BackendFactory::getFactory()
-{
- static SDL2BackendFactory factory{};
- return factory;
-}
-
-bool SDL2BackendFactory::init()
-{ return (SDL_InitSubSystem(SDL_INIT_AUDIO) == 0); }
-
-bool SDL2BackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback; }
-
-void SDL2BackendFactory::probe(DevProbe type, std::string *outnames)
-{
- if(type != DevProbe::Playback)
- return;
-
- int num_devices{SDL_GetNumAudioDevices(SDL_FALSE)};
-
- /* Includes null char. */
- outnames->append(defaultDeviceName, sizeof(defaultDeviceName));
- for(int i{0};i < num_devices;++i)
- {
- std::string name{DEVNAME_PREFIX};
- name += SDL_GetAudioDeviceName(i, SDL_FALSE);
- if(!name.empty())
- outnames->append(name.c_str(), name.length()+1);
- }
-}
-
-BackendPtr SDL2BackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new Sdl2Backend{device}};
- return nullptr;
-}
diff --git a/Alc/backends/sdl2.h b/Alc/backends/sdl2.h
deleted file mode 100644
index 041d47ee..00000000
--- a/Alc/backends/sdl2.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_SDL2_H
-#define BACKENDS_SDL2_H
-
-#include "backends/base.h"
-
-struct SDL2BackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_SDL2_H */
diff --git a/Alc/backends/sndio.cpp b/Alc/backends/sndio.cpp
deleted file mode 100644
index 587f67bb..00000000
--- a/Alc/backends/sndio.cpp
+++ /dev/null
@@ -1,495 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/sndio.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <thread>
-#include <functional>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "threads.h"
-#include "vector.h"
-#include "ringbuffer.h"
-
-#include <sndio.h>
-
-
-namespace {
-
-static const ALCchar sndio_device[] = "SndIO Default";
-
-
-struct SndioPlayback final : public BackendBase {
- SndioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~SndioPlayback() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- sio_hdl *mSndHandle{nullptr};
-
- al::vector<ALubyte> mBuffer;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(SndioPlayback)
-};
-
-SndioPlayback::~SndioPlayback()
-{
- if(mSndHandle)
- sio_close(mSndHandle);
- mSndHandle = nullptr;
-}
-
-int SndioPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- const ALsizei frameSize{mDevice->frameSizeFromFmt()};
-
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- auto WritePtr = static_cast<ALubyte*>(mBuffer.data());
- size_t len{mBuffer.size()};
-
- lock();
- aluMixData(mDevice, WritePtr, len/frameSize);
- unlock();
- while(len > 0 && !mKillNow.load(std::memory_order_acquire))
- {
- size_t wrote{sio_write(mSndHandle, WritePtr, len)};
- if(wrote == 0)
- {
- ERR("sio_write failed\n");
- aluHandleDisconnect(mDevice, "Failed to write playback samples");
- break;
- }
-
- len -= wrote;
- WritePtr += wrote;
- }
- }
-
- return 0;
-}
-
-
-ALCenum SndioPlayback::open(const ALCchar *name)
-{
- if(!name)
- name = sndio_device;
- else if(strcmp(name, sndio_device) != 0)
- return ALC_INVALID_VALUE;
-
- mSndHandle = sio_open(nullptr, SIO_PLAY, 0);
- if(mSndHandle == nullptr)
- {
- ERR("Could not open device\n");
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean SndioPlayback::reset()
-{
- sio_par par;
- sio_initpar(&par);
-
- par.rate = mDevice->Frequency;
- par.pchan = ((mDevice->FmtChans != DevFmtMono) ? 2 : 1);
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- par.bits = 8;
- par.sig = 1;
- break;
- case DevFmtUByte:
- par.bits = 8;
- par.sig = 0;
- break;
- case DevFmtFloat:
- case DevFmtShort:
- par.bits = 16;
- par.sig = 1;
- break;
- case DevFmtUShort:
- par.bits = 16;
- par.sig = 0;
- break;
- case DevFmtInt:
- par.bits = 32;
- par.sig = 1;
- break;
- case DevFmtUInt:
- par.bits = 32;
- par.sig = 0;
- break;
- }
- par.le = SIO_LE_NATIVE;
-
- par.round = mDevice->UpdateSize;
- par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize;
- if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
-
- if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
- {
- ERR("Failed to set device parameters\n");
- return ALC_FALSE;
- }
-
- if(par.bits != par.bps*8)
- {
- ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
- return ALC_FALSE;
- }
-
- mDevice->Frequency = par.rate;
- mDevice->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
-
- if(par.bits == 8 && par.sig == 1)
- mDevice->FmtType = DevFmtByte;
- else if(par.bits == 8 && par.sig == 0)
- mDevice->FmtType = DevFmtUByte;
- else if(par.bits == 16 && par.sig == 1)
- mDevice->FmtType = DevFmtShort;
- else if(par.bits == 16 && par.sig == 0)
- mDevice->FmtType = DevFmtUShort;
- else if(par.bits == 32 && par.sig == 1)
- mDevice->FmtType = DevFmtInt;
- else if(par.bits == 32 && par.sig == 0)
- mDevice->FmtType = DevFmtUInt;
- else
- {
- ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
- return ALC_FALSE;
- }
-
- SetDefaultChannelOrder(mDevice);
-
- mDevice->UpdateSize = par.round;
- mDevice->BufferSize = par.bufsz + par.round;
-
- mBuffer.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
- std::fill(mBuffer.begin(), mBuffer.end(), 0);
-
- return ALC_TRUE;
-}
-
-ALCboolean SndioPlayback::start()
-{
- if(!sio_start(mSndHandle))
- {
- ERR("Error starting playback\n");
- return ALC_FALSE;
- }
-
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&SndioPlayback::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- sio_stop(mSndHandle);
- return ALC_FALSE;
-}
-
-void SndioPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- if(!sio_stop(mSndHandle))
- ERR("Error stopping device\n");
-}
-
-
-struct SndioCapture final : public BackendBase {
- SndioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~SndioCapture() override;
-
- int recordProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- sio_hdl *mSndHandle{nullptr};
-
- RingBufferPtr mRing;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(SndioCapture)
-};
-
-SndioCapture::~SndioCapture()
-{
- if(mSndHandle)
- sio_close(mSndHandle);
- mSndHandle = nullptr;
-}
-
-int SndioCapture::recordProc()
-{
- SetRTPriority();
- althrd_setname(RECORD_THREAD_NAME);
-
- const ALsizei frameSize{mDevice->frameSizeFromFmt()};
-
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- auto data = mRing->getWriteVector();
- size_t todo{data.first.len + data.second.len};
- if(todo == 0)
- {
- static char junk[4096];
- sio_read(mSndHandle, junk,
- minz(sizeof(junk)/frameSize, mDevice->UpdateSize)*frameSize);
- continue;
- }
-
- size_t total{0u};
- data.first.len *= frameSize;
- data.second.len *= frameSize;
- todo = minz(todo, mDevice->UpdateSize) * frameSize;
- while(total < todo)
- {
- if(!data.first.len)
- data.first = data.second;
-
- size_t got{sio_read(mSndHandle, data.first.buf, minz(todo-total, data.first.len))};
- if(!got)
- {
- aluHandleDisconnect(mDevice, "Failed to read capture samples");
- break;
- }
-
- data.first.buf += got;
- data.first.len -= got;
- total += got;
- }
- mRing->writeAdvance(total / frameSize);
- }
-
- return 0;
-}
-
-
-ALCenum SndioCapture::open(const ALCchar *name)
-{
- if(!name)
- name = sndio_device;
- else if(strcmp(name, sndio_device) != 0)
- return ALC_INVALID_VALUE;
-
- mSndHandle = sio_open(nullptr, SIO_REC, 0);
- if(mSndHandle == nullptr)
- {
- ERR("Could not open device\n");
- return ALC_INVALID_VALUE;
- }
-
- sio_par par;
- sio_initpar(&par);
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- par.bps = 1;
- par.sig = 1;
- break;
- case DevFmtUByte:
- par.bps = 1;
- par.sig = 0;
- break;
- case DevFmtShort:
- par.bps = 2;
- par.sig = 1;
- break;
- case DevFmtUShort:
- par.bps = 2;
- par.sig = 0;
- break;
- case DevFmtInt:
- par.bps = 4;
- par.sig = 1;
- break;
- case DevFmtUInt:
- par.bps = 4;
- par.sig = 0;
- break;
- case DevFmtFloat:
- ERR("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
- return ALC_INVALID_VALUE;
- }
- par.bits = par.bps * 8;
- par.le = SIO_LE_NATIVE;
- par.msb = SIO_LE_NATIVE ? 0 : 1;
- par.rchan = mDevice->channelsFromFmt();
- par.rate = mDevice->Frequency;
-
- par.appbufsz = maxu(mDevice->BufferSize, mDevice->Frequency/10);
- par.round = minu(par.appbufsz, mDevice->Frequency/40);
-
- mDevice->UpdateSize = par.round;
- mDevice->BufferSize = par.appbufsz;
-
- if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
- {
- ERR("Failed to set device parameters\n");
- return ALC_INVALID_VALUE;
- }
-
- if(par.bits != par.bps*8)
- {
- ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
- return ALC_INVALID_VALUE;
- }
-
- if(!((mDevice->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0) ||
- (mDevice->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0) ||
- (mDevice->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0) ||
- (mDevice->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0) ||
- (mDevice->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0) ||
- (mDevice->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0)) ||
- mDevice->channelsFromFmt() != (ALsizei)par.rchan ||
- mDevice->Frequency != par.rate)
- {
- ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
- DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans),
- mDevice->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate);
- return ALC_INVALID_VALUE;
- }
-
- mRing = CreateRingBuffer(mDevice->BufferSize, par.bps*par.rchan, false);
- if(!mRing)
- {
- ERR("Failed to allocate %u-byte ringbuffer\n", mDevice->BufferSize*par.bps*par.rchan);
- return ALC_OUT_OF_MEMORY;
- }
-
- SetDefaultChannelOrder(mDevice);
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean SndioCapture::start()
-{
- if(!sio_start(mSndHandle))
- {
- ERR("Error starting playback\n");
- return ALC_FALSE;
- }
-
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&SndioCapture::recordProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create record thread: %s\n", e.what());
- }
- catch(...) {
- }
- sio_stop(mSndHandle);
- return ALC_FALSE;
-}
-
-void SndioCapture::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- if(!sio_stop(mSndHandle))
- ERR("Error stopping device\n");
-}
-
-ALCenum SndioCapture::captureSamples(void *buffer, ALCuint samples)
-{
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
-}
-
-ALCuint SndioCapture::availableSamples()
-{ return mRing->readSpace(); }
-
-} // namespace
-
-BackendFactory &SndIOBackendFactory::getFactory()
-{
- static SndIOBackendFactory factory{};
- return factory;
-}
-
-bool SndIOBackendFactory::init()
-{ return true; }
-
-bool SndIOBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void SndIOBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- case DevProbe::Capture:
- /* Includes null char. */
- outnames->append(sndio_device, sizeof(sndio_device));
- break;
- }
-}
-
-BackendPtr SndIOBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new SndioPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new SndioCapture{device}};
- return nullptr;
-}
diff --git a/Alc/backends/sndio.h b/Alc/backends/sndio.h
deleted file mode 100644
index 1ed63d5e..00000000
--- a/Alc/backends/sndio.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_SNDIO_H
-#define BACKENDS_SNDIO_H
-
-#include "backends/base.h"
-
-struct SndIOBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_SNDIO_H */
diff --git a/Alc/backends/solaris.cpp b/Alc/backends/solaris.cpp
deleted file mode 100644
index 584f6e66..00000000
--- a/Alc/backends/solaris.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/solaris.h"
-
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <memory.h>
-#include <unistd.h>
-#include <errno.h>
-#include <poll.h>
-#include <math.h>
-
-#include <thread>
-#include <functional>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "alconfig.h"
-#include "threads.h"
-#include "vector.h"
-#include "compat.h"
-
-#include <sys/audioio.h>
-
-
-namespace {
-
-constexpr ALCchar solaris_device[] = "Solaris Default";
-
-std::string solaris_driver{"/dev/audio"};
-
-
-struct SolarisBackend final : public BackendBase {
- SolarisBackend(ALCdevice *device) noexcept : BackendBase{device} { }
- ~SolarisBackend() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- int mFd{-1};
-
- al::vector<ALubyte> mBuffer;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(SolarisBackend)
-};
-
-SolarisBackend::~SolarisBackend()
-{
- if(mFd != -1)
- close(mFd);
- mFd = -1;
-}
-
-int SolarisBackend::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- const int frame_size{mDevice->frameSizeFromFmt()};
-
- lock();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- pollfd pollitem{};
- pollitem.fd = mFd;
- pollitem.events = POLLOUT;
-
- unlock();
- int pret{poll(&pollitem, 1, 1000)};
- lock();
- if(pret < 0)
- {
- if(errno == EINTR || errno == EAGAIN)
- continue;
- ERR("poll failed: %s\n", strerror(errno));
- aluHandleDisconnect(mDevice, "Failed to wait for playback buffer: %s",
- strerror(errno));
- break;
- }
- else if(pret == 0)
- {
- WARN("poll timeout\n");
- continue;
- }
-
- ALubyte *write_ptr{mBuffer.data()};
- size_t to_write{mBuffer.size()};
- aluMixData(mDevice, write_ptr, to_write/frame_size);
- while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
- {
- ssize_t wrote{write(mFd, write_ptr, to_write)};
- if(wrote < 0)
- {
- if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
- continue;
- ERR("write failed: %s\n", strerror(errno));
- aluHandleDisconnect(mDevice, "Failed to write playback samples: %s",
- strerror(errno));
- break;
- }
-
- to_write -= wrote;
- write_ptr += wrote;
- }
- }
- unlock();
-
- return 0;
-}
-
-
-ALCenum SolarisBackend::open(const ALCchar *name)
-{
- if(!name)
- name = solaris_device;
- else if(strcmp(name, solaris_device) != 0)
- return ALC_INVALID_VALUE;
-
- mFd = ::open(solaris_driver.c_str(), O_WRONLY);
- if(mFd == -1)
- {
- ERR("Could not open %s: %s\n", solaris_driver.c_str(), strerror(errno));
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean SolarisBackend::reset()
-{
- audio_info_t info;
- AUDIO_INITINFO(&info);
-
- info.play.sample_rate = mDevice->Frequency;
-
- if(mDevice->FmtChans != DevFmtMono)
- mDevice->FmtChans = DevFmtStereo;
- ALsizei numChannels{mDevice->channelsFromFmt()};
- info.play.channels = numChannels;
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- info.play.precision = 8;
- info.play.encoding = AUDIO_ENCODING_LINEAR;
- break;
- case DevFmtUByte:
- info.play.precision = 8;
- info.play.encoding = AUDIO_ENCODING_LINEAR8;
- break;
- case DevFmtUShort:
- case DevFmtInt:
- case DevFmtUInt:
- case DevFmtFloat:
- mDevice->FmtType = DevFmtShort;
- /* fall-through */
- case DevFmtShort:
- info.play.precision = 16;
- info.play.encoding = AUDIO_ENCODING_LINEAR;
- break;
- }
-
- ALsizei frameSize{numChannels * mDevice->bytesFromFmt()};
- info.play.buffer_size = mDevice->BufferSize * frameSize;
-
- if(ioctl(mFd, AUDIO_SETINFO, &info) < 0)
- {
- ERR("ioctl failed: %s\n", strerror(errno));
- return ALC_FALSE;
- }
-
- if(mDevice->channelsFromFmt() != (ALsizei)info.play.channels)
- {
- ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
- info.play.channels);
- return ALC_FALSE;
- }
-
- if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && mDevice->FmtType == DevFmtUByte) ||
- (info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtByte) ||
- (info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtShort) ||
- (info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtInt)))
- {
- ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(mDevice->FmtType),
- info.play.precision, info.play.encoding);
- return ALC_FALSE;
- }
-
- mDevice->Frequency = info.play.sample_rate;
- mDevice->BufferSize = info.play.buffer_size / frameSize;
- mDevice->UpdateSize = mDevice->BufferSize / 2;
-
- SetDefaultChannelOrder(mDevice);
-
- mBuffer.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
- std::fill(mBuffer.begin(), mBuffer.end(), 0);
-
- return ALC_TRUE;
-}
-
-ALCboolean SolarisBackend::start()
-{
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&SolarisBackend::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void SolarisBackend::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- if(ioctl(mFd, AUDIO_DRAIN) < 0)
- ERR("Error draining device: %s\n", strerror(errno));
-}
-
-} // namespace
-
-BackendFactory &SolarisBackendFactory::getFactory()
-{
- static SolarisBackendFactory factory{};
- return factory;
-}
-
-bool SolarisBackendFactory::init()
-{
- if(auto devopt = ConfigValueStr(nullptr, "solaris", "device"))
- solaris_driver = std::move(*devopt);
- return true;
-}
-
-bool SolarisBackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback; }
-
-void SolarisBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- {
-#ifdef HAVE_STAT
- struct stat buf;
- if(stat(solaris_driver.c_str(), &buf) == 0)
-#endif
- outnames->append(solaris_device, sizeof(solaris_device));
- }
- break;
-
- case DevProbe::Capture:
- break;
- }
-}
-
-BackendPtr SolarisBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new SolarisBackend{device}};
- return nullptr;
-}
diff --git a/Alc/backends/solaris.h b/Alc/backends/solaris.h
deleted file mode 100644
index 98b10593..00000000
--- a/Alc/backends/solaris.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_SOLARIS_H
-#define BACKENDS_SOLARIS_H
-
-#include "backends/base.h"
-
-struct SolarisBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_SOLARIS_H */
diff --git a/Alc/backends/wasapi.cpp b/Alc/backends/wasapi.cpp
deleted file mode 100644
index bd009463..00000000
--- a/Alc/backends/wasapi.cpp
+++ /dev/null
@@ -1,1763 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2011 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/wasapi.h"
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <memory.h>
-
-#include <wtypes.h>
-#include <mmdeviceapi.h>
-#include <audioclient.h>
-#include <cguid.h>
-#include <devpropdef.h>
-#include <mmreg.h>
-#include <propsys.h>
-#include <propkey.h>
-#include <devpkey.h>
-#ifndef _WAVEFORMATEXTENSIBLE_
-#include <ks.h>
-#include <ksmedia.h>
-#endif
-
-#include <deque>
-#include <mutex>
-#include <atomic>
-#include <thread>
-#include <vector>
-#include <string>
-#include <future>
-#include <algorithm>
-#include <functional>
-#include <condition_variable>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "ringbuffer.h"
-#include "compat.h"
-#include "converter.h"
-#include "threads.h"
-
-
-/* Some headers seem to define these as macros for __uuidof, which is annoying
- * since some headers don't declare them at all. Hopefully the ifdef is enough
- * to tell if they need to be declared.
- */
-#ifndef KSDATAFORMAT_SUBTYPE_PCM
-DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
-#endif
-#ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
-DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
-#endif
-
-DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
-DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
-DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
-
-
-namespace {
-
-#define MONO SPEAKER_FRONT_CENTER
-#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
-#define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
-#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
-#define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
-#define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
-#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
-#define X7DOT1_WIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_LEFT_OF_CENTER|SPEAKER_FRONT_RIGHT_OF_CENTER)
-
-#define REFTIME_PER_SEC ((REFERENCE_TIME)10000000)
-
-#define DEVNAME_HEAD "OpenAL Soft on "
-
-
-/* Scales the given value using 64-bit integer math, ceiling the result. */
-inline int64_t ScaleCeil(int64_t val, int64_t new_scale, int64_t old_scale)
-{
- return (val*new_scale + old_scale-1) / old_scale;
-}
-
-
-struct PropVariant {
- PROPVARIANT mProp;
-
-public:
- PropVariant() { PropVariantInit(&mProp); }
- ~PropVariant() { clear(); }
-
- void clear() { PropVariantClear(&mProp); }
-
- PROPVARIANT* get() noexcept { return &mProp; }
-
- PROPVARIANT& operator*() noexcept { return mProp; }
- const PROPVARIANT& operator*() const noexcept { return mProp; }
-
- PROPVARIANT* operator->() noexcept { return &mProp; }
- const PROPVARIANT* operator->() const noexcept { return &mProp; }
-};
-
-struct DevMap {
- std::string name;
- std::string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
- std::wstring devid;
-
- template<typename T0, typename T1, typename T2>
- DevMap(T0&& name_, T1&& guid_, T2&& devid_)
- : name{std::forward<T0>(name_)}
- , endpoint_guid{std::forward<T1>(guid_)}
- , devid{std::forward<T2>(devid_)}
- { }
-};
-
-bool checkName(const al::vector<DevMap> &list, const std::string &name)
-{
- return std::find_if(list.cbegin(), list.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- ) != list.cend();
-}
-
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
-
-
-using NameGUIDPair = std::pair<std::string,std::string>;
-NameGUIDPair get_device_name_and_guid(IMMDevice *device)
-{
- std::string name{DEVNAME_HEAD};
- std::string guid;
-
- IPropertyStore *ps;
- HRESULT hr = device->OpenPropertyStore(STGM_READ, &ps);
- if(FAILED(hr))
- {
- WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
- return { name+"Unknown Device Name", "Unknown Device GUID" };
- }
-
- PropVariant pvprop;
- hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(DEVPKEY_Device_FriendlyName), pvprop.get());
- if(FAILED(hr))
- {
- WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
- name += "Unknown Device Name";
- }
- else if(pvprop->vt == VT_LPWSTR)
- name += wstr_to_utf8(pvprop->pwszVal);
- else
- {
- WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt);
- name += "Unknown Device Name";
- }
-
- pvprop.clear();
- hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(PKEY_AudioEndpoint_GUID), pvprop.get());
- if(FAILED(hr))
- {
- WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
- guid = "Unknown Device GUID";
- }
- else if(pvprop->vt == VT_LPWSTR)
- guid = wstr_to_utf8(pvprop->pwszVal);
- else
- {
- WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt);
- guid = "Unknown Device GUID";
- }
-
- ps->Release();
-
- return {name, guid};
-}
-
-void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor)
-{
- IPropertyStore *ps;
- HRESULT hr = device->OpenPropertyStore(STGM_READ, &ps);
- if(FAILED(hr))
- {
- WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
- return;
- }
-
- PropVariant pvform;
- hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(PKEY_AudioEndpoint_FormFactor), pvform.get());
- if(FAILED(hr))
- WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
- else if(pvform->vt == VT_UI4)
- *formfactor = static_cast<EndpointFormFactor>(pvform->ulVal);
- else if(pvform->vt == VT_EMPTY)
- *formfactor = UnknownFormFactor;
- else
- WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt);
-
- ps->Release();
-}
-
-
-void add_device(IMMDevice *device, const WCHAR *devid, al::vector<DevMap> &list)
-{
- std::string basename, guidstr;
- std::tie(basename, guidstr) = get_device_name_and_guid(device);
-
- int count{1};
- std::string newname{basename};
- while(checkName(list, newname))
- {
- newname = basename;
- newname += " #";
- newname += std::to_string(++count);
- }
- list.emplace_back(std::move(newname), std::move(guidstr), devid);
- const DevMap &newentry = list.back();
-
- TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", newentry.name.c_str(),
- newentry.endpoint_guid.c_str(), newentry.devid.c_str());
-}
-
-WCHAR *get_device_id(IMMDevice *device)
-{
- WCHAR *devid;
-
- HRESULT hr = device->GetId(&devid);
- if(FAILED(hr))
- {
- ERR("Failed to get device id: %lx\n", hr);
- return nullptr;
- }
-
- return devid;
-}
-
-HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, al::vector<DevMap> &list)
-{
- IMMDeviceCollection *coll;
- HRESULT hr{devenum->EnumAudioEndpoints(flowdir, DEVICE_STATE_ACTIVE, &coll)};
- if(FAILED(hr))
- {
- ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
- return hr;
- }
-
- IMMDevice *defdev{nullptr};
- WCHAR *defdevid{nullptr};
- UINT count{0};
- hr = coll->GetCount(&count);
- if(SUCCEEDED(hr) && count > 0)
- {
- list.clear();
- list.reserve(count);
-
- hr = devenum->GetDefaultAudioEndpoint(flowdir, eMultimedia, &defdev);
- }
- if(SUCCEEDED(hr) && defdev != nullptr)
- {
- defdevid = get_device_id(defdev);
- if(defdevid)
- add_device(defdev, defdevid, list);
- }
-
- for(UINT i{0};i < count;++i)
- {
- IMMDevice *device;
- hr = coll->Item(i, &device);
- if(FAILED(hr)) continue;
-
- WCHAR *devid{get_device_id(device)};
- if(devid)
- {
- if(!defdevid || wcscmp(devid, defdevid) != 0)
- add_device(device, devid, list);
- CoTaskMemFree(devid);
- }
- device->Release();
- }
-
- if(defdev) defdev->Release();
- if(defdevid) CoTaskMemFree(defdevid);
- coll->Release();
-
- return S_OK;
-}
-
-
-bool MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
-{
- *out = WAVEFORMATEXTENSIBLE{};
- if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
- {
- *out = *CONTAINING_RECORD(in, const WAVEFORMATEXTENSIBLE, Format);
- out->Format.cbSize = sizeof(*out) - sizeof(out->Format);
- }
- else if(in->wFormatTag == WAVE_FORMAT_PCM)
- {
- out->Format = *in;
- out->Format.cbSize = 0;
- out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample;
- if(out->Format.nChannels == 1)
- out->dwChannelMask = MONO;
- else if(out->Format.nChannels == 2)
- out->dwChannelMask = STEREO;
- else
- ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
- out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- }
- else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
- {
- out->Format = *in;
- out->Format.cbSize = 0;
- out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample;
- if(out->Format.nChannels == 1)
- out->dwChannelMask = MONO;
- else if(out->Format.nChannels == 2)
- out->dwChannelMask = STEREO;
- else
- ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
- out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- }
- else
- {
- ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
- return false;
- }
- return true;
-}
-
-void TraceFormat(const char *msg, const WAVEFORMATEX *format)
-{
- constexpr size_t fmtex_extra_size{sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)};
- if(format->wFormatTag == WAVE_FORMAT_EXTENSIBLE && format->cbSize >= fmtex_extra_size)
- {
- class GuidPrinter {
- char mMsg[64];
-
- public:
- GuidPrinter(const GUID &guid)
- {
- std::snprintf(mMsg, al::size(mMsg),
- "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
- DWORD{guid.Data1}, guid.Data2, guid.Data3,
- guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
- guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
- }
- const char *c_str() const { return mMsg; }
- };
-
- const WAVEFORMATEXTENSIBLE *fmtex{
- CONTAINING_RECORD(format, const WAVEFORMATEXTENSIBLE, Format)};
- TRACE("%s:\n"
- " FormatTag = 0x%04x\n"
- " Channels = %d\n"
- " SamplesPerSec = %lu\n"
- " AvgBytesPerSec = %lu\n"
- " BlockAlign = %d\n"
- " BitsPerSample = %d\n"
- " Size = %d\n"
- " Samples = %d\n"
- " ChannelMask = 0x%lx\n"
- " SubFormat = %s\n",
- msg, fmtex->Format.wFormatTag, fmtex->Format.nChannels, fmtex->Format.nSamplesPerSec,
- fmtex->Format.nAvgBytesPerSec, fmtex->Format.nBlockAlign, fmtex->Format.wBitsPerSample,
- fmtex->Format.cbSize, fmtex->Samples.wReserved, fmtex->dwChannelMask,
- GuidPrinter{fmtex->SubFormat}.c_str());
- }
- else
- TRACE("%s:\n"
- " FormatTag = 0x%04x\n"
- " Channels = %d\n"
- " SamplesPerSec = %lu\n"
- " AvgBytesPerSec = %lu\n"
- " BlockAlign = %d\n"
- " BitsPerSample = %d\n"
- " Size = %d\n",
- msg, format->wFormatTag, format->nChannels, format->nSamplesPerSec,
- format->nAvgBytesPerSec, format->nBlockAlign, format->wBitsPerSample, format->cbSize);
-}
-
-
-enum class MsgType : unsigned int {
- OpenDevice,
- ResetDevice,
- StartDevice,
- StopDevice,
- CloseDevice,
- EnumeratePlayback,
- EnumerateCapture,
- QuitThread,
-
- Count
-};
-
-constexpr char MessageStr[static_cast<unsigned int>(MsgType::Count)][20]{
- "Open Device",
- "Reset Device",
- "Start Device",
- "Stop Device",
- "Close Device",
- "Enumerate Playback",
- "Enumerate Capture",
- "Quit"
-};
-
-
-/* Proxy interface used by the message handler. */
-struct WasapiProxy {
- virtual HRESULT openProxy() = 0;
- virtual void closeProxy() = 0;
-
- virtual HRESULT resetProxy() = 0;
- virtual HRESULT startProxy() = 0;
- virtual void stopProxy() = 0;
-
- struct Msg {
- MsgType mType;
- WasapiProxy *mProxy;
- std::promise<HRESULT> mPromise;
- };
- static std::deque<Msg> mMsgQueue;
- static std::mutex mMsgQueueLock;
- static std::condition_variable mMsgQueueCond;
-
- std::future<HRESULT> pushMessage(MsgType type)
- {
- std::promise<HRESULT> promise;
- std::future<HRESULT> future{promise.get_future()};
- { std::lock_guard<std::mutex> _{mMsgQueueLock};
- mMsgQueue.emplace_back(Msg{type, this, std::move(promise)});
- }
- mMsgQueueCond.notify_one();
- return future;
- }
-
- static std::future<HRESULT> pushMessageStatic(MsgType type)
- {
- std::promise<HRESULT> promise;
- std::future<HRESULT> future{promise.get_future()};
- { std::lock_guard<std::mutex> _{mMsgQueueLock};
- mMsgQueue.emplace_back(Msg{type, nullptr, std::move(promise)});
- }
- mMsgQueueCond.notify_one();
- return future;
- }
-
- static bool popMessage(Msg &msg)
- {
- std::unique_lock<std::mutex> lock{mMsgQueueLock};
- while(mMsgQueue.empty())
- mMsgQueueCond.wait(lock);
- msg = std::move(mMsgQueue.front());
- mMsgQueue.pop_front();
- return msg.mType != MsgType::QuitThread;
- }
-
- static int messageHandler(std::promise<HRESULT> *promise);
-};
-std::deque<WasapiProxy::Msg> WasapiProxy::mMsgQueue;
-std::mutex WasapiProxy::mMsgQueueLock;
-std::condition_variable WasapiProxy::mMsgQueueCond;
-
-int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
-{
- TRACE("Starting message thread\n");
-
- HRESULT cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
- if(FAILED(cohr))
- {
- WARN("Failed to initialize COM: 0x%08lx\n", cohr);
- promise->set_value(cohr);
- return 0;
- }
-
- void *ptr{};
- HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
- IID_IMMDeviceEnumerator, &ptr)};
- if(FAILED(hr))
- {
- WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
- promise->set_value(hr);
- CoUninitialize();
- return 0;
- }
- auto Enumerator = static_cast<IMMDeviceEnumerator*>(ptr);
- Enumerator->Release();
- Enumerator = nullptr;
- CoUninitialize();
-
- TRACE("Message thread initialization complete\n");
- promise->set_value(S_OK);
- promise = nullptr;
-
- TRACE("Starting message loop\n");
- ALuint deviceCount{0};
- Msg msg;
- while(popMessage(msg))
- {
- TRACE("Got message \"%s\" (0x%04x, this=%p)\n",
- MessageStr[static_cast<unsigned int>(msg.mType)], static_cast<unsigned int>(msg.mType),
- msg.mProxy);
-
- switch(msg.mType)
- {
- case MsgType::OpenDevice:
- hr = cohr = S_OK;
- if(++deviceCount == 1)
- hr = cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
- if(SUCCEEDED(hr))
- hr = msg.mProxy->openProxy();
- msg.mPromise.set_value(hr);
-
- if(FAILED(hr))
- {
- if(--deviceCount == 0 && SUCCEEDED(cohr))
- CoUninitialize();
- }
- continue;
-
- case MsgType::ResetDevice:
- hr = msg.mProxy->resetProxy();
- msg.mPromise.set_value(hr);
- continue;
-
- case MsgType::StartDevice:
- hr = msg.mProxy->startProxy();
- msg.mPromise.set_value(hr);
- continue;
-
- case MsgType::StopDevice:
- msg.mProxy->stopProxy();
- msg.mPromise.set_value(S_OK);
- continue;
-
- case MsgType::CloseDevice:
- msg.mProxy->closeProxy();
- msg.mPromise.set_value(S_OK);
-
- if(--deviceCount == 0)
- CoUninitialize();
- continue;
-
- case MsgType::EnumeratePlayback:
- case MsgType::EnumerateCapture:
- hr = cohr = S_OK;
- if(++deviceCount == 1)
- hr = cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
- if(SUCCEEDED(hr))
- hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, &ptr);
- if(FAILED(hr))
- msg.mPromise.set_value(hr);
- else
- {
- Enumerator = static_cast<IMMDeviceEnumerator*>(ptr);
-
- if(msg.mType == MsgType::EnumeratePlayback)
- hr = probe_devices(Enumerator, eRender, PlaybackDevices);
- else if(msg.mType == MsgType::EnumerateCapture)
- hr = probe_devices(Enumerator, eCapture, CaptureDevices);
- msg.mPromise.set_value(hr);
-
- Enumerator->Release();
- Enumerator = nullptr;
- }
-
- if(--deviceCount == 0 && SUCCEEDED(cohr))
- CoUninitialize();
- continue;
-
- default:
- ERR("Unexpected message: %u\n", static_cast<unsigned int>(msg.mType));
- msg.mPromise.set_value(E_FAIL);
- continue;
- }
- }
- TRACE("Message loop finished\n");
-
- return 0;
-}
-
-
-struct WasapiPlayback final : public BackendBase, WasapiProxy {
- WasapiPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~WasapiPlayback() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- HRESULT openProxy() override;
- void closeProxy() override;
-
- ALCboolean reset() override;
- HRESULT resetProxy() override;
- ALCboolean start() override;
- HRESULT startProxy() override;
- void stop() override;
- void stopProxy() override;
-
- ClockLatency getClockLatency() override;
-
- std::wstring mDevId;
-
- IMMDevice *mMMDev{nullptr};
- IAudioClient *mClient{nullptr};
- IAudioRenderClient *mRender{nullptr};
- HANDLE mNotifyEvent{nullptr};
-
- std::atomic<UINT32> mPadding{0u};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(WasapiPlayback)
-};
-
-WasapiPlayback::~WasapiPlayback()
-{
- pushMessage(MsgType::CloseDevice).wait();
-
- if(mNotifyEvent != nullptr)
- CloseHandle(mNotifyEvent);
- mNotifyEvent = nullptr;
-}
-
-
-FORCE_ALIGN int WasapiPlayback::mixerProc()
-{
- HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
- if(FAILED(hr))
- {
- ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "COM init failed: 0x%08lx", hr);
- return 1;
- }
-
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- const ALuint update_size{mDevice->UpdateSize};
- const UINT32 buffer_len{mDevice->BufferSize};
- while(!mKillNow.load(std::memory_order_relaxed))
- {
- UINT32 written;
- hr = mClient->GetCurrentPadding(&written);
- if(FAILED(hr))
- {
- ERR("Failed to get padding: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failed to retrieve buffer padding: 0x%08lx", hr);
- break;
- }
- mPadding.store(written, std::memory_order_relaxed);
-
- ALuint len{buffer_len - written};
- if(len < update_size)
- {
- DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)};
- if(res != WAIT_OBJECT_0)
- ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
- continue;
- }
-
- BYTE *buffer;
- hr = mRender->GetBuffer(len, &buffer);
- if(SUCCEEDED(hr))
- {
- lock();
- aluMixData(mDevice, buffer, len);
- mPadding.store(written + len, std::memory_order_relaxed);
- unlock();
- hr = mRender->ReleaseBuffer(len, 0);
- }
- if(FAILED(hr))
- {
- ERR("Failed to buffer data: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failed to send playback samples: 0x%08lx", hr);
- break;
- }
- }
- mPadding.store(0u, std::memory_order_release);
-
- CoUninitialize();
- return 0;
-}
-
-
-ALCenum WasapiPlayback::open(const ALCchar *name)
-{
- HRESULT hr{S_OK};
-
- mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
- if(mNotifyEvent == nullptr)
- {
- ERR("Failed to create notify events: %lu\n", GetLastError());
- hr = E_FAIL;
- }
-
- if(SUCCEEDED(hr))
- {
- if(name)
- {
- if(PlaybackDevices.empty())
- pushMessage(MsgType::EnumeratePlayback).wait();
-
- hr = E_FAIL;
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name || entry.endpoint_guid == name; }
- );
- if(iter == PlaybackDevices.cend())
- {
- std::wstring wname{utf8_to_wstr(name)};
- iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [&wname](const DevMap &entry) -> bool
- { return entry.devid == wname; }
- );
- }
- if(iter == PlaybackDevices.cend())
- WARN("Failed to find device name matching \"%s\"\n", name);
- else
- {
- mDevId = iter->devid;
- mDevice->DeviceName = iter->name;
- hr = S_OK;
- }
- }
- }
-
- if(SUCCEEDED(hr))
- hr = pushMessage(MsgType::OpenDevice).get();
-
- if(FAILED(hr))
- {
- if(mNotifyEvent != nullptr)
- CloseHandle(mNotifyEvent);
- mNotifyEvent = nullptr;
-
- mDevId.clear();
-
- ERR("Device init failed: 0x%08lx\n", hr);
- return ALC_INVALID_VALUE;
- }
-
- return ALC_NO_ERROR;
-}
-
-HRESULT WasapiPlayback::openProxy()
-{
- void *ptr;
- HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, &ptr)};
- if(SUCCEEDED(hr))
- {
- auto Enumerator = static_cast<IMMDeviceEnumerator*>(ptr);
- if(mDevId.empty())
- hr = Enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &mMMDev);
- else
- hr = Enumerator->GetDevice(mDevId.c_str(), &mMMDev);
- Enumerator->Release();
- }
- if(SUCCEEDED(hr))
- hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
- if(SUCCEEDED(hr))
- {
- mClient = static_cast<IAudioClient*>(ptr);
- if(mDevice->DeviceName.empty())
- mDevice->DeviceName = get_device_name_and_guid(mMMDev).first;
- }
-
- if(FAILED(hr))
- {
- if(mMMDev)
- mMMDev->Release();
- mMMDev = nullptr;
- }
-
- return hr;
-}
-
-void WasapiPlayback::closeProxy()
-{
- if(mClient)
- mClient->Release();
- mClient = nullptr;
-
- if(mMMDev)
- mMMDev->Release();
- mMMDev = nullptr;
-}
-
-
-ALCboolean WasapiPlayback::reset()
-{
- HRESULT hr{pushMessage(MsgType::ResetDevice).get()};
- return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
-}
-
-HRESULT WasapiPlayback::resetProxy()
-{
- if(mClient)
- mClient->Release();
- mClient = nullptr;
-
- void *ptr;
- HRESULT hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
- if(FAILED(hr))
- {
- ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
- return hr;
- }
- mClient = static_cast<IAudioClient*>(ptr);
-
- WAVEFORMATEX *wfx;
- hr = mClient->GetMixFormat(&wfx);
- if(FAILED(hr))
- {
- ERR("Failed to get mix format: 0x%08lx\n", hr);
- return hr;
- }
-
- WAVEFORMATEXTENSIBLE OutputType;
- if(!MakeExtensible(&OutputType, wfx))
- {
- CoTaskMemFree(wfx);
- return E_FAIL;
- }
- CoTaskMemFree(wfx);
- wfx = nullptr;
-
- const REFERENCE_TIME per_time{mDevice->UpdateSize * REFTIME_PER_SEC / mDevice->Frequency};
- const REFERENCE_TIME buf_time{mDevice->BufferSize * REFTIME_PER_SEC / mDevice->Frequency};
-
- if(!mDevice->Flags.get<FrequencyRequest>())
- mDevice->Frequency = OutputType.Format.nSamplesPerSec;
- if(!mDevice->Flags.get<ChannelsRequest>())
- {
- if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
- mDevice->FmtChans = DevFmtMono;
- else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
- mDevice->FmtChans = DevFmtStereo;
- else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
- mDevice->FmtChans = DevFmtQuad;
- else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
- mDevice->FmtChans = DevFmtX51;
- else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
- mDevice->FmtChans = DevFmtX51Rear;
- else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
- mDevice->FmtChans = DevFmtX61;
- else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
- mDevice->FmtChans = DevFmtX71;
- else
- ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
- }
-
- OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- OutputType.Format.nChannels = 1;
- OutputType.dwChannelMask = MONO;
- break;
- case DevFmtAmbi3D:
- mDevice->FmtChans = DevFmtStereo;
- /*fall-through*/
- case DevFmtStereo:
- OutputType.Format.nChannels = 2;
- OutputType.dwChannelMask = STEREO;
- break;
- case DevFmtQuad:
- OutputType.Format.nChannels = 4;
- OutputType.dwChannelMask = QUAD;
- break;
- case DevFmtX51:
- OutputType.Format.nChannels = 6;
- OutputType.dwChannelMask = X5DOT1;
- break;
- case DevFmtX51Rear:
- OutputType.Format.nChannels = 6;
- OutputType.dwChannelMask = X5DOT1REAR;
- break;
- case DevFmtX61:
- OutputType.Format.nChannels = 7;
- OutputType.dwChannelMask = X6DOT1;
- break;
- case DevFmtX71:
- OutputType.Format.nChannels = 8;
- OutputType.dwChannelMask = X7DOT1;
- break;
- }
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mDevice->FmtType = DevFmtUByte;
- /* fall-through */
- case DevFmtUByte:
- OutputType.Format.wBitsPerSample = 8;
- OutputType.Samples.wValidBitsPerSample = 8;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- break;
- case DevFmtUShort:
- mDevice->FmtType = DevFmtShort;
- /* fall-through */
- case DevFmtShort:
- OutputType.Format.wBitsPerSample = 16;
- OutputType.Samples.wValidBitsPerSample = 16;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- break;
- case DevFmtUInt:
- mDevice->FmtType = DevFmtInt;
- /* fall-through */
- case DevFmtInt:
- OutputType.Format.wBitsPerSample = 32;
- OutputType.Samples.wValidBitsPerSample = 32;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- break;
- case DevFmtFloat:
- OutputType.Format.wBitsPerSample = 32;
- OutputType.Samples.wValidBitsPerSample = 32;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- break;
- }
- OutputType.Format.nSamplesPerSec = mDevice->Frequency;
-
- OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
- OutputType.Format.wBitsPerSample / 8;
- OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
- OutputType.Format.nBlockAlign;
-
- TraceFormat("Requesting playback format", &OutputType.Format);
- hr = mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
- if(FAILED(hr))
- {
- ERR("Failed to check format support: 0x%08lx\n", hr);
- hr = mClient->GetMixFormat(&wfx);
- }
- if(FAILED(hr))
- {
- ERR("Failed to find a supported format: 0x%08lx\n", hr);
- return hr;
- }
-
- if(wfx != nullptr)
- {
- TraceFormat("Got playback format", wfx);
- if(!MakeExtensible(&OutputType, wfx))
- {
- CoTaskMemFree(wfx);
- return E_FAIL;
- }
- CoTaskMemFree(wfx);
- wfx = nullptr;
-
- mDevice->Frequency = OutputType.Format.nSamplesPerSec;
- if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
- mDevice->FmtChans = DevFmtMono;
- else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
- mDevice->FmtChans = DevFmtStereo;
- else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
- mDevice->FmtChans = DevFmtQuad;
- else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
- mDevice->FmtChans = DevFmtX51;
- else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
- mDevice->FmtChans = DevFmtX51Rear;
- else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
- mDevice->FmtChans = DevFmtX61;
- else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
- mDevice->FmtChans = DevFmtX71;
- else
- {
- ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
- mDevice->FmtChans = DevFmtStereo;
- OutputType.Format.nChannels = 2;
- OutputType.dwChannelMask = STEREO;
- }
-
- if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM))
- {
- if(OutputType.Format.wBitsPerSample == 8)
- mDevice->FmtType = DevFmtUByte;
- else if(OutputType.Format.wBitsPerSample == 16)
- mDevice->FmtType = DevFmtShort;
- else if(OutputType.Format.wBitsPerSample == 32)
- mDevice->FmtType = DevFmtInt;
- else
- {
- mDevice->FmtType = DevFmtShort;
- OutputType.Format.wBitsPerSample = 16;
- }
- }
- else if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
- {
- mDevice->FmtType = DevFmtFloat;
- OutputType.Format.wBitsPerSample = 32;
- }
- else
- {
- ERR("Unhandled format sub-type\n");
- mDevice->FmtType = DevFmtShort;
- if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE)
- OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- OutputType.Format.wBitsPerSample = 16;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- }
- OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
- }
-
- EndpointFormFactor formfactor = UnknownFormFactor;
- get_device_formfactor(mMMDev, &formfactor);
- mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo &&
- (formfactor == Headphones || formfactor == Headset));
-
- SetDefaultWFXChannelOrder(mDevice);
-
- hr = mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buf_time,
- 0, &OutputType.Format, nullptr);
- if(FAILED(hr))
- {
- ERR("Failed to initialize audio client: 0x%08lx\n", hr);
- return hr;
- }
-
- UINT32 buffer_len, min_len;
- REFERENCE_TIME min_per;
- hr = mClient->GetDevicePeriod(&min_per, nullptr);
- if(SUCCEEDED(hr))
- hr = mClient->GetBufferSize(&buffer_len);
- if(FAILED(hr))
- {
- ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
- return hr;
- }
-
- /* Find the nearest multiple of the period size to the update size */
- if(min_per < per_time)
- min_per *= maxi64((per_time + min_per/2) / min_per, 1);
- min_len = (UINT32)ScaleCeil(min_per, mDevice->Frequency, REFTIME_PER_SEC);
- min_len = minu(min_len, buffer_len/2);
-
- mDevice->UpdateSize = min_len;
- mDevice->BufferSize = buffer_len;
-
- hr = mClient->SetEventHandle(mNotifyEvent);
- if(FAILED(hr))
- {
- ERR("Failed to set event handle: 0x%08lx\n", hr);
- return hr;
- }
-
- return hr;
-}
-
-
-ALCboolean WasapiPlayback::start()
-{
- HRESULT hr{pushMessage(MsgType::StartDevice).get()};
- return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
-}
-
-HRESULT WasapiPlayback::startProxy()
-{
- ResetEvent(mNotifyEvent);
-
- HRESULT hr = mClient->Start();
- if(FAILED(hr))
- {
- ERR("Failed to start audio client: 0x%08lx\n", hr);
- return hr;
- }
-
- void *ptr;
- hr = mClient->GetService(IID_IAudioRenderClient, &ptr);
- if(SUCCEEDED(hr))
- {
- mRender = static_cast<IAudioRenderClient*>(ptr);
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerProc), this};
- }
- catch(...) {
- mRender->Release();
- mRender = nullptr;
- ERR("Failed to start thread\n");
- hr = E_FAIL;
- }
- }
-
- if(FAILED(hr))
- mClient->Stop();
-
- return hr;
-}
-
-
-void WasapiPlayback::stop()
-{ pushMessage(MsgType::StopDevice).wait(); }
-
-void WasapiPlayback::stopProxy()
-{
- if(!mRender || !mThread.joinable())
- return;
-
- mKillNow.store(true, std::memory_order_release);
- mThread.join();
-
- mRender->Release();
- mRender = nullptr;
- mClient->Stop();
-}
-
-
-ClockLatency WasapiPlayback::getClockLatency()
-{
- ClockLatency ret;
-
- lock();
- ret.ClockTime = GetDeviceClockTime(mDevice);
- ret.Latency = std::chrono::seconds{mPadding.load(std::memory_order_relaxed)};
- ret.Latency /= mDevice->Frequency;
- unlock();
-
- return ret;
-}
-
-
-struct WasapiCapture final : public BackendBase, WasapiProxy {
- WasapiCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~WasapiCapture() override;
-
- int recordProc();
-
- ALCenum open(const ALCchar *name) override;
- HRESULT openProxy() override;
- void closeProxy() override;
-
- HRESULT resetProxy() override;
- ALCboolean start() override;
- HRESULT startProxy() override;
- void stop() override;
- void stopProxy() override;
-
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- std::wstring mDevId;
-
- IMMDevice *mMMDev{nullptr};
- IAudioClient *mClient{nullptr};
- IAudioCaptureClient *mCapture{nullptr};
- HANDLE mNotifyEvent{nullptr};
-
- ChannelConverterPtr mChannelConv;
- SampleConverterPtr mSampleConv;
- RingBufferPtr mRing;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(WasapiCapture)
-};
-
-WasapiCapture::~WasapiCapture()
-{
- pushMessage(MsgType::CloseDevice).wait();
-
- if(mNotifyEvent != nullptr)
- CloseHandle(mNotifyEvent);
- mNotifyEvent = nullptr;
-}
-
-
-FORCE_ALIGN int WasapiCapture::recordProc()
-{
- HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
- if(FAILED(hr))
- {
- ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "COM init failed: 0x%08lx", hr);
- return 1;
- }
-
- althrd_setname(RECORD_THREAD_NAME);
-
- al::vector<float> samples;
- while(!mKillNow.load(std::memory_order_relaxed))
- {
- UINT32 avail;
- hr = mCapture->GetNextPacketSize(&avail);
- if(FAILED(hr))
- ERR("Failed to get next packet size: 0x%08lx\n", hr);
- else if(avail > 0)
- {
- UINT32 numsamples;
- DWORD flags;
- BYTE *rdata;
-
- hr = mCapture->GetBuffer(&rdata, &numsamples, &flags, nullptr, nullptr);
- if(FAILED(hr))
- ERR("Failed to get capture buffer: 0x%08lx\n", hr);
- else
- {
- if(mChannelConv)
- {
- samples.resize(numsamples*2);
- mChannelConv->convert(rdata, samples.data(), numsamples);
- rdata = reinterpret_cast<BYTE*>(samples.data());
- }
-
- auto data = mRing->getWriteVector();
-
- size_t dstframes;
- if(mSampleConv)
- {
- const ALvoid *srcdata{rdata};
- auto srcframes = static_cast<ALsizei>(numsamples);
-
- dstframes = mSampleConv->convert(&srcdata, &srcframes, data.first.buf,
- static_cast<ALsizei>(minz(data.first.len, INT_MAX)));
- if(srcframes > 0 && dstframes == data.first.len && data.second.len > 0)
- {
- /* If some source samples remain, all of the first dest
- * block was filled, and there's space in the second
- * dest block, do another run for the second block.
- */
- dstframes += mSampleConv->convert(&srcdata, &srcframes, data.second.buf,
- static_cast<ALsizei>(minz(data.second.len, INT_MAX)));
- }
- }
- else
- {
- const auto framesize = static_cast<ALuint>(mDevice->frameSizeFromFmt());
- size_t len1 = minz(data.first.len, numsamples);
- size_t len2 = minz(data.second.len, numsamples-len1);
-
- memcpy(data.first.buf, rdata, len1*framesize);
- if(len2 > 0)
- memcpy(data.second.buf, rdata+len1*framesize, len2*framesize);
- dstframes = len1 + len2;
- }
-
- mRing->writeAdvance(dstframes);
-
- hr = mCapture->ReleaseBuffer(numsamples);
- if(FAILED(hr)) ERR("Failed to release capture buffer: 0x%08lx\n", hr);
- }
- }
-
- if(FAILED(hr))
- {
- aluHandleDisconnect(mDevice, "Failed to capture samples: 0x%08lx", hr);
- break;
- }
-
- DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)};
- if(res != WAIT_OBJECT_0)
- ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
- }
-
- CoUninitialize();
- return 0;
-}
-
-
-ALCenum WasapiCapture::open(const ALCchar *name)
-{
- HRESULT hr{S_OK};
-
- mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
- if(mNotifyEvent == nullptr)
- {
- ERR("Failed to create notify event: %lu\n", GetLastError());
- hr = E_FAIL;
- }
-
- if(SUCCEEDED(hr))
- {
- if(name)
- {
- if(CaptureDevices.empty())
- pushMessage(MsgType::EnumerateCapture).wait();
-
- hr = E_FAIL;
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name || entry.endpoint_guid == name; }
- );
- if(iter == CaptureDevices.cend())
- {
- std::wstring wname{utf8_to_wstr(name)};
- iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [&wname](const DevMap &entry) -> bool
- { return entry.devid == wname; }
- );
- }
- if(iter == CaptureDevices.cend())
- WARN("Failed to find device name matching \"%s\"\n", name);
- else
- {
- mDevId = iter->devid;
- mDevice->DeviceName = iter->name;
- hr = S_OK;
- }
- }
- }
-
- if(SUCCEEDED(hr))
- hr = pushMessage(MsgType::OpenDevice).get();
-
- if(FAILED(hr))
- {
- if(mNotifyEvent != nullptr)
- CloseHandle(mNotifyEvent);
- mNotifyEvent = nullptr;
-
- mDevId.clear();
-
- ERR("Device init failed: 0x%08lx\n", hr);
- return ALC_INVALID_VALUE;
- }
-
- hr = pushMessage(MsgType::ResetDevice).get();
- if(FAILED(hr))
- {
- if(hr == E_OUTOFMEMORY)
- return ALC_OUT_OF_MEMORY;
- return ALC_INVALID_VALUE;
- }
-
- return ALC_NO_ERROR;
-}
-
-HRESULT WasapiCapture::openProxy()
-{
- void *ptr;
- HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
- IID_IMMDeviceEnumerator, &ptr)};
- if(SUCCEEDED(hr))
- {
- auto Enumerator = static_cast<IMMDeviceEnumerator*>(ptr);
- if(mDevId.empty())
- hr = Enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, &mMMDev);
- else
- hr = Enumerator->GetDevice(mDevId.c_str(), &mMMDev);
- Enumerator->Release();
- }
- if(SUCCEEDED(hr))
- hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
- if(SUCCEEDED(hr))
- {
- mClient = static_cast<IAudioClient*>(ptr);
- if(mDevice->DeviceName.empty())
- mDevice->DeviceName = get_device_name_and_guid(mMMDev).first;
- }
-
- if(FAILED(hr))
- {
- if(mMMDev)
- mMMDev->Release();
- mMMDev = nullptr;
- }
-
- return hr;
-}
-
-void WasapiCapture::closeProxy()
-{
- if(mClient)
- mClient->Release();
- mClient = nullptr;
-
- if(mMMDev)
- mMMDev->Release();
- mMMDev = nullptr;
-}
-
-HRESULT WasapiCapture::resetProxy()
-{
- if(mClient)
- mClient->Release();
- mClient = nullptr;
-
- void *ptr;
- HRESULT hr{mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr)};
- if(FAILED(hr))
- {
- ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
- return hr;
- }
- mClient = static_cast<IAudioClient*>(ptr);
-
- // Make sure buffer is at least 100ms in size
- REFERENCE_TIME buf_time{mDevice->BufferSize * REFTIME_PER_SEC / mDevice->Frequency};
- buf_time = maxu64(buf_time, REFTIME_PER_SEC/10);
-
- WAVEFORMATEXTENSIBLE OutputType;
- OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- OutputType.Format.nChannels = 1;
- OutputType.dwChannelMask = MONO;
- break;
- case DevFmtStereo:
- OutputType.Format.nChannels = 2;
- OutputType.dwChannelMask = STEREO;
- break;
- case DevFmtQuad:
- OutputType.Format.nChannels = 4;
- OutputType.dwChannelMask = QUAD;
- break;
- case DevFmtX51:
- OutputType.Format.nChannels = 6;
- OutputType.dwChannelMask = X5DOT1;
- break;
- case DevFmtX51Rear:
- OutputType.Format.nChannels = 6;
- OutputType.dwChannelMask = X5DOT1REAR;
- break;
- case DevFmtX61:
- OutputType.Format.nChannels = 7;
- OutputType.dwChannelMask = X6DOT1;
- break;
- case DevFmtX71:
- OutputType.Format.nChannels = 8;
- OutputType.dwChannelMask = X7DOT1;
- break;
-
- case DevFmtAmbi3D:
- return E_FAIL;
- }
- switch(mDevice->FmtType)
- {
- /* NOTE: Signedness doesn't matter, the converter will handle it. */
- case DevFmtByte:
- case DevFmtUByte:
- OutputType.Format.wBitsPerSample = 8;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- break;
- case DevFmtShort:
- case DevFmtUShort:
- OutputType.Format.wBitsPerSample = 16;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- break;
- case DevFmtInt:
- case DevFmtUInt:
- OutputType.Format.wBitsPerSample = 32;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- break;
- case DevFmtFloat:
- OutputType.Format.wBitsPerSample = 32;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- break;
- }
- OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
- OutputType.Format.nSamplesPerSec = mDevice->Frequency;
-
- OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
- OutputType.Format.wBitsPerSample / 8;
- OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
- OutputType.Format.nBlockAlign;
- OutputType.Format.cbSize = sizeof(OutputType) - sizeof(OutputType.Format);
-
- TraceFormat("Requesting capture format", &OutputType.Format);
- WAVEFORMATEX *wfx;
- hr = mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
- if(FAILED(hr))
- {
- ERR("Failed to check format support: 0x%08lx\n", hr);
- return hr;
- }
-
- mSampleConv = nullptr;
- mChannelConv = nullptr;
-
- if(wfx != nullptr)
- {
- TraceFormat("Got capture format", wfx);
- if(!(wfx->nChannels == OutputType.Format.nChannels ||
- (wfx->nChannels == 1 && OutputType.Format.nChannels == 2) ||
- (wfx->nChannels == 2 && OutputType.Format.nChannels == 1)))
- {
- ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
- DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
- mDevice->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample,
- wfx->nSamplesPerSec);
- CoTaskMemFree(wfx);
- return E_FAIL;
- }
-
- if(!MakeExtensible(&OutputType, wfx))
- {
- CoTaskMemFree(wfx);
- return E_FAIL;
- }
- CoTaskMemFree(wfx);
- wfx = nullptr;
- }
-
- DevFmtType srcType;
- if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM))
- {
- if(OutputType.Format.wBitsPerSample == 8)
- srcType = DevFmtUByte;
- else if(OutputType.Format.wBitsPerSample == 16)
- srcType = DevFmtShort;
- else if(OutputType.Format.wBitsPerSample == 32)
- srcType = DevFmtInt;
- else
- {
- ERR("Unhandled integer bit depth: %d\n", OutputType.Format.wBitsPerSample);
- return E_FAIL;
- }
- }
- else if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
- {
- if(OutputType.Format.wBitsPerSample == 32)
- srcType = DevFmtFloat;
- else
- {
- ERR("Unhandled float bit depth: %d\n", OutputType.Format.wBitsPerSample);
- return E_FAIL;
- }
- }
- else
- {
- ERR("Unhandled format sub-type\n");
- return E_FAIL;
- }
-
- if(mDevice->FmtChans == DevFmtMono && OutputType.Format.nChannels == 2)
- {
- mChannelConv = CreateChannelConverter(srcType, DevFmtStereo, mDevice->FmtChans);
- if(!mChannelConv)
- {
- ERR("Failed to create %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
- return E_FAIL;
- }
- TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
- /* The channel converter always outputs float, so change the input type
- * for the resampler/type-converter.
- */
- srcType = DevFmtFloat;
- }
- else if(mDevice->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 1)
- {
- mChannelConv = CreateChannelConverter(srcType, DevFmtMono, mDevice->FmtChans);
- if(!mChannelConv)
- {
- ERR("Failed to create %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
- return E_FAIL;
- }
- TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
- srcType = DevFmtFloat;
- }
-
- if(mDevice->Frequency != OutputType.Format.nSamplesPerSec || mDevice->FmtType != srcType)
- {
- mSampleConv = CreateSampleConverter(srcType, mDevice->FmtType, mDevice->channelsFromFmt(),
- OutputType.Format.nSamplesPerSec, mDevice->Frequency, BSinc24Resampler);
- if(!mSampleConv)
- {
- ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
- DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
- mDevice->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
- return E_FAIL;
- }
- TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
- DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
- mDevice->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
- }
-
- hr = mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buf_time,
- 0, &OutputType.Format, nullptr);
- if(FAILED(hr))
- {
- ERR("Failed to initialize audio client: 0x%08lx\n", hr);
- return hr;
- }
-
- UINT32 buffer_len;
- REFERENCE_TIME min_per;
- hr = mClient->GetDevicePeriod(&min_per, nullptr);
- if(SUCCEEDED(hr))
- hr = mClient->GetBufferSize(&buffer_len);
- if(FAILED(hr))
- {
- ERR("Failed to get buffer size: 0x%08lx\n", hr);
- return hr;
- }
- mDevice->UpdateSize = static_cast<ALuint>(ScaleCeil(min_per, mDevice->Frequency,
- REFTIME_PER_SEC));
- mDevice->BufferSize = buffer_len;
-
- buffer_len = maxu(mDevice->BufferSize, buffer_len);
- mRing = CreateRingBuffer(buffer_len, mDevice->frameSizeFromFmt(), false);
- if(!mRing)
- {
- ERR("Failed to allocate capture ring buffer\n");
- return E_OUTOFMEMORY;
- }
-
- hr = mClient->SetEventHandle(mNotifyEvent);
- if(FAILED(hr))
- {
- ERR("Failed to set event handle: 0x%08lx\n", hr);
- return hr;
- }
-
- return hr;
-}
-
-
-ALCboolean WasapiCapture::start()
-{
- HRESULT hr{pushMessage(MsgType::StartDevice).get()};
- return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
-}
-
-HRESULT WasapiCapture::startProxy()
-{
- ResetEvent(mNotifyEvent);
-
- HRESULT hr{mClient->Start()};
- if(FAILED(hr))
- {
- ERR("Failed to start audio client: 0x%08lx\n", hr);
- return hr;
- }
-
- void *ptr;
- hr = mClient->GetService(IID_IAudioCaptureClient, &ptr);
- if(SUCCEEDED(hr))
- {
- mCapture = static_cast<IAudioCaptureClient*>(ptr);
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&WasapiCapture::recordProc), this};
- }
- catch(...) {
- mCapture->Release();
- mCapture = nullptr;
- ERR("Failed to start thread\n");
- hr = E_FAIL;
- }
- }
-
- if(FAILED(hr))
- {
- mClient->Stop();
- mClient->Reset();
- }
-
- return hr;
-}
-
-
-void WasapiCapture::stop()
-{ pushMessage(MsgType::StopDevice).wait(); }
-
-void WasapiCapture::stopProxy()
-{
- if(!mCapture || !mThread.joinable())
- return;
-
- mKillNow.store(true, std::memory_order_release);
- mThread.join();
-
- mCapture->Release();
- mCapture = nullptr;
- mClient->Stop();
- mClient->Reset();
-}
-
-
-ALCuint WasapiCapture::availableSamples()
-{ return (ALCuint)mRing->readSpace(); }
-
-ALCenum WasapiCapture::captureSamples(void *buffer, ALCuint samples)
-{
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
-}
-
-} // namespace
-
-
-bool WasapiBackendFactory::init()
-{
- static HRESULT InitResult{E_FAIL};
-
- if(FAILED(InitResult)) try
- {
- std::promise<HRESULT> promise;
- auto future = promise.get_future();
-
- std::thread{&WasapiProxy::messageHandler, &promise}.detach();
- InitResult = future.get();
- }
- catch(...) {
- }
-
- return SUCCEEDED(InitResult) ? ALC_TRUE : ALC_FALSE;
-}
-
-bool WasapiBackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback || type == BackendType::Capture; }
-
-void WasapiBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const DevMap &entry) -> void
- {
- /* +1 to also append the null char (to ensure a null-separated list and
- * double-null terminated list).
- */
- outnames->append(entry.name.c_str(), entry.name.length()+1);
- };
- HRESULT hr{};
- switch(type)
- {
- case DevProbe::Playback:
- hr = WasapiProxy::pushMessageStatic(MsgType::EnumeratePlayback).get();
- if(SUCCEEDED(hr))
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- hr = WasapiProxy::pushMessageStatic(MsgType::EnumerateCapture).get();
- if(SUCCEEDED(hr))
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
-}
-
-BackendPtr WasapiBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new WasapiPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new WasapiCapture{device}};
- return nullptr;
-}
-
-BackendFactory &WasapiBackendFactory::getFactory()
-{
- static WasapiBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/wasapi.h b/Alc/backends/wasapi.h
deleted file mode 100644
index 067dd259..00000000
--- a/Alc/backends/wasapi.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_WASAPI_H
-#define BACKENDS_WASAPI_H
-
-#include "backends/base.h"
-
-struct WasapiBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_WASAPI_H */
diff --git a/Alc/backends/wave.cpp b/Alc/backends/wave.cpp
deleted file mode 100644
index 67ed7e79..00000000
--- a/Alc/backends/wave.cpp
+++ /dev/null
@@ -1,402 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/wave.h"
-
-#include <algorithm>
-#include <atomic>
-#include <cerrno>
-#include <chrono>
-#include <cstdint>
-#include <cstdio>
-#include <cstring>
-#include <exception>
-#include <functional>
-#include <thread>
-
-#include "AL/al.h"
-
-#include "alcmain.h"
-#include "alconfig.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-#include "alu.h"
-#include "compat.h"
-#include "logging.h"
-#include "threads.h"
-#include "vector.h"
-
-
-namespace {
-
-using std::chrono::seconds;
-using std::chrono::milliseconds;
-using std::chrono::nanoseconds;
-
-constexpr ALCchar waveDevice[] = "Wave File Writer";
-
-constexpr ALubyte SUBTYPE_PCM[]{
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
- 0x00, 0x38, 0x9b, 0x71
-};
-constexpr ALubyte SUBTYPE_FLOAT[]{
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
- 0x00, 0x38, 0x9b, 0x71
-};
-
-constexpr ALubyte SUBTYPE_BFORMAT_PCM[]{
- 0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
- 0xca, 0x00, 0x00, 0x00
-};
-
-constexpr ALubyte SUBTYPE_BFORMAT_FLOAT[]{
- 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
- 0xca, 0x00, 0x00, 0x00
-};
-
-void fwrite16le(ALushort val, FILE *f)
-{
- ALubyte data[2]{ static_cast<ALubyte>(val&0xff), static_cast<ALubyte>((val>>8)&0xff) };
- fwrite(data, 1, 2, f);
-}
-
-void fwrite32le(ALuint val, FILE *f)
-{
- ALubyte data[4]{ static_cast<ALubyte>(val&0xff), static_cast<ALubyte>((val>>8)&0xff),
- static_cast<ALubyte>((val>>16)&0xff), static_cast<ALubyte>((val>>24)&0xff) };
- fwrite(data, 1, 4, f);
-}
-
-
-struct WaveBackend final : public BackendBase {
- WaveBackend(ALCdevice *device) noexcept : BackendBase{device} { }
- ~WaveBackend() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- FILE *mFile{nullptr};
- long mDataStart{-1};
-
- al::vector<ALbyte> mBuffer;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(WaveBackend)
-};
-
-WaveBackend::~WaveBackend()
-{
- if(mFile)
- fclose(mFile);
- mFile = nullptr;
-}
-
-int WaveBackend::mixerProc()
-{
- const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
-
- althrd_setname(MIXER_THREAD_NAME);
-
- const ALsizei frameSize{mDevice->frameSizeFromFmt()};
-
- int64_t done{0};
- auto start = std::chrono::steady_clock::now();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- auto now = std::chrono::steady_clock::now();
-
- /* This converts from nanoseconds to nanosamples, then to samples. */
- int64_t avail{std::chrono::duration_cast<seconds>((now-start) *
- mDevice->Frequency).count()};
- if(avail-done < mDevice->UpdateSize)
- {
- std::this_thread::sleep_for(restTime);
- continue;
- }
- while(avail-done >= mDevice->UpdateSize)
- {
- lock();
- aluMixData(mDevice, mBuffer.data(), mDevice->UpdateSize);
- unlock();
- done += mDevice->UpdateSize;
-
- if(!IS_LITTLE_ENDIAN)
- {
- const ALsizei bytesize{mDevice->bytesFromFmt()};
- ALsizei i;
-
- if(bytesize == 2)
- {
- ALushort *samples = reinterpret_cast<ALushort*>(mBuffer.data());
- const auto len = static_cast<ALsizei>(mBuffer.size() / 2);
- for(i = 0;i < len;i++)
- {
- ALushort samp = samples[i];
- samples[i] = (samp>>8) | (samp<<8);
- }
- }
- else if(bytesize == 4)
- {
- ALuint *samples = reinterpret_cast<ALuint*>(mBuffer.data());
- const auto len = static_cast<ALsizei>(mBuffer.size() / 4);
- for(i = 0;i < len;i++)
- {
- ALuint samp = samples[i];
- samples[i] = (samp>>24) | ((samp>>8)&0x0000ff00) |
- ((samp<<8)&0x00ff0000) | (samp<<24);
- }
- }
- }
-
- size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile)};
- (void)fs;
- if(ferror(mFile))
- {
- ERR("Error writing to file\n");
- aluHandleDisconnect(mDevice, "Failed to write playback samples");
- break;
- }
- }
-
- /* For every completed second, increment the start time and reduce the
- * samples done. This prevents the difference between the start time
- * and current time from growing too large, while maintaining the
- * correct number of samples to render.
- */
- if(done >= mDevice->Frequency)
- {
- seconds s{done/mDevice->Frequency};
- start += s;
- done -= mDevice->Frequency*s.count();
- }
- }
-
- return 0;
-}
-
-ALCenum WaveBackend::open(const ALCchar *name)
-{
- const char *fname{GetConfigValue(nullptr, "wave", "file", "")};
- if(!fname[0]) return ALC_INVALID_VALUE;
-
- if(!name)
- name = waveDevice;
- else if(strcmp(name, waveDevice) != 0)
- return ALC_INVALID_VALUE;
-
-#ifdef _WIN32
- {
- std::wstring wname = utf8_to_wstr(fname);
- mFile = _wfopen(wname.c_str(), L"wb");
- }
-#else
- mFile = fopen(fname, "wb");
-#endif
- if(!mFile)
- {
- ERR("Could not open file '%s': %s\n", fname, strerror(errno));
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
-
- return ALC_NO_ERROR;
-}
-
-ALCboolean WaveBackend::reset()
-{
- ALuint channels=0, bytes=0, chanmask=0;
- int isbformat = 0;
- size_t val;
-
- fseek(mFile, 0, SEEK_SET);
- clearerr(mFile);
-
- if(GetConfigValueBool(nullptr, "wave", "bformat", 0))
- {
- mDevice->FmtChans = DevFmtAmbi3D;
- mDevice->mAmbiOrder = 1;
- }
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mDevice->FmtType = DevFmtUByte;
- break;
- case DevFmtUShort:
- mDevice->FmtType = DevFmtShort;
- break;
- case DevFmtUInt:
- mDevice->FmtType = DevFmtInt;
- break;
- case DevFmtUByte:
- case DevFmtShort:
- case DevFmtInt:
- case DevFmtFloat:
- break;
- }
- switch(mDevice->FmtChans)
- {
- case DevFmtMono: chanmask = 0x04; break;
- case DevFmtStereo: chanmask = 0x01 | 0x02; break;
- case DevFmtQuad: chanmask = 0x01 | 0x02 | 0x10 | 0x20; break;
- case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
- case DevFmtX51Rear: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020; break;
- case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
- case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
- case DevFmtAmbi3D:
- /* .amb output requires FuMa */
- mDevice->mAmbiOrder = mini(mDevice->mAmbiOrder, 3);
- mDevice->mAmbiLayout = AmbiLayout::FuMa;
- mDevice->mAmbiScale = AmbiNorm::FuMa;
- isbformat = 1;
- chanmask = 0;
- break;
- }
- bytes = mDevice->bytesFromFmt();
- channels = mDevice->channelsFromFmt();
-
- rewind(mFile);
-
- fputs("RIFF", mFile);
- fwrite32le(0xFFFFFFFF, mFile); // 'RIFF' header len; filled in at close
-
- fputs("WAVE", mFile);
-
- fputs("fmt ", mFile);
- fwrite32le(40, mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
-
- // 16-bit val, format type id (extensible: 0xFFFE)
- fwrite16le(0xFFFE, mFile);
- // 16-bit val, channel count
- fwrite16le(channels, mFile);
- // 32-bit val, frequency
- fwrite32le(mDevice->Frequency, mFile);
- // 32-bit val, bytes per second
- fwrite32le(mDevice->Frequency * channels * bytes, mFile);
- // 16-bit val, frame size
- fwrite16le(channels * bytes, mFile);
- // 16-bit val, bits per sample
- fwrite16le(bytes * 8, mFile);
- // 16-bit val, extra byte count
- fwrite16le(22, mFile);
- // 16-bit val, valid bits per sample
- fwrite16le(bytes * 8, mFile);
- // 32-bit val, channel mask
- fwrite32le(chanmask, mFile);
- // 16 byte GUID, sub-type format
- val = fwrite((mDevice->FmtType == DevFmtFloat) ?
- (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
- (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, mFile);
- (void)val;
-
- fputs("data", mFile);
- fwrite32le(0xFFFFFFFF, mFile); // 'data' header len; filled in at close
-
- if(ferror(mFile))
- {
- ERR("Error writing header: %s\n", strerror(errno));
- return ALC_FALSE;
- }
- mDataStart = ftell(mFile);
-
- SetDefaultWFXChannelOrder(mDevice);
-
- const ALuint bufsize{mDevice->frameSizeFromFmt() * mDevice->UpdateSize};
- mBuffer.resize(bufsize);
-
- return ALC_TRUE;
-}
-
-ALCboolean WaveBackend::start()
-{
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&WaveBackend::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Failed to start mixing thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void WaveBackend::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- long size{ftell(mFile)};
- if(size > 0)
- {
- long dataLen{size - mDataStart};
- if(fseek(mFile, mDataStart-4, SEEK_SET) == 0)
- fwrite32le(dataLen, mFile); // 'data' header len
- if(fseek(mFile, 4, SEEK_SET) == 0)
- fwrite32le(size-8, mFile); // 'WAVE' header len
- }
-}
-
-} // namespace
-
-
-bool WaveBackendFactory::init()
-{ return true; }
-
-bool WaveBackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback; }
-
-void WaveBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- /* Includes null char. */
- outnames->append(waveDevice, sizeof(waveDevice));
- break;
- case DevProbe::Capture:
- break;
- }
-}
-
-BackendPtr WaveBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new WaveBackend{device}};
- return nullptr;
-}
-
-BackendFactory &WaveBackendFactory::getFactory()
-{
- static WaveBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/wave.h b/Alc/backends/wave.h
deleted file mode 100644
index b9b62d7f..00000000
--- a/Alc/backends/wave.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_WAVE_H
-#define BACKENDS_WAVE_H
-
-#include "backends/base.h"
-
-struct WaveBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_WAVE_H */
diff --git a/Alc/backends/winmm.cpp b/Alc/backends/winmm.cpp
deleted file mode 100644
index cd32e95b..00000000
--- a/Alc/backends/winmm.cpp
+++ /dev/null
@@ -1,640 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/winmm.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <memory.h>
-
-#include <windows.h>
-#include <mmsystem.h>
-
-#include <array>
-#include <atomic>
-#include <thread>
-#include <vector>
-#include <string>
-#include <algorithm>
-#include <functional>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "compat.h"
-
-#ifndef WAVE_FORMAT_IEEE_FLOAT
-#define WAVE_FORMAT_IEEE_FLOAT 0x0003
-#endif
-
-namespace {
-
-#define DEVNAME_HEAD "OpenAL Soft on "
-
-
-al::vector<std::string> PlaybackDevices;
-al::vector<std::string> CaptureDevices;
-
-bool checkName(const al::vector<std::string> &list, const std::string &name)
-{ return std::find(list.cbegin(), list.cend(), name) != list.cend(); }
-
-void ProbePlaybackDevices(void)
-{
- PlaybackDevices.clear();
-
- ALuint numdevs{waveOutGetNumDevs()};
- PlaybackDevices.reserve(numdevs);
- for(ALuint i{0};i < numdevs;i++)
- {
- std::string dname;
-
- WAVEOUTCAPSW WaveCaps{};
- if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
- {
- const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
-
- int count{1};
- std::string newname{basename};
- while(checkName(PlaybackDevices, newname))
- {
- newname = basename;
- newname += " #";
- newname += std::to_string(++count);
- }
- dname = std::move(newname);
-
- TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i);
- }
- PlaybackDevices.emplace_back(std::move(dname));
- }
-}
-
-void ProbeCaptureDevices(void)
-{
- CaptureDevices.clear();
-
- ALuint numdevs{waveInGetNumDevs()};
- CaptureDevices.reserve(numdevs);
- for(ALuint i{0};i < numdevs;i++)
- {
- std::string dname;
-
- WAVEINCAPSW WaveCaps{};
- if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
- {
- const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
-
- int count{1};
- std::string newname{basename};
- while(checkName(CaptureDevices, newname))
- {
- newname = basename;
- newname += " #";
- newname += std::to_string(++count);
- }
- dname = std::move(newname);
-
- TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i);
- }
- CaptureDevices.emplace_back(std::move(dname));
- }
-}
-
-
-struct WinMMPlayback final : public BackendBase {
- WinMMPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~WinMMPlayback() override;
-
- static void CALLBACK waveOutProcC(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
- void CALLBACK waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR param1, DWORD_PTR param2);
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- std::atomic<ALuint> mWritable{0u};
- al::semaphore mSem;
- int mIdx{0};
- std::array<WAVEHDR,4> mWaveBuffer{};
-
- HWAVEOUT mOutHdl{nullptr};
-
- WAVEFORMATEX mFormat{};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(WinMMPlayback)
-};
-
-WinMMPlayback::~WinMMPlayback()
-{
- if(mOutHdl)
- waveOutClose(mOutHdl);
- mOutHdl = nullptr;
-
- al_free(mWaveBuffer[0].lpData);
- std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
-}
-
-
-void CALLBACK WinMMPlayback::waveOutProcC(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
-{ reinterpret_cast<WinMMPlayback*>(instance)->waveOutProc(device, msg, param1, param2); }
-
-/* WinMMPlayback::waveOutProc
- *
- * Posts a message to 'WinMMPlayback::mixerProc' everytime a WaveOut Buffer is
- * completed and returns to the application (for more data)
- */
-void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT, UINT msg, DWORD_PTR, DWORD_PTR)
-{
- if(msg != WOM_DONE) return;
- mWritable.fetch_add(1, std::memory_order_acq_rel);
- mSem.post();
-}
-
-FORCE_ALIGN int WinMMPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- lock();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- ALsizei todo = mWritable.load(std::memory_order_acquire);
- if(todo < 1)
- {
- unlock();
- mSem.wait();
- lock();
- continue;
- }
-
- int widx{mIdx};
- do {
- WAVEHDR &waveHdr = mWaveBuffer[widx];
- widx = (widx+1) % mWaveBuffer.size();
-
- aluMixData(mDevice, waveHdr.lpData, mDevice->UpdateSize);
- mWritable.fetch_sub(1, std::memory_order_acq_rel);
- waveOutWrite(mOutHdl, &waveHdr, sizeof(WAVEHDR));
- } while(--todo);
- mIdx = widx;
- }
- unlock();
-
- return 0;
-}
-
-
-ALCenum WinMMPlayback::open(const ALCchar *name)
-{
- if(PlaybackDevices.empty())
- ProbePlaybackDevices();
-
- // Find the Device ID matching the deviceName if valid
- auto iter = name ?
- std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), name) :
- PlaybackDevices.cbegin();
- if(iter == PlaybackDevices.cend()) return ALC_INVALID_VALUE;
- auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
-
-retry_open:
- mFormat = WAVEFORMATEX{};
- if(mDevice->FmtType == DevFmtFloat)
- {
- mFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
- mFormat.wBitsPerSample = 32;
- }
- else
- {
- mFormat.wFormatTag = WAVE_FORMAT_PCM;
- if(mDevice->FmtType == DevFmtUByte || mDevice->FmtType == DevFmtByte)
- mFormat.wBitsPerSample = 8;
- else
- mFormat.wBitsPerSample = 16;
- }
- mFormat.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
- mFormat.nBlockAlign = mFormat.wBitsPerSample * mFormat.nChannels / 8;
- mFormat.nSamplesPerSec = mDevice->Frequency;
- mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
- mFormat.cbSize = 0;
-
- MMRESULT res{waveOutOpen(&mOutHdl, DeviceID, &mFormat, (DWORD_PTR)&WinMMPlayback::waveOutProcC,
- reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
- if(res != MMSYSERR_NOERROR)
- {
- if(mDevice->FmtType == DevFmtFloat)
- {
- mDevice->FmtType = DevFmtShort;
- goto retry_open;
- }
- ERR("waveOutOpen failed: %u\n", res);
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = PlaybackDevices[DeviceID];
- return ALC_NO_ERROR;
-}
-
-ALCboolean WinMMPlayback::reset()
-{
- mDevice->BufferSize = static_cast<ALuint>(uint64_t{mDevice->BufferSize} *
- mFormat.nSamplesPerSec / mDevice->Frequency);
- mDevice->BufferSize = (mDevice->BufferSize+3) & ~0x3;
- mDevice->UpdateSize = mDevice->BufferSize / 4;
- mDevice->Frequency = mFormat.nSamplesPerSec;
-
- if(mFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
- {
- if(mFormat.wBitsPerSample == 32)
- mDevice->FmtType = DevFmtFloat;
- else
- {
- ERR("Unhandled IEEE float sample depth: %d\n", mFormat.wBitsPerSample);
- return ALC_FALSE;
- }
- }
- else if(mFormat.wFormatTag == WAVE_FORMAT_PCM)
- {
- if(mFormat.wBitsPerSample == 16)
- mDevice->FmtType = DevFmtShort;
- else if(mFormat.wBitsPerSample == 8)
- mDevice->FmtType = DevFmtUByte;
- else
- {
- ERR("Unhandled PCM sample depth: %d\n", mFormat.wBitsPerSample);
- return ALC_FALSE;
- }
- }
- else
- {
- ERR("Unhandled format tag: 0x%04x\n", mFormat.wFormatTag);
- return ALC_FALSE;
- }
-
- if(mFormat.nChannels == 2)
- mDevice->FmtChans = DevFmtStereo;
- else if(mFormat.nChannels == 1)
- mDevice->FmtChans = DevFmtMono;
- else
- {
- ERR("Unhandled channel count: %d\n", mFormat.nChannels);
- return ALC_FALSE;
- }
- SetDefaultWFXChannelOrder(mDevice);
-
- ALuint BufferSize{mDevice->UpdateSize * mDevice->frameSizeFromFmt()};
-
- al_free(mWaveBuffer[0].lpData);
- mWaveBuffer[0] = WAVEHDR{};
- mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize * mWaveBuffer.size()));
- mWaveBuffer[0].dwBufferLength = BufferSize;
- for(size_t i{1};i < mWaveBuffer.size();i++)
- {
- mWaveBuffer[i] = WAVEHDR{};
- mWaveBuffer[i].lpData = mWaveBuffer[i-1].lpData + mWaveBuffer[i-1].dwBufferLength;
- mWaveBuffer[i].dwBufferLength = BufferSize;
- }
- mIdx = 0;
-
- return ALC_TRUE;
-}
-
-ALCboolean WinMMPlayback::start()
-{
- try {
- std::for_each(mWaveBuffer.begin(), mWaveBuffer.end(),
- [this](WAVEHDR &waveHdr) -> void
- { waveOutPrepareHeader(mOutHdl, &waveHdr, static_cast<UINT>(sizeof(WAVEHDR))); }
- );
- mWritable.store(static_cast<ALuint>(mWaveBuffer.size()), std::memory_order_release);
-
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&WinMMPlayback::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Failed to start mixing thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void WinMMPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- while(mWritable.load(std::memory_order_acquire) < mWaveBuffer.size())
- mSem.wait();
- std::for_each(mWaveBuffer.begin(), mWaveBuffer.end(),
- [this](WAVEHDR &waveHdr) -> void
- { waveOutUnprepareHeader(mOutHdl, &waveHdr, sizeof(WAVEHDR)); }
- );
- mWritable.store(0, std::memory_order_release);
-}
-
-
-struct WinMMCapture final : public BackendBase {
- WinMMCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~WinMMCapture() override;
-
- static void CALLBACK waveInProcC(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
- void CALLBACK waveInProc(HWAVEIN device, UINT msg, DWORD_PTR param1, DWORD_PTR param2);
-
- int captureProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- std::atomic<ALuint> mReadable{0u};
- al::semaphore mSem;
- int mIdx{0};
- std::array<WAVEHDR,4> mWaveBuffer{};
-
- HWAVEIN mInHdl{nullptr};
-
- RingBufferPtr mRing{nullptr};
-
- WAVEFORMATEX mFormat{};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(WinMMCapture)
-};
-
-WinMMCapture::~WinMMCapture()
-{
- // Close the Wave device
- if(mInHdl)
- waveInClose(mInHdl);
- mInHdl = nullptr;
-
- al_free(mWaveBuffer[0].lpData);
- std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
-}
-
-void CALLBACK WinMMCapture::waveInProcC(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
-{ reinterpret_cast<WinMMCapture*>(instance)->waveInProc(device, msg, param1, param2); }
-
-/* WinMMCapture::waveInProc
- *
- * Posts a message to 'WinMMCapture::captureProc' everytime a WaveIn Buffer is
- * completed and returns to the application (with more data).
- */
-void CALLBACK WinMMCapture::waveInProc(HWAVEIN, UINT msg, DWORD_PTR, DWORD_PTR)
-{
- if(msg != WIM_DATA) return;
- mReadable.fetch_add(1, std::memory_order_acq_rel);
- mSem.post();
-}
-
-int WinMMCapture::captureProc()
-{
- althrd_setname(RECORD_THREAD_NAME);
-
- lock();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- ALuint todo{mReadable.load(std::memory_order_acquire)};
- if(todo < 1)
- {
- unlock();
- mSem.wait();
- lock();
- continue;
- }
-
- int widx{mIdx};
- do {
- WAVEHDR &waveHdr = mWaveBuffer[widx];
- widx = (widx+1) % mWaveBuffer.size();
-
- mRing->write(waveHdr.lpData, waveHdr.dwBytesRecorded / mFormat.nBlockAlign);
- mReadable.fetch_sub(1, std::memory_order_acq_rel);
- waveInAddBuffer(mInHdl, &waveHdr, sizeof(WAVEHDR));
- } while(--todo);
- mIdx = widx;
- }
- unlock();
-
- return 0;
-}
-
-
-ALCenum WinMMCapture::open(const ALCchar *name)
-{
- if(CaptureDevices.empty())
- ProbeCaptureDevices();
-
- // Find the Device ID matching the deviceName if valid
- auto iter = name ?
- std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), name) :
- CaptureDevices.cbegin();
- if(iter == CaptureDevices.cend()) return ALC_INVALID_VALUE;
- auto DeviceID = static_cast<UINT>(std::distance(CaptureDevices.cbegin(), iter));
-
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- case DevFmtStereo:
- break;
-
- case DevFmtQuad:
- case DevFmtX51:
- case DevFmtX51Rear:
- case DevFmtX61:
- case DevFmtX71:
- case DevFmtAmbi3D:
- return ALC_INVALID_ENUM;
- }
-
- switch(mDevice->FmtType)
- {
- case DevFmtUByte:
- case DevFmtShort:
- case DevFmtInt:
- case DevFmtFloat:
- break;
-
- case DevFmtByte:
- case DevFmtUShort:
- case DevFmtUInt:
- return ALC_INVALID_ENUM;
- }
-
- mFormat = WAVEFORMATEX{};
- mFormat.wFormatTag = (mDevice->FmtType == DevFmtFloat) ?
- WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
- mFormat.nChannels = mDevice->channelsFromFmt();
- mFormat.wBitsPerSample = mDevice->bytesFromFmt() * 8;
- mFormat.nBlockAlign = mFormat.wBitsPerSample * mFormat.nChannels / 8;
- mFormat.nSamplesPerSec = mDevice->Frequency;
- mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
- mFormat.cbSize = 0;
-
- MMRESULT res{waveInOpen(&mInHdl, DeviceID, &mFormat, (DWORD_PTR)&WinMMCapture::waveInProcC,
- reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
- if(res != MMSYSERR_NOERROR)
- {
- ERR("waveInOpen failed: %u\n", res);
- return ALC_INVALID_VALUE;
- }
-
- // Ensure each buffer is 50ms each
- DWORD BufferSize{mFormat.nAvgBytesPerSec / 20u};
- BufferSize -= (BufferSize % mFormat.nBlockAlign);
-
- // Allocate circular memory buffer for the captured audio
- // Make sure circular buffer is at least 100ms in size
- ALuint CapturedDataSize{mDevice->BufferSize};
- CapturedDataSize = static_cast<ALuint>(maxz(CapturedDataSize, BufferSize*mWaveBuffer.size()));
-
- mRing = CreateRingBuffer(CapturedDataSize, mFormat.nBlockAlign, false);
- if(!mRing) return ALC_INVALID_VALUE;
-
- al_free(mWaveBuffer[0].lpData);
- mWaveBuffer[0] = WAVEHDR{};
- mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize*4));
- mWaveBuffer[0].dwBufferLength = BufferSize;
- for(size_t i{1};i < mWaveBuffer.size();++i)
- {
- mWaveBuffer[i] = WAVEHDR{};
- mWaveBuffer[i].lpData = mWaveBuffer[i-1].lpData + mWaveBuffer[i-1].dwBufferLength;
- mWaveBuffer[i].dwBufferLength = mWaveBuffer[i-1].dwBufferLength;
- }
-
- mDevice->DeviceName = CaptureDevices[DeviceID];
- return ALC_NO_ERROR;
-}
-
-ALCboolean WinMMCapture::start()
-{
- try {
- for(size_t i{0};i < mWaveBuffer.size();++i)
- {
- waveInPrepareHeader(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
- waveInAddBuffer(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
- }
-
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&WinMMCapture::captureProc), this};
-
- waveInStart(mInHdl);
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Failed to start mixing thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void WinMMCapture::stop()
-{
- waveInStop(mInHdl);
-
- mKillNow.store(true, std::memory_order_release);
- if(mThread.joinable())
- {
- mSem.post();
- mThread.join();
- }
-
- waveInReset(mInHdl);
- for(size_t i{0};i < mWaveBuffer.size();++i)
- waveInUnprepareHeader(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
-
- mReadable.store(0, std::memory_order_release);
- mIdx = 0;
-}
-
-ALCenum WinMMCapture::captureSamples(void *buffer, ALCuint samples)
-{
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
-}
-
-ALCuint WinMMCapture::availableSamples()
-{ return (ALCuint)mRing->readSpace(); }
-
-} // namespace
-
-
-bool WinMMBackendFactory::init()
-{ return true; }
-
-bool WinMMBackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback || type == BackendType::Capture; }
-
-void WinMMBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const std::string &dname) -> void
- {
- /* +1 to also append the null char (to ensure a null-separated list and
- * double-null terminated list).
- */
- if(!dname.empty())
- outnames->append(dname.c_str(), dname.length()+1);
- };
- switch(type)
- {
- case DevProbe::Playback:
- ProbePlaybackDevices();
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- ProbeCaptureDevices();
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
-}
-
-BackendPtr WinMMBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new WinMMPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new WinMMCapture{device}};
- return nullptr;
-}
-
-BackendFactory &WinMMBackendFactory::getFactory()
-{
- static WinMMBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/winmm.h b/Alc/backends/winmm.h
deleted file mode 100644
index e357ec19..00000000
--- a/Alc/backends/winmm.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_WINMM_H
-#define BACKENDS_WINMM_H
-
-#include "backends/base.h"
-
-struct WinMMBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_WINMM_H */
diff --git a/Alc/bformatdec.cpp b/Alc/bformatdec.cpp
deleted file mode 100644
index 889bbf3a..00000000
--- a/Alc/bformatdec.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-
-#include "config.h"
-
-#include "bformatdec.h"
-
-#include <algorithm>
-#include <array>
-#include <cassert>
-#include <cmath>
-#include <iterator>
-#include <numeric>
-
-#include "almalloc.h"
-#include "alu.h"
-#include "ambdec.h"
-#include "filters/splitter.h"
-#include "opthelpers.h"
-
-
-namespace {
-
-constexpr ALfloat Ambi3DDecoderHFScale[MAX_AMBI_ORDER+1] = {
- 1.00000000e+00f, 1.00000000e+00f
-};
-constexpr ALfloat Ambi3DDecoderHFScale2O[MAX_AMBI_ORDER+1] = {
- 7.45355990e-01f, 1.00000000e+00f
-};
-constexpr ALfloat Ambi3DDecoderHFScale3O[MAX_AMBI_ORDER+1] = {
- 5.89792205e-01f, 8.79693856e-01f
-};
-
-inline auto GetDecoderHFScales(ALsizei order) noexcept -> const ALfloat(&)[MAX_AMBI_ORDER+1]
-{
- if(order >= 3) return Ambi3DDecoderHFScale3O;
- if(order == 2) return Ambi3DDecoderHFScale2O;
- return Ambi3DDecoderHFScale;
-}
-
-inline auto GetAmbiScales(AmbDecScale scaletype) noexcept -> const std::array<float,MAX_AMBI_CHANNELS>&
-{
- if(scaletype == AmbDecScale::FuMa) return AmbiScale::FromFuMa;
- if(scaletype == AmbDecScale::SN3D) return AmbiScale::FromSN3D;
- return AmbiScale::FromN3D;
-}
-
-} // namespace
-
-
-BFormatDec::BFormatDec(const AmbDecConf *conf, const bool allow_2band, const ALuint inchans,
- const ALuint srate, const ALsizei (&chanmap)[MAX_OUTPUT_CHANNELS])
-{
- mDualBand = allow_2band && (conf->FreqBands == 2);
- if(!mDualBand)
- mSamples.resize(2);
- else
- {
- ASSUME(inchans > 0);
- mSamples.resize(inchans * 2);
- mSamplesHF = mSamples.data();
- mSamplesLF = mSamplesHF + inchans;
- }
- mNumChannels = inchans;
-
- mEnabled = std::accumulate(std::begin(chanmap), std::begin(chanmap)+conf->Speakers.size(), 0u,
- [](ALuint mask, const ALsizei &chan) noexcept -> ALuint
- { return mask | (1 << chan); }
- );
-
- const ALfloat xover_norm{conf->XOverFreq / static_cast<float>(srate)};
-
- const bool periphonic{(conf->ChanMask&AMBI_PERIPHONIC_MASK) != 0};
- const std::array<float,MAX_AMBI_CHANNELS> &coeff_scale = GetAmbiScales(conf->CoeffScale);
- const size_t coeff_count{periphonic ? MAX_AMBI_CHANNELS : MAX_AMBI2D_CHANNELS};
-
- if(!mDualBand)
- {
- for(size_t i{0u};i < conf->Speakers.size();i++)
- {
- ALfloat (&mtx)[MAX_AMBI_CHANNELS] = mMatrix.Single[chanmap[i]];
- for(size_t j{0},k{0};j < coeff_count;j++)
- {
- const size_t l{periphonic ? j : AmbiIndex::From2D[j]};
- if(!(conf->ChanMask&(1u<<l))) continue;
- mtx[j] = conf->HFMatrix[i][k] / coeff_scale[l] *
- ((l>=9) ? conf->HFOrderGain[3] :
- (l>=4) ? conf->HFOrderGain[2] :
- (l>=1) ? conf->HFOrderGain[1] : conf->HFOrderGain[0]);
- ++k;
- }
- }
- }
- else
- {
- mXOver[0].init(xover_norm);
- std::fill(std::begin(mXOver)+1, std::end(mXOver), mXOver[0]);
-
- const float ratio{std::pow(10.0f, conf->XOverRatio / 40.0f)};
- for(size_t i{0u};i < conf->Speakers.size();i++)
- {
- ALfloat (&mtx)[sNumBands][MAX_AMBI_CHANNELS] = mMatrix.Dual[chanmap[i]];
- for(size_t j{0},k{0};j < coeff_count;j++)
- {
- const size_t l{periphonic ? j : AmbiIndex::From2D[j]};
- if(!(conf->ChanMask&(1u<<l))) continue;
- mtx[sHFBand][j] = conf->HFMatrix[i][k] / coeff_scale[l] *
- ((l>=9) ? conf->HFOrderGain[3] :
- (l>=4) ? conf->HFOrderGain[2] :
- (l>=1) ? conf->HFOrderGain[1] : conf->HFOrderGain[0]) * ratio;
- mtx[sLFBand][j] = conf->LFMatrix[i][k] / coeff_scale[l] *
- ((l>=9) ? conf->LFOrderGain[3] :
- (l>=4) ? conf->LFOrderGain[2] :
- (l>=1) ? conf->LFOrderGain[1] : conf->LFOrderGain[0]) / ratio;
- ++k;
- }
- }
- }
-}
-
-BFormatDec::BFormatDec(const ALuint inchans, const ALsizei chancount,
- const ChannelDec (&chancoeffs)[MAX_OUTPUT_CHANNELS],
- const ALsizei (&chanmap)[MAX_OUTPUT_CHANNELS])
-{
- mSamples.resize(2);
- mNumChannels = inchans;
-
- ASSUME(chancount > 0);
- mEnabled = std::accumulate(std::begin(chanmap), std::begin(chanmap)+chancount, 0u,
- [](ALuint mask, const ALsizei &chan) noexcept -> ALuint
- { return mask | (1 << chan); }
- );
-
- const ChannelDec *incoeffs{chancoeffs};
- auto set_coeffs = [this,inchans,&incoeffs](const ALsizei chanidx) noexcept -> void
- {
- ASSUME(chanidx >= 0);
- ALfloat (&mtx)[MAX_AMBI_CHANNELS] = mMatrix.Single[chanidx];
- const ALfloat (&coeffs)[MAX_AMBI_CHANNELS] = *(incoeffs++);
-
- ASSUME(inchans > 0);
- std::copy_n(std::begin(coeffs), inchans, std::begin(mtx));
- };
- std::for_each(chanmap, chanmap+chancount, set_coeffs);
-}
-
-
-void BFormatDec::process(const al::span<FloatBufferLine> OutBuffer,
- const FloatBufferLine *InSamples, const ALsizei SamplesToDo)
-{
- if(mDualBand)
- {
- for(ALuint i{0};i < mNumChannels;i++)
- mXOver[i].process(mSamplesHF[i].data(), mSamplesLF[i].data(), InSamples[i].data(),
- SamplesToDo);
-
- const al::span<const FloatBufferLine> hfsamples{mSamplesHF, mNumChannels};
- const al::span<const FloatBufferLine> lfsamples{mSamplesLF, mNumChannels};
- ALfloat (*mixmtx)[sNumBands][MAX_AMBI_CHANNELS]{mMatrix.Dual};
- ALuint enabled{mEnabled};
- for(FloatBufferLine &outbuf : OutBuffer)
- {
- if(LIKELY(enabled&1))
- {
- MixRowSamples(outbuf, (*mixmtx)[sHFBand], hfsamples, 0, SamplesToDo);
- MixRowSamples(outbuf, (*mixmtx)[sLFBand], lfsamples, 0, SamplesToDo);
- }
- ++mixmtx;
- enabled >>= 1;
- }
- }
- else
- {
- const al::span<const FloatBufferLine> insamples{InSamples, mNumChannels};
- ALfloat (*mixmtx)[MAX_AMBI_CHANNELS]{mMatrix.Single};
- ALuint enabled{mEnabled};
- for(FloatBufferLine &outbuf : OutBuffer)
- {
- if(LIKELY(enabled&1))
- MixRowSamples(outbuf, *mixmtx, insamples, 0, SamplesToDo);
- ++mixmtx;
- enabled >>= 1;
- }
- }
-}
-
-
-std::array<ALfloat,MAX_AMBI_ORDER+1> BFormatDec::GetHFOrderScales(const ALsizei in_order, const ALsizei out_order) noexcept
-{
- std::array<ALfloat,MAX_AMBI_ORDER+1> ret{};
-
- assert(out_order >= in_order);
- ASSUME(out_order >= in_order);
-
- const ALfloat (&target)[MAX_AMBI_ORDER+1] = GetDecoderHFScales(out_order);
- const ALfloat (&input)[MAX_AMBI_ORDER+1] = GetDecoderHFScales(in_order);
-
- for(ALsizei i{0};i < in_order+1;++i)
- ret[i] = input[i] / target[i];
-
- return ret;
-}
diff --git a/Alc/bformatdec.h b/Alc/bformatdec.h
deleted file mode 100644
index 06974651..00000000
--- a/Alc/bformatdec.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef BFORMATDEC_H
-#define BFORMATDEC_H
-
-#include <array>
-#include <cstddef>
-
-#include "AL/al.h"
-
-#include "alcmain.h"
-#include "almalloc.h"
-#include "alspan.h"
-#include "ambidefs.h"
-#include "filters/splitter.h"
-#include "vector.h"
-
-struct AmbDecConf;
-
-
-using ChannelDec = ALfloat[MAX_AMBI_CHANNELS];
-
-class BFormatDec {
- static constexpr size_t sHFBand{0};
- static constexpr size_t sLFBand{1};
- static constexpr size_t sNumBands{2};
-
- ALuint mEnabled{0u}; /* Bitfield of enabled channels. */
-
- union MatrixU {
- ALfloat Dual[MAX_OUTPUT_CHANNELS][sNumBands][MAX_AMBI_CHANNELS];
- ALfloat Single[MAX_OUTPUT_CHANNELS][MAX_AMBI_CHANNELS];
- } mMatrix{};
-
- /* NOTE: BandSplitter filters are unused with single-band decoding */
- BandSplitter mXOver[MAX_AMBI_CHANNELS];
-
- al::vector<FloatBufferLine, 16> mSamples;
- /* These two alias into Samples */
- FloatBufferLine *mSamplesHF{nullptr};
- FloatBufferLine *mSamplesLF{nullptr};
-
- ALuint mNumChannels{0u};
- bool mDualBand{false};
-
-public:
- BFormatDec(const AmbDecConf *conf, const bool allow_2band, const ALuint inchans,
- const ALuint srate, const ALsizei (&chanmap)[MAX_OUTPUT_CHANNELS]);
- BFormatDec(const ALuint inchans, const ALsizei chancount,
- const ChannelDec (&chancoeffs)[MAX_OUTPUT_CHANNELS],
- const ALsizei (&chanmap)[MAX_OUTPUT_CHANNELS]);
-
- /* Decodes the ambisonic input to the given output channels. */
- void process(const al::span<FloatBufferLine> OutBuffer, const FloatBufferLine *InSamples,
- const ALsizei SamplesToDo);
-
- /* Retrieves per-order HF scaling factors for "upsampling" ambisonic data. */
- static std::array<ALfloat,MAX_AMBI_ORDER+1> GetHFOrderScales(const ALsizei in_order,
- const ALsizei out_order) noexcept;
-
- DEF_NEWDEL(BFormatDec)
-};
-
-#endif /* BFORMATDEC_H */
diff --git a/Alc/bs2b.cpp b/Alc/bs2b.cpp
deleted file mode 100644
index 2d1b96aa..00000000
--- a/Alc/bs2b.cpp
+++ /dev/null
@@ -1,188 +0,0 @@
-/*-
- * Copyright (c) 2005 Boris Mikhaylov
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstring>
-#include <algorithm>
-
-#include "bs2b.h"
-#include "math_defs.h"
-
-
-/* Set up all data. */
-static void init(struct bs2b *bs2b)
-{
- float Fc_lo, Fc_hi;
- float G_lo, G_hi;
- float x, g;
-
- switch(bs2b->level)
- {
- case BS2B_LOW_CLEVEL: /* Low crossfeed level */
- Fc_lo = 360.0f;
- Fc_hi = 501.0f;
- G_lo = 0.398107170553497f;
- G_hi = 0.205671765275719f;
- break;
-
- case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */
- Fc_lo = 500.0f;
- Fc_hi = 711.0f;
- G_lo = 0.459726988530872f;
- G_hi = 0.228208484414988f;
- break;
-
- case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */
- Fc_lo = 700.0f;
- Fc_hi = 1021.0f;
- G_lo = 0.530884444230988f;
- G_hi = 0.250105790667544f;
- break;
-
- case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */
- Fc_lo = 360.0f;
- Fc_hi = 494.0f;
- G_lo = 0.316227766016838f;
- G_hi = 0.168236228897329f;
- break;
-
- case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */
- Fc_lo = 500.0f;
- Fc_hi = 689.0f;
- G_lo = 0.354813389233575f;
- G_hi = 0.187169483835901f;
- break;
-
- default: /* High easy crossfeed level */
- bs2b->level = BS2B_HIGH_ECLEVEL;
-
- Fc_lo = 700.0f;
- Fc_hi = 975.0f;
- G_lo = 0.398107170553497f;
- G_hi = 0.205671765275719f;
- break;
- } /* switch */
-
- g = 1.0f / (1.0f - G_hi + G_lo);
-
- /* $fc = $Fc / $s;
- * $d = 1 / 2 / pi / $fc;
- * $x = exp(-1 / $d);
- */
- x = std::exp(-al::MathDefs<float>::Tau() * Fc_lo / bs2b->srate);
- bs2b->b1_lo = x;
- bs2b->a0_lo = G_lo * (1.0f - x) * g;
-
- x = std::exp(-al::MathDefs<float>::Tau() * Fc_hi / bs2b->srate);
- bs2b->b1_hi = x;
- bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g;
- bs2b->a1_hi = -x * g;
-} /* init */
-
-
-/* Exported functions.
- * See descriptions in "bs2b.h"
- */
-
-void bs2b_set_params(struct bs2b *bs2b, int level, int srate)
-{
- if(srate <= 0) srate = 1;
-
- bs2b->level = level;
- bs2b->srate = srate;
- init(bs2b);
-} /* bs2b_set_params */
-
-int bs2b_get_level(struct bs2b *bs2b)
-{
- return bs2b->level;
-} /* bs2b_get_level */
-
-int bs2b_get_srate(struct bs2b *bs2b)
-{
- return bs2b->srate;
-} /* bs2b_get_srate */
-
-void bs2b_clear(struct bs2b *bs2b)
-{
- std::fill(std::begin(bs2b->last_sample), std::end(bs2b->last_sample), bs2b::t_last_sample{});
-} /* bs2b_clear */
-
-void bs2b_cross_feed(struct bs2b *bs2b, float *RESTRICT Left, float *RESTRICT Right, int SamplesToDo)
-{
- float lsamples[128][2];
- float rsamples[128][2];
- int base;
-
- for(base = 0;base < SamplesToDo;)
- {
- int todo = std::min(128, SamplesToDo-base);
- int i;
-
- /* Process left input */
- lsamples[0][0] = bs2b->a0_lo*Left[0] +
- bs2b->b1_lo*bs2b->last_sample[0].lo;
- lsamples[0][1] = bs2b->a0_hi*Left[0] +
- bs2b->a1_hi*bs2b->last_sample[0].asis +
- bs2b->b1_hi*bs2b->last_sample[0].hi;
- for(i = 1;i < todo;i++)
- {
- lsamples[i][0] = bs2b->a0_lo*Left[i] +
- bs2b->b1_lo*lsamples[i-1][0];
- lsamples[i][1] = bs2b->a0_hi*Left[i] +
- bs2b->a1_hi*Left[i-1] +
- bs2b->b1_hi*lsamples[i-1][1];
- }
- bs2b->last_sample[0].asis = Left[i-1];
- bs2b->last_sample[0].lo = lsamples[i-1][0];
- bs2b->last_sample[0].hi = lsamples[i-1][1];
-
- /* Process right input */
- rsamples[0][0] = bs2b->a0_lo*Right[0] +
- bs2b->b1_lo*bs2b->last_sample[1].lo;
- rsamples[0][1] = bs2b->a0_hi*Right[0] +
- bs2b->a1_hi*bs2b->last_sample[1].asis +
- bs2b->b1_hi*bs2b->last_sample[1].hi;
- for(i = 1;i < todo;i++)
- {
- rsamples[i][0] = bs2b->a0_lo*Right[i] +
- bs2b->b1_lo*rsamples[i-1][0];
- rsamples[i][1] = bs2b->a0_hi*Right[i] +
- bs2b->a1_hi*Right[i-1] +
- bs2b->b1_hi*rsamples[i-1][1];
- }
- bs2b->last_sample[1].asis = Right[i-1];
- bs2b->last_sample[1].lo = rsamples[i-1][0];
- bs2b->last_sample[1].hi = rsamples[i-1][1];
-
- /* Crossfeed */
- for(i = 0;i < todo;i++)
- *(Left++) = lsamples[i][1] + rsamples[i][0];
- for(i = 0;i < todo;i++)
- *(Right++) = rsamples[i][1] + lsamples[i][0];
-
- base += todo;
- }
-} /* bs2b_cross_feed */
diff --git a/Alc/bs2b.h b/Alc/bs2b.h
deleted file mode 100644
index e235e765..00000000
--- a/Alc/bs2b.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*-
- * Copyright (c) 2005 Boris Mikhaylov
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#ifndef BS2B_H
-#define BS2B_H
-
-#include "almalloc.h"
-
-/* Number of crossfeed levels */
-#define BS2B_CLEVELS 3
-
-/* Normal crossfeed levels */
-#define BS2B_HIGH_CLEVEL 3
-#define BS2B_MIDDLE_CLEVEL 2
-#define BS2B_LOW_CLEVEL 1
-
-/* Easy crossfeed levels */
-#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS
-#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS
-#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS
-
-/* Default crossfeed levels */
-#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL
-/* Default sample rate (Hz) */
-#define BS2B_DEFAULT_SRATE 44100
-
-struct bs2b {
- int level; /* Crossfeed level */
- int srate; /* Sample rate (Hz) */
-
- /* Lowpass IIR filter coefficients */
- float a0_lo;
- float b1_lo;
-
- /* Highboost IIR filter coefficients */
- float a0_hi;
- float a1_hi;
- float b1_hi;
-
- /* Buffer of last filtered sample.
- * [0] - first channel, [1] - second channel
- */
- struct t_last_sample {
- float asis;
- float lo;
- float hi;
- } last_sample[2];
-
- DEF_NEWDEL(bs2b)
-};
-
-/* Clear buffers and set new coefficients with new crossfeed level and sample
- * rate values.
- * level - crossfeed level of *LEVEL values.
- * srate - sample rate by Hz.
- */
-void bs2b_set_params(bs2b *bs2b, int level, int srate);
-
-/* Return current crossfeed level value */
-int bs2b_get_level(bs2b *bs2b);
-
-/* Return current sample rate value */
-int bs2b_get_srate(bs2b *bs2b);
-
-/* Clear buffer */
-void bs2b_clear(bs2b *bs2b);
-
-void bs2b_cross_feed(bs2b *bs2b, float *RESTRICT Left, float *RESTRICT Right, int SamplesToDo);
-
-#endif /* BS2B_H */
diff --git a/Alc/compat.h b/Alc/compat.h
deleted file mode 100644
index 4ffc40bf..00000000
--- a/Alc/compat.h
+++ /dev/null
@@ -1,121 +0,0 @@
-#ifndef AL_COMPAT_H
-#define AL_COMPAT_H
-
-#ifdef __cplusplus
-
-#ifdef _WIN32
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-#include <array>
-#include <string>
-#include <fstream>
-
-inline std::string wstr_to_utf8(const WCHAR *wstr)
-{
- std::string ret;
-
- int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
- if(len > 0)
- {
- ret.resize(len);
- WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &ret[0], len, nullptr, nullptr);
- ret.pop_back();
- }
-
- return ret;
-}
-
-inline std::wstring utf8_to_wstr(const char *str)
-{
- std::wstring ret;
-
- int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
- if(len > 0)
- {
- ret.resize(len);
- MultiByteToWideChar(CP_UTF8, 0, str, -1, &ret[0], len);
- ret.pop_back();
- }
-
- return ret;
-}
-
-
-namespace al {
-
-// Windows' std::ifstream fails with non-ANSI paths since the standard only
-// specifies names using const char* (or std::string). MSVC has a non-standard
-// extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
-// but not all Windows compilers support it. So we have to make our own istream
-// that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
-class filebuf final : public std::streambuf {
- std::array<char_type,4096> mBuffer;
- HANDLE mFile{INVALID_HANDLE_VALUE};
-
- int_type underflow() override;
- pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override;
- pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override;
-
-public:
- filebuf() = default;
- ~filebuf() override;
-
- bool open(const wchar_t *filename, std::ios_base::openmode mode);
- bool open(const char *filename, std::ios_base::openmode mode);
-
- bool is_open() const noexcept { return mFile != INVALID_HANDLE_VALUE; }
-};
-
-// Inherit from std::istream to use our custom streambuf
-class ifstream final : public std::istream {
- filebuf mStreamBuf;
-
-public:
- ifstream(const wchar_t *filename, std::ios_base::openmode mode = std::ios_base::in);
- ifstream(const std::wstring &filename, std::ios_base::openmode mode = std::ios_base::in)
- : ifstream(filename.c_str(), mode) { }
- ifstream(const char *filename, std::ios_base::openmode mode = std::ios_base::in);
- ifstream(const std::string &filename, std::ios_base::openmode mode = std::ios_base::in)
- : ifstream(filename.c_str(), mode) { }
- ~ifstream() override;
-
- bool is_open() const noexcept { return mStreamBuf.is_open(); }
-};
-
-} // namespace al
-
-#define HAVE_DYNLOAD 1
-
-#else /* _WIN32 */
-
-#include <fstream>
-
-namespace al {
-
-using filebuf = std::filebuf;
-using ifstream = std::ifstream;
-
-} // namespace al
-
-#if defined(HAVE_DLFCN_H)
-#define HAVE_DYNLOAD 1
-#endif
-
-#endif /* _WIN32 */
-
-#include <string>
-
-struct PathNamePair { std::string path, fname; };
-const PathNamePair &GetProcBinary(void);
-
-#ifdef HAVE_DYNLOAD
-void *LoadLib(const char *name);
-void CloseLib(void *handle);
-void *GetSymbol(void *handle, const char *name);
-#endif
-
-#endif /* __cplusplus */
-
-#endif /* AL_COMPAT_H */
diff --git a/Alc/converter.cpp b/Alc/converter.cpp
deleted file mode 100644
index 0f8e8941..00000000
--- a/Alc/converter.cpp
+++ /dev/null
@@ -1,367 +0,0 @@
-
-#include "config.h"
-
-#include "converter.h"
-
-#include <algorithm>
-
-#include "fpu_modes.h"
-#include "mixer/defs.h"
-
-
-namespace {
-
-/* Base template left undefined. Should be marked =delete, but Clang 3.8.1
- * chokes on that given the inline specializations.
- */
-template<DevFmtType T>
-inline ALfloat LoadSample(typename DevFmtTypeTraits<T>::Type val) noexcept;
-
-template<> inline ALfloat LoadSample<DevFmtByte>(DevFmtTypeTraits<DevFmtByte>::Type val) noexcept
-{ return val * (1.0f/128.0f); }
-template<> inline ALfloat LoadSample<DevFmtShort>(DevFmtTypeTraits<DevFmtShort>::Type val) noexcept
-{ return val * (1.0f/32768.0f); }
-template<> inline ALfloat LoadSample<DevFmtInt>(DevFmtTypeTraits<DevFmtInt>::Type val) noexcept
-{ return val * (1.0f/2147483648.0f); }
-template<> inline ALfloat LoadSample<DevFmtFloat>(DevFmtTypeTraits<DevFmtFloat>::Type val) noexcept
-{ return val; }
-
-template<> inline ALfloat LoadSample<DevFmtUByte>(DevFmtTypeTraits<DevFmtUByte>::Type val) noexcept
-{ return LoadSample<DevFmtByte>(val - 128); }
-template<> inline ALfloat LoadSample<DevFmtUShort>(DevFmtTypeTraits<DevFmtUShort>::Type val) noexcept
-{ return LoadSample<DevFmtShort>(val - 32768); }
-template<> inline ALfloat LoadSample<DevFmtUInt>(DevFmtTypeTraits<DevFmtUInt>::Type val) noexcept
-{ return LoadSample<DevFmtInt>(val - 2147483648u); }
-
-
-template<DevFmtType T>
-inline void LoadSampleArray(ALfloat *RESTRICT dst, const void *src, const size_t srcstep,
- const ALsizei samples) noexcept
-{
- using SampleType = typename DevFmtTypeTraits<T>::Type;
-
- const SampleType *ssrc = static_cast<const SampleType*>(src);
- for(ALsizei i{0};i < samples;i++)
- dst[i] = LoadSample<T>(ssrc[i*srcstep]);
-}
-
-void LoadSamples(ALfloat *dst, const ALvoid *src, const size_t srcstep, const DevFmtType srctype,
- const ALsizei samples) noexcept
-{
-#define HANDLE_FMT(T) \
- case T: LoadSampleArray<T>(dst, src, srcstep, samples); break
- switch(srctype)
- {
- HANDLE_FMT(DevFmtByte);
- HANDLE_FMT(DevFmtUByte);
- HANDLE_FMT(DevFmtShort);
- HANDLE_FMT(DevFmtUShort);
- HANDLE_FMT(DevFmtInt);
- HANDLE_FMT(DevFmtUInt);
- HANDLE_FMT(DevFmtFloat);
- }
-#undef HANDLE_FMT
-}
-
-
-template<DevFmtType T>
-inline typename DevFmtTypeTraits<T>::Type StoreSample(ALfloat) noexcept;
-
-template<> inline ALfloat StoreSample<DevFmtFloat>(ALfloat val) noexcept
-{ return val; }
-template<> inline ALint StoreSample<DevFmtInt>(ALfloat val) noexcept
-{ return fastf2i(clampf(val*2147483648.0f, -2147483648.0f, 2147483520.0f)); }
-template<> inline ALshort StoreSample<DevFmtShort>(ALfloat val) noexcept
-{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
-template<> inline ALbyte StoreSample<DevFmtByte>(ALfloat val) noexcept
-{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
-
-/* Define unsigned output variations. */
-template<> inline ALuint StoreSample<DevFmtUInt>(ALfloat val) noexcept
-{ return StoreSample<DevFmtInt>(val) + 2147483648u; }
-template<> inline ALushort StoreSample<DevFmtUShort>(ALfloat val) noexcept
-{ return StoreSample<DevFmtShort>(val) + 32768; }
-template<> inline ALubyte StoreSample<DevFmtUByte>(ALfloat val) noexcept
-{ return StoreSample<DevFmtByte>(val) + 128; }
-
-template<DevFmtType T>
-inline void StoreSampleArray(void *dst, const ALfloat *RESTRICT src, const size_t dststep,
- const ALsizei samples) noexcept
-{
- using SampleType = typename DevFmtTypeTraits<T>::Type;
-
- SampleType *sdst = static_cast<SampleType*>(dst);
- for(ALsizei i{0};i < samples;i++)
- sdst[i*dststep] = StoreSample<T>(src[i]);
-}
-
-
-void StoreSamples(ALvoid *dst, const ALfloat *src, const size_t dststep, const DevFmtType dsttype,
- const ALsizei samples) noexcept
-{
-#define HANDLE_FMT(T) \
- case T: StoreSampleArray<T>(dst, src, dststep, samples); break
- switch(dsttype)
- {
- HANDLE_FMT(DevFmtByte);
- HANDLE_FMT(DevFmtUByte);
- HANDLE_FMT(DevFmtShort);
- HANDLE_FMT(DevFmtUShort);
- HANDLE_FMT(DevFmtInt);
- HANDLE_FMT(DevFmtUInt);
- HANDLE_FMT(DevFmtFloat);
- }
-#undef HANDLE_FMT
-}
-
-
-template<DevFmtType T>
-void Mono2Stereo(ALfloat *RESTRICT dst, const void *src, const ALsizei frames) noexcept
-{
- using SampleType = typename DevFmtTypeTraits<T>::Type;
-
- const SampleType *ssrc = static_cast<const SampleType*>(src);
- for(ALsizei i{0};i < frames;i++)
- dst[i*2 + 1] = dst[i*2 + 0] = LoadSample<T>(ssrc[i]) * 0.707106781187f;
-}
-
-template<DevFmtType T>
-void Stereo2Mono(ALfloat *RESTRICT dst, const void *src, const ALsizei frames) noexcept
-{
- using SampleType = typename DevFmtTypeTraits<T>::Type;
-
- const SampleType *ssrc = static_cast<const SampleType*>(src);
- for(ALsizei i{0};i < frames;i++)
- dst[i] = (LoadSample<T>(ssrc[i*2 + 0])+LoadSample<T>(ssrc[i*2 + 1])) *
- 0.707106781187f;
-}
-
-} // namespace
-
-SampleConverterPtr CreateSampleConverter(DevFmtType srcType, DevFmtType dstType, ALsizei numchans,
- ALsizei srcRate, ALsizei dstRate, Resampler resampler)
-{
- if(numchans <= 0 || srcRate <= 0 || dstRate <= 0)
- return nullptr;
-
- void *ptr{al_calloc(16, SampleConverter::Sizeof(numchans))};
- SampleConverterPtr converter{new (ptr) SampleConverter{static_cast<size_t>(numchans)}};
- converter->mSrcType = srcType;
- converter->mDstType = dstType;
- converter->mSrcTypeSize = BytesFromDevFmt(srcType);
- converter->mDstTypeSize = BytesFromDevFmt(dstType);
-
- converter->mSrcPrepCount = 0;
- converter->mFracOffset = 0;
-
- /* Have to set the mixer FPU mode since that's what the resampler code expects. */
- FPUCtl mixer_mode{};
- auto step = static_cast<ALsizei>(
- mind(static_cast<ALdouble>(srcRate)/dstRate*FRACTIONONE + 0.5, MAX_PITCH*FRACTIONONE));
- converter->mIncrement = maxi(step, 1);
- if(converter->mIncrement == FRACTIONONE)
- converter->mResample = Resample_<CopyTag,CTag>;
- else
- {
- if(resampler == BSinc24Resampler)
- BsincPrepare(converter->mIncrement, &converter->mState.bsinc, &bsinc24);
- else if(resampler == BSinc12Resampler)
- BsincPrepare(converter->mIncrement, &converter->mState.bsinc, &bsinc12);
- converter->mResample = SelectResampler(resampler);
- }
-
- return converter;
-}
-
-ALsizei SampleConverter::availableOut(ALsizei srcframes) const
-{
- ALint prepcount{mSrcPrepCount};
- if(prepcount < 0)
- {
- /* Negative prepcount means we need to skip that many input samples. */
- if(-prepcount >= srcframes)
- return 0;
- srcframes += prepcount;
- prepcount = 0;
- }
-
- if(srcframes < 1)
- {
- /* No output samples if there's no input samples. */
- return 0;
- }
-
- if(prepcount < MAX_RESAMPLE_PADDING*2 &&
- MAX_RESAMPLE_PADDING*2 - prepcount >= srcframes)
- {
- /* Not enough input samples to generate an output sample. */
- return 0;
- }
-
- auto DataSize64 = static_cast<uint64_t>(prepcount);
- DataSize64 += srcframes;
- DataSize64 -= MAX_RESAMPLE_PADDING*2;
- DataSize64 <<= FRACTIONBITS;
- DataSize64 -= mFracOffset;
-
- /* If we have a full prep, we can generate at least one sample. */
- return static_cast<ALsizei>(clampu64((DataSize64 + mIncrement-1)/mIncrement, 1, BUFFERSIZE));
-}
-
-ALsizei SampleConverter::convert(const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes)
-{
- const ALsizei SrcFrameSize{static_cast<ALsizei>(mChan.size()) * mSrcTypeSize};
- const ALsizei DstFrameSize{static_cast<ALsizei>(mChan.size()) * mDstTypeSize};
- const ALsizei increment{mIncrement};
- auto SamplesIn = static_cast<const al::byte*>(*src);
- ALsizei NumSrcSamples{*srcframes};
-
- FPUCtl mixer_mode{};
- ALsizei pos{0};
- while(pos < dstframes && NumSrcSamples > 0)
- {
- ALint prepcount{mSrcPrepCount};
- if(prepcount < 0)
- {
- /* Negative prepcount means we need to skip that many input samples. */
- if(-prepcount >= NumSrcSamples)
- {
- mSrcPrepCount = prepcount + NumSrcSamples;
- NumSrcSamples = 0;
- break;
- }
- SamplesIn += SrcFrameSize*-prepcount;
- NumSrcSamples += prepcount;
- mSrcPrepCount = 0;
- continue;
- }
- ALint toread{mini(NumSrcSamples, BUFFERSIZE - MAX_RESAMPLE_PADDING*2)};
-
- if(prepcount < MAX_RESAMPLE_PADDING*2 &&
- MAX_RESAMPLE_PADDING*2 - prepcount >= toread)
- {
- /* Not enough input samples to generate an output sample. Store
- * what we're given for later.
- */
- for(size_t chan{0u};chan < mChan.size();chan++)
- LoadSamples(&mChan[chan].PrevSamples[prepcount], SamplesIn + mSrcTypeSize*chan,
- mChan.size(), mSrcType, toread);
-
- mSrcPrepCount = prepcount + toread;
- NumSrcSamples = 0;
- break;
- }
-
- ALfloat *RESTRICT SrcData{mSrcSamples};
- ALfloat *RESTRICT DstData{mDstSamples};
- ALsizei DataPosFrac{mFracOffset};
- auto DataSize64 = static_cast<uint64_t>(prepcount);
- DataSize64 += toread;
- DataSize64 -= MAX_RESAMPLE_PADDING*2;
- DataSize64 <<= FRACTIONBITS;
- DataSize64 -= DataPosFrac;
-
- /* If we have a full prep, we can generate at least one sample. */
- auto DstSize = static_cast<ALsizei>(
- clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE));
- DstSize = mini(DstSize, dstframes-pos);
-
- for(size_t chan{0u};chan < mChan.size();chan++)
- {
- const al::byte *SrcSamples{SamplesIn + mSrcTypeSize*chan};
- al::byte *DstSamples = static_cast<al::byte*>(dst) + mDstTypeSize*chan;
-
- /* Load the previous samples into the source data first, then the
- * new samples from the input buffer.
- */
- std::copy_n(mChan[chan].PrevSamples, prepcount, SrcData);
- LoadSamples(SrcData + prepcount, SrcSamples, mChan.size(), mSrcType, toread);
-
- /* Store as many prep samples for next time as possible, given the
- * number of output samples being generated.
- */
- ALsizei SrcDataEnd{(DstSize*increment + DataPosFrac)>>FRACTIONBITS};
- if(SrcDataEnd >= prepcount+toread)
- std::fill(std::begin(mChan[chan].PrevSamples),
- std::end(mChan[chan].PrevSamples), 0.0f);
- else
- {
- size_t len = mini(MAX_RESAMPLE_PADDING*2, prepcount+toread-SrcDataEnd);
- std::copy_n(SrcData+SrcDataEnd, len, mChan[chan].PrevSamples);
- std::fill(std::begin(mChan[chan].PrevSamples)+len,
- std::end(mChan[chan].PrevSamples), 0.0f);
- }
-
- /* Now resample, and store the result in the output buffer. */
- const ALfloat *ResampledData{mResample(&mState, SrcData+MAX_RESAMPLE_PADDING,
- DataPosFrac, increment, DstData, DstSize)};
-
- StoreSamples(DstSamples, ResampledData, mChan.size(), mDstType, DstSize);
- }
-
- /* Update the number of prep samples still available, as well as the
- * fractional offset.
- */
- DataPosFrac += increment*DstSize;
- mSrcPrepCount = mini(prepcount + toread - (DataPosFrac>>FRACTIONBITS),
- MAX_RESAMPLE_PADDING*2);
- mFracOffset = DataPosFrac & FRACTIONMASK;
-
- /* Update the src and dst pointers in case there's still more to do. */
- SamplesIn += SrcFrameSize*(DataPosFrac>>FRACTIONBITS);
- NumSrcSamples -= mini(NumSrcSamples, (DataPosFrac>>FRACTIONBITS));
-
- dst = static_cast<al::byte*>(dst) + DstFrameSize*DstSize;
- pos += DstSize;
- }
-
- *src = SamplesIn;
- *srcframes = NumSrcSamples;
-
- return pos;
-}
-
-
-ChannelConverterPtr CreateChannelConverter(DevFmtType srcType, DevFmtChannels srcChans, DevFmtChannels dstChans)
-{
- if(srcChans != dstChans && !((srcChans == DevFmtMono && dstChans == DevFmtStereo) ||
- (srcChans == DevFmtStereo && dstChans == DevFmtMono)))
- return nullptr;
- return al::make_unique<ChannelConverter>(srcType, srcChans, dstChans);
-}
-
-void ChannelConverter::convert(const ALvoid *src, ALfloat *dst, ALsizei frames) const
-{
- if(mSrcChans == DevFmtStereo && mDstChans == DevFmtMono)
- {
- switch(mSrcType)
- {
-#define HANDLE_FMT(T) case T: Stereo2Mono<T>(dst, src, frames); break
- HANDLE_FMT(DevFmtByte);
- HANDLE_FMT(DevFmtUByte);
- HANDLE_FMT(DevFmtShort);
- HANDLE_FMT(DevFmtUShort);
- HANDLE_FMT(DevFmtInt);
- HANDLE_FMT(DevFmtUInt);
- HANDLE_FMT(DevFmtFloat);
-#undef HANDLE_FMT
- }
- }
- else if(mSrcChans == DevFmtMono && mDstChans == DevFmtStereo)
- {
- switch(mSrcType)
- {
-#define HANDLE_FMT(T) case T: Mono2Stereo<T>(dst, src, frames); break
- HANDLE_FMT(DevFmtByte);
- HANDLE_FMT(DevFmtUByte);
- HANDLE_FMT(DevFmtShort);
- HANDLE_FMT(DevFmtUShort);
- HANDLE_FMT(DevFmtInt);
- HANDLE_FMT(DevFmtUInt);
- HANDLE_FMT(DevFmtFloat);
-#undef HANDLE_FMT
- }
- }
- else
- LoadSamples(dst, src, 1u, mSrcType, frames*ChannelsFromDevFmt(mSrcChans, 0));
-}
diff --git a/Alc/converter.h b/Alc/converter.h
deleted file mode 100644
index 033e4d3f..00000000
--- a/Alc/converter.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef CONVERTER_H
-#define CONVERTER_H
-
-#include <memory>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "almalloc.h"
-
-struct SampleConverter {
- DevFmtType mSrcType{};
- DevFmtType mDstType{};
- ALsizei mSrcTypeSize{};
- ALsizei mDstTypeSize{};
-
- ALint mSrcPrepCount{};
-
- ALsizei mFracOffset{};
- ALsizei mIncrement{};
- InterpState mState{};
- ResamplerFunc mResample{};
-
- alignas(16) ALfloat mSrcSamples[BUFFERSIZE]{};
- alignas(16) ALfloat mDstSamples[BUFFERSIZE]{};
-
- struct ChanSamples {
- alignas(16) ALfloat PrevSamples[MAX_RESAMPLE_PADDING*2];
- };
- al::FlexArray<ChanSamples> mChan;
-
- SampleConverter(size_t numchans) : mChan{numchans} { }
- SampleConverter(const SampleConverter&) = delete;
- SampleConverter& operator=(const SampleConverter&) = delete;
-
- ALsizei convert(const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes);
- ALsizei availableOut(ALsizei srcframes) const;
-
- static constexpr size_t Sizeof(size_t length) noexcept
- {
- return maxz(sizeof(SampleConverter),
- al::FlexArray<ChanSamples>::Sizeof(length, offsetof(SampleConverter, mChan)));
- }
-
- DEF_PLACE_NEWDEL()
-};
-using SampleConverterPtr = std::unique_ptr<SampleConverter>;
-
-SampleConverterPtr CreateSampleConverter(DevFmtType srcType, DevFmtType dstType, ALsizei numchans,
- ALsizei srcRate, ALsizei dstRate, Resampler resampler);
-
-
-struct ChannelConverter {
- DevFmtType mSrcType;
- DevFmtChannels mSrcChans;
- DevFmtChannels mDstChans;
-
- ChannelConverter(DevFmtType srctype, DevFmtChannels srcchans, DevFmtChannels dstchans)
- : mSrcType(srctype), mSrcChans(srcchans), mDstChans(dstchans)
- { }
-
- void convert(const ALvoid *src, ALfloat *dst, ALsizei frames) const;
-
- DEF_NEWDEL(ChannelConverter)
-};
-using ChannelConverterPtr = std::unique_ptr<ChannelConverter>;
-
-ChannelConverterPtr CreateChannelConverter(DevFmtType srcType, DevFmtChannels srcChans,
- DevFmtChannels dstChans);
-
-#endif /* CONVERTER_H */
diff --git a/Alc/cpu_caps.h b/Alc/cpu_caps.h
deleted file mode 100644
index 64a4ee45..00000000
--- a/Alc/cpu_caps.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef CPU_CAPS_H
-#define CPU_CAPS_H
-
-
-extern int CPUCapFlags;
-enum {
- CPU_CAP_SSE = 1<<0,
- CPU_CAP_SSE2 = 1<<1,
- CPU_CAP_SSE3 = 1<<2,
- CPU_CAP_SSE4_1 = 1<<3,
- CPU_CAP_NEON = 1<<4,
-};
-
-void FillCPUCaps(int capfilter);
-
-#endif /* CPU_CAPS_H */
diff --git a/Alc/effects/autowah.cpp b/Alc/effects/autowah.cpp
deleted file mode 100644
index 96292636..00000000
--- a/Alc/effects/autowah.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2018 by Raul Herraiz.
- * 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 <cmath>
-#include <cstdlib>
-
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/biquad.h"
-#include "vecmat.h"
-
-namespace {
-
-#define MIN_FREQ 20.0f
-#define MAX_FREQ 2500.0f
-#define Q_FACTOR 5.0f
-
-struct ALautowahState final : public EffectState {
- /* Effect parameters */
- ALfloat mAttackRate;
- ALfloat mReleaseRate;
- ALfloat mResonanceGain;
- ALfloat mPeakGain;
- ALfloat mFreqMinNorm;
- ALfloat mBandwidthNorm;
- ALfloat mEnvDelay;
-
- /* Filter components derived from the envelope. */
- struct {
- ALfloat cos_w0;
- ALfloat alpha;
- } mEnv[BUFFERSIZE];
-
- struct {
- /* Effect filters' history. */
- struct {
- ALfloat z1, z2;
- } Filter;
-
- /* Effect gains for each output channel */
- ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
- ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
- } mChans[MAX_AMBI_CHANNELS];
-
- /* Effects buffers */
- alignas(16) ALfloat mBufferOut[BUFFERSIZE];
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(ALautowahState)
-};
-
-ALboolean ALautowahState::deviceUpdate(const ALCdevice*)
-{
- /* (Re-)initializing parameters and clear the buffers. */
-
- mAttackRate = 1.0f;
- mReleaseRate = 1.0f;
- mResonanceGain = 10.0f;
- mPeakGain = 4.5f;
- mFreqMinNorm = 4.5e-4f;
- mBandwidthNorm = 0.05f;
- mEnvDelay = 0.0f;
-
- for(auto &e : mEnv)
- {
- e.cos_w0 = 0.0f;
- e.alpha = 0.0f;
- }
-
- for(auto &chan : mChans)
- {
- std::fill(std::begin(chan.CurrentGains), std::end(chan.CurrentGains), 0.0f);
- chan.Filter.z1 = 0.0f;
- chan.Filter.z2 = 0.0f;
- }
-
- return AL_TRUE;
-}
-
-void ALautowahState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device{context->Device};
-
- const ALfloat ReleaseTime{clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f)};
-
- mAttackRate = expf(-1.0f / (props->Autowah.AttackTime*device->Frequency));
- mReleaseRate = expf(-1.0f / (ReleaseTime*device->Frequency));
- /* 0-20dB Resonance Peak gain */
- mResonanceGain = std::sqrt(std::log10(props->Autowah.Resonance)*10.0f / 3.0f);
- mPeakGain = 1.0f - std::log10(props->Autowah.PeakGain/AL_AUTOWAH_MAX_PEAK_GAIN);
- mFreqMinNorm = MIN_FREQ / device->Frequency;
- mBandwidthNorm = (MAX_FREQ-MIN_FREQ) / device->Frequency;
-
- mOutTarget = target.Main->Buffer;
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
- {
- auto coeffs = GetAmbiIdentityRow(i);
- ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mChans[i].TargetGains);
- }
-}
-
-void ALautowahState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
-{
- const ALfloat attack_rate = mAttackRate;
- const ALfloat release_rate = mReleaseRate;
- const ALfloat res_gain = mResonanceGain;
- const ALfloat peak_gain = mPeakGain;
- const ALfloat freq_min = mFreqMinNorm;
- const ALfloat bandwidth = mBandwidthNorm;
-
- ALfloat env_delay{mEnvDelay};
- for(ALsizei i{0};i < samplesToDo;i++)
- {
- ALfloat w0, sample, a;
-
- /* Envelope follower described on the book: Audio Effects, Theory,
- * Implementation and Application.
- */
- sample = peak_gain * std::fabs(samplesIn[0][i]);
- a = (sample > env_delay) ? attack_rate : release_rate;
- env_delay = lerp(sample, env_delay, a);
-
- /* Calculate the cos and alpha components for this sample's filter. */
- w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * al::MathDefs<float>::Tau();
- mEnv[i].cos_w0 = cosf(w0);
- mEnv[i].alpha = sinf(w0)/(2.0f * Q_FACTOR);
- }
- mEnvDelay = env_delay;
-
- ASSUME(numInput > 0);
- for(ALsizei c{0};c < numInput;++c)
- {
- /* This effectively inlines BiquadFilter_setParams for a peaking
- * filter and BiquadFilter_processC. The alpha and cosine components
- * for the filter coefficients were previously calculated with the
- * envelope. Because the filter changes for each sample, the
- * coefficients are transient and don't need to be held.
- */
- ALfloat z1{mChans[c].Filter.z1};
- ALfloat z2{mChans[c].Filter.z2};
-
- for(ALsizei i{0};i < samplesToDo;i++)
- {
- const ALfloat alpha = mEnv[i].alpha;
- const ALfloat cos_w0 = mEnv[i].cos_w0;
- ALfloat input, output;
- ALfloat a[3], b[3];
-
- b[0] = 1.0f + alpha*res_gain;
- b[1] = -2.0f * cos_w0;
- b[2] = 1.0f - alpha*res_gain;
- a[0] = 1.0f + alpha/res_gain;
- a[1] = -2.0f * cos_w0;
- a[2] = 1.0f - alpha/res_gain;
-
- input = samplesIn[c][i];
- output = input*(b[0]/a[0]) + z1;
- z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2;
- z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
- mBufferOut[i] = output;
- }
- mChans[c].Filter.z1 = z1;
- mChans[c].Filter.z2 = z2;
-
- /* Now, mix the processed sound data to the output. */
- MixSamples(mBufferOut, samplesOut, mChans[c].CurrentGains, mChans[c].TargetGains,
- samplesToDo, 0, samplesToDo);
- }
-}
-
-
-void ALautowah_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_AUTOWAH_ATTACK_TIME:
- if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah attack time out of range");
- props->Autowah.AttackTime = val;
- break;
-
- case AL_AUTOWAH_RELEASE_TIME:
- if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah release time out of range");
- props->Autowah.ReleaseTime = val;
- break;
-
- case AL_AUTOWAH_RESONANCE:
- if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah resonance out of range");
- props->Autowah.Resonance = val;
- break;
-
- case AL_AUTOWAH_PEAK_GAIN:
- if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah peak gain out of range");
- props->Autowah.PeakGain = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
- }
-}
-void ALautowah_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ ALautowah_setParamf(props, context, param, vals[0]); }
-
-void ALautowah_setParami(EffectProps*, ALCcontext *context, ALenum param, ALint)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param); }
-void ALautowah_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param); }
-
-void ALautowah_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_AUTOWAH_ATTACK_TIME:
- *val = props->Autowah.AttackTime;
- break;
-
- case AL_AUTOWAH_RELEASE_TIME:
- *val = props->Autowah.ReleaseTime;
- break;
-
- case AL_AUTOWAH_RESONANCE:
- *val = props->Autowah.Resonance;
- break;
-
- case AL_AUTOWAH_PEAK_GAIN:
- *val = props->Autowah.PeakGain;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
- }
-
-}
-void ALautowah_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ ALautowah_getParamf(props, context, param, vals); }
-
-void ALautowah_getParami(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param); }
-void ALautowah_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param); }
-
-DEFINE_ALEFFECT_VTABLE(ALautowah);
-
-
-struct AutowahStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new ALautowahState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &ALautowah_vtable; }
-};
-
-EffectProps AutowahStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
- props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
- props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
- props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *AutowahStateFactory_getFactory()
-{
- static AutowahStateFactory AutowahFactory{};
- return &AutowahFactory;
-}
diff --git a/Alc/effects/base.h b/Alc/effects/base.h
deleted file mode 100644
index 4f48de22..00000000
--- a/Alc/effects/base.h
+++ /dev/null
@@ -1,196 +0,0 @@
-#ifndef EFFECTS_BASE_H
-#define EFFECTS_BASE_H
-
-#include "alcmain.h"
-#include "almalloc.h"
-#include "alspan.h"
-#include "atomic.h"
-
-
-struct ALeffectslot;
-
-
-union EffectProps {
- struct {
- // Shared Reverb Properties
- ALfloat Density;
- ALfloat Diffusion;
- ALfloat Gain;
- ALfloat GainHF;
- ALfloat DecayTime;
- ALfloat DecayHFRatio;
- ALfloat ReflectionsGain;
- ALfloat ReflectionsDelay;
- ALfloat LateReverbGain;
- ALfloat LateReverbDelay;
- ALfloat AirAbsorptionGainHF;
- ALfloat RoomRolloffFactor;
- ALboolean DecayHFLimit;
-
- // Additional EAX Reverb Properties
- ALfloat GainLF;
- ALfloat DecayLFRatio;
- ALfloat ReflectionsPan[3];
- ALfloat LateReverbPan[3];
- ALfloat EchoTime;
- ALfloat EchoDepth;
- ALfloat ModulationTime;
- ALfloat ModulationDepth;
- ALfloat HFReference;
- ALfloat LFReference;
- } Reverb;
-
- struct {
- ALfloat AttackTime;
- ALfloat ReleaseTime;
- ALfloat Resonance;
- ALfloat PeakGain;
- } Autowah;
-
- struct {
- ALint Waveform;
- ALint Phase;
- ALfloat Rate;
- ALfloat Depth;
- ALfloat Feedback;
- ALfloat Delay;
- } Chorus; /* Also Flanger */
-
- struct {
- ALboolean OnOff;
- } Compressor;
-
- struct {
- ALfloat Edge;
- ALfloat Gain;
- ALfloat LowpassCutoff;
- ALfloat EQCenter;
- ALfloat EQBandwidth;
- } Distortion;
-
- struct {
- ALfloat Delay;
- ALfloat LRDelay;
-
- ALfloat Damping;
- ALfloat Feedback;
-
- ALfloat Spread;
- } Echo;
-
- struct {
- ALfloat LowCutoff;
- ALfloat LowGain;
- ALfloat Mid1Center;
- ALfloat Mid1Gain;
- ALfloat Mid1Width;
- ALfloat Mid2Center;
- ALfloat Mid2Gain;
- ALfloat Mid2Width;
- ALfloat HighCutoff;
- ALfloat HighGain;
- } Equalizer;
-
- struct {
- ALfloat Frequency;
- ALint LeftDirection;
- ALint RightDirection;
- } Fshifter;
-
- struct {
- ALfloat Frequency;
- ALfloat HighPassCutoff;
- ALint Waveform;
- } Modulator;
-
- struct {
- ALint CoarseTune;
- ALint FineTune;
- } Pshifter;
-
- struct {
- ALfloat Rate;
- ALint PhonemeA;
- ALint PhonemeB;
- ALint PhonemeACoarseTuning;
- ALint PhonemeBCoarseTuning;
- ALint Waveform;
- } Vmorpher;
-
- struct {
- ALfloat Gain;
- } Dedicated;
-};
-
-
-struct EffectVtable {
- void (*const setParami)(EffectProps *props, ALCcontext *context, ALenum param, ALint val);
- void (*const setParamiv)(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals);
- void (*const setParamf)(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val);
- void (*const setParamfv)(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals);
-
- void (*const getParami)(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val);
- void (*const getParamiv)(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals);
- void (*const getParamf)(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val);
- void (*const getParamfv)(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals);
-};
-
-#define DEFINE_ALEFFECT_VTABLE(T) \
-const EffectVtable T##_vtable = { \
- T##_setParami, T##_setParamiv, \
- T##_setParamf, T##_setParamfv, \
- T##_getParami, T##_getParamiv, \
- T##_getParamf, T##_getParamfv, \
-}
-
-
-struct EffectTarget {
- MixParams *Main;
- RealMixParams *RealOut;
-};
-
-struct EffectState {
- RefCount mRef{1u};
-
- al::span<FloatBufferLine> mOutTarget;
-
-
- virtual ~EffectState() = default;
-
- virtual ALboolean deviceUpdate(const ALCdevice *device) = 0;
- virtual void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) = 0;
- virtual void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) = 0;
-
- void IncRef() noexcept;
- void DecRef() noexcept;
-};
-
-
-struct EffectStateFactory {
- virtual ~EffectStateFactory() { }
-
- virtual EffectState *create() = 0;
- virtual EffectProps getDefaultProps() const noexcept = 0;
- virtual const EffectVtable *getEffectVtable() const noexcept = 0;
-};
-
-
-EffectStateFactory *NullStateFactory_getFactory(void);
-EffectStateFactory *ReverbStateFactory_getFactory(void);
-EffectStateFactory *StdReverbStateFactory_getFactory(void);
-EffectStateFactory *AutowahStateFactory_getFactory(void);
-EffectStateFactory *ChorusStateFactory_getFactory(void);
-EffectStateFactory *CompressorStateFactory_getFactory(void);
-EffectStateFactory *DistortionStateFactory_getFactory(void);
-EffectStateFactory *EchoStateFactory_getFactory(void);
-EffectStateFactory *EqualizerStateFactory_getFactory(void);
-EffectStateFactory *FlangerStateFactory_getFactory(void);
-EffectStateFactory *FshifterStateFactory_getFactory(void);
-EffectStateFactory *ModulatorStateFactory_getFactory(void);
-EffectStateFactory *PshifterStateFactory_getFactory(void);
-EffectStateFactory* VmorpherStateFactory_getFactory(void);
-
-EffectStateFactory *DedicatedStateFactory_getFactory(void);
-
-
-#endif /* EFFECTS_BASE_H */
diff --git a/Alc/effects/chorus.cpp b/Alc/effects/chorus.cpp
deleted file mode 100644
index d475b57a..00000000
--- a/Alc/effects/chorus.cpp
+++ /dev/null
@@ -1,538 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Mike Gorchak
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <algorithm>
-#include <climits>
-#include <cmath>
-#include <cstdlib>
-#include <iterator>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "AL/efx.h"
-
-#include "alAuxEffectSlot.h"
-#include "alcmain.h"
-#include "alError.h"
-#include "alcontext.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-#include "alspan.h"
-#include "alu.h"
-#include "ambidefs.h"
-#include "effects/base.h"
-#include "math_defs.h"
-#include "opthelpers.h"
-#include "vector.h"
-
-
-namespace {
-
-static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
-static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
-
-enum class WaveForm {
- Sinusoid,
- Triangle
-};
-
-void GetTriangleDelays(ALint *delays, const ALsizei start_offset, const ALsizei lfo_range,
- const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay, const ALsizei todo)
-{
- ASSUME(start_offset >= 0);
- ASSUME(lfo_range > 0);
- ASSUME(todo > 0);
-
- ALsizei offset{start_offset};
- auto gen_lfo = [&offset,lfo_range,lfo_scale,depth,delay]() -> ALint
- {
- offset = (offset+1)%lfo_range;
- return fastf2i((1.0f - std::abs(2.0f - lfo_scale*offset)) * depth) + delay;
- };
- std::generate_n(delays, todo, gen_lfo);
-}
-
-void GetSinusoidDelays(ALint *delays, const ALsizei start_offset, const ALsizei lfo_range,
- const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay, const ALsizei todo)
-{
- ASSUME(start_offset >= 0);
- ASSUME(lfo_range > 0);
- ASSUME(todo > 0);
-
- ALsizei offset{start_offset};
- auto gen_lfo = [&offset,lfo_range,lfo_scale,depth,delay]() -> ALint
- {
- ASSUME(delay >= 0);
- offset = (offset+1)%lfo_range;
- return fastf2i(std::sin(lfo_scale*offset) * depth) + delay;
- };
- std::generate_n(delays, todo, gen_lfo);
-}
-
-struct ChorusState final : public EffectState {
- al::vector<ALfloat,16> mSampleBuffer;
- ALsizei mOffset{0};
-
- ALsizei mLfoOffset{0};
- ALsizei mLfoRange{1};
- ALfloat mLfoScale{0.0f};
- ALint mLfoDisp{0};
-
- /* Gains for left and right sides */
- struct {
- ALfloat Current[MAX_OUTPUT_CHANNELS]{};
- ALfloat Target[MAX_OUTPUT_CHANNELS]{};
- } mGains[2];
-
- /* effect parameters */
- WaveForm mWaveform{};
- ALint mDelay{0};
- ALfloat mDepth{0.0f};
- ALfloat mFeedback{0.0f};
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(ChorusState)
-};
-
-ALboolean ChorusState::deviceUpdate(const ALCdevice *Device)
-{
- const ALfloat max_delay = maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY);
- size_t maxlen;
-
- maxlen = NextPowerOf2(float2int(max_delay*2.0f*Device->Frequency) + 1u);
- if(maxlen <= 0) return AL_FALSE;
-
- if(maxlen != mSampleBuffer.size())
- {
- mSampleBuffer.resize(maxlen);
- mSampleBuffer.shrink_to_fit();
- }
-
- std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f);
- for(auto &e : mGains)
- {
- std::fill(std::begin(e.Current), std::end(e.Current), 0.0f);
- std::fill(std::begin(e.Target), std::end(e.Target), 0.0f);
- }
-
- return AL_TRUE;
-}
-
-void ChorusState::update(const ALCcontext *Context, const ALeffectslot *Slot, const EffectProps *props, const EffectTarget target)
-{
- static constexpr ALsizei mindelay = MAX_RESAMPLE_PADDING << FRACTIONBITS;
-
- switch(props->Chorus.Waveform)
- {
- case AL_CHORUS_WAVEFORM_TRIANGLE:
- mWaveform = WaveForm::Triangle;
- break;
- case AL_CHORUS_WAVEFORM_SINUSOID:
- mWaveform = WaveForm::Sinusoid;
- break;
- }
-
- /* The LFO depth is scaled to be relative to the sample delay. Clamp the
- * delay and depth to allow enough padding for resampling.
- */
- const ALCdevice *device{Context->Device};
- const auto frequency = static_cast<ALfloat>(device->Frequency);
- mDelay = maxi(float2int(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f), mindelay);
- mDepth = minf(props->Chorus.Depth * mDelay, static_cast<ALfloat>(mDelay - mindelay));
-
- mFeedback = props->Chorus.Feedback;
-
- /* Gains for left and right sides */
- ALfloat coeffs[2][MAX_AMBI_CHANNELS];
- CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f}, 0.0f, coeffs[0]);
- CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f}, 0.0f, coeffs[1]);
-
- mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs[0], Slot->Params.Gain, mGains[0].Target);
- ComputePanGains(target.Main, coeffs[1], Slot->Params.Gain, mGains[1].Target);
-
- ALfloat rate{props->Chorus.Rate};
- if(!(rate > 0.0f))
- {
- mLfoOffset = 0;
- mLfoRange = 1;
- mLfoScale = 0.0f;
- mLfoDisp = 0;
- }
- else
- {
- /* Calculate LFO coefficient (number of samples per cycle). Limit the
- * max range to avoid overflow when calculating the displacement.
- */
- ALsizei lfo_range = float2int(minf(frequency/rate + 0.5f, static_cast<ALfloat>(INT_MAX/360 - 180)));
-
- mLfoOffset = float2int(static_cast<ALfloat>(mLfoOffset)/mLfoRange*lfo_range + 0.5f) % lfo_range;
- mLfoRange = lfo_range;
- switch(mWaveform)
- {
- case WaveForm::Triangle:
- mLfoScale = 4.0f / mLfoRange;
- break;
- case WaveForm::Sinusoid:
- mLfoScale = al::MathDefs<float>::Tau() / mLfoRange;
- break;
- }
-
- /* Calculate lfo phase displacement */
- ALint phase{props->Chorus.Phase};
- if(phase < 0) phase = 360 + phase;
- mLfoDisp = (mLfoRange*phase + 180) / 360;
- }
-}
-
-void ChorusState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei /*numInput*/, const al::span<FloatBufferLine> samplesOut)
-{
- const auto bufmask = static_cast<ALsizei>(mSampleBuffer.size()-1);
- const ALfloat feedback{mFeedback};
- const ALsizei avgdelay{(mDelay + (FRACTIONONE>>1)) >> FRACTIONBITS};
- ALfloat *RESTRICT delaybuf{mSampleBuffer.data()};
- ALsizei offset{mOffset};
-
- for(ALsizei base{0};base < samplesToDo;)
- {
- const ALsizei todo = mini(256, samplesToDo-base);
- ALint moddelays[2][256];
- alignas(16) ALfloat temps[2][256];
-
- if(mWaveform == WaveForm::Sinusoid)
- {
- GetSinusoidDelays(moddelays[0], mLfoOffset, mLfoRange, mLfoScale, mDepth, mDelay,
- todo);
- GetSinusoidDelays(moddelays[1], (mLfoOffset+mLfoDisp)%mLfoRange, mLfoRange, mLfoScale,
- mDepth, mDelay, todo);
- }
- else /*if(mWaveform == WaveForm::Triangle)*/
- {
- GetTriangleDelays(moddelays[0], mLfoOffset, mLfoRange, mLfoScale, mDepth, mDelay,
- todo);
- GetTriangleDelays(moddelays[1], (mLfoOffset+mLfoDisp)%mLfoRange, mLfoRange, mLfoScale,
- mDepth, mDelay, todo);
- }
- mLfoOffset = (mLfoOffset+todo) % mLfoRange;
-
- for(ALsizei i{0};i < todo;i++)
- {
- // Feed the buffer's input first (necessary for delays < 1).
- delaybuf[offset&bufmask] = samplesIn[0][base+i];
-
- // Tap for the left output.
- ALint delay{offset - (moddelays[0][i]>>FRACTIONBITS)};
- ALfloat mu{(moddelays[0][i]&FRACTIONMASK) * (1.0f/FRACTIONONE)};
- temps[0][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
- delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
- mu);
-
- // Tap for the right output.
- delay = offset - (moddelays[1][i]>>FRACTIONBITS);
- mu = (moddelays[1][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
- temps[1][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
- delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
- mu);
-
- // Accumulate feedback from the average delay of the taps.
- delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
- offset++;
- }
-
- for(ALsizei c{0};c < 2;c++)
- MixSamples(temps[c], samplesOut, mGains[c].Current, mGains[c].Target, samplesToDo-base,
- base, todo);
-
- base += todo;
- }
-
- mOffset = offset;
-}
-
-
-void Chorus_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_CHORUS_WAVEFORM:
- if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid chorus waveform");
- props->Chorus.Waveform = val;
- break;
-
- case AL_CHORUS_PHASE:
- if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus phase out of range");
- props->Chorus.Phase = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
- }
-}
-void Chorus_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ Chorus_setParami(props, context, param, vals[0]); }
-void Chorus_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_CHORUS_RATE:
- if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus rate out of range");
- props->Chorus.Rate = val;
- break;
-
- case AL_CHORUS_DEPTH:
- if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus depth out of range");
- props->Chorus.Depth = val;
- break;
-
- case AL_CHORUS_FEEDBACK:
- if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus feedback out of range");
- props->Chorus.Feedback = val;
- break;
-
- case AL_CHORUS_DELAY:
- if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus delay out of range");
- props->Chorus.Delay = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
- }
-}
-void Chorus_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Chorus_setParamf(props, context, param, vals[0]); }
-
-void Chorus_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_CHORUS_WAVEFORM:
- *val = props->Chorus.Waveform;
- break;
-
- case AL_CHORUS_PHASE:
- *val = props->Chorus.Phase;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
- }
-}
-void Chorus_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ Chorus_getParami(props, context, param, vals); }
-void Chorus_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_CHORUS_RATE:
- *val = props->Chorus.Rate;
- break;
-
- case AL_CHORUS_DEPTH:
- *val = props->Chorus.Depth;
- break;
-
- case AL_CHORUS_FEEDBACK:
- *val = props->Chorus.Feedback;
- break;
-
- case AL_CHORUS_DELAY:
- *val = props->Chorus.Delay;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
- }
-}
-void Chorus_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Chorus_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Chorus);
-
-
-struct ChorusStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new ChorusState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Chorus_vtable; }
-};
-
-EffectProps ChorusStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM;
- props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
- props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE;
- props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH;
- props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
- props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY;
- return props;
-}
-
-
-void Flanger_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_FLANGER_WAVEFORM:
- if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid flanger waveform");
- props->Chorus.Waveform = val;
- break;
-
- case AL_FLANGER_PHASE:
- if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger phase out of range");
- props->Chorus.Phase = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
- }
-}
-void Flanger_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ Flanger_setParami(props, context, param, vals[0]); }
-void Flanger_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_FLANGER_RATE:
- if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger rate out of range");
- props->Chorus.Rate = val;
- break;
-
- case AL_FLANGER_DEPTH:
- if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger depth out of range");
- props->Chorus.Depth = val;
- break;
-
- case AL_FLANGER_FEEDBACK:
- if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger feedback out of range");
- props->Chorus.Feedback = val;
- break;
-
- case AL_FLANGER_DELAY:
- if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger delay out of range");
- props->Chorus.Delay = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
- }
-}
-void Flanger_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Flanger_setParamf(props, context, param, vals[0]); }
-
-void Flanger_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_FLANGER_WAVEFORM:
- *val = props->Chorus.Waveform;
- break;
-
- case AL_FLANGER_PHASE:
- *val = props->Chorus.Phase;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
- }
-}
-void Flanger_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ Flanger_getParami(props, context, param, vals); }
-void Flanger_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_FLANGER_RATE:
- *val = props->Chorus.Rate;
- break;
-
- case AL_FLANGER_DEPTH:
- *val = props->Chorus.Depth;
- break;
-
- case AL_FLANGER_FEEDBACK:
- *val = props->Chorus.Feedback;
- break;
-
- case AL_FLANGER_DELAY:
- *val = props->Chorus.Delay;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
- }
-}
-void Flanger_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Flanger_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Flanger);
-
-
-/* Flanger is basically a chorus with a really short delay. They can both use
- * the same processing functions, so piggyback flanger on the chorus functions.
- */
-struct FlangerStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new ChorusState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Flanger_vtable; }
-};
-
-EffectProps FlangerStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Chorus.Waveform = AL_FLANGER_DEFAULT_WAVEFORM;
- props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE;
- props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE;
- props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH;
- props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
- props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *ChorusStateFactory_getFactory()
-{
- static ChorusStateFactory ChorusFactory{};
- return &ChorusFactory;
-}
-
-EffectStateFactory *FlangerStateFactory_getFactory()
-{
- static FlangerStateFactory FlangerFactory{};
- return &FlangerFactory;
-}
diff --git a/Alc/effects/compressor.cpp b/Alc/effects/compressor.cpp
deleted file mode 100644
index 4a487097..00000000
--- a/Alc/effects/compressor.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Anis A. Hireche
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cstdlib>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alu.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "vecmat.h"
-
-
-namespace {
-
-#define AMP_ENVELOPE_MIN 0.5f
-#define AMP_ENVELOPE_MAX 2.0f
-
-#define ATTACK_TIME 0.1f /* 100ms to rise from min to max */
-#define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
-
-
-struct CompressorState final : public EffectState {
- /* Effect gains for each channel */
- ALfloat mGain[MAX_AMBI_CHANNELS][MAX_OUTPUT_CHANNELS]{};
-
- /* Effect parameters */
- ALboolean mEnabled{AL_TRUE};
- ALfloat mAttackMult{1.0f};
- ALfloat mReleaseMult{1.0f};
- ALfloat mEnvFollower{1.0f};
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(CompressorState)
-};
-
-ALboolean CompressorState::deviceUpdate(const ALCdevice *device)
-{
- /* Number of samples to do a full attack and release (non-integer sample
- * counts are okay).
- */
- const ALfloat attackCount = static_cast<ALfloat>(device->Frequency) * ATTACK_TIME;
- const ALfloat releaseCount = static_cast<ALfloat>(device->Frequency) * RELEASE_TIME;
-
- /* Calculate per-sample multipliers to attack and release at the desired
- * rates.
- */
- mAttackMult = std::pow(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount);
- mReleaseMult = std::pow(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
-
- return AL_TRUE;
-}
-
-void CompressorState::update(const ALCcontext*, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- mEnabled = props->Compressor.OnOff;
-
- mOutTarget = target.Main->Buffer;
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
- {
- auto coeffs = GetAmbiIdentityRow(i);
- ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mGain[i]);
- }
-}
-
-void CompressorState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
-{
- for(ALsizei base{0};base < samplesToDo;)
- {
- ALfloat gains[256];
- const ALsizei td{mini(256, samplesToDo-base)};
-
- /* Generate the per-sample gains from the signal envelope. */
- ALfloat env{mEnvFollower};
- if(mEnabled)
- {
- for(ALsizei i{0};i < td;++i)
- {
- /* Clamp the absolute amplitude to the defined envelope limits,
- * then attack or release the envelope to reach it.
- */
- const ALfloat amplitude{clampf(std::fabs(samplesIn[0][base+i]), AMP_ENVELOPE_MIN,
- AMP_ENVELOPE_MAX)};
- if(amplitude > env)
- env = minf(env*mAttackMult, amplitude);
- else if(amplitude < env)
- env = maxf(env*mReleaseMult, amplitude);
-
- /* Apply the reciprocal of the envelope to normalize the volume
- * (compress the dynamic range).
- */
- gains[i] = 1.0f / env;
- }
- }
- else
- {
- /* Same as above, except the amplitude is forced to 1. This helps
- * ensure smooth gain changes when the compressor is turned on and
- * off.
- */
- for(ALsizei i{0};i < td;++i)
- {
- const ALfloat amplitude{1.0f};
- if(amplitude > env)
- env = minf(env*mAttackMult, amplitude);
- else if(amplitude < env)
- env = maxf(env*mReleaseMult, amplitude);
-
- gains[i] = 1.0f / env;
- }
- }
- mEnvFollower = env;
-
- /* Now compress the signal amplitude to output. */
- ASSUME(numInput > 0);
- for(ALsizei j{0};j < numInput;j++)
- {
- const ALfloat *outgains{mGain[j]};
- for(FloatBufferLine &output : samplesOut)
- {
- const ALfloat gain{*(outgains++)};
- if(!(std::fabs(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
-
- for(ALsizei i{0};i < td;i++)
- output[base+i] += samplesIn[j][base+i] * gains[i] * gain;
- }
- }
-
- base += td;
- }
-}
-
-
-void Compressor_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_COMPRESSOR_ONOFF:
- if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Compressor state out of range");
- props->Compressor.OnOff = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
- param);
- }
-}
-void Compressor_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ Compressor_setParami(props, context, param, vals[0]); }
-void Compressor_setParamf(EffectProps*, ALCcontext *context, ALenum param, ALfloat)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
-void Compressor_setParamfv(EffectProps*, ALCcontext *context, ALenum param, const ALfloat*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
-
-void Compressor_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_COMPRESSOR_ONOFF:
- *val = props->Compressor.OnOff;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
- param);
- }
-}
-void Compressor_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ Compressor_getParami(props, context, param, vals); }
-void Compressor_getParamf(const EffectProps*, ALCcontext *context, ALenum param, ALfloat*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
-void Compressor_getParamfv(const EffectProps*, ALCcontext *context, ALenum param, ALfloat*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
-
-DEFINE_ALEFFECT_VTABLE(Compressor);
-
-
-struct CompressorStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new CompressorState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Compressor_vtable; }
-};
-
-EffectProps CompressorStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *CompressorStateFactory_getFactory()
-{
- static CompressorStateFactory CompressorFactory{};
- return &CompressorFactory;
-}
diff --git a/Alc/effects/dedicated.cpp b/Alc/effects/dedicated.cpp
deleted file mode 100644
index b31b3750..00000000
--- a/Alc/effects/dedicated.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2011 by Chris Robinson.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cstdlib>
-#include <cmath>
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-
-
-namespace {
-
-struct DedicatedState final : public EffectState {
- ALfloat mCurrentGains[MAX_OUTPUT_CHANNELS];
- ALfloat mTargetGains[MAX_OUTPUT_CHANNELS];
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(DedicatedState)
-};
-
-ALboolean DedicatedState::deviceUpdate(const ALCdevice*)
-{
- std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f);
- return AL_TRUE;
-}
-
-void DedicatedState::update(const ALCcontext*, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
-
- const ALfloat Gain{slot->Params.Gain * props->Dedicated.Gain};
-
- if(slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
- {
- const int idx{!target.RealOut ? -1 : GetChannelIdxByName(*target.RealOut, LFE)};
- if(idx != -1)
- {
- mOutTarget = target.RealOut->Buffer;
- mTargetGains[idx] = Gain;
- }
- }
- else if(slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
- {
- /* Dialog goes to the front-center speaker if it exists, otherwise it
- * plays from the front-center location. */
- const int idx{!target.RealOut ? -1 : GetChannelIdxByName(*target.RealOut, FrontCenter)};
- if(idx != -1)
- {
- mOutTarget = target.RealOut->Buffer;
- mTargetGains[idx] = Gain;
- }
- else
- {
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f, coeffs);
-
- mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs, Gain, mTargetGains);
- }
- }
-}
-
-void DedicatedState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei /*numInput*/, const al::span<FloatBufferLine> samplesOut)
-{
- MixSamples(samplesIn[0].data(), samplesOut, mCurrentGains, mTargetGains, samplesToDo, 0,
- samplesToDo);
-}
-
-
-void Dedicated_setParami(EffectProps*, ALCcontext *context, ALenum param, ALint)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
-void Dedicated_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
-void Dedicated_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_DEDICATED_GAIN:
- if(!(val >= 0.0f && std::isfinite(val)))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Dedicated gain out of range");
- props->Dedicated.Gain = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
- }
-}
-void Dedicated_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Dedicated_setParamf(props, context, param, vals[0]); }
-
-void Dedicated_getParami(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
-void Dedicated_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
-void Dedicated_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_DEDICATED_GAIN:
- *val = props->Dedicated.Gain;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
- }
-}
-void Dedicated_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Dedicated_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Dedicated);
-
-
-struct DedicatedStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new DedicatedState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Dedicated_vtable; }
-};
-
-EffectProps DedicatedStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Dedicated.Gain = 1.0f;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *DedicatedStateFactory_getFactory()
-{
- static DedicatedStateFactory DedicatedFactory{};
- return &DedicatedFactory;
-}
diff --git a/Alc/effects/distortion.cpp b/Alc/effects/distortion.cpp
deleted file mode 100644
index 59557395..00000000
--- a/Alc/effects/distortion.cpp
+++ /dev/null
@@ -1,269 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Mike Gorchak
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-
-#include <cmath>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/biquad.h"
-
-
-namespace {
-
-struct DistortionState final : public EffectState {
- /* Effect gains for each channel */
- ALfloat mGain[MAX_OUTPUT_CHANNELS]{};
-
- /* Effect parameters */
- BiquadFilter mLowpass;
- BiquadFilter mBandpass;
- ALfloat mAttenuation{};
- ALfloat mEdgeCoeff{};
-
- ALfloat mBuffer[2][BUFFERSIZE]{};
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(DistortionState)
-};
-
-ALboolean DistortionState::deviceUpdate(const ALCdevice*)
-{
- mLowpass.clear();
- mBandpass.clear();
- return AL_TRUE;
-}
-
-void DistortionState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device{context->Device};
-
- /* Store waveshaper edge settings. */
- const ALfloat edge{
- minf(std::sin(al::MathDefs<float>::Pi()*0.5f * props->Distortion.Edge), 0.99f)};
- mEdgeCoeff = 2.0f * edge / (1.0f-edge);
-
- ALfloat cutoff{props->Distortion.LowpassCutoff};
- /* Bandwidth value is constant in octaves. */
- ALfloat bandwidth{(cutoff / 2.0f) / (cutoff * 0.67f)};
- /* Multiply sampling frequency by the amount of oversampling done during
- * processing.
- */
- auto frequency = static_cast<ALfloat>(device->Frequency);
- mLowpass.setParams(BiquadType::LowPass, 1.0f, cutoff / (frequency*4.0f),
- mLowpass.rcpQFromBandwidth(cutoff / (frequency*4.0f), bandwidth));
-
- cutoff = props->Distortion.EQCenter;
- /* Convert bandwidth in Hz to octaves. */
- bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
- mBandpass.setParams(BiquadType::BandPass, 1.0f, cutoff / (frequency*4.0f),
- mBandpass.rcpQFromBandwidth(cutoff / (frequency*4.0f), bandwidth));
-
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f, coeffs);
-
- mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs, slot->Params.Gain*props->Distortion.Gain, mGain);
-}
-
-void DistortionState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei /*numInput*/, const al::span<FloatBufferLine> samplesOut)
-{
- const ALfloat fc{mEdgeCoeff};
- for(ALsizei base{0};base < samplesToDo;)
- {
- /* Perform 4x oversampling to avoid aliasing. Oversampling greatly
- * improves distortion quality and allows to implement lowpass and
- * bandpass filters using high frequencies, at which classic IIR
- * filters became unstable.
- */
- ALsizei todo{mini(BUFFERSIZE, (samplesToDo-base) * 4)};
-
- /* Fill oversample buffer using zero stuffing. Multiply the sample by
- * the amount of oversampling to maintain the signal's power.
- */
- for(ALsizei i{0};i < todo;i++)
- mBuffer[0][i] = !(i&3) ? samplesIn[0][(i>>2)+base] * 4.0f : 0.0f;
-
- /* First step, do lowpass filtering of original signal. Additionally
- * perform buffer interpolation and lowpass cutoff for oversampling
- * (which is fortunately first step of distortion). So combine three
- * operations into the one.
- */
- mLowpass.process(mBuffer[1], mBuffer[0], todo);
-
- /* Second step, do distortion using waveshaper function to emulate
- * signal processing during tube overdriving. Three steps of
- * waveshaping are intended to modify waveform without boost/clipping/
- * attenuation process.
- */
- for(ALsizei i{0};i < todo;i++)
- {
- ALfloat smp{mBuffer[1][i]};
-
- smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
- smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
- smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
-
- mBuffer[0][i] = smp;
- }
-
- /* Third step, do bandpass filtering of distorted signal. */
- mBandpass.process(mBuffer[1], mBuffer[0], todo);
-
- todo >>= 2;
- const ALfloat *outgains{mGain};
- for(FloatBufferLine &output : samplesOut)
- {
- /* Fourth step, final, do attenuation and perform decimation,
- * storing only one sample out of four.
- */
- const ALfloat gain{*(outgains++)};
- if(!(std::fabs(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
-
- for(ALsizei i{0};i < todo;i++)
- output[base+i] += gain * mBuffer[1][i*4];
- }
-
- base += todo;
- }
-}
-
-
-void Distortion_setParami(EffectProps*, ALCcontext *context, ALenum param, ALint)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
-void Distortion_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
-void Distortion_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_DISTORTION_EDGE:
- if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion edge out of range");
- props->Distortion.Edge = val;
- break;
-
- case AL_DISTORTION_GAIN:
- if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion gain out of range");
- props->Distortion.Gain = val;
- break;
-
- case AL_DISTORTION_LOWPASS_CUTOFF:
- if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion low-pass cutoff out of range");
- props->Distortion.LowpassCutoff = val;
- break;
-
- case AL_DISTORTION_EQCENTER:
- if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ center out of range");
- props->Distortion.EQCenter = val;
- break;
-
- case AL_DISTORTION_EQBANDWIDTH:
- if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ bandwidth out of range");
- props->Distortion.EQBandwidth = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
- param);
- }
-}
-void Distortion_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Distortion_setParamf(props, context, param, vals[0]); }
-
-void Distortion_getParami(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
-void Distortion_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
-void Distortion_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_DISTORTION_EDGE:
- *val = props->Distortion.Edge;
- break;
-
- case AL_DISTORTION_GAIN:
- *val = props->Distortion.Gain;
- break;
-
- case AL_DISTORTION_LOWPASS_CUTOFF:
- *val = props->Distortion.LowpassCutoff;
- break;
-
- case AL_DISTORTION_EQCENTER:
- *val = props->Distortion.EQCenter;
- break;
-
- case AL_DISTORTION_EQBANDWIDTH:
- *val = props->Distortion.EQBandwidth;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
- param);
- }
-}
-void Distortion_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Distortion_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Distortion);
-
-
-struct DistortionStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new DistortionState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Distortion_vtable; }
-};
-
-EffectProps DistortionStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE;
- props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN;
- props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
- props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
- props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *DistortionStateFactory_getFactory()
-{
- static DistortionStateFactory DistortionFactory{};
- return &DistortionFactory;
-}
diff --git a/Alc/effects/echo.cpp b/Alc/effects/echo.cpp
deleted file mode 100644
index c10f2eb2..00000000
--- a/Alc/effects/echo.cpp
+++ /dev/null
@@ -1,271 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2009 by Chris Robinson.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alFilter.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/biquad.h"
-#include "vector.h"
-
-
-namespace {
-
-struct EchoState final : public EffectState {
- al::vector<ALfloat,16> mSampleBuffer;
-
- // The echo is two tap. The delay is the number of samples from before the
- // current offset
- struct {
- ALsizei delay{0};
- } mTap[2];
- ALsizei mOffset{0};
-
- /* The panning gains for the two taps */
- struct {
- ALfloat Current[MAX_OUTPUT_CHANNELS]{};
- ALfloat Target[MAX_OUTPUT_CHANNELS]{};
- } mGains[2];
-
- BiquadFilter mFilter;
- ALfloat mFeedGain{0.0f};
-
- alignas(16) ALfloat mTempBuffer[2][BUFFERSIZE];
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(EchoState)
-};
-
-ALboolean EchoState::deviceUpdate(const ALCdevice *Device)
-{
- ALuint maxlen;
-
- // Use the next power of 2 for the buffer length, so the tap offsets can be
- // wrapped using a mask instead of a modulo
- maxlen = float2int(AL_ECHO_MAX_DELAY*Device->Frequency + 0.5f) +
- float2int(AL_ECHO_MAX_LRDELAY*Device->Frequency + 0.5f);
- maxlen = NextPowerOf2(maxlen);
- if(maxlen <= 0) return AL_FALSE;
-
- if(maxlen != mSampleBuffer.size())
- {
- mSampleBuffer.resize(maxlen);
- mSampleBuffer.shrink_to_fit();
- }
-
- std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f);
- for(auto &e : mGains)
- {
- std::fill(std::begin(e.Current), std::end(e.Current), 0.0f);
- std::fill(std::begin(e.Target), std::end(e.Target), 0.0f);
- }
-
- return AL_TRUE;
-}
-
-void EchoState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device = context->Device;
- const auto frequency = static_cast<ALfloat>(device->Frequency);
-
- mTap[0].delay = maxi(float2int(props->Echo.Delay*frequency + 0.5f), 1);
- mTap[1].delay = float2int(props->Echo.LRDelay*frequency + 0.5f) + mTap[0].delay;
-
- const ALfloat gainhf{maxf(1.0f - props->Echo.Damping, 0.0625f)}; /* Limit -24dB */
- mFilter.setParams(BiquadType::HighShelf, gainhf, LOWPASSFREQREF/frequency,
- mFilter.rcpQFromSlope(gainhf, 1.0f));
-
- mFeedGain = props->Echo.Feedback;
-
- /* Convert echo spread (where 0 = center, +/-1 = sides) to angle. */
- const ALfloat angle{std::asin(props->Echo.Spread)};
-
- ALfloat coeffs[2][MAX_AMBI_CHANNELS];
- CalcAngleCoeffs(-angle, 0.0f, 0.0f, coeffs[0]);
- CalcAngleCoeffs( angle, 0.0f, 0.0f, coeffs[1]);
-
- mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs[0], slot->Params.Gain, mGains[0].Target);
- ComputePanGains(target.Main, coeffs[1], slot->Params.Gain, mGains[1].Target);
-}
-
-void EchoState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei /*numInput*/, const al::span<FloatBufferLine> samplesOut)
-{
- const auto mask = static_cast<ALsizei>(mSampleBuffer.size()-1);
- ALfloat *RESTRICT delaybuf{mSampleBuffer.data()};
- ALsizei offset{mOffset};
- ALsizei tap1{offset - mTap[0].delay};
- ALsizei tap2{offset - mTap[1].delay};
- ALfloat z1, z2;
-
- ASSUME(samplesToDo > 0);
- ASSUME(mask > 0);
-
- std::tie(z1, z2) = mFilter.getComponents();
- for(ALsizei i{0};i < samplesToDo;)
- {
- offset &= mask;
- tap1 &= mask;
- tap2 &= mask;
-
- ALsizei td{mini(mask+1 - maxi(offset, maxi(tap1, tap2)), samplesToDo-i)};
- do {
- /* Feed the delay buffer's input first. */
- delaybuf[offset] = samplesIn[0][i];
-
- /* Get delayed output from the first and second taps. Use the
- * second tap for feedback.
- */
- mTempBuffer[0][i] = delaybuf[tap1++];
- mTempBuffer[1][i] = delaybuf[tap2++];
- const float feedb{mTempBuffer[1][i++]};
-
- /* Add feedback to the delay buffer with damping and attenuation. */
- delaybuf[offset++] += mFilter.processOne(feedb, z1, z2) * mFeedGain;
- } while(--td);
- }
- mFilter.setComponents(z1, z2);
- mOffset = offset;
-
- for(ALsizei c{0};c < 2;c++)
- MixSamples(mTempBuffer[c], samplesOut, mGains[c].Current, mGains[c].Target, samplesToDo, 0,
- samplesToDo);
-}
-
-
-void Echo_setParami(EffectProps*, ALCcontext *context, ALenum param, ALint)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
-void Echo_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
-void Echo_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_ECHO_DELAY:
- if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo delay out of range");
- props->Echo.Delay = val;
- break;
-
- case AL_ECHO_LRDELAY:
- if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo LR delay out of range");
- props->Echo.LRDelay = val;
- break;
-
- case AL_ECHO_DAMPING:
- if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo damping out of range");
- props->Echo.Damping = val;
- break;
-
- case AL_ECHO_FEEDBACK:
- if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo feedback out of range");
- props->Echo.Feedback = val;
- break;
-
- case AL_ECHO_SPREAD:
- if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo spread out of range");
- props->Echo.Spread = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
- }
-}
-void Echo_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Echo_setParamf(props, context, param, vals[0]); }
-
-void Echo_getParami(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
-void Echo_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
-void Echo_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_ECHO_DELAY:
- *val = props->Echo.Delay;
- break;
-
- case AL_ECHO_LRDELAY:
- *val = props->Echo.LRDelay;
- break;
-
- case AL_ECHO_DAMPING:
- *val = props->Echo.Damping;
- break;
-
- case AL_ECHO_FEEDBACK:
- *val = props->Echo.Feedback;
- break;
-
- case AL_ECHO_SPREAD:
- *val = props->Echo.Spread;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
- }
-}
-void Echo_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Echo_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Echo);
-
-
-struct EchoStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new EchoState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Echo_vtable; }
-};
-
-EffectProps EchoStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Echo.Delay = AL_ECHO_DEFAULT_DELAY;
- props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY;
- props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING;
- props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
- props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *EchoStateFactory_getFactory()
-{
- static EchoStateFactory EchoFactory{};
- return &EchoFactory;
-}
diff --git a/Alc/effects/equalizer.cpp b/Alc/effects/equalizer.cpp
deleted file mode 100644
index 69ab5021..00000000
--- a/Alc/effects/equalizer.cpp
+++ /dev/null
@@ -1,337 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Mike Gorchak
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-
-#include <algorithm>
-#include <functional>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/biquad.h"
-#include "vecmat.h"
-
-
-namespace {
-
-/* The document "Effects Extension Guide.pdf" says that low and high *
- * frequencies are cutoff frequencies. This is not fully correct, they *
- * are corner frequencies for low and high shelf filters. If they were *
- * just cutoff frequencies, there would be no need in cutoff frequency *
- * gains, which are present. Documentation for "Creative Proteus X2" *
- * software describes 4-band equalizer functionality in a much better *
- * way. This equalizer seems to be a predecessor of OpenAL 4-band *
- * equalizer. With low and high shelf filters we are able to cutoff *
- * frequencies below and/or above corner frequencies using attenuation *
- * gains (below 1.0) and amplify all low and/or high frequencies using *
- * gains above 1.0. *
- * *
- * Low-shelf Low Mid Band High Mid Band High-shelf *
- * corner center center corner *
- * frequency frequency frequency frequency *
- * 50Hz..800Hz 200Hz..3000Hz 1000Hz..8000Hz 4000Hz..16000Hz *
- * *
- * | | | | *
- * | | | | *
- * B -----+ /--+--\ /--+--\ +----- *
- * O |\ | | | | | | /| *
- * O | \ - | - - | - / | *
- * S + | \ | | | | | | / | *
- * T | | | | | | | | | | *
- * ---------+---------------+------------------+---------------+-------- *
- * C | | | | | | | | | | *
- * U - | / | | | | | | \ | *
- * T | / - | - - | - \ | *
- * O |/ | | | | | | \| *
- * F -----+ \--+--/ \--+--/ +----- *
- * F | | | | *
- * | | | | *
- * *
- * Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
- * up to +18dB amplification. Band width varies from 0.01 up to 1.0 in *
- * octaves for two mid bands. *
- * *
- * Implementation is based on the "Cookbook formulae for audio EQ biquad *
- * filter coefficients" by Robert Bristow-Johnson *
- * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
-
-
-struct EqualizerState final : public EffectState {
- struct {
- /* Effect parameters */
- BiquadFilter filter[4];
-
- /* Effect gains for each channel */
- ALfloat CurrentGains[MAX_OUTPUT_CHANNELS]{};
- ALfloat TargetGains[MAX_OUTPUT_CHANNELS]{};
- } mChans[MAX_AMBI_CHANNELS];
-
- ALfloat mSampleBuffer[BUFFERSIZE]{};
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(EqualizerState)
-};
-
-ALboolean EqualizerState::deviceUpdate(const ALCdevice*)
-{
- for(auto &e : mChans)
- {
- std::for_each(std::begin(e.filter), std::end(e.filter),
- std::mem_fn(&BiquadFilter::clear));
- std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
- }
- return AL_TRUE;
-}
-
-void EqualizerState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device = context->Device;
- auto frequency = static_cast<ALfloat>(device->Frequency);
- ALfloat gain, f0norm;
-
- /* Calculate coefficients for the each type of filter. Note that the shelf
- * filters' gain is for the reference frequency, which is the centerpoint
- * of the transition band.
- */
- gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
- f0norm = props->Equalizer.LowCutoff/frequency;
- mChans[0].filter[0].setParams(BiquadType::LowShelf, gain, f0norm,
- BiquadFilter::rcpQFromSlope(gain, 0.75f));
-
- gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
- f0norm = props->Equalizer.Mid1Center/frequency;
- mChans[0].filter[1].setParams(BiquadType::Peaking, gain, f0norm,
- BiquadFilter::rcpQFromBandwidth(f0norm, props->Equalizer.Mid1Width));
-
- gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
- f0norm = props->Equalizer.Mid2Center/frequency;
- mChans[0].filter[2].setParams(BiquadType::Peaking, gain, f0norm,
- BiquadFilter::rcpQFromBandwidth(f0norm, props->Equalizer.Mid2Width));
-
- gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
- f0norm = props->Equalizer.HighCutoff/frequency;
- mChans[0].filter[3].setParams(BiquadType::HighShelf, gain, f0norm,
- BiquadFilter::rcpQFromSlope(gain, 0.75f));
-
- /* Copy the filter coefficients for the other input channels. */
- for(size_t i{1u};i < slot->Wet.Buffer.size();++i)
- {
- mChans[i].filter[0].copyParamsFrom(mChans[0].filter[0]);
- mChans[i].filter[1].copyParamsFrom(mChans[0].filter[1]);
- mChans[i].filter[2].copyParamsFrom(mChans[0].filter[2]);
- mChans[i].filter[3].copyParamsFrom(mChans[0].filter[3]);
- }
-
- mOutTarget = target.Main->Buffer;
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
- {
- auto coeffs = GetAmbiIdentityRow(i);
- ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mChans[i].TargetGains);
- }
-}
-
-void EqualizerState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
-{
- ASSUME(numInput > 0);
- for(ALsizei c{0};c < numInput;c++)
- {
- mChans[c].filter[0].process(mSampleBuffer, samplesIn[c].data(), samplesToDo);
- mChans[c].filter[1].process(mSampleBuffer, mSampleBuffer, samplesToDo);
- mChans[c].filter[2].process(mSampleBuffer, mSampleBuffer, samplesToDo);
- mChans[c].filter[3].process(mSampleBuffer, mSampleBuffer, samplesToDo);
-
- MixSamples(mSampleBuffer, samplesOut, mChans[c].CurrentGains, mChans[c].TargetGains,
- samplesToDo, 0, samplesToDo);
- }
-}
-
-
-void Equalizer_setParami(EffectProps*, ALCcontext *context, ALenum param, ALint)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
-void Equalizer_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
-void Equalizer_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_EQUALIZER_LOW_GAIN:
- if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band gain out of range");
- props->Equalizer.LowGain = val;
- break;
-
- case AL_EQUALIZER_LOW_CUTOFF:
- if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band cutoff out of range");
- props->Equalizer.LowCutoff = val;
- break;
-
- case AL_EQUALIZER_MID1_GAIN:
- if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band gain out of range");
- props->Equalizer.Mid1Gain = val;
- break;
-
- case AL_EQUALIZER_MID1_CENTER:
- if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band center out of range");
- props->Equalizer.Mid1Center = val;
- break;
-
- case AL_EQUALIZER_MID1_WIDTH:
- if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band width out of range");
- props->Equalizer.Mid1Width = val;
- break;
-
- case AL_EQUALIZER_MID2_GAIN:
- if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band gain out of range");
- props->Equalizer.Mid2Gain = val;
- break;
-
- case AL_EQUALIZER_MID2_CENTER:
- if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band center out of range");
- props->Equalizer.Mid2Center = val;
- break;
-
- case AL_EQUALIZER_MID2_WIDTH:
- if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band width out of range");
- props->Equalizer.Mid2Width = val;
- break;
-
- case AL_EQUALIZER_HIGH_GAIN:
- if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band gain out of range");
- props->Equalizer.HighGain = val;
- break;
-
- case AL_EQUALIZER_HIGH_CUTOFF:
- if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band cutoff out of range");
- props->Equalizer.HighCutoff = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
- }
-}
-void Equalizer_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Equalizer_setParamf(props, context, param, vals[0]); }
-
-void Equalizer_getParami(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
-void Equalizer_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
-void Equalizer_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_EQUALIZER_LOW_GAIN:
- *val = props->Equalizer.LowGain;
- break;
-
- case AL_EQUALIZER_LOW_CUTOFF:
- *val = props->Equalizer.LowCutoff;
- break;
-
- case AL_EQUALIZER_MID1_GAIN:
- *val = props->Equalizer.Mid1Gain;
- break;
-
- case AL_EQUALIZER_MID1_CENTER:
- *val = props->Equalizer.Mid1Center;
- break;
-
- case AL_EQUALIZER_MID1_WIDTH:
- *val = props->Equalizer.Mid1Width;
- break;
-
- case AL_EQUALIZER_MID2_GAIN:
- *val = props->Equalizer.Mid2Gain;
- break;
-
- case AL_EQUALIZER_MID2_CENTER:
- *val = props->Equalizer.Mid2Center;
- break;
-
- case AL_EQUALIZER_MID2_WIDTH:
- *val = props->Equalizer.Mid2Width;
- break;
-
- case AL_EQUALIZER_HIGH_GAIN:
- *val = props->Equalizer.HighGain;
- break;
-
- case AL_EQUALIZER_HIGH_CUTOFF:
- *val = props->Equalizer.HighCutoff;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
- }
-}
-void Equalizer_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Equalizer_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Equalizer);
-
-
-struct EqualizerStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new EqualizerState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Equalizer_vtable; }
-};
-
-EffectProps EqualizerStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
- props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
- props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
- props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
- props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
- props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
- props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
- props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
- props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
- props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *EqualizerStateFactory_getFactory()
-{
- static EqualizerStateFactory EqualizerFactory{};
- return &EqualizerFactory;
-}
diff --git a/Alc/effects/fshifter.cpp b/Alc/effects/fshifter.cpp
deleted file mode 100644
index b47aa00e..00000000
--- a/Alc/effects/fshifter.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2018 by Raul Herraiz.
- * 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 <cmath>
-#include <cstdlib>
-#include <array>
-#include <complex>
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-
-#include "alcomplex.h"
-
-namespace {
-
-using complex_d = std::complex<double>;
-
-#define HIL_SIZE 1024
-#define OVERSAMP (1<<2)
-
-#define HIL_STEP (HIL_SIZE / OVERSAMP)
-#define FIFO_LATENCY (HIL_STEP * (OVERSAMP-1))
-
-/* Define a Hann window, used to filter the HIL input and output. */
-/* Making this constexpr seems to require C++14. */
-std::array<ALdouble,HIL_SIZE> InitHannWindow()
-{
- std::array<ALdouble,HIL_SIZE> ret;
- /* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
- for(ALsizei i{0};i < HIL_SIZE>>1;i++)
- {
- ALdouble val = std::sin(al::MathDefs<double>::Pi() * i / ALdouble{HIL_SIZE-1});
- ret[i] = ret[HIL_SIZE-1-i] = val * val;
- }
- return ret;
-}
-alignas(16) const std::array<ALdouble,HIL_SIZE> HannWindow = InitHannWindow();
-
-
-struct FshifterState final : public EffectState {
- /* Effect parameters */
- ALsizei mCount{};
- ALsizei mPhaseStep{};
- ALsizei mPhase{};
- ALdouble mLdSign{};
-
- /*Effects buffers*/
- ALfloat mInFIFO[HIL_SIZE]{};
- complex_d mOutFIFO[HIL_SIZE]{};
- complex_d mOutputAccum[HIL_SIZE]{};
- complex_d mAnalytic[HIL_SIZE]{};
- complex_d mOutdata[BUFFERSIZE]{};
-
- alignas(16) ALfloat mBufferOut[BUFFERSIZE]{};
-
- /* Effect gains for each output channel */
- ALfloat mCurrentGains[MAX_OUTPUT_CHANNELS]{};
- ALfloat mTargetGains[MAX_OUTPUT_CHANNELS]{};
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(FshifterState)
-};
-
-ALboolean FshifterState::deviceUpdate(const ALCdevice*)
-{
- /* (Re-)initializing parameters and clear the buffers. */
- mCount = FIFO_LATENCY;
- mPhaseStep = 0;
- mPhase = 0;
- mLdSign = 1.0;
-
- std::fill(std::begin(mInFIFO), std::end(mInFIFO), 0.0f);
- std::fill(std::begin(mOutFIFO), std::end(mOutFIFO), complex_d{});
- std::fill(std::begin(mOutputAccum), std::end(mOutputAccum), complex_d{});
- std::fill(std::begin(mAnalytic), std::end(mAnalytic), complex_d{});
-
- std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f);
- std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
-
- return AL_TRUE;
-}
-
-void FshifterState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device{context->Device};
-
- ALfloat step{props->Fshifter.Frequency / static_cast<ALfloat>(device->Frequency)};
- mPhaseStep = fastf2i(minf(step, 0.5f) * FRACTIONONE);
-
- switch(props->Fshifter.LeftDirection)
- {
- case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN:
- mLdSign = -1.0;
- break;
-
- case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
- mLdSign = 1.0;
- break;
-
- case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
- mPhase = 0;
- mPhaseStep = 0;
- break;
- }
-
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f, coeffs);
-
- mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs, slot->Params.Gain, mTargetGains);
-}
-
-void FshifterState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei /*numInput*/, const al::span<FloatBufferLine> samplesOut)
-{
- static constexpr complex_d complex_zero{0.0, 0.0};
- ALfloat *RESTRICT BufferOut = mBufferOut;
- ALsizei j, k, base;
-
- for(base = 0;base < samplesToDo;)
- {
- const ALsizei todo{mini(HIL_SIZE-mCount, samplesToDo-base)};
-
- ASSUME(todo > 0);
-
- /* Fill FIFO buffer with samples data */
- k = mCount;
- for(j = 0;j < todo;j++,k++)
- {
- mInFIFO[k] = samplesIn[0][base+j];
- mOutdata[base+j] = mOutFIFO[k-FIFO_LATENCY];
- }
- mCount += todo;
- base += todo;
-
- /* Check whether FIFO buffer is filled */
- if(mCount < HIL_SIZE) continue;
- mCount = FIFO_LATENCY;
-
- /* Real signal windowing and store in Analytic buffer */
- for(k = 0;k < HIL_SIZE;k++)
- {
- mAnalytic[k].real(mInFIFO[k] * HannWindow[k]);
- mAnalytic[k].imag(0.0);
- }
-
- /* Processing signal by Discrete Hilbert Transform (analytical signal). */
- complex_hilbert(mAnalytic);
-
- /* Windowing and add to output accumulator */
- for(k = 0;k < HIL_SIZE;k++)
- mOutputAccum[k] += 2.0/OVERSAMP*HannWindow[k]*mAnalytic[k];
-
- /* Shift accumulator, input & output FIFO */
- for(k = 0;k < HIL_STEP;k++) mOutFIFO[k] = mOutputAccum[k];
- for(j = 0;k < HIL_SIZE;k++,j++) mOutputAccum[j] = mOutputAccum[k];
- for(;j < HIL_SIZE;j++) mOutputAccum[j] = complex_zero;
- for(k = 0;k < FIFO_LATENCY;k++)
- mInFIFO[k] = mInFIFO[k+HIL_STEP];
- }
-
- /* Process frequency shifter using the analytic signal obtained. */
- for(k = 0;k < samplesToDo;k++)
- {
- double phase = mPhase * ((1.0/FRACTIONONE) * al::MathDefs<double>::Tau());
- BufferOut[k] = static_cast<float>(mOutdata[k].real()*std::cos(phase) +
- mOutdata[k].imag()*std::sin(phase)*mLdSign);
-
- mPhase += mPhaseStep;
- mPhase &= FRACTIONMASK;
- }
-
- /* Now, mix the processed sound data to the output. */
- MixSamples(BufferOut, samplesOut, mCurrentGains, mTargetGains, maxi(samplesToDo, 512), 0,
- samplesToDo);
-}
-
-
-void Fshifter_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_FREQUENCY_SHIFTER_FREQUENCY:
- if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter frequency out of range");
- props->Fshifter.Frequency = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", param);
- }
-}
-void Fshifter_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Fshifter_setParamf(props, context, param, vals[0]); }
-
-void Fshifter_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
- if(!(val >= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter left direction out of range");
- props->Fshifter.LeftDirection = val;
- break;
-
- case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
- if(!(val >= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter right direction out of range");
- props->Fshifter.RightDirection = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x", param);
- }
-}
-void Fshifter_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ Fshifter_setParami(props, context, param, vals[0]); }
-
-void Fshifter_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
- *val = props->Fshifter.LeftDirection;
- break;
- case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
- *val = props->Fshifter.RightDirection;
- break;
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x", param);
- }
-}
-void Fshifter_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ Fshifter_getParami(props, context, param, vals); }
-
-void Fshifter_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_FREQUENCY_SHIFTER_FREQUENCY:
- *val = props->Fshifter.Frequency;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", param);
- }
-}
-void Fshifter_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Fshifter_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Fshifter);
-
-
-struct FshifterStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new FshifterState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Fshifter_vtable; }
-};
-
-EffectProps FshifterStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
- props.Fshifter.LeftDirection = AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION;
- props.Fshifter.RightDirection = AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *FshifterStateFactory_getFactory()
-{
- static FshifterStateFactory FshifterFactory{};
- return &FshifterFactory;
-}
diff --git a/Alc/effects/modulator.cpp b/Alc/effects/modulator.cpp
deleted file mode 100644
index 086482d7..00000000
--- a/Alc/effects/modulator.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2009 by Chris Robinson.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-
-#include <cmath>
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/biquad.h"
-#include "vecmat.h"
-
-
-namespace {
-
-#define MAX_UPDATE_SAMPLES 128
-
-#define WAVEFORM_FRACBITS 24
-#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
-#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
-
-inline ALfloat Sin(ALsizei index)
-{
- return std::sin(static_cast<ALfloat>(index) *
- (al::MathDefs<float>::Tau() / ALfloat{WAVEFORM_FRACONE}));
-}
-
-inline ALfloat Saw(ALsizei index)
-{
- return static_cast<ALfloat>(index)*(2.0f/WAVEFORM_FRACONE) - 1.0f;
-}
-
-inline ALfloat Square(ALsizei index)
-{
- return static_cast<ALfloat>(((index>>(WAVEFORM_FRACBITS-2))&2) - 1);
-}
-
-inline ALfloat One(ALsizei)
-{
- return 1.0f;
-}
-
-template<ALfloat func(ALsizei)>
-void Modulate(ALfloat *RESTRICT dst, ALsizei index, const ALsizei step, ALsizei todo)
-{
- ALsizei i;
- for(i = 0;i < todo;i++)
- {
- index += step;
- index &= WAVEFORM_FRACMASK;
- dst[i] = func(index);
- }
-}
-
-
-struct ModulatorState final : public EffectState {
- void (*mGetSamples)(ALfloat*RESTRICT, ALsizei, const ALsizei, ALsizei){};
-
- ALsizei mIndex{0};
- ALsizei mStep{1};
-
- struct {
- BiquadFilter Filter;
-
- ALfloat CurrentGains[MAX_OUTPUT_CHANNELS]{};
- ALfloat TargetGains[MAX_OUTPUT_CHANNELS]{};
- } mChans[MAX_AMBI_CHANNELS];
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(ModulatorState)
-};
-
-ALboolean ModulatorState::deviceUpdate(const ALCdevice*)
-{
- for(auto &e : mChans)
- {
- e.Filter.clear();
- std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
- }
- return AL_TRUE;
-}
-
-void ModulatorState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device{context->Device};
-
- const float step{props->Modulator.Frequency / static_cast<ALfloat>(device->Frequency)};
- mStep = fastf2i(clampf(step*WAVEFORM_FRACONE, 0.0f, ALfloat{WAVEFORM_FRACONE-1}));
-
- if(mStep == 0)
- mGetSamples = Modulate<One>;
- else if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
- mGetSamples = Modulate<Sin>;
- else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
- mGetSamples = Modulate<Saw>;
- else /*if(props->Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
- mGetSamples = Modulate<Square>;
-
- ALfloat f0norm{props->Modulator.HighPassCutoff / static_cast<ALfloat>(device->Frequency)};
- f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f);
- /* Bandwidth value is constant in octaves. */
- mChans[0].Filter.setParams(BiquadType::HighPass, 1.0f, f0norm,
- BiquadFilter::rcpQFromBandwidth(f0norm, 0.75f));
- for(size_t i{1u};i < slot->Wet.Buffer.size();++i)
- mChans[i].Filter.copyParamsFrom(mChans[0].Filter);
-
- mOutTarget = target.Main->Buffer;
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
- {
- auto coeffs = GetAmbiIdentityRow(i);
- ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mChans[i].TargetGains);
- }
-}
-
-void ModulatorState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
-{
- for(ALsizei base{0};base < samplesToDo;)
- {
- alignas(16) ALfloat modsamples[MAX_UPDATE_SAMPLES];
- ALsizei td = mini(MAX_UPDATE_SAMPLES, samplesToDo-base);
- ALsizei c, i;
-
- mGetSamples(modsamples, mIndex, mStep, td);
- mIndex += (mStep*td) & WAVEFORM_FRACMASK;
- mIndex &= WAVEFORM_FRACMASK;
-
- ASSUME(numInput > 0);
- for(c = 0;c < numInput;c++)
- {
- alignas(16) ALfloat temps[MAX_UPDATE_SAMPLES];
-
- mChans[c].Filter.process(temps, &samplesIn[c][base], td);
- for(i = 0;i < td;i++)
- temps[i] *= modsamples[i];
-
- MixSamples(temps, samplesOut, mChans[c].CurrentGains, mChans[c].TargetGains,
- samplesToDo-base, base, td);
- }
-
- base += td;
- }
-}
-
-
-void Modulator_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_RING_MODULATOR_FREQUENCY:
- if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator frequency out of range");
- props->Modulator.Frequency = val;
- break;
-
- case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
- if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator high-pass cutoff out of range");
- props->Modulator.HighPassCutoff = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param);
- }
-}
-void Modulator_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Modulator_setParamf(props, context, param, vals[0]); }
-void Modulator_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_RING_MODULATOR_FREQUENCY:
- case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
- Modulator_setParamf(props, context, param, static_cast<ALfloat>(val));
- break;
-
- case AL_RING_MODULATOR_WAVEFORM:
- if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid modulator waveform");
- props->Modulator.Waveform = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param);
- }
-}
-void Modulator_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ Modulator_setParami(props, context, param, vals[0]); }
-
-void Modulator_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_RING_MODULATOR_FREQUENCY:
- *val = static_cast<ALint>(props->Modulator.Frequency);
- break;
- case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
- *val = static_cast<ALint>(props->Modulator.HighPassCutoff);
- break;
- case AL_RING_MODULATOR_WAVEFORM:
- *val = props->Modulator.Waveform;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param);
- }
-}
-void Modulator_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ Modulator_getParami(props, context, param, vals); }
-void Modulator_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_RING_MODULATOR_FREQUENCY:
- *val = props->Modulator.Frequency;
- break;
- case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
- *val = props->Modulator.HighPassCutoff;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param);
- }
-}
-void Modulator_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Modulator_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Modulator);
-
-
-struct ModulatorStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new ModulatorState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Modulator_vtable; }
-};
-
-EffectProps ModulatorStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
- props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
- props.Modulator.Waveform = AL_RING_MODULATOR_DEFAULT_WAVEFORM;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *ModulatorStateFactory_getFactory()
-{
- static ModulatorStateFactory ModulatorFactory{};
- return &ModulatorFactory;
-}
diff --git a/Alc/effects/null.cpp b/Alc/effects/null.cpp
deleted file mode 100644
index e55c8699..00000000
--- a/Alc/effects/null.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-#include "config.h"
-
-#include <cstdlib>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-
-
-namespace {
-
-struct NullState final : public EffectState {
- NullState();
- ~NullState() override;
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(NullState)
-};
-
-/* This constructs the effect state. It's called when the object is first
- * created.
- */
-NullState::NullState() = default;
-
-/* This destructs the effect state. It's called only when the effect instance
- * is no longer used.
- */
-NullState::~NullState() = default;
-
-/* This updates the device-dependant effect state. This is called on state
- * initialization and any time the device parameters (e.g. playback frequency,
- * format) have been changed. Will always be followed by a call to the update
- * method, if successful.
- */
-ALboolean NullState::deviceUpdate(const ALCdevice* /*device*/)
-{
- return AL_TRUE;
-}
-
-/* This updates the effect state with new properties. This is called any time
- * the effect is (re)loaded into a slot.
- */
-void NullState::update(const ALCcontext* /*context*/, const ALeffectslot* /*slot*/,
- const EffectProps* /*props*/, const EffectTarget /*target*/)
-{
-}
-
-/* This processes the effect state, for the given number of samples from the
- * input to the output buffer. The result should be added to the output buffer,
- * not replace it.
- */
-void NullState::process(const ALsizei /*samplesToDo*/,
- const FloatBufferLine *RESTRICT /*samplesIn*/, const ALsizei /*numInput*/,
- const al::span<FloatBufferLine> /*samplesOut*/)
-{
-}
-
-
-void NullEffect_setParami(EffectProps* /*props*/, ALCcontext *context, ALenum param, ALint /*val*/)
-{
- switch(param)
- {
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param);
- }
-}
-void NullEffect_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{
- switch(param)
- {
- default:
- NullEffect_setParami(props, context, param, vals[0]);
- }
-}
-void NullEffect_setParamf(EffectProps* /*props*/, ALCcontext *context, ALenum param, ALfloat /*val*/)
-{
- switch(param)
- {
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param);
- }
-}
-void NullEffect_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- switch(param)
- {
- default:
- NullEffect_setParamf(props, context, param, vals[0]);
- }
-}
-
-void NullEffect_getParami(const EffectProps* /*props*/, ALCcontext *context, ALenum param, ALint* /*val*/)
-{
- switch(param)
- {
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param);
- }
-}
-void NullEffect_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{
- switch(param)
- {
- default:
- NullEffect_getParami(props, context, param, vals);
- }
-}
-void NullEffect_getParamf(const EffectProps* /*props*/, ALCcontext *context, ALenum param, ALfloat* /*val*/)
-{
- switch(param)
- {
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param);
- }
-}
-void NullEffect_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{
- switch(param)
- {
- default:
- NullEffect_getParamf(props, context, param, vals);
- }
-}
-
-DEFINE_ALEFFECT_VTABLE(NullEffect);
-
-
-struct NullStateFactory final : public EffectStateFactory {
- EffectState *create() override;
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override;
-};
-
-/* Creates EffectState objects of the appropriate type. */
-EffectState *NullStateFactory::create()
-{ return new NullState{}; }
-
-/* Returns an ALeffectProps initialized with this effect type's default
- * property values.
- */
-EffectProps NullStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- return props;
-}
-
-/* Returns a pointer to this effect type's global set/get vtable. */
-const EffectVtable *NullStateFactory::getEffectVtable() const noexcept
-{ return &NullEffect_vtable; }
-
-} // namespace
-
-EffectStateFactory *NullStateFactory_getFactory()
-{
- static NullStateFactory NullFactory{};
- return &NullFactory;
-}
diff --git a/Alc/effects/pshifter.cpp b/Alc/effects/pshifter.cpp
deleted file mode 100644
index 39d3cf1a..00000000
--- a/Alc/effects/pshifter.cpp
+++ /dev/null
@@ -1,405 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2018 by Raul Herraiz.
- * 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"
-
-#ifdef HAVE_SSE_INTRINSICS
-#include <emmintrin.h>
-#endif
-
-#include <cmath>
-#include <cstdlib>
-#include <array>
-#include <complex>
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-
-#include "alcomplex.h"
-
-
-namespace {
-
-using complex_d = std::complex<double>;
-
-#define STFT_SIZE 1024
-#define STFT_HALF_SIZE (STFT_SIZE>>1)
-#define OVERSAMP (1<<2)
-
-#define STFT_STEP (STFT_SIZE / OVERSAMP)
-#define FIFO_LATENCY (STFT_STEP * (OVERSAMP-1))
-
-inline int double2int(double d)
-{
-#if defined(HAVE_SSE_INTRINSICS)
- return _mm_cvttsd_si32(_mm_set_sd(d));
-
-#elif ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
- !defined(__SSE2_MATH__)) || (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2)
-
- int sign, shift;
- int64_t mant;
- union {
- double d;
- int64_t i64;
- } conv;
-
- conv.d = d;
- sign = (conv.i64>>63) | 1;
- shift = ((conv.i64>>52)&0x7ff) - (1023+52);
-
- /* Over/underflow */
- if(UNLIKELY(shift >= 63 || shift < -52))
- return 0;
-
- mant = (conv.i64&0xfffffffffffff_i64) | 0x10000000000000_i64;
- if(LIKELY(shift < 0))
- return (int)(mant >> -shift) * sign;
- return (int)(mant << shift) * sign;
-
-#else
-
- return static_cast<int>(d);
-#endif
-}
-
-/* Define a Hann window, used to filter the STFT input and output. */
-/* Making this constexpr seems to require C++14. */
-std::array<ALdouble,STFT_SIZE> InitHannWindow()
-{
- std::array<ALdouble,STFT_SIZE> ret;
- /* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
- for(ALsizei i{0};i < STFT_SIZE>>1;i++)
- {
- ALdouble val = std::sin(al::MathDefs<double>::Pi() * i / ALdouble{STFT_SIZE-1});
- ret[i] = ret[STFT_SIZE-1-i] = val * val;
- }
- return ret;
-}
-alignas(16) const std::array<ALdouble,STFT_SIZE> HannWindow = InitHannWindow();
-
-
-struct ALphasor {
- ALdouble Amplitude;
- ALdouble Phase;
-};
-
-struct ALfrequencyDomain {
- ALdouble Amplitude;
- ALdouble Frequency;
-};
-
-
-/* Converts complex to ALphasor */
-inline ALphasor rect2polar(const complex_d &number)
-{
- ALphasor polar;
- polar.Amplitude = std::abs(number);
- polar.Phase = std::arg(number);
- return polar;
-}
-
-/* Converts ALphasor to complex */
-inline complex_d polar2rect(const ALphasor &number)
-{ return std::polar<double>(number.Amplitude, number.Phase); }
-
-
-struct PshifterState final : public EffectState {
- /* Effect parameters */
- ALsizei mCount;
- ALsizei mPitchShiftI;
- ALfloat mPitchShift;
- ALfloat mFreqPerBin;
-
- /* Effects buffers */
- ALfloat mInFIFO[STFT_SIZE];
- ALfloat mOutFIFO[STFT_STEP];
- ALdouble mLastPhase[STFT_HALF_SIZE+1];
- ALdouble mSumPhase[STFT_HALF_SIZE+1];
- ALdouble mOutputAccum[STFT_SIZE];
-
- complex_d mFFTbuffer[STFT_SIZE];
-
- ALfrequencyDomain mAnalysis_buffer[STFT_HALF_SIZE+1];
- ALfrequencyDomain mSyntesis_buffer[STFT_HALF_SIZE+1];
-
- alignas(16) ALfloat mBufferOut[BUFFERSIZE];
-
- /* Effect gains for each output channel */
- ALfloat mCurrentGains[MAX_OUTPUT_CHANNELS];
- ALfloat mTargetGains[MAX_OUTPUT_CHANNELS];
-
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(PshifterState)
-};
-
-ALboolean PshifterState::deviceUpdate(const ALCdevice *device)
-{
- /* (Re-)initializing parameters and clear the buffers. */
- mCount = FIFO_LATENCY;
- mPitchShiftI = FRACTIONONE;
- mPitchShift = 1.0f;
- mFreqPerBin = device->Frequency / static_cast<ALfloat>(STFT_SIZE);
-
- std::fill(std::begin(mInFIFO), std::end(mInFIFO), 0.0f);
- std::fill(std::begin(mOutFIFO), std::end(mOutFIFO), 0.0f);
- std::fill(std::begin(mLastPhase), std::end(mLastPhase), 0.0);
- std::fill(std::begin(mSumPhase), std::end(mSumPhase), 0.0);
- std::fill(std::begin(mOutputAccum), std::end(mOutputAccum), 0.0);
- std::fill(std::begin(mFFTbuffer), std::end(mFFTbuffer), complex_d{});
- std::fill(std::begin(mAnalysis_buffer), std::end(mAnalysis_buffer), ALfrequencyDomain{});
- std::fill(std::begin(mSyntesis_buffer), std::end(mSyntesis_buffer), ALfrequencyDomain{});
-
- std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f);
- std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
-
- return AL_TRUE;
-}
-
-void PshifterState::update(const ALCcontext*, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const float pitch{std::pow(2.0f,
- static_cast<ALfloat>(props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune) / 1200.0f
- )};
- mPitchShiftI = fastf2i(pitch*FRACTIONONE);
- mPitchShift = mPitchShiftI * (1.0f/FRACTIONONE);
-
- ALfloat coeffs[MAX_AMBI_CHANNELS];
- CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f, coeffs);
-
- mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs, slot->Params.Gain, mTargetGains);
-}
-
-void PshifterState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei /*numInput*/, const al::span<FloatBufferLine> samplesOut)
-{
- /* Pitch shifter engine based on the work of Stephan Bernsee.
- * http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
- */
-
- static constexpr ALdouble expected{al::MathDefs<double>::Tau() / OVERSAMP};
- const ALdouble freq_per_bin{mFreqPerBin};
- ALfloat *RESTRICT bufferOut{mBufferOut};
- ALsizei count{mCount};
-
- for(ALsizei i{0};i < samplesToDo;)
- {
- do {
- /* Fill FIFO buffer with samples data */
- mInFIFO[count] = samplesIn[0][i];
- bufferOut[i] = mOutFIFO[count - FIFO_LATENCY];
-
- count++;
- } while(++i < samplesToDo && count < STFT_SIZE);
-
- /* Check whether FIFO buffer is filled */
- if(count < STFT_SIZE) break;
- count = FIFO_LATENCY;
-
- /* Real signal windowing and store in FFTbuffer */
- for(ALsizei k{0};k < STFT_SIZE;k++)
- {
- mFFTbuffer[k].real(mInFIFO[k] * HannWindow[k]);
- mFFTbuffer[k].imag(0.0);
- }
-
- /* ANALYSIS */
- /* Apply FFT to FFTbuffer data */
- complex_fft(mFFTbuffer, -1.0);
-
- /* Analyze the obtained data. Since the real FFT is symmetric, only
- * STFT_HALF_SIZE+1 samples are needed.
- */
- for(ALsizei k{0};k < STFT_HALF_SIZE+1;k++)
- {
- /* Compute amplitude and phase */
- ALphasor component{rect2polar(mFFTbuffer[k])};
-
- /* Compute phase difference and subtract expected phase difference */
- double tmp{(component.Phase - mLastPhase[k]) - k*expected};
-
- /* Map delta phase into +/- Pi interval */
- int qpd{double2int(tmp / al::MathDefs<double>::Pi())};
- tmp -= al::MathDefs<double>::Pi() * (qpd + (qpd%2));
-
- /* Get deviation from bin frequency from the +/- Pi interval */
- tmp /= expected;
-
- /* Compute the k-th partials' true frequency, twice the amplitude
- * for maintain the gain (because half of bins are used) and store
- * amplitude and true frequency in analysis buffer.
- */
- mAnalysis_buffer[k].Amplitude = 2.0 * component.Amplitude;
- mAnalysis_buffer[k].Frequency = (k + tmp) * freq_per_bin;
-
- /* Store actual phase[k] for the calculations in the next frame*/
- mLastPhase[k] = component.Phase;
- }
-
- /* PROCESSING */
- /* pitch shifting */
- for(ALsizei k{0};k < STFT_HALF_SIZE+1;k++)
- {
- mSyntesis_buffer[k].Amplitude = 0.0;
- mSyntesis_buffer[k].Frequency = 0.0;
- }
-
- for(ALsizei k{0};k < STFT_HALF_SIZE+1;k++)
- {
- ALsizei j{(k*mPitchShiftI) >> FRACTIONBITS};
- if(j >= STFT_HALF_SIZE+1) break;
-
- mSyntesis_buffer[j].Amplitude += mAnalysis_buffer[k].Amplitude;
- mSyntesis_buffer[j].Frequency = mAnalysis_buffer[k].Frequency * mPitchShift;
- }
-
- /* SYNTHESIS */
- /* Synthesis the processing data */
- for(ALsizei k{0};k < STFT_HALF_SIZE+1;k++)
- {
- ALphasor component;
- ALdouble tmp;
-
- /* Compute bin deviation from scaled freq */
- tmp = mSyntesis_buffer[k].Frequency/freq_per_bin - k;
-
- /* Calculate actual delta phase and accumulate it to get bin phase */
- mSumPhase[k] += (k + tmp) * expected;
-
- component.Amplitude = mSyntesis_buffer[k].Amplitude;
- component.Phase = mSumPhase[k];
-
- /* Compute phasor component to cartesian complex number and storage it into FFTbuffer*/
- mFFTbuffer[k] = polar2rect(component);
- }
- /* zero negative frequencies for recontruct a real signal */
- for(ALsizei k{STFT_HALF_SIZE+1};k < STFT_SIZE;k++)
- mFFTbuffer[k] = complex_d{};
-
- /* Apply iFFT to buffer data */
- complex_fft(mFFTbuffer, 1.0);
-
- /* Windowing and add to output */
- for(ALsizei k{0};k < STFT_SIZE;k++)
- mOutputAccum[k] += HannWindow[k] * mFFTbuffer[k].real() /
- (0.5 * STFT_HALF_SIZE * OVERSAMP);
-
- /* Shift accumulator, input & output FIFO */
- ALsizei j, k;
- for(k = 0;k < STFT_STEP;k++) mOutFIFO[k] = static_cast<ALfloat>(mOutputAccum[k]);
- for(j = 0;k < STFT_SIZE;k++,j++) mOutputAccum[j] = mOutputAccum[k];
- for(;j < STFT_SIZE;j++) mOutputAccum[j] = 0.0;
- for(k = 0;k < FIFO_LATENCY;k++)
- mInFIFO[k] = mInFIFO[k+STFT_STEP];
- }
- mCount = count;
-
- /* Now, mix the processed sound data to the output. */
- MixSamples(bufferOut, samplesOut, mCurrentGains, mTargetGains, maxi(samplesToDo, 512), 0,
- samplesToDo);
-}
-
-
-void Pshifter_setParamf(EffectProps*, ALCcontext *context, ALenum param, ALfloat)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param); }
-void Pshifter_setParamfv(EffectProps*, ALCcontext *context, ALenum param, const ALfloat*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", param); }
-
-void Pshifter_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_PITCH_SHIFTER_COARSE_TUNE:
- if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Pitch shifter coarse tune out of range");
- props->Pshifter.CoarseTune = val;
- break;
-
- case AL_PITCH_SHIFTER_FINE_TUNE:
- if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE))
- SETERR_RETURN(context, AL_INVALID_VALUE,,"Pitch shifter fine tune out of range");
- props->Pshifter.FineTune = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param);
- }
-}
-void Pshifter_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ Pshifter_setParami(props, context, param, vals[0]); }
-
-void Pshifter_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_PITCH_SHIFTER_COARSE_TUNE:
- *val = props->Pshifter.CoarseTune;
- break;
- case AL_PITCH_SHIFTER_FINE_TUNE:
- *val = props->Pshifter.FineTune;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param);
- }
-}
-void Pshifter_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ Pshifter_getParami(props, context, param, vals); }
-
-void Pshifter_getParamf(const EffectProps*, ALCcontext *context, ALenum param, ALfloat*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param); }
-void Pshifter_getParamfv(const EffectProps*, ALCcontext *context, ALenum param, ALfloat*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x", param); }
-
-DEFINE_ALEFFECT_VTABLE(Pshifter);
-
-
-struct PshifterStateFactory final : public EffectStateFactory {
- EffectState *create() override;
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Pshifter_vtable; }
-};
-
-EffectState *PshifterStateFactory::create()
-{ return new PshifterState{}; }
-
-EffectProps PshifterStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
- props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *PshifterStateFactory_getFactory()
-{
- static PshifterStateFactory PshifterFactory{};
- return &PshifterFactory;
-}
diff --git a/Alc/effects/reverb.cpp b/Alc/effects/reverb.cpp
deleted file mode 100644
index ac996b3f..00000000
--- a/Alc/effects/reverb.cpp
+++ /dev/null
@@ -1,2102 +0,0 @@
-/**
- * Ambisonic reverb engine for the OpenAL cross platform audio library
- * Copyright (C) 2008-2017 by Chris Robinson and Christopher Fitzgerald.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cstdio>
-#include <cstdlib>
-#include <cmath>
-
-#include <array>
-#include <numeric>
-#include <algorithm>
-#include <functional>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alu.h"
-#include "alAuxEffectSlot.h"
-#include "alListener.h"
-#include "alError.h"
-#include "bformatdec.h"
-#include "filters/biquad.h"
-#include "vector.h"
-#include "vecmat.h"
-
-/* This is a user config option for modifying the overall output of the reverb
- * effect.
- */
-ALfloat ReverbBoost = 1.0f;
-
-namespace {
-
-using namespace std::placeholders;
-
-/* The number of samples used for cross-faded delay lines. This can be used
- * to balance the compensation for abrupt line changes and attenuation due to
- * minimally lengthed recursive lines. Try to keep this below the device
- * update size.
- */
-constexpr int FADE_SAMPLES{128};
-
-/* The number of spatialized lines or channels to process. Four channels allows
- * for a 3D A-Format response. NOTE: This can't be changed without taking care
- * of the conversion matrices, and a few places where the length arrays are
- * assumed to have 4 elements.
- */
-constexpr int NUM_LINES{4};
-
-
-/* The B-Format to A-Format conversion matrix. The arrangement of rows is
- * deliberately chosen to align the resulting lines to their spatial opposites
- * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
- * back left). It's not quite opposite, since the A-Format results in a
- * tetrahedron, but it's close enough. Should the model be extended to 8-lines
- * in the future, true opposites can be used.
- */
-alignas(16) constexpr ALfloat B2A[NUM_LINES][MAX_AMBI_CHANNELS]{
- { 0.288675134595f, 0.288675134595f, 0.288675134595f, 0.288675134595f },
- { 0.288675134595f, -0.288675134595f, -0.288675134595f, 0.288675134595f },
- { 0.288675134595f, 0.288675134595f, -0.288675134595f, -0.288675134595f },
- { 0.288675134595f, -0.288675134595f, 0.288675134595f, -0.288675134595f }
-};
-
-/* Converts A-Format to B-Format. */
-alignas(16) constexpr ALfloat A2B[NUM_LINES][NUM_LINES]{
- { 0.866025403785f, 0.866025403785f, 0.866025403785f, 0.866025403785f },
- { 0.866025403785f, -0.866025403785f, 0.866025403785f, -0.866025403785f },
- { 0.866025403785f, -0.866025403785f, -0.866025403785f, 0.866025403785f },
- { 0.866025403785f, 0.866025403785f, -0.866025403785f, -0.866025403785f }
-};
-
-
-constexpr ALfloat FadeStep{1.0f / FADE_SAMPLES};
-
-/* The all-pass and delay lines have a variable length dependent on the
- * effect's density parameter, which helps alter the perceived environment
- * size. The size-to-density conversion is a cubed scale:
- *
- * density = min(1.0, pow(size, 3.0) / DENSITY_SCALE);
- *
- * The line lengths scale linearly with room size, so the inverse density
- * conversion is needed, taking the cube root of the re-scaled density to
- * calculate the line length multiplier:
- *
- * length_mult = max(5.0, cbrt(density*DENSITY_SCALE));
- *
- * The density scale below will result in a max line multiplier of 50, for an
- * effective size range of 5m to 50m.
- */
-constexpr ALfloat DENSITY_SCALE{125000.0f};
-
-/* All delay line lengths are specified in seconds.
- *
- * To approximate early reflections, we break them up into primary (those
- * arriving from the same direction as the source) and secondary (those
- * arriving from the opposite direction).
- *
- * The early taps decorrelate the 4-channel signal to approximate an average
- * room response for the primary reflections after the initial early delay.
- *
- * Given an average room dimension (d_a) and the speed of sound (c) we can
- * calculate the average reflection delay (r_a) regardless of listener and
- * source positions as:
- *
- * r_a = d_a / c
- * c = 343.3
- *
- * This can extended to finding the average difference (r_d) between the
- * maximum (r_1) and minimum (r_0) reflection delays:
- *
- * r_0 = 2 / 3 r_a
- * = r_a - r_d / 2
- * = r_d
- * r_1 = 4 / 3 r_a
- * = r_a + r_d / 2
- * = 2 r_d
- * r_d = 2 / 3 r_a
- * = r_1 - r_0
- *
- * As can be determined by integrating the 1D model with a source (s) and
- * listener (l) positioned across the dimension of length (d_a):
- *
- * r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
- *
- * The initial taps (T_(i=0)^N) are then specified by taking a power series
- * that ranges between r_0 and half of r_1 less r_0:
- *
- * R_i = 2^(i / (2 N - 1)) r_d
- * = r_0 + (2^(i / (2 N - 1)) - 1) r_d
- * = r_0 + T_i
- * T_i = R_i - r_0
- * = (2^(i / (2 N - 1)) - 1) r_d
- *
- * Assuming an average of 1m, we get the following taps:
- */
-constexpr std::array<ALfloat,NUM_LINES> EARLY_TAP_LENGTHS{{
- 0.0000000e+0f, 2.0213520e-4f, 4.2531060e-4f, 6.7171600e-4f
-}};
-
-/* The early all-pass filter lengths are based on the early tap lengths:
- *
- * A_i = R_i / a
- *
- * Where a is the approximate maximum all-pass cycle limit (20).
- */
-constexpr std::array<ALfloat,NUM_LINES> EARLY_ALLPASS_LENGTHS{{
- 9.7096800e-5f, 1.0720356e-4f, 1.1836234e-4f, 1.3068260e-4f
-}};
-
-/* The early delay lines are used to transform the primary reflections into
- * the secondary reflections. The A-format is arranged in such a way that
- * the channels/lines are spatially opposite:
- *
- * C_i is opposite C_(N-i-1)
- *
- * The delays of the two opposing reflections (R_i and O_i) from a source
- * anywhere along a particular dimension always sum to twice its full delay:
- *
- * 2 r_a = R_i + O_i
- *
- * With that in mind we can determine the delay between the two reflections
- * and thus specify our early line lengths (L_(i=0)^N) using:
- *
- * O_i = 2 r_a - R_(N-i-1)
- * L_i = O_i - R_(N-i-1)
- * = 2 (r_a - R_(N-i-1))
- * = 2 (r_a - T_(N-i-1) - r_0)
- * = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
- *
- * Using an average dimension of 1m, we get:
- */
-constexpr std::array<ALfloat,NUM_LINES> EARLY_LINE_LENGTHS{{
- 5.9850400e-4f, 1.0913150e-3f, 1.5376658e-3f, 1.9419362e-3f
-}};
-
-/* The late all-pass filter lengths are based on the late line lengths:
- *
- * A_i = (5 / 3) L_i / r_1
- */
-constexpr std::array<ALfloat,NUM_LINES> LATE_ALLPASS_LENGTHS{{
- 1.6182800e-4f, 2.0389060e-4f, 2.8159360e-4f, 3.2365600e-4f
-}};
-constexpr auto LATE_ALLPASS_LENGTHS_size = LATE_ALLPASS_LENGTHS.size();
-
-/* The late lines are used to approximate the decaying cycle of recursive
- * late reflections.
- *
- * Splitting the lines in half, we start with the shortest reflection paths
- * (L_(i=0)^(N/2)):
- *
- * L_i = 2^(i / (N - 1)) r_d
- *
- * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
- *
- * L_i = 2 r_a - L_(i-N/2)
- * = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
- *
- * For our 1m average room, we get:
- */
-constexpr std::array<ALfloat,NUM_LINES> LATE_LINE_LENGTHS{{
- 1.9419362e-3f, 2.4466860e-3f, 3.3791220e-3f, 3.8838720e-3f
-}};
-constexpr auto LATE_LINE_LENGTHS_size = LATE_LINE_LENGTHS.size();
-
-
-struct DelayLineI {
- /* The delay lines use interleaved samples, with the lengths being powers
- * of 2 to allow the use of bit-masking instead of a modulus for wrapping.
- */
- ALsizei Mask{0};
- ALfloat (*Line)[NUM_LINES]{nullptr};
-
-
- void write(ALsizei offset, const ALsizei c, const ALfloat *RESTRICT in, const ALsizei count) const noexcept
- {
- ASSUME(count > 0);
- for(ALsizei i{0};i < count;)
- {
- offset &= Mask;
- ALsizei td{mini(Mask+1 - offset, count - i)};
- do {
- Line[offset++][c] = in[i++];
- } while(--td);
- }
- }
-};
-
-struct VecAllpass {
- DelayLineI Delay;
- ALfloat Coeff{0.0f};
- ALsizei Offset[NUM_LINES][2]{};
-
- void processFaded(const al::span<FloatBufferLine,NUM_LINES> samples, ALsizei offset,
- const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fade, const ALsizei todo);
- void processUnfaded(const al::span<FloatBufferLine,NUM_LINES> samples, ALsizei offset,
- const ALfloat xCoeff, const ALfloat yCoeff, const ALsizei todo);
-};
-
-struct T60Filter {
- /* Two filters are used to adjust the signal. One to control the low
- * frequencies, and one to control the high frequencies.
- */
- ALfloat MidGain[2]{0.0f, 0.0f};
- BiquadFilter HFFilter, LFFilter;
-
- void calcCoeffs(const ALfloat length, const ALfloat lfDecayTime, const ALfloat mfDecayTime,
- const ALfloat hfDecayTime, const ALfloat lf0norm, const ALfloat hf0norm);
-
- /* Applies the two T60 damping filter sections. */
- void process(ALfloat *samples, const ALsizei todo)
- {
- HFFilter.process(samples, samples, todo);
- LFFilter.process(samples, samples, todo);
- }
-};
-
-struct EarlyReflections {
- /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
- * The spread from this filter also helps smooth out the reverb tail.
- */
- VecAllpass VecAp;
-
- /* An echo line is used to complete the second half of the early
- * reflections.
- */
- DelayLineI Delay;
- ALsizei Offset[NUM_LINES][2]{};
- ALfloat Coeff[NUM_LINES][2]{};
-
- /* The gain for each output channel based on 3D panning. */
- ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS]{};
- ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS]{};
-
- void updateLines(const ALfloat density, const ALfloat diffusion, const ALfloat decayTime,
- const ALfloat frequency);
-};
-
-struct LateReverb {
- /* A recursive delay line is used fill in the reverb tail. */
- DelayLineI Delay;
- ALsizei Offset[NUM_LINES][2]{};
-
- /* Attenuation to compensate for the modal density and decay rate of the
- * late lines.
- */
- ALfloat DensityGain[2]{0.0f, 0.0f};
-
- /* T60 decay filters are used to simulate absorption. */
- T60Filter T60[NUM_LINES];
-
- /* A Gerzon vector all-pass filter is used to simulate diffusion. */
- VecAllpass VecAp;
-
- /* The gain for each output channel based on 3D panning. */
- ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS]{};
- ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS]{};
-
- void updateLines(const ALfloat density, const ALfloat diffusion, const ALfloat lfDecayTime,
- const ALfloat mfDecayTime, const ALfloat hfDecayTime, const ALfloat lf0norm,
- const ALfloat hf0norm, const ALfloat frequency);
-};
-
-struct ReverbState final : public EffectState {
- /* All delay lines are allocated as a single buffer to reduce memory
- * fragmentation and management code.
- */
- al::vector<ALfloat,16> mSampleBuffer;
-
- struct {
- /* Calculated parameters which indicate if cross-fading is needed after
- * an update.
- */
- ALfloat Density{AL_EAXREVERB_DEFAULT_DENSITY};
- ALfloat Diffusion{AL_EAXREVERB_DEFAULT_DIFFUSION};
- ALfloat DecayTime{AL_EAXREVERB_DEFAULT_DECAY_TIME};
- ALfloat HFDecayTime{AL_EAXREVERB_DEFAULT_DECAY_HFRATIO * AL_EAXREVERB_DEFAULT_DECAY_TIME};
- ALfloat LFDecayTime{AL_EAXREVERB_DEFAULT_DECAY_LFRATIO * AL_EAXREVERB_DEFAULT_DECAY_TIME};
- ALfloat HFReference{AL_EAXREVERB_DEFAULT_HFREFERENCE};
- ALfloat LFReference{AL_EAXREVERB_DEFAULT_LFREFERENCE};
- } mParams;
-
- /* Master effect filters */
- struct {
- BiquadFilter Lp;
- BiquadFilter Hp;
- } mFilter[NUM_LINES];
-
- /* Core delay line (early reflections and late reverb tap from this). */
- DelayLineI mDelay;
-
- /* Tap points for early reflection delay. */
- ALsizei mEarlyDelayTap[NUM_LINES][2]{};
- ALfloat mEarlyDelayCoeff[NUM_LINES][2]{};
-
- /* Tap points for late reverb feed and delay. */
- ALsizei mLateFeedTap{};
- ALsizei mLateDelayTap[NUM_LINES][2]{};
-
- /* Coefficients for the all-pass and line scattering matrices. */
- ALfloat mMixX{0.0f};
- ALfloat mMixY{0.0f};
-
- EarlyReflections mEarly;
-
- LateReverb mLate;
-
- /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
- ALsizei mFadeCount{0};
-
- /* Maximum number of samples to process at once. */
- ALsizei mMaxUpdate[2]{BUFFERSIZE, BUFFERSIZE};
-
- /* The current write offset for all delay lines. */
- ALsizei mOffset{0};
-
- /* Temporary storage used when processing. */
- alignas(16) std::array<FloatBufferLine,NUM_LINES> mTempSamples{};
- alignas(16) std::array<FloatBufferLine,NUM_LINES> mEarlyBuffer{};
- alignas(16) std::array<FloatBufferLine,NUM_LINES> mLateBuffer{};
-
- using MixOutT = void (ReverbState::*)(const al::span<FloatBufferLine> samplesOut,
- const ALsizei todo);
-
- MixOutT mMixOut{&ReverbState::MixOutPlain};
- std::array<ALfloat,MAX_AMBI_ORDER+1> mOrderScales{};
- std::array<std::array<BandSplitter,NUM_LINES>,2> mAmbiSplitter;
-
-
- void MixOutPlain(const al::span<FloatBufferLine> samplesOut, const ALsizei todo)
- {
- ASSUME(todo > 0);
-
- /* Convert back to B-Format, and mix the results to output. */
- for(ALsizei c{0};c < NUM_LINES;c++)
- {
- std::fill_n(mTempSamples[0].begin(), todo, 0.0f);
- MixRowSamples(mTempSamples[0], A2B[c], mEarlyBuffer, 0, todo);
- MixSamples(mTempSamples[0].data(), samplesOut, mEarly.CurrentGain[c],
- mEarly.PanGain[c], todo, 0, todo);
- }
-
- for(ALsizei c{0};c < NUM_LINES;c++)
- {
- std::fill_n(mTempSamples[0].begin(), todo, 0.0f);
- MixRowSamples(mTempSamples[0], A2B[c], mLateBuffer, 0, todo);
- MixSamples(mTempSamples[0].data(), samplesOut, mLate.CurrentGain[c], mLate.PanGain[c],
- todo, 0, todo);
- }
- }
-
- void MixOutAmbiUp(const al::span<FloatBufferLine> samplesOut, const ALsizei todo)
- {
- ASSUME(todo > 0);
-
- for(ALsizei c{0};c < NUM_LINES;c++)
- {
- std::fill_n(mTempSamples[0].begin(), todo, 0.0f);
- MixRowSamples(mTempSamples[0], A2B[c], mEarlyBuffer, 0, todo);
-
- /* Apply scaling to the B-Format's HF response to "upsample" it to
- * higher-order output.
- */
- const ALfloat hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]};
- mAmbiSplitter[0][c].applyHfScale(mTempSamples[0].data(), hfscale, todo);
-
- MixSamples(mTempSamples[0].data(), samplesOut, mEarly.CurrentGain[c],
- mEarly.PanGain[c], todo, 0, todo);
- }
-
- for(ALsizei c{0};c < NUM_LINES;c++)
- {
- std::fill_n(mTempSamples[0].begin(), todo, 0.0f);
- MixRowSamples(mTempSamples[0], A2B[c], mLateBuffer, 0, todo);
-
- const ALfloat hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]};
- mAmbiSplitter[1][c].applyHfScale(mTempSamples[0].data(), hfscale, todo);
-
- MixSamples(mTempSamples[0].data(), samplesOut, mLate.CurrentGain[c], mLate.PanGain[c],
- todo, 0, todo);
- }
- }
-
- bool allocLines(const ALfloat frequency);
-
- void updateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density,
- const ALfloat decayTime, const ALfloat frequency);
- void update3DPanning(const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan,
- const ALfloat earlyGain, const ALfloat lateGain, const EffectTarget &target);
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- DEF_NEWDEL(ReverbState)
-};
-
-/**************************************
- * Device Update *
- **************************************/
-
-inline ALfloat CalcDelayLengthMult(ALfloat density)
-{ return maxf(5.0f, std::cbrt(density*DENSITY_SCALE)); }
-
-/* Given the allocated sample buffer, this function updates each delay line
- * offset.
- */
-inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay)
-{
- union {
- ALfloat *f;
- ALfloat (*f4)[NUM_LINES];
- } u;
- u.f = &sampleBuffer[reinterpret_cast<ptrdiff_t>(Delay->Line) * NUM_LINES];
- Delay->Line = u.f4;
-}
-
-/* Calculate the length of a delay line and store its mask and offset. */
-ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALfloat frequency,
- const ALuint extra, DelayLineI *Delay)
-{
- /* All line lengths are powers of 2, calculated from their lengths in
- * seconds, rounded up.
- */
- auto samples = static_cast<ALuint>(float2int(std::ceil(length*frequency)));
- samples = NextPowerOf2(samples + extra);
-
- /* All lines share a single sample buffer. */
- Delay->Mask = samples - 1;
- Delay->Line = reinterpret_cast<ALfloat(*)[NUM_LINES]>(offset);
-
- /* Return the sample count for accumulation. */
- return samples;
-}
-
-/* Calculates the delay line metrics and allocates the shared sample buffer
- * for all lines given the sample rate (frequency). If an allocation failure
- * occurs, it returns AL_FALSE.
- */
-bool ReverbState::allocLines(const ALfloat frequency)
-{
- /* All delay line lengths are calculated to accomodate the full range of
- * lengths given their respective paramters.
- */
- ALuint totalSamples{0u};
-
- /* Multiplier for the maximum density value, i.e. density=1, which is
- * actually the least density...
- */
- ALfloat multiplier{CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY)};
-
- /* The main delay length includes the maximum early reflection delay, the
- * largest early tap width, the maximum late reverb delay, and the
- * largest late tap width. Finally, it must also be extended by the
- * update size (BUFFERSIZE) for block processing.
- */
- ALfloat length{AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS.back()*multiplier +
- AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
- (LATE_LINE_LENGTHS.back() - LATE_LINE_LENGTHS.front())/float{LATE_LINE_LENGTHS_size}*multiplier};
- totalSamples += CalcLineLength(length, totalSamples, frequency, BUFFERSIZE, &mDelay);
-
- /* The early vector all-pass line. */
- length = EARLY_ALLPASS_LENGTHS.back() * multiplier;
- totalSamples += CalcLineLength(length, totalSamples, frequency, 0, &mEarly.VecAp.Delay);
-
- /* The early reflection line. */
- length = EARLY_LINE_LENGTHS.back() * multiplier;
- totalSamples += CalcLineLength(length, totalSamples, frequency, 0, &mEarly.Delay);
-
- /* The late vector all-pass line. */
- length = LATE_ALLPASS_LENGTHS.back() * multiplier;
- totalSamples += CalcLineLength(length, totalSamples, frequency, 0, &mLate.VecAp.Delay);
-
- /* The late delay lines are calculated from the largest maximum density
- * line length.
- */
- length = LATE_LINE_LENGTHS.back() * multiplier;
- totalSamples += CalcLineLength(length, totalSamples, frequency, 0, &mLate.Delay);
-
- totalSamples *= NUM_LINES;
- if(totalSamples != mSampleBuffer.size())
- {
- mSampleBuffer.resize(totalSamples);
- mSampleBuffer.shrink_to_fit();
- }
-
- /* Clear the sample buffer. */
- std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f);
-
- /* Update all delays to reflect the new sample buffer. */
- RealizeLineOffset(mSampleBuffer.data(), &mDelay);
- RealizeLineOffset(mSampleBuffer.data(), &mEarly.VecAp.Delay);
- RealizeLineOffset(mSampleBuffer.data(), &mEarly.Delay);
- RealizeLineOffset(mSampleBuffer.data(), &mLate.VecAp.Delay);
- RealizeLineOffset(mSampleBuffer.data(), &mLate.Delay);
-
- return true;
-}
-
-ALboolean ReverbState::deviceUpdate(const ALCdevice *device)
-{
- const auto frequency = static_cast<ALfloat>(device->Frequency);
-
- /* Allocate the delay lines. */
- if(!allocLines(frequency))
- return AL_FALSE;
-
- const ALfloat multiplier{CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY)};
-
- /* The late feed taps are set a fixed position past the latest delay tap. */
- mLateFeedTap = float2int(
- (AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS.back()*multiplier) * frequency);
-
- /* Clear filters and gain coefficients since the delay lines were all just
- * cleared (if not reallocated).
- */
- for(auto &filter : mFilter)
- {
- filter.Lp.clear();
- filter.Hp.clear();
- }
-
- for(auto &coeff : mEarlyDelayCoeff)
- std::fill(std::begin(coeff), std::end(coeff), 0.0f);
- for(auto &coeff : mEarly.Coeff)
- std::fill(std::begin(coeff), std::end(coeff), 0.0f);
-
- mLate.DensityGain[0] = 0.0f;
- mLate.DensityGain[1] = 0.0f;
- for(auto &t60 : mLate.T60)
- {
- t60.MidGain[0] = 0.0f;
- t60.MidGain[1] = 0.0f;
- t60.HFFilter.clear();
- t60.LFFilter.clear();
- }
-
- for(auto &gains : mEarly.CurrentGain)
- std::fill(std::begin(gains), std::end(gains), 0.0f);
- for(auto &gains : mEarly.PanGain)
- std::fill(std::begin(gains), std::end(gains), 0.0f);
- for(auto &gains : mLate.CurrentGain)
- std::fill(std::begin(gains), std::end(gains), 0.0f);
- for(auto &gains : mLate.PanGain)
- std::fill(std::begin(gains), std::end(gains), 0.0f);
-
- /* Reset counters and offset base. */
- mFadeCount = 0;
- std::fill(std::begin(mMaxUpdate), std::end(mMaxUpdate), BUFFERSIZE);
- mOffset = 0;
-
- if(device->mAmbiOrder > 1)
- {
- mMixOut = &ReverbState::MixOutAmbiUp;
- mOrderScales = BFormatDec::GetHFOrderScales(1, device->mAmbiOrder);
- }
- else
- {
- mMixOut = &ReverbState::MixOutPlain;
- mOrderScales.fill(1.0f);
- }
- mAmbiSplitter[0][0].init(400.0f / frequency);
- std::fill(mAmbiSplitter[0].begin()+1, mAmbiSplitter[0].end(), mAmbiSplitter[0][0]);
- std::fill(mAmbiSplitter[1].begin(), mAmbiSplitter[1].end(), mAmbiSplitter[0][0]);
-
- return AL_TRUE;
-}
-
-/**************************************
- * Effect Update *
- **************************************/
-
-/* Calculate a decay coefficient given the length of each cycle and the time
- * until the decay reaches -60 dB.
- */
-inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime)
-{ return std::pow(REVERB_DECAY_GAIN, length/decayTime); }
-
-/* Calculate a decay length from a coefficient and the time until the decay
- * reaches -60 dB.
- */
-inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime)
-{ return std::log10(coeff) * decayTime / std::log10(REVERB_DECAY_GAIN); }
-
-/* Calculate an attenuation to be applied to the input of any echo models to
- * compensate for modal density and decay time.
- */
-inline ALfloat CalcDensityGain(const ALfloat a)
-{
- /* The energy of a signal can be obtained by finding the area under the
- * squared signal. This takes the form of Sum(x_n^2), where x is the
- * amplitude for the sample n.
- *
- * Decaying feedback matches exponential decay of the form Sum(a^n),
- * where a is the attenuation coefficient, and n is the sample. The area
- * under this decay curve can be calculated as: 1 / (1 - a).
- *
- * Modifying the above equation to find the area under the squared curve
- * (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
- * calculated by inverting the square root of this approximation,
- * yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
- */
- return std::sqrt(1.0f - a*a);
-}
-
-/* Calculate the scattering matrix coefficients given a diffusion factor. */
-inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y)
-{
- /* The matrix is of order 4, so n is sqrt(4 - 1). */
- ALfloat n{std::sqrt(3.0f)};
- ALfloat t{diffusion * std::atan(n)};
-
- /* Calculate the first mixing matrix coefficient. */
- *x = std::cos(t);
- /* Calculate the second mixing matrix coefficient. */
- *y = std::sin(t) / n;
-}
-
-/* Calculate the limited HF ratio for use with the late reverb low-pass
- * filters.
- */
-ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF,
- const ALfloat decayTime, const ALfloat SpeedOfSound)
-{
- /* Find the attenuation due to air absorption in dB (converting delay
- * time to meters using the speed of sound). Then reversing the decay
- * equation, solve for HF ratio. The delay length is cancelled out of
- * the equation, so it can be calculated once for all lines.
- */
- ALfloat limitRatio{1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SpeedOfSound)};
-
- /* Using the limit calculated above, apply the upper bound to the HF ratio.
- */
- return minf(limitRatio, hfRatio);
-}
-
-
-/* Calculates the 3-band T60 damping coefficients for a particular delay line
- * of specified length, using a combination of two shelf filter sections given
- * decay times for each band split at two reference frequencies.
- */
-void T60Filter::calcCoeffs(const ALfloat length, const ALfloat lfDecayTime,
- const ALfloat mfDecayTime, const ALfloat hfDecayTime, const ALfloat lf0norm,
- const ALfloat hf0norm)
-{
- const ALfloat mfGain{CalcDecayCoeff(length, mfDecayTime)};
- const ALfloat lfGain{maxf(CalcDecayCoeff(length, lfDecayTime)/mfGain, 0.001f)};
- const ALfloat hfGain{maxf(CalcDecayCoeff(length, hfDecayTime)/mfGain, 0.001f)};
-
- MidGain[1] = mfGain;
- LFFilter.setParams(BiquadType::LowShelf, lfGain, lf0norm,
- LFFilter.rcpQFromSlope(lfGain, 1.0f));
- HFFilter.setParams(BiquadType::HighShelf, hfGain, hf0norm,
- HFFilter.rcpQFromSlope(hfGain, 1.0f));
-}
-
-/* Update the early reflection line lengths and gain coefficients. */
-void EarlyReflections::updateLines(const ALfloat density, const ALfloat diffusion,
- const ALfloat decayTime, const ALfloat frequency)
-{
- const ALfloat multiplier{CalcDelayLengthMult(density)};
-
- /* Calculate the all-pass feed-back/forward coefficient. */
- VecAp.Coeff = std::sqrt(0.5f) * std::pow(diffusion, 2.0f);
-
- for(ALsizei i{0};i < NUM_LINES;i++)
- {
- /* Calculate the length (in seconds) of each all-pass line. */
- ALfloat length{EARLY_ALLPASS_LENGTHS[i] * multiplier};
-
- /* Calculate the delay offset for each all-pass line. */
- VecAp.Offset[i][1] = float2int(length * frequency);
-
- /* Calculate the length (in seconds) of each delay line. */
- length = EARLY_LINE_LENGTHS[i] * multiplier;
-
- /* Calculate the delay offset for each delay line. */
- Offset[i][1] = float2int(length * frequency);
-
- /* Calculate the gain (coefficient) for each line. */
- Coeff[i][1] = CalcDecayCoeff(length, decayTime);
- }
-}
-
-/* Update the late reverb line lengths and T60 coefficients. */
-void LateReverb::updateLines(const ALfloat density, const ALfloat diffusion,
- const ALfloat lfDecayTime, const ALfloat mfDecayTime, const ALfloat hfDecayTime,
- const ALfloat lf0norm, const ALfloat hf0norm, const ALfloat frequency)
-{
- /* Scaling factor to convert the normalized reference frequencies from
- * representing 0...freq to 0...max_reference.
- */
- const ALfloat norm_weight_factor{frequency / AL_EAXREVERB_MAX_HFREFERENCE};
-
- const ALfloat late_allpass_avg{
- std::accumulate(LATE_ALLPASS_LENGTHS.begin(), LATE_ALLPASS_LENGTHS.end(), 0.0f) /
- float{LATE_ALLPASS_LENGTHS_size}};
-
- /* To compensate for changes in modal density and decay time of the late
- * reverb signal, the input is attenuated based on the maximal energy of
- * the outgoing signal. This approximation is used to keep the apparent
- * energy of the signal equal for all ranges of density and decay time.
- *
- * The average length of the delay lines is used to calculate the
- * attenuation coefficient.
- */
- const ALfloat multiplier{CalcDelayLengthMult(density)};
- ALfloat length{std::accumulate(LATE_LINE_LENGTHS.begin(), LATE_LINE_LENGTHS.end(), 0.0f) /
- float{LATE_LINE_LENGTHS_size} * multiplier};
- length += late_allpass_avg * multiplier;
- /* The density gain calculation uses an average decay time weighted by
- * approximate bandwidth. This attempts to compensate for losses of energy
- * that reduce decay time due to scattering into highly attenuated bands.
- */
- const ALfloat bandWeights[3]{
- lf0norm*norm_weight_factor,
- hf0norm*norm_weight_factor - lf0norm*norm_weight_factor,
- 1.0f - hf0norm*norm_weight_factor};
- DensityGain[1] = CalcDensityGain(
- CalcDecayCoeff(length,
- bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime + bandWeights[2]*hfDecayTime
- )
- );
-
- /* Calculate the all-pass feed-back/forward coefficient. */
- VecAp.Coeff = std::sqrt(0.5f) * std::pow(diffusion, 2.0f);
-
- for(ALsizei i{0};i < NUM_LINES;i++)
- {
- /* Calculate the length (in seconds) of each all-pass line. */
- length = LATE_ALLPASS_LENGTHS[i] * multiplier;
-
- /* Calculate the delay offset for each all-pass line. */
- VecAp.Offset[i][1] = float2int(length * frequency);
-
- /* Calculate the length (in seconds) of each delay line. */
- length = LATE_LINE_LENGTHS[i] * multiplier;
-
- /* Calculate the delay offset for each delay line. */
- Offset[i][1] = float2int(length*frequency + 0.5f);
-
- /* Approximate the absorption that the vector all-pass would exhibit
- * given the current diffusion so we don't have to process a full T60
- * filter for each of its four lines.
- */
- length += lerp(LATE_ALLPASS_LENGTHS[i], late_allpass_avg, diffusion) * multiplier;
-
- /* Calculate the T60 damping coefficients for each line. */
- T60[i].calcCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime, lf0norm, hf0norm);
- }
-}
-
-
-/* Update the offsets for the main effect delay line. */
-void ReverbState::updateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay,
- const ALfloat density, const ALfloat decayTime, const ALfloat frequency)
-{
- const ALfloat multiplier{CalcDelayLengthMult(density)};
-
- /* Early reflection taps are decorrelated by means of an average room
- * reflection approximation described above the definition of the taps.
- * This approximation is linear and so the above density multiplier can
- * be applied to adjust the width of the taps. A single-band decay
- * coefficient is applied to simulate initial attenuation and absorption.
- *
- * Late reverb taps are based on the late line lengths to allow a zero-
- * delay path and offsets that would continue the propagation naturally
- * into the late lines.
- */
- for(ALsizei i{0};i < NUM_LINES;i++)
- {
- ALfloat length{earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier};
- mEarlyDelayTap[i][1] = float2int(length * frequency);
-
- length = EARLY_TAP_LENGTHS[i]*multiplier;
- mEarlyDelayCoeff[i][1] = CalcDecayCoeff(length, decayTime);
-
- length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS.front()) /
- float{LATE_LINE_LENGTHS_size} * multiplier;
- mLateDelayTap[i][1] = mLateFeedTap + float2int(length * frequency);
- }
-}
-
-/* Creates a transform matrix given a reverb vector. The vector pans the reverb
- * reflections toward the given direction, using its magnitude (up to 1) as a
- * focal strength. This function results in a B-Format transformation matrix
- * that spatially focuses the signal in the desired direction.
- */
-alu::Matrix GetTransformFromVector(const ALfloat *vec)
-{
- /* Normalize the panning vector according to the N3D scale, which has an
- * extra sqrt(3) term on the directional components. Converting from OpenAL
- * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however
- * that the reverb panning vectors use left-handed coordinates, unlike the
- * rest of OpenAL which use right-handed. This is fixed by negating Z,
- * which cancels out with the B-Format Z negation.
- */
- ALfloat norm[3];
- ALfloat mag{std::sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2])};
- if(mag > 1.0f)
- {
- norm[0] = vec[0] / mag * -al::MathDefs<float>::Sqrt3();
- norm[1] = vec[1] / mag * al::MathDefs<float>::Sqrt3();
- norm[2] = vec[2] / mag * al::MathDefs<float>::Sqrt3();
- mag = 1.0f;
- }
- else
- {
- /* If the magnitude is less than or equal to 1, just apply the sqrt(3)
- * term. There's no need to renormalize the magnitude since it would
- * just be reapplied in the matrix.
- */
- norm[0] = vec[0] * -al::MathDefs<float>::Sqrt3();
- norm[1] = vec[1] * al::MathDefs<float>::Sqrt3();
- norm[2] = vec[2] * al::MathDefs<float>::Sqrt3();
- }
-
- return alu::Matrix{
- 1.0f, 0.0f, 0.0f, 0.0f,
- norm[0], 1.0f-mag, 0.0f, 0.0f,
- norm[1], 0.0f, 1.0f-mag, 0.0f,
- norm[2], 0.0f, 0.0f, 1.0f-mag
- };
-}
-
-/* Update the early and late 3D panning gains. */
-void ReverbState::update3DPanning(const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan,
- const ALfloat earlyGain, const ALfloat lateGain, const EffectTarget &target)
-{
- /* Create matrices that transform a B-Format signal according to the
- * panning vectors.
- */
- const alu::Matrix earlymat{GetTransformFromVector(ReflectionsPan)};
- const alu::Matrix latemat{GetTransformFromVector(LateReverbPan)};
-
- mOutTarget = target.Main->Buffer;
- for(ALsizei i{0};i < NUM_LINES;i++)
- {
- const ALfloat coeffs[MAX_AMBI_CHANNELS]{earlymat[0][i], earlymat[1][i], earlymat[2][i],
- earlymat[3][i]};
- ComputePanGains(target.Main, coeffs, earlyGain, mEarly.PanGain[i]);
- }
- for(ALsizei i{0};i < NUM_LINES;i++)
- {
- const ALfloat coeffs[MAX_AMBI_CHANNELS]{latemat[0][i], latemat[1][i], latemat[2][i],
- latemat[3][i]};
- ComputePanGains(target.Main, coeffs, lateGain, mLate.PanGain[i]);
- }
-}
-
-void ReverbState::update(const ALCcontext *Context, const ALeffectslot *Slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *Device{Context->Device};
- const ALlistener &Listener = Context->Listener;
- const auto frequency = static_cast<ALfloat>(Device->Frequency);
-
- /* Calculate the master filters */
- ALfloat hf0norm{minf(props->Reverb.HFReference / frequency, 0.49f)};
- /* Restrict the filter gains from going below -60dB to keep the filter from
- * killing most of the signal.
- */
- ALfloat gainhf{maxf(props->Reverb.GainHF, 0.001f)};
- mFilter[0].Lp.setParams(BiquadType::HighShelf, gainhf, hf0norm,
- mFilter[0].Lp.rcpQFromSlope(gainhf, 1.0f));
- ALfloat lf0norm{minf(props->Reverb.LFReference / frequency, 0.49f)};
- ALfloat gainlf{maxf(props->Reverb.GainLF, 0.001f)};
- mFilter[0].Hp.setParams(BiquadType::LowShelf, gainlf, lf0norm,
- mFilter[0].Hp.rcpQFromSlope(gainlf, 1.0f));
- for(ALsizei i{1};i < NUM_LINES;i++)
- {
- mFilter[i].Lp.copyParamsFrom(mFilter[0].Lp);
- mFilter[i].Hp.copyParamsFrom(mFilter[0].Hp);
- }
-
- /* Update the main effect delay and associated taps. */
- updateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
- props->Reverb.Density, props->Reverb.DecayTime, frequency);
-
- /* Update the early lines. */
- mEarly.updateLines(props->Reverb.Density, props->Reverb.Diffusion, props->Reverb.DecayTime,
- frequency);
-
- /* Get the mixing matrix coefficients. */
- CalcMatrixCoeffs(props->Reverb.Diffusion, &mMixX, &mMixY);
-
- /* If the HF limit parameter is flagged, calculate an appropriate limit
- * based on the air absorption parameter.
- */
- ALfloat hfRatio{props->Reverb.DecayHFRatio};
- if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
- hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
- props->Reverb.DecayTime, Listener.Params.ReverbSpeedOfSound
- );
-
- /* Calculate the LF/HF decay times. */
- const ALfloat lfDecayTime{clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
- AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME)};
- const ALfloat hfDecayTime{clampf(props->Reverb.DecayTime * hfRatio,
- AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME)};
-
- /* Update the late lines. */
- mLate.updateLines(props->Reverb.Density, props->Reverb.Diffusion, lfDecayTime,
- props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm, frequency);
-
- /* Update early and late 3D panning. */
- const ALfloat gain{props->Reverb.Gain * Slot->Params.Gain * ReverbBoost};
- update3DPanning(props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan,
- props->Reverb.ReflectionsGain*gain, props->Reverb.LateReverbGain*gain, target);
-
- /* Calculate the max update size from the smallest relevant delay. */
- mMaxUpdate[1] = mini(BUFFERSIZE, mini(mEarly.Offset[0][1], mLate.Offset[0][1]));
-
- /* Determine if delay-line cross-fading is required. Density is essentially
- * a master control for the feedback delays, so changes the offsets of many
- * delay lines.
- */
- if(mParams.Density != props->Reverb.Density ||
- /* Diffusion and decay times influences the decay rate (gain) of the
- * late reverb T60 filter.
- */
- mParams.Diffusion != props->Reverb.Diffusion ||
- mParams.DecayTime != props->Reverb.DecayTime ||
- mParams.HFDecayTime != hfDecayTime ||
- mParams.LFDecayTime != lfDecayTime ||
- /* HF/LF References control the weighting used to calculate the density
- * gain.
- */
- mParams.HFReference != props->Reverb.HFReference ||
- mParams.LFReference != props->Reverb.LFReference)
- mFadeCount = 0;
- mParams.Density = props->Reverb.Density;
- mParams.Diffusion = props->Reverb.Diffusion;
- mParams.DecayTime = props->Reverb.DecayTime;
- mParams.HFDecayTime = hfDecayTime;
- mParams.LFDecayTime = lfDecayTime;
- mParams.HFReference = props->Reverb.HFReference;
- mParams.LFReference = props->Reverb.LFReference;
-}
-
-
-/**************************************
- * Effect Processing *
- **************************************/
-
-/* Applies a scattering matrix to the 4-line (vector) input. This is used
- * for both the below vector all-pass model and to perform modal feed-back
- * delay network (FDN) mixing.
- *
- * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
- * matrix with a single unitary rotational parameter:
- *
- * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
- * [ -a, d, c, -b ]
- * [ -b, -c, d, a ]
- * [ -c, b, -a, d ]
- *
- * The rotation is constructed from the effect's diffusion parameter,
- * yielding:
- *
- * 1 = x^2 + 3 y^2
- *
- * Where a, b, and c are the coefficient y with differing signs, and d is the
- * coefficient x. The final matrix is thus:
- *
- * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
- * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
- * [ y, -y, x, y ] x = cos(t)
- * [ -y, -y, -y, x ] y = sin(t) / n
- *
- * Any square orthogonal matrix with an order that is a power of two will
- * work (where ^T is transpose, ^-1 is inverse):
- *
- * M^T = M^-1
- *
- * Using that knowledge, finding an appropriate matrix can be accomplished
- * naively by searching all combinations of:
- *
- * M = D + S - S^T
- *
- * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
- * whose combination of signs are being iterated.
- */
-inline void VectorPartialScatter(ALfloat *RESTRICT out, const ALfloat *RESTRICT in,
- const ALfloat xCoeff, const ALfloat yCoeff)
-{
- out[0] = xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]);
- out[1] = xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]);
- out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
- out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
-}
-
-/* Utilizes the above, but reverses the input channels. */
-void VectorScatterRevDelayIn(const DelayLineI delay, ALint offset, const ALfloat xCoeff,
- const ALfloat yCoeff, const ALsizei base, const al::span<const FloatBufferLine,NUM_LINES> in,
- const ALsizei count)
-{
- ASSUME(base >= 0);
- ASSUME(count > 0);
-
- for(ALsizei i{0};i < count;)
- {
- offset &= delay.Mask;
- ALsizei td{mini(delay.Mask+1 - offset, count-i)};
- do {
- ALfloat f[NUM_LINES];
- for(ALsizei j{0};j < NUM_LINES;j++)
- f[NUM_LINES-1-j] = in[j][base+i];
- ++i;
-
- VectorPartialScatter(delay.Line[offset++], f, xCoeff, yCoeff);
- } while(--td);
- }
-}
-
-/* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
- * filter to the 4-line input.
- *
- * It works by vectorizing a regular all-pass filter and replacing the delay
- * element with a scattering matrix (like the one above) and a diagonal
- * matrix of delay elements.
- *
- * Two static specializations are used for transitional (cross-faded) delay
- * line processing and non-transitional processing.
- */
-void VecAllpass::processUnfaded(const al::span<FloatBufferLine,NUM_LINES> samples, ALsizei offset,
- const ALfloat xCoeff, const ALfloat yCoeff, const ALsizei todo)
-{
- const DelayLineI delay{Delay};
- const ALfloat feedCoeff{Coeff};
-
- ASSUME(todo > 0);
-
- ALsizei vap_offset[NUM_LINES];
- for(ALsizei j{0};j < NUM_LINES;j++)
- vap_offset[j] = offset - Offset[j][0];
- for(ALsizei i{0};i < todo;)
- {
- for(ALsizei j{0};j < NUM_LINES;j++)
- vap_offset[j] &= delay.Mask;
- offset &= delay.Mask;
-
- ALsizei maxoff{offset};
- for(ALsizei j{0};j < NUM_LINES;j++)
- maxoff = maxi(maxoff, vap_offset[j]);
- ALsizei td{mini(delay.Mask+1 - maxoff, todo - i)};
-
- do {
- ALfloat f[NUM_LINES];
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- const ALfloat input{samples[j][i]};
- const ALfloat out{delay.Line[vap_offset[j]++][j] - feedCoeff*input};
- f[j] = input + feedCoeff*out;
-
- samples[j][i] = out;
- }
- ++i;
-
- VectorPartialScatter(delay.Line[offset++], f, xCoeff, yCoeff);
- } while(--td);
- }
-}
-void VecAllpass::processFaded(const al::span<FloatBufferLine,NUM_LINES> samples, ALsizei offset,
- const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fade, const ALsizei todo)
-{
- const DelayLineI delay{Delay};
- const ALfloat feedCoeff{Coeff};
-
- ASSUME(todo > 0);
-
- fade *= 1.0f/FADE_SAMPLES;
- ALsizei vap_offset[NUM_LINES][2];
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- vap_offset[j][0] = offset - Offset[j][0];
- vap_offset[j][1] = offset - Offset[j][1];
- }
- for(ALsizei i{0};i < todo;)
- {
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- vap_offset[j][0] &= delay.Mask;
- vap_offset[j][1] &= delay.Mask;
- }
- offset &= delay.Mask;
-
- ALsizei maxoff{offset};
- for(ALsizei j{0};j < NUM_LINES;j++)
- maxoff = maxi(maxoff, maxi(vap_offset[j][0], vap_offset[j][1]));
- ALsizei td{mini(delay.Mask+1 - maxoff, todo - i)};
-
- do {
- fade += FadeStep;
- ALfloat f[NUM_LINES];
- for(ALsizei j{0};j < NUM_LINES;j++)
- f[j] = delay.Line[vap_offset[j][0]++][j]*(1.0f-fade) +
- delay.Line[vap_offset[j][1]++][j]*fade;
-
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- const ALfloat input{samples[j][i]};
- const ALfloat out{f[j] - feedCoeff*input};
- f[j] = input + feedCoeff*out;
-
- samples[j][i] = out;
- }
- ++i;
-
- VectorPartialScatter(delay.Line[offset++], f, xCoeff, yCoeff);
- } while(--td);
- }
-}
-
-/* This generates early reflections.
- *
- * This is done by obtaining the primary reflections (those arriving from the
- * same direction as the source) from the main delay line. These are
- * attenuated and all-pass filtered (based on the diffusion parameter).
- *
- * The early lines are then fed in reverse (according to the approximately
- * opposite spatial location of the A-Format lines) to create the secondary
- * reflections (those arriving from the opposite direction as the source).
- *
- * The early response is then completed by combining the primary reflections
- * with the delayed and attenuated output from the early lines.
- *
- * Finally, the early response is reversed, scattered (based on diffusion),
- * and fed into the late reverb section of the main delay line.
- *
- * Two static specializations are used for transitional (cross-faded) delay
- * line processing and non-transitional processing.
- */
-void EarlyReflection_Unfaded(ReverbState *State, const ALsizei offset, const ALsizei todo,
- const ALsizei base, const al::span<FloatBufferLine,NUM_LINES> out)
-{
- const al::span<FloatBufferLine,NUM_LINES> temps{State->mTempSamples};
- const DelayLineI early_delay{State->mEarly.Delay};
- const DelayLineI main_delay{State->mDelay};
- const ALfloat mixX{State->mMixX};
- const ALfloat mixY{State->mMixY};
-
- ASSUME(todo > 0);
-
- /* First, load decorrelated samples from the main delay line as the primary
- * reflections.
- */
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- ALsizei early_delay_tap{offset - State->mEarlyDelayTap[j][0]};
- const ALfloat coeff{State->mEarlyDelayCoeff[j][0]};
- for(ALsizei i{0};i < todo;)
- {
- early_delay_tap &= main_delay.Mask;
- ALsizei td{mini(main_delay.Mask+1 - early_delay_tap, todo - i)};
- do {
- temps[j][i++] = main_delay.Line[early_delay_tap++][j] * coeff;
- } while(--td);
- }
- }
-
- /* Apply a vector all-pass, to help color the initial reflections based on
- * the diffusion strength.
- */
- State->mEarly.VecAp.processUnfaded(temps, offset, mixX, mixY, todo);
-
- /* Apply a delay and bounce to generate secondary reflections, combine with
- * the primary reflections and write out the result for mixing.
- */
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- ALint feedb_tap{offset - State->mEarly.Offset[j][0]};
- const ALfloat feedb_coeff{State->mEarly.Coeff[j][0]};
-
- ASSUME(base >= 0);
- for(ALsizei i{0};i < todo;)
- {
- feedb_tap &= early_delay.Mask;
- ALsizei td{mini(early_delay.Mask+1 - feedb_tap, todo - i)};
- do {
- out[j][base+i] = temps[j][i] + early_delay.Line[feedb_tap++][j]*feedb_coeff;
- ++i;
- } while(--td);
- }
- }
- for(ALsizei j{0};j < NUM_LINES;j++)
- early_delay.write(offset, NUM_LINES-1-j, temps[j].data(), todo);
-
- /* Also write the result back to the main delay line for the late reverb
- * stage to pick up at the appropriate time, appplying a scatter and
- * bounce to improve the initial diffusion in the late reverb.
- */
- const ALsizei late_feed_tap{offset - State->mLateFeedTap};
- VectorScatterRevDelayIn(main_delay, late_feed_tap, mixX, mixY, base,
- {out.cbegin(), out.cend()}, todo);
-}
-void EarlyReflection_Faded(ReverbState *State, const ALsizei offset, const ALsizei todo,
- const ALfloat fade, const ALsizei base, const al::span<FloatBufferLine,NUM_LINES> out)
-{
- const al::span<FloatBufferLine,NUM_LINES> temps{State->mTempSamples};
- const DelayLineI early_delay{State->mEarly.Delay};
- const DelayLineI main_delay{State->mDelay};
- const ALfloat mixX{State->mMixX};
- const ALfloat mixY{State->mMixY};
-
- ASSUME(todo > 0);
-
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- ALsizei early_delay_tap0{offset - State->mEarlyDelayTap[j][0]};
- ALsizei early_delay_tap1{offset - State->mEarlyDelayTap[j][1]};
- const ALfloat oldCoeff{State->mEarlyDelayCoeff[j][0]};
- const ALfloat oldCoeffStep{-oldCoeff / FADE_SAMPLES};
- const ALfloat newCoeffStep{State->mEarlyDelayCoeff[j][1] / FADE_SAMPLES};
- ALfloat fadeCount{fade};
-
- for(ALsizei i{0};i < todo;)
- {
- early_delay_tap0 &= main_delay.Mask;
- early_delay_tap1 &= main_delay.Mask;
- ALsizei td{mini(main_delay.Mask+1 - maxi(early_delay_tap0, early_delay_tap1), todo-i)};
- do {
- fadeCount += 1.0f;
- const ALfloat fade0{oldCoeff + oldCoeffStep*fadeCount};
- const ALfloat fade1{newCoeffStep*fadeCount};
- temps[j][i++] =
- main_delay.Line[early_delay_tap0++][j]*fade0 +
- main_delay.Line[early_delay_tap1++][j]*fade1;
- } while(--td);
- }
- }
-
- State->mEarly.VecAp.processFaded(temps, offset, mixX, mixY, fade, todo);
-
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- ALint feedb_tap0{offset - State->mEarly.Offset[j][0]};
- ALint feedb_tap1{offset - State->mEarly.Offset[j][1]};
- const ALfloat feedb_oldCoeff{State->mEarly.Coeff[j][0]};
- const ALfloat feedb_oldCoeffStep{-feedb_oldCoeff / FADE_SAMPLES};
- const ALfloat feedb_newCoeffStep{State->mEarly.Coeff[j][1] / FADE_SAMPLES};
- ALfloat fadeCount{fade};
-
- ASSUME(base >= 0);
- for(ALsizei i{0};i < todo;)
- {
- feedb_tap0 &= early_delay.Mask;
- feedb_tap1 &= early_delay.Mask;
- ALsizei td{mini(early_delay.Mask+1 - maxi(feedb_tap0, feedb_tap1), todo - i)};
-
- do {
- fadeCount += 1.0f;
- const ALfloat fade0{feedb_oldCoeff + feedb_oldCoeffStep*fadeCount};
- const ALfloat fade1{feedb_newCoeffStep*fadeCount};
- out[j][base+i] = temps[j][i] +
- early_delay.Line[feedb_tap0++][j]*fade0 +
- early_delay.Line[feedb_tap1++][j]*fade1;
- ++i;
- } while(--td);
- }
- }
- for(ALsizei j{0};j < NUM_LINES;j++)
- early_delay.write(offset, NUM_LINES-1-j, temps[j].data(), todo);
-
- const ALsizei late_feed_tap{offset - State->mLateFeedTap};
- VectorScatterRevDelayIn(main_delay, late_feed_tap, mixX, mixY, base,
- {out.cbegin(), out.cend()}, todo);
-}
-
-/* This generates the reverb tail using a modified feed-back delay network
- * (FDN).
- *
- * Results from the early reflections are mixed with the output from the late
- * delay lines.
- *
- * The late response is then completed by T60 and all-pass filtering the mix.
- *
- * Finally, the lines are reversed (so they feed their opposite directions)
- * and scattered with the FDN matrix before re-feeding the delay lines.
- *
- * Two variations are made, one for for transitional (cross-faded) delay line
- * processing and one for non-transitional processing.
- */
-void LateReverb_Unfaded(ReverbState *State, const ALsizei offset, const ALsizei todo,
- const ALsizei base, const al::span<FloatBufferLine,NUM_LINES> out)
-{
- const al::span<FloatBufferLine,NUM_LINES> temps{State->mTempSamples};
- const DelayLineI late_delay{State->mLate.Delay};
- const DelayLineI main_delay{State->mDelay};
- const ALfloat mixX{State->mMixX};
- const ALfloat mixY{State->mMixY};
-
- ASSUME(todo > 0);
-
- /* First, load decorrelated samples from the main and feedback delay lines.
- * Filter the signal to apply its frequency-dependent decay.
- */
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- ALsizei late_delay_tap{offset - State->mLateDelayTap[j][0]};
- ALsizei late_feedb_tap{offset - State->mLate.Offset[j][0]};
- const ALfloat midGain{State->mLate.T60[j].MidGain[0]};
- const ALfloat densityGain{State->mLate.DensityGain[0] * midGain};
- for(ALsizei i{0};i < todo;)
- {
- late_delay_tap &= main_delay.Mask;
- late_feedb_tap &= late_delay.Mask;
- ALsizei td{mini(
- mini(main_delay.Mask+1 - late_delay_tap, late_delay.Mask+1 - late_feedb_tap),
- todo - i)};
- do {
- temps[j][i++] =
- main_delay.Line[late_delay_tap++][j]*densityGain +
- late_delay.Line[late_feedb_tap++][j]*midGain;
- } while(--td);
- }
- State->mLate.T60[j].process(temps[j].data(), todo);
- }
-
- /* Apply a vector all-pass to improve micro-surface diffusion, and write
- * out the results for mixing.
- */
- State->mLate.VecAp.processUnfaded(temps, offset, mixX, mixY, todo);
-
- for(ALsizei j{0};j < NUM_LINES;j++)
- std::copy_n(temps[j].begin(), todo, out[j].begin()+base);
-
- /* Finally, scatter and bounce the results to refeed the feedback buffer. */
- VectorScatterRevDelayIn(late_delay, offset, mixX, mixY, base,
- {out.cbegin(), out.cend()}, todo);
-}
-void LateReverb_Faded(ReverbState *State, const ALsizei offset, const ALsizei todo,
- const ALfloat fade, const ALsizei base, const al::span<FloatBufferLine,NUM_LINES> out)
-{
- const al::span<FloatBufferLine,NUM_LINES> temps{State->mTempSamples};
- const DelayLineI late_delay{State->mLate.Delay};
- const DelayLineI main_delay{State->mDelay};
- const ALfloat mixX{State->mMixX};
- const ALfloat mixY{State->mMixY};
-
- ASSUME(todo > 0);
-
- for(ALsizei j{0};j < NUM_LINES;j++)
- {
- const ALfloat oldMidGain{State->mLate.T60[j].MidGain[0]};
- const ALfloat midGain{State->mLate.T60[j].MidGain[1]};
- const ALfloat oldMidStep{-oldMidGain / FADE_SAMPLES};
- const ALfloat midStep{midGain / FADE_SAMPLES};
- const ALfloat oldDensityGain{State->mLate.DensityGain[0] * oldMidGain};
- const ALfloat densityGain{State->mLate.DensityGain[1] * midGain};
- const ALfloat oldDensityStep{-oldDensityGain / FADE_SAMPLES};
- const ALfloat densityStep{densityGain / FADE_SAMPLES};
- ALsizei late_delay_tap0{offset - State->mLateDelayTap[j][0]};
- ALsizei late_delay_tap1{offset - State->mLateDelayTap[j][1]};
- ALsizei late_feedb_tap0{offset - State->mLate.Offset[j][0]};
- ALsizei late_feedb_tap1{offset - State->mLate.Offset[j][1]};
- ALfloat fadeCount{fade};
-
- for(ALsizei i{0};i < todo;)
- {
- late_delay_tap0 &= main_delay.Mask;
- late_delay_tap1 &= main_delay.Mask;
- late_feedb_tap0 &= late_delay.Mask;
- late_feedb_tap1 &= late_delay.Mask;
- ALsizei td{mini(
- mini(main_delay.Mask+1 - maxi(late_delay_tap0, late_delay_tap1),
- late_delay.Mask+1 - maxi(late_feedb_tap0, late_feedb_tap1)),
- todo - i)};
- do {
- fadeCount += 1.0f;
- const ALfloat fade0{oldDensityGain + oldDensityStep*fadeCount};
- const ALfloat fade1{densityStep*fadeCount};
- const ALfloat gfade0{oldMidGain + oldMidStep*fadeCount};
- const ALfloat gfade1{midStep*fadeCount};
- temps[j][i++] =
- main_delay.Line[late_delay_tap0++][j]*fade0 +
- main_delay.Line[late_delay_tap1++][j]*fade1 +
- late_delay.Line[late_feedb_tap0++][j]*gfade0 +
- late_delay.Line[late_feedb_tap1++][j]*gfade1;
- } while(--td);
- }
- State->mLate.T60[j].process(temps[j].data(), todo);
- }
-
- State->mLate.VecAp.processFaded(temps, offset, mixX, mixY, fade, todo);
-
- for(ALsizei j{0};j < NUM_LINES;j++)
- std::copy_n(temps[j].begin(), todo, out[j].begin()+base);
-
- VectorScatterRevDelayIn(late_delay, offset, mixX, mixY, base,
- {out.cbegin(), out.cend()}, todo);
-}
-
-void ReverbState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
-{
- ALsizei fadeCount{mFadeCount};
-
- ASSUME(samplesToDo > 0);
-
- /* Convert B-Format to A-Format for processing. */
- const al::span<FloatBufferLine,NUM_LINES> afmt{mTempSamples};
- for(ALsizei c{0};c < NUM_LINES;c++)
- {
- std::fill_n(afmt[c].begin(), samplesToDo, 0.0f);
- MixRowSamples(afmt[c], B2A[c], {samplesIn, samplesIn+numInput}, 0, samplesToDo);
-
- /* Band-pass the incoming samples. */
- mFilter[c].Lp.process(afmt[c].data(), afmt[c].data(), samplesToDo);
- mFilter[c].Hp.process(afmt[c].data(), afmt[c].data(), samplesToDo);
- }
-
- /* Process reverb for these samples. */
- for(ALsizei base{0};base < samplesToDo;)
- {
- ALsizei todo{samplesToDo - base};
- /* If cross-fading, don't do more samples than there are to fade. */
- if(FADE_SAMPLES-fadeCount > 0)
- {
- todo = mini(todo, FADE_SAMPLES-fadeCount);
- todo = mini(todo, mMaxUpdate[0]);
- }
- todo = mini(todo, mMaxUpdate[1]);
- ASSUME(todo > 0 && todo <= BUFFERSIZE);
-
- const ALsizei offset{mOffset + base};
- ASSUME(offset >= 0);
-
- /* Feed the initial delay line. */
- for(ALsizei c{0};c < NUM_LINES;c++)
- mDelay.write(offset, c, afmt[c].data()+base, todo);
-
- /* Process the samples for reverb. */
- if(UNLIKELY(fadeCount < FADE_SAMPLES))
- {
- auto fade = static_cast<ALfloat>(fadeCount);
-
- /* Generate early reflections and late reverb. */
- EarlyReflection_Faded(this, offset, todo, fade, base, mEarlyBuffer);
-
- LateReverb_Faded(this, offset, todo, fade, base, mLateBuffer);
-
- /* Step fading forward. */
- fadeCount += todo;
- if(fadeCount >= FADE_SAMPLES)
- {
- /* Update the cross-fading delay line taps. */
- fadeCount = FADE_SAMPLES;
- for(ALsizei c{0};c < NUM_LINES;c++)
- {
- mEarlyDelayTap[c][0] = mEarlyDelayTap[c][1];
- mEarlyDelayCoeff[c][0] = mEarlyDelayCoeff[c][1];
- mEarly.VecAp.Offset[c][0] = mEarly.VecAp.Offset[c][1];
- mEarly.Offset[c][0] = mEarly.Offset[c][1];
- mEarly.Coeff[c][0] = mEarly.Coeff[c][1];
- mLateDelayTap[c][0] = mLateDelayTap[c][1];
- mLate.VecAp.Offset[c][0] = mLate.VecAp.Offset[c][1];
- mLate.Offset[c][0] = mLate.Offset[c][1];
- mLate.T60[c].MidGain[0] = mLate.T60[c].MidGain[1];
- }
- mLate.DensityGain[0] = mLate.DensityGain[1];
- mMaxUpdate[0] = mMaxUpdate[1];
- }
- }
- else
- {
- /* Generate early reflections and late reverb. */
- EarlyReflection_Unfaded(this, offset, todo, base, mEarlyBuffer);
-
- LateReverb_Unfaded(this, offset, todo, base, mLateBuffer);
- }
-
- base += todo;
- }
- mOffset = (mOffset+samplesToDo) & 0x3fffffff;
- mFadeCount = fadeCount;
-
- /* Finally, mix early reflections and late reverb. */
- (this->*mMixOut)(samplesOut, samplesToDo);
-}
-
-
-void EAXReverb_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_EAXREVERB_DECAY_HFLIMIT:
- if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
- props->Reverb.DecayHFLimit = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
- param);
- }
-}
-void EAXReverb_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ EAXReverb_setParami(props, context, param, vals[0]); }
-void EAXReverb_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_EAXREVERB_DENSITY:
- if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
- props->Reverb.Density = val;
- break;
-
- case AL_EAXREVERB_DIFFUSION:
- if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
- props->Reverb.Diffusion = val;
- break;
-
- case AL_EAXREVERB_GAIN:
- if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
- props->Reverb.Gain = val;
- break;
-
- case AL_EAXREVERB_GAINHF:
- if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
- props->Reverb.GainHF = val;
- break;
-
- case AL_EAXREVERB_GAINLF:
- if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
- props->Reverb.GainLF = val;
- break;
-
- case AL_EAXREVERB_DECAY_TIME:
- if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
- props->Reverb.DecayTime = val;
- break;
-
- case AL_EAXREVERB_DECAY_HFRATIO:
- if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
- props->Reverb.DecayHFRatio = val;
- break;
-
- case AL_EAXREVERB_DECAY_LFRATIO:
- if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
- props->Reverb.DecayLFRatio = val;
- break;
-
- case AL_EAXREVERB_REFLECTIONS_GAIN:
- if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
- props->Reverb.ReflectionsGain = val;
- break;
-
- case AL_EAXREVERB_REFLECTIONS_DELAY:
- if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
- props->Reverb.ReflectionsDelay = val;
- break;
-
- case AL_EAXREVERB_LATE_REVERB_GAIN:
- if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
- props->Reverb.LateReverbGain = val;
- break;
-
- case AL_EAXREVERB_LATE_REVERB_DELAY:
- if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
- props->Reverb.LateReverbDelay = val;
- break;
-
- case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
- if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
- props->Reverb.AirAbsorptionGainHF = val;
- break;
-
- case AL_EAXREVERB_ECHO_TIME:
- if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
- props->Reverb.EchoTime = val;
- break;
-
- case AL_EAXREVERB_ECHO_DEPTH:
- if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
- props->Reverb.EchoDepth = val;
- break;
-
- case AL_EAXREVERB_MODULATION_TIME:
- if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
- props->Reverb.ModulationTime = val;
- break;
-
- case AL_EAXREVERB_MODULATION_DEPTH:
- if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
- props->Reverb.ModulationDepth = val;
- break;
-
- case AL_EAXREVERB_HFREFERENCE:
- if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
- props->Reverb.HFReference = val;
- break;
-
- case AL_EAXREVERB_LFREFERENCE:
- if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
- props->Reverb.LFReference = val;
- break;
-
- case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
- if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
- props->Reverb.RoomRolloffFactor = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
- param);
- }
-}
-void EAXReverb_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- switch(param)
- {
- case AL_EAXREVERB_REFLECTIONS_PAN:
- if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2])))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
- props->Reverb.ReflectionsPan[0] = vals[0];
- props->Reverb.ReflectionsPan[1] = vals[1];
- props->Reverb.ReflectionsPan[2] = vals[2];
- break;
- case AL_EAXREVERB_LATE_REVERB_PAN:
- if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2])))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
- props->Reverb.LateReverbPan[0] = vals[0];
- props->Reverb.LateReverbPan[1] = vals[1];
- props->Reverb.LateReverbPan[2] = vals[2];
- break;
-
- default:
- EAXReverb_setParamf(props, context, param, vals[0]);
- break;
- }
-}
-
-void EAXReverb_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_EAXREVERB_DECAY_HFLIMIT:
- *val = props->Reverb.DecayHFLimit;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
- param);
- }
-}
-void EAXReverb_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ EAXReverb_getParami(props, context, param, vals); }
-void EAXReverb_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_EAXREVERB_DENSITY:
- *val = props->Reverb.Density;
- break;
-
- case AL_EAXREVERB_DIFFUSION:
- *val = props->Reverb.Diffusion;
- break;
-
- case AL_EAXREVERB_GAIN:
- *val = props->Reverb.Gain;
- break;
-
- case AL_EAXREVERB_GAINHF:
- *val = props->Reverb.GainHF;
- break;
-
- case AL_EAXREVERB_GAINLF:
- *val = props->Reverb.GainLF;
- break;
-
- case AL_EAXREVERB_DECAY_TIME:
- *val = props->Reverb.DecayTime;
- break;
-
- case AL_EAXREVERB_DECAY_HFRATIO:
- *val = props->Reverb.DecayHFRatio;
- break;
-
- case AL_EAXREVERB_DECAY_LFRATIO:
- *val = props->Reverb.DecayLFRatio;
- break;
-
- case AL_EAXREVERB_REFLECTIONS_GAIN:
- *val = props->Reverb.ReflectionsGain;
- break;
-
- case AL_EAXREVERB_REFLECTIONS_DELAY:
- *val = props->Reverb.ReflectionsDelay;
- break;
-
- case AL_EAXREVERB_LATE_REVERB_GAIN:
- *val = props->Reverb.LateReverbGain;
- break;
-
- case AL_EAXREVERB_LATE_REVERB_DELAY:
- *val = props->Reverb.LateReverbDelay;
- break;
-
- case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
- *val = props->Reverb.AirAbsorptionGainHF;
- break;
-
- case AL_EAXREVERB_ECHO_TIME:
- *val = props->Reverb.EchoTime;
- break;
-
- case AL_EAXREVERB_ECHO_DEPTH:
- *val = props->Reverb.EchoDepth;
- break;
-
- case AL_EAXREVERB_MODULATION_TIME:
- *val = props->Reverb.ModulationTime;
- break;
-
- case AL_EAXREVERB_MODULATION_DEPTH:
- *val = props->Reverb.ModulationDepth;
- break;
-
- case AL_EAXREVERB_HFREFERENCE:
- *val = props->Reverb.HFReference;
- break;
-
- case AL_EAXREVERB_LFREFERENCE:
- *val = props->Reverb.LFReference;
- break;
-
- case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
- *val = props->Reverb.RoomRolloffFactor;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
- param);
- }
-}
-void EAXReverb_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{
- switch(param)
- {
- case AL_EAXREVERB_REFLECTIONS_PAN:
- vals[0] = props->Reverb.ReflectionsPan[0];
- vals[1] = props->Reverb.ReflectionsPan[1];
- vals[2] = props->Reverb.ReflectionsPan[2];
- break;
- case AL_EAXREVERB_LATE_REVERB_PAN:
- vals[0] = props->Reverb.LateReverbPan[0];
- vals[1] = props->Reverb.LateReverbPan[1];
- vals[2] = props->Reverb.LateReverbPan[2];
- break;
-
- default:
- EAXReverb_getParamf(props, context, param, vals);
- break;
- }
-}
-
-DEFINE_ALEFFECT_VTABLE(EAXReverb);
-
-
-struct ReverbStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new ReverbState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &EAXReverb_vtable; }
-};
-
-EffectProps ReverbStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Reverb.Density = AL_EAXREVERB_DEFAULT_DENSITY;
- props.Reverb.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION;
- props.Reverb.Gain = AL_EAXREVERB_DEFAULT_GAIN;
- props.Reverb.GainHF = AL_EAXREVERB_DEFAULT_GAINHF;
- props.Reverb.GainLF = AL_EAXREVERB_DEFAULT_GAINLF;
- props.Reverb.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME;
- props.Reverb.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO;
- props.Reverb.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO;
- props.Reverb.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN;
- props.Reverb.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY;
- props.Reverb.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
- props.Reverb.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
- props.Reverb.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
- props.Reverb.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN;
- props.Reverb.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY;
- props.Reverb.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
- props.Reverb.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
- props.Reverb.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
- props.Reverb.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME;
- props.Reverb.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH;
- props.Reverb.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME;
- props.Reverb.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH;
- props.Reverb.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF;
- props.Reverb.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE;
- props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE;
- props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
- props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT;
- return props;
-}
-
-
-void StdReverb_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_REVERB_DECAY_HFLIMIT:
- if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
- props->Reverb.DecayHFLimit = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
- }
-}
-void StdReverb_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
-{ StdReverb_setParami(props, context, param, vals[0]); }
-void StdReverb_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_REVERB_DENSITY:
- if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
- props->Reverb.Density = val;
- break;
-
- case AL_REVERB_DIFFUSION:
- if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
- props->Reverb.Diffusion = val;
- break;
-
- case AL_REVERB_GAIN:
- if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
- props->Reverb.Gain = val;
- break;
-
- case AL_REVERB_GAINHF:
- if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
- props->Reverb.GainHF = val;
- break;
-
- case AL_REVERB_DECAY_TIME:
- if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
- props->Reverb.DecayTime = val;
- break;
-
- case AL_REVERB_DECAY_HFRATIO:
- if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
- props->Reverb.DecayHFRatio = val;
- break;
-
- case AL_REVERB_REFLECTIONS_GAIN:
- if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
- props->Reverb.ReflectionsGain = val;
- break;
-
- case AL_REVERB_REFLECTIONS_DELAY:
- if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
- props->Reverb.ReflectionsDelay = val;
- break;
-
- case AL_REVERB_LATE_REVERB_GAIN:
- if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
- props->Reverb.LateReverbGain = val;
- break;
-
- case AL_REVERB_LATE_REVERB_DELAY:
- if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
- props->Reverb.LateReverbDelay = val;
- break;
-
- case AL_REVERB_AIR_ABSORPTION_GAINHF:
- if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
- props->Reverb.AirAbsorptionGainHF = val;
- break;
-
- case AL_REVERB_ROOM_ROLLOFF_FACTOR:
- if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
- props->Reverb.RoomRolloffFactor = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
- }
-}
-void StdReverb_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ StdReverb_setParamf(props, context, param, vals[0]); }
-
-void StdReverb_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
-{
- switch(param)
- {
- case AL_REVERB_DECAY_HFLIMIT:
- *val = props->Reverb.DecayHFLimit;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
- }
-}
-void StdReverb_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
-{ StdReverb_getParami(props, context, param, vals); }
-void StdReverb_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_REVERB_DENSITY:
- *val = props->Reverb.Density;
- break;
-
- case AL_REVERB_DIFFUSION:
- *val = props->Reverb.Diffusion;
- break;
-
- case AL_REVERB_GAIN:
- *val = props->Reverb.Gain;
- break;
-
- case AL_REVERB_GAINHF:
- *val = props->Reverb.GainHF;
- break;
-
- case AL_REVERB_DECAY_TIME:
- *val = props->Reverb.DecayTime;
- break;
-
- case AL_REVERB_DECAY_HFRATIO:
- *val = props->Reverb.DecayHFRatio;
- break;
-
- case AL_REVERB_REFLECTIONS_GAIN:
- *val = props->Reverb.ReflectionsGain;
- break;
-
- case AL_REVERB_REFLECTIONS_DELAY:
- *val = props->Reverb.ReflectionsDelay;
- break;
-
- case AL_REVERB_LATE_REVERB_GAIN:
- *val = props->Reverb.LateReverbGain;
- break;
-
- case AL_REVERB_LATE_REVERB_DELAY:
- *val = props->Reverb.LateReverbDelay;
- break;
-
- case AL_REVERB_AIR_ABSORPTION_GAINHF:
- *val = props->Reverb.AirAbsorptionGainHF;
- break;
-
- case AL_REVERB_ROOM_ROLLOFF_FACTOR:
- *val = props->Reverb.RoomRolloffFactor;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
- }
-}
-void StdReverb_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ StdReverb_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(StdReverb);
-
-
-struct StdReverbStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new ReverbState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &StdReverb_vtable; }
-};
-
-EffectProps StdReverbStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Reverb.Density = AL_REVERB_DEFAULT_DENSITY;
- props.Reverb.Diffusion = AL_REVERB_DEFAULT_DIFFUSION;
- props.Reverb.Gain = AL_REVERB_DEFAULT_GAIN;
- props.Reverb.GainHF = AL_REVERB_DEFAULT_GAINHF;
- props.Reverb.GainLF = 1.0f;
- props.Reverb.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME;
- props.Reverb.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO;
- props.Reverb.DecayLFRatio = 1.0f;
- props.Reverb.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN;
- props.Reverb.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY;
- props.Reverb.ReflectionsPan[0] = 0.0f;
- props.Reverb.ReflectionsPan[1] = 0.0f;
- props.Reverb.ReflectionsPan[2] = 0.0f;
- props.Reverb.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN;
- props.Reverb.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY;
- props.Reverb.LateReverbPan[0] = 0.0f;
- props.Reverb.LateReverbPan[1] = 0.0f;
- props.Reverb.LateReverbPan[2] = 0.0f;
- props.Reverb.EchoTime = 0.25f;
- props.Reverb.EchoDepth = 0.0f;
- props.Reverb.ModulationTime = 0.25f;
- props.Reverb.ModulationDepth = 0.0f;
- props.Reverb.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF;
- props.Reverb.HFReference = 5000.0f;
- props.Reverb.LFReference = 250.0f;
- props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
- props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *ReverbStateFactory_getFactory()
-{
- static ReverbStateFactory ReverbFactory{};
- return &ReverbFactory;
-}
-
-EffectStateFactory *StdReverbStateFactory_getFactory()
-{
- static StdReverbStateFactory ReverbFactory{};
- return &ReverbFactory;
-}
diff --git a/Alc/effects/vmorpher.cpp b/Alc/effects/vmorpher.cpp
deleted file mode 100644
index eebba3f1..00000000
--- a/Alc/effects/vmorpher.cpp
+++ /dev/null
@@ -1,430 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2019 by Anis A. Hireche
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-#include <algorithm>
-#include <functional>
-
-#include "alcmain.h"
-#include "alcontext.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-
-namespace {
-
-#define MAX_UPDATE_SAMPLES 128
-#define NUM_FORMANTS 4
-#define NUM_FILTERS 2
-#define Q_FACTOR 5.0f
-
-#define VOWEL_A_INDEX 0
-#define VOWEL_B_INDEX 1
-
-#define WAVEFORM_FRACBITS 24
-#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
-#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
-
-inline ALfloat Sin(ALsizei index)
-{
- constexpr ALfloat scale{al::MathDefs<float>::Tau() / ALfloat{WAVEFORM_FRACONE}};
- return std::sin(static_cast<ALfloat>(index) * scale)*0.5f + 0.5f;
-}
-
-inline ALfloat Saw(ALsizei index)
-{
- return static_cast<ALfloat>(index) / ALfloat{WAVEFORM_FRACONE};
-}
-
-inline ALfloat Triangle(ALsizei index)
-{
- return std::fabs(static_cast<ALfloat>(index)*(2.0f/WAVEFORM_FRACONE) - 1.0f);
-}
-
-inline ALfloat Half(ALsizei)
-{
- return 0.5f;
-}
-
-template<ALfloat func(ALsizei)>
-void Oscillate(ALfloat *RESTRICT dst, ALsizei index, const ALsizei step, ALsizei todo)
-{
- for(ALsizei i{0};i < todo;i++)
- {
- index += step;
- index &= WAVEFORM_FRACMASK;
- dst[i] = func(index);
- }
-}
-
-struct FormantFilter
-{
- ALfloat f0norm{0.0f};
- ALfloat fGain{1.0f};
- ALfloat s1{0.0f};
- ALfloat s2{0.0f};
-
- FormantFilter() = default;
- FormantFilter(ALfloat f0norm_, ALfloat gain) : f0norm{f0norm_}, fGain{gain} { }
-
- inline void process(const ALfloat* samplesIn, ALfloat* samplesOut, const ALsizei numInput)
- {
- /* A state variable filter from a topology-preserving transform.
- * Based on a talk given by Ivan Cohen: https://www.youtube.com/watch?v=esjHXGPyrhg
- */
- const ALfloat g = std::tan(al::MathDefs<float>::Pi() * f0norm);
- const ALfloat h = 1.0f / (1 + (g / Q_FACTOR) + (g * g));
-
- for (ALsizei i{0};i < numInput;i++)
- {
- const ALfloat H = h * (samplesIn[i] - (1.0f / Q_FACTOR + g) * s1 - s2);
- const ALfloat B = g * H + s1;
- const ALfloat L = g * B + s2;
-
- s1 = g * H + B;
- s2 = g * B + L;
-
- // Apply peak and accumulate samples.
- samplesOut[i] += B * fGain;
- }
- }
-
- inline void clear()
- {
- s1 = 0.0f;
- s2 = 0.0f;
- }
-};
-
-
-struct VmorpherState final : public EffectState {
- struct {
- /* Effect parameters */
- FormantFilter Formants[NUM_FILTERS][NUM_FORMANTS];
-
- /* Effect gains for each channel */
- ALfloat CurrentGains[MAX_OUTPUT_CHANNELS]{};
- ALfloat TargetGains[MAX_OUTPUT_CHANNELS]{};
- } mChans[MAX_AMBI_CHANNELS];
-
- void (*mGetSamples)(ALfloat* RESTRICT, ALsizei, const ALsizei, ALsizei) {};
-
- ALsizei mIndex{0};
- ALsizei mStep{1};
-
- /* Effects buffers */
- ALfloat mSampleBufferA[MAX_UPDATE_SAMPLES]{};
- ALfloat mSampleBufferB[MAX_UPDATE_SAMPLES]{};
-
- ALboolean deviceUpdate(const ALCdevice *device) override;
- void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
- void process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut) override;
-
- static std::array<FormantFilter,4> getFiltersByPhoneme(ALenum phoneme, ALfloat frequency, ALfloat pitch);
-
- DEF_NEWDEL(VmorpherState)
-};
-
-std::array<FormantFilter,4> VmorpherState::getFiltersByPhoneme(ALenum phoneme, ALfloat frequency, ALfloat pitch)
-{
- /* Using soprano formant set of values to
- * better match mid-range frequency space.
- *
- * See: https://www.classes.cs.uchicago.edu/archive/1999/spring/CS295/Computing_Resources/Csound/CsManual3.48b1.HTML/Appendices/table3.html
- */
- switch(phoneme)
- {
- case AL_VOCAL_MORPHER_PHONEME_A:
- return {{
- {( 800 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
- {(1150 * pitch) / frequency, 0.501187f}, /* std::pow(10.0f, -6 / 20.0f); */
- {(2900 * pitch) / frequency, 0.025118f}, /* std::pow(10.0f, -32 / 20.0f); */
- {(3900 * pitch) / frequency, 0.100000f} /* std::pow(10.0f, -20 / 20.0f); */
- }};
- case AL_VOCAL_MORPHER_PHONEME_E:
- return {{
- {( 350 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
- {(2000 * pitch) / frequency, 0.100000f}, /* std::pow(10.0f, -20 / 20.0f); */
- {(2800 * pitch) / frequency, 0.177827f}, /* std::pow(10.0f, -15 / 20.0f); */
- {(3600 * pitch) / frequency, 0.009999f} /* std::pow(10.0f, -40 / 20.0f); */
- }};
- case AL_VOCAL_MORPHER_PHONEME_I:
- return {{
- {( 270 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
- {(2140 * pitch) / frequency, 0.251188f}, /* std::pow(10.0f, -12 / 20.0f); */
- {(2950 * pitch) / frequency, 0.050118f}, /* std::pow(10.0f, -26 / 20.0f); */
- {(3900 * pitch) / frequency, 0.050118f} /* std::pow(10.0f, -26 / 20.0f); */
- }};
- case AL_VOCAL_MORPHER_PHONEME_O:
- return {{
- {( 450 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
- {( 800 * pitch) / frequency, 0.281838f}, /* std::pow(10.0f, -11 / 20.0f); */
- {(2830 * pitch) / frequency, 0.079432f}, /* std::pow(10.0f, -22 / 20.0f); */
- {(3800 * pitch) / frequency, 0.079432f} /* std::pow(10.0f, -22 / 20.0f); */
- }};
- case AL_VOCAL_MORPHER_PHONEME_U:
- return {{
- {( 325 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
- {( 700 * pitch) / frequency, 0.158489f}, /* std::pow(10.0f, -16 / 20.0f); */
- {(2700 * pitch) / frequency, 0.017782f}, /* std::pow(10.0f, -35 / 20.0f); */
- {(3800 * pitch) / frequency, 0.009999f} /* std::pow(10.0f, -40 / 20.0f); */
- }};
- }
- return {};
-}
-
-
-ALboolean VmorpherState::deviceUpdate(const ALCdevice* /*device*/)
-{
- for(auto &e : mChans)
- {
- std::for_each(std::begin(e.Formants[VOWEL_A_INDEX]), std::end(e.Formants[VOWEL_A_INDEX]),
- std::mem_fn(&FormantFilter::clear));
- std::for_each(std::begin(e.Formants[VOWEL_B_INDEX]), std::end(e.Formants[VOWEL_B_INDEX]),
- std::mem_fn(&FormantFilter::clear));
- std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
- }
-
- return AL_TRUE;
-}
-
-void VmorpherState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
-{
- const ALCdevice *device{context->Device};
- const ALfloat frequency{static_cast<ALfloat>(device->Frequency)};
- const ALfloat step{props->Vmorpher.Rate / static_cast<ALfloat>(device->Frequency)};
- mStep = fastf2i(clampf(step*WAVEFORM_FRACONE, 0.0f, ALfloat{WAVEFORM_FRACONE-1}));
-
- if(mStep == 0)
- mGetSamples = Oscillate<Half>;
- else if(props->Vmorpher.Waveform == AL_VOCAL_MORPHER_WAVEFORM_SINUSOID)
- mGetSamples = Oscillate<Sin>;
- else if(props->Vmorpher.Waveform == AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH)
- mGetSamples = Oscillate<Saw>;
- else /*if(props->Vmorpher.Waveform == AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE)*/
- mGetSamples = Oscillate<Triangle>;
-
- const ALfloat pitchA{fastf2i(std::pow(2.0f, props->Vmorpher.PhonemeACoarseTuning*100.0f / 2400.0f)*FRACTIONONE) * (1.0f/FRACTIONONE)};
- const ALfloat pitchB{fastf2i(std::pow(2.0f, props->Vmorpher.PhonemeBCoarseTuning*100.0f / 2400.0f)*FRACTIONONE) * (1.0f/FRACTIONONE)};
-
- auto vowelA = getFiltersByPhoneme(props->Vmorpher.PhonemeA, frequency, pitchA);
- auto vowelB = getFiltersByPhoneme(props->Vmorpher.PhonemeB, frequency, pitchB);
-
- /* Copy the filter coefficients to the input channels. */
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
- {
- std::copy(vowelA.begin(), vowelA.end(), std::begin(mChans[i].Formants[VOWEL_A_INDEX]));
- std::copy(vowelB.begin(), vowelB.end(), std::begin(mChans[i].Formants[VOWEL_B_INDEX]));
- }
-
- mOutTarget = target.Main->Buffer;
- for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
- {
- auto coeffs = GetAmbiIdentityRow(i);
- ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mChans[i].TargetGains);
- }
-}
-
-void VmorpherState::process(const ALsizei samplesToDo, const FloatBufferLine *RESTRICT samplesIn, const ALsizei numInput, const al::span<FloatBufferLine> samplesOut)
-{
- /* Following the EFX specification for a conformant implementation which describes
- * the effect as a pair of 4-band formant filters blended together using an LFO.
- */
- for(ALsizei base{0};base < samplesToDo;)
- {
- alignas(16) ALfloat lfo[MAX_UPDATE_SAMPLES];
- const ALsizei td = mini(MAX_UPDATE_SAMPLES, samplesToDo-base);
-
- mGetSamples(lfo, mIndex, mStep, td);
- mIndex += (mStep * td) & WAVEFORM_FRACMASK;
- mIndex &= WAVEFORM_FRACMASK;
-
- ASSUME(numInput > 0);
- for(ALsizei c{0};c < numInput;c++)
- {
- for (ALsizei i{0};i < td;i++)
- {
- mSampleBufferA[i] = 0.0f;
- mSampleBufferB[i] = 0.0f;
- }
-
- auto& vowelA = mChans[c].Formants[VOWEL_A_INDEX];
- auto& vowelB = mChans[c].Formants[VOWEL_B_INDEX];
-
- /* Process first vowel. */
- vowelA[0].process(&samplesIn[c][base], mSampleBufferA, td);
- vowelA[1].process(&samplesIn[c][base], mSampleBufferA, td);
- vowelA[2].process(&samplesIn[c][base], mSampleBufferA, td);
- vowelA[3].process(&samplesIn[c][base], mSampleBufferA, td);
-
- /* Process second vowel. */
- vowelB[0].process(&samplesIn[c][base], mSampleBufferB, td);
- vowelB[1].process(&samplesIn[c][base], mSampleBufferB, td);
- vowelB[2].process(&samplesIn[c][base], mSampleBufferB, td);
- vowelB[3].process(&samplesIn[c][base], mSampleBufferB, td);
-
- alignas(16) ALfloat samplesBlended[MAX_UPDATE_SAMPLES];
-
- for (ALsizei i{0};i < td;i++)
- samplesBlended[i] = lerp(mSampleBufferA[i], mSampleBufferB[i], lfo[i]);
-
- /* Now, mix the processed sound data to the output. */
- MixSamples(samplesBlended, samplesOut, mChans[c].CurrentGains, mChans[c].TargetGains,
- samplesToDo-base, base, td);
- }
-
- base += td;
- }
-}
-
-
-void Vmorpher_setParami(EffectProps* props, ALCcontext *context, ALenum param, ALint val)
-{
- switch(param)
- {
- case AL_VOCAL_MORPHER_WAVEFORM:
- if(!(val >= AL_VOCAL_MORPHER_MIN_WAVEFORM && val <= AL_VOCAL_MORPHER_MAX_WAVEFORM))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Vocal morpher waveform out of range");
- props->Vmorpher.Waveform = val;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEA:
- if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Vocal morpher phoneme-a out of range");
- props->Vmorpher.PhonemeA = val;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEB:
- if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Vocal morpher phoneme-b out of range");
- props->Vmorpher.PhonemeB = val;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
- if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Vocal morpher phoneme-a coarse tuning out of range");
- props->Vmorpher.PhonemeACoarseTuning = val;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
- if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Vocal morpher phoneme-b coarse tuning out of range");
- props->Vmorpher.PhonemeBCoarseTuning = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x", param);
- }
-}
-void Vmorpher_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", param); }
-void Vmorpher_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
-{
- switch(param)
- {
- case AL_VOCAL_MORPHER_RATE:
- if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_RATE))
- SETERR_RETURN(context, AL_INVALID_VALUE,, "Vocal morpher rate out of range");
- props->Vmorpher.Rate = val;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x", param);
- }
-}
-void Vmorpher_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ Vmorpher_setParamf(props, context, param, vals[0]); }
-
-void Vmorpher_getParami(const EffectProps* props, ALCcontext *context, ALenum param, ALint* val)
-{
- switch(param)
- {
- case AL_VOCAL_MORPHER_PHONEMEA:
- *val = props->Vmorpher.PhonemeA;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEB:
- *val = props->Vmorpher.PhonemeB;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
- *val = props->Vmorpher.PhonemeACoarseTuning;
- break;
-
- case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
- *val = props->Vmorpher.PhonemeBCoarseTuning;
- break;
-
- case AL_VOCAL_MORPHER_WAVEFORM:
- *val = props->Vmorpher.Waveform;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x", param);
- }
-}
-void Vmorpher_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
-{ alSetError(context, AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", param); }
-void Vmorpher_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
-{
- switch(param)
- {
- case AL_VOCAL_MORPHER_RATE:
- *val = props->Vmorpher.Rate;
- break;
-
- default:
- alSetError(context, AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x", param);
- }
-}
-void Vmorpher_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
-{ Vmorpher_getParamf(props, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(Vmorpher);
-
-
-struct VmorpherStateFactory final : public EffectStateFactory {
- EffectState *create() override { return new VmorpherState{}; }
- EffectProps getDefaultProps() const noexcept override;
- const EffectVtable *getEffectVtable() const noexcept override { return &Vmorpher_vtable; }
-};
-
-EffectProps VmorpherStateFactory::getDefaultProps() const noexcept
-{
- EffectProps props{};
- props.Vmorpher.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE;
- props.Vmorpher.PhonemeA = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA;
- props.Vmorpher.PhonemeB = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB;
- props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
- props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
- props.Vmorpher.Waveform = AL_VOCAL_MORPHER_DEFAULT_WAVEFORM;
- return props;
-}
-
-} // namespace
-
-EffectStateFactory *VmorpherStateFactory_getFactory()
-{
- static VmorpherStateFactory VmorpherFactory{};
- return &VmorpherFactory;
-}
diff --git a/Alc/filters/biquad.cpp b/Alc/filters/biquad.cpp
deleted file mode 100644
index 6a3cef64..00000000
--- a/Alc/filters/biquad.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-
-#include "config.h"
-
-#include "biquad.h"
-
-#include <algorithm>
-#include <cassert>
-#include <cmath>
-
-#include "opthelpers.h"
-
-
-template<typename Real>
-void BiquadFilterR<Real>::setParams(BiquadType type, Real gain, Real f0norm, Real rcpQ)
-{
- // Limit gain to -100dB
- assert(gain > 0.00001f);
-
- const Real w0{al::MathDefs<Real>::Tau() * f0norm};
- const Real sin_w0{std::sin(w0)};
- const Real cos_w0{std::cos(w0)};
- const Real alpha{sin_w0/2.0f * rcpQ};
-
- Real sqrtgain_alpha_2;
- Real a[3]{ 1.0f, 0.0f, 0.0f };
- Real b[3]{ 1.0f, 0.0f, 0.0f };
-
- /* Calculate filter coefficients depending on filter type */
- switch(type)
- {
- case BiquadType::HighShelf:
- sqrtgain_alpha_2 = 2.0f * std::sqrt(gain) * alpha;
- b[0] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
- b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cos_w0 );
- b[2] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
- a[0] = (gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
- a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cos_w0 );
- a[2] = (gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
- break;
- case BiquadType::LowShelf:
- sqrtgain_alpha_2 = 2.0f * std::sqrt(gain) * alpha;
- b[0] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
- b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cos_w0 );
- b[2] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
- a[0] = (gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
- a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cos_w0 );
- a[2] = (gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
- break;
- case BiquadType::Peaking:
- gain = std::sqrt(gain);
- b[0] = 1.0f + alpha * gain;
- b[1] = -2.0f * cos_w0;
- b[2] = 1.0f - alpha * gain;
- a[0] = 1.0f + alpha / gain;
- a[1] = -2.0f * cos_w0;
- a[2] = 1.0f - alpha / gain;
- break;
-
- case BiquadType::LowPass:
- b[0] = (1.0f - cos_w0) / 2.0f;
- b[1] = 1.0f - cos_w0;
- b[2] = (1.0f - cos_w0) / 2.0f;
- a[0] = 1.0f + alpha;
- a[1] = -2.0f * cos_w0;
- a[2] = 1.0f - alpha;
- break;
- case BiquadType::HighPass:
- b[0] = (1.0f + cos_w0) / 2.0f;
- b[1] = -(1.0f + cos_w0);
- b[2] = (1.0f + cos_w0) / 2.0f;
- a[0] = 1.0f + alpha;
- a[1] = -2.0f * cos_w0;
- a[2] = 1.0f - alpha;
- break;
- case BiquadType::BandPass:
- b[0] = alpha;
- b[1] = 0.0f;
- b[2] = -alpha;
- a[0] = 1.0f + alpha;
- a[1] = -2.0f * cos_w0;
- a[2] = 1.0f - alpha;
- break;
- }
-
- a1 = a[1] / a[0];
- a2 = a[2] / a[0];
- b0 = b[0] / a[0];
- b1 = b[1] / a[0];
- b2 = b[2] / a[0];
-}
-
-template<typename Real>
-void BiquadFilterR<Real>::process(Real *dst, const Real *src, int numsamples)
-{
- ASSUME(numsamples > 0);
-
- const Real b0{this->b0};
- const Real b1{this->b1};
- const Real b2{this->b2};
- const Real a1{this->a1};
- const Real a2{this->a2};
- Real z1{this->z1};
- Real z2{this->z2};
-
- /* Processing loop is Transposed Direct Form II. This requires less storage
- * compared to Direct Form I (only two delay components, instead of a four-
- * sample history; the last two inputs and outputs), and works better for
- * floating-point which favors summing similarly-sized values while being
- * less bothered by overflow.
- *
- * See: http://www.earlevel.com/main/2003/02/28/biquads/
- */
- auto proc_sample = [b0,b1,b2,a1,a2,&z1,&z2](Real input) noexcept -> Real
- {
- Real output = input*b0 + z1;
- z1 = input*b1 - output*a1 + z2;
- z2 = input*b2 - output*a2;
- return output;
- };
- std::transform(src, src+numsamples, dst, proc_sample);
-
- this->z1 = z1;
- this->z2 = z2;
-}
-
-template class BiquadFilterR<float>;
-template class BiquadFilterR<double>;
diff --git a/Alc/filters/biquad.h b/Alc/filters/biquad.h
deleted file mode 100644
index 893a69a9..00000000
--- a/Alc/filters/biquad.h
+++ /dev/null
@@ -1,113 +0,0 @@
-#ifndef FILTERS_BIQUAD_H
-#define FILTERS_BIQUAD_H
-
-#include <cmath>
-#include <utility>
-
-#include "math_defs.h"
-
-
-/* Filters implementation is based on the "Cookbook formulae for audio
- * EQ biquad filter coefficients" by Robert Bristow-Johnson
- * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
- */
-/* Implementation note: For the shelf filters, the specified gain is for the
- * reference frequency, which is the centerpoint of the transition band. This
- * better matches EFX filter design. To set the gain for the shelf itself, use
- * the square root of the desired linear gain (or halve the dB gain).
- */
-
-enum class BiquadType {
- /** EFX-style low-pass filter, specifying a gain and reference frequency. */
- HighShelf,
- /** EFX-style high-pass filter, specifying a gain and reference frequency. */
- LowShelf,
- /** Peaking filter, specifying a gain and reference frequency. */
- Peaking,
-
- /** Low-pass cut-off filter, specifying a cut-off frequency. */
- LowPass,
- /** High-pass cut-off filter, specifying a cut-off frequency. */
- HighPass,
- /** Band-pass filter, specifying a center frequency. */
- BandPass,
-};
-
-template<typename Real>
-class BiquadFilterR {
- /* Last two delayed components for direct form II. */
- Real z1{0.0f}, z2{0.0f};
- /* Transfer function coefficients "b" (numerator) */
- Real b0{1.0f}, b1{0.0f}, b2{0.0f};
- /* Transfer function coefficients "a" (denominator; a0 is pre-applied). */
- Real a1{0.0f}, a2{0.0f};
-
-public:
- void clear() noexcept { z1 = z2 = 0.0f; }
-
- /**
- * Sets the filter state for the specified filter type and its parameters.
- *
- * \param type The type of filter to apply.
- * \param gain The gain for the reference frequency response. Only used by
- * the Shelf and Peaking filter types.
- * \param f0norm The reference frequency normal (ref_freq / sample_rate).
- * This is the center point for the Shelf, Peaking, and
- * BandPass filter types, or the cutoff frequency for the
- * LowPass and HighPass filter types.
- * \param rcpQ The reciprocal of the Q coefficient for the filter's
- * transition band. Can be generated from rcpQFromSlope or
- * rcpQFromBandwidth as needed.
- */
- void setParams(BiquadType type, Real gain, Real f0norm, Real rcpQ);
-
- void copyParamsFrom(const BiquadFilterR &other)
- {
- b0 = other.b0;
- b1 = other.b1;
- b2 = other.b2;
- a1 = other.a1;
- a2 = other.a2;
- }
-
-
- void process(Real *dst, const Real *src, int numsamples);
-
- /* Rather hacky. It's just here to support "manual" processing. */
- std::pair<Real,Real> getComponents() const noexcept
- { return {z1, z2}; }
- void setComponents(Real z1_, Real z2_) noexcept
- { z1 = z1_; z2 = z2_; }
- Real processOne(const Real in, Real &z1_, Real &z2_) const noexcept
- {
- Real out{in*b0 + z1_};
- z1_ = in*b1 - out*a1 + z2_;
- z2_ = in*b2 - out*a2;
- return out;
- }
-
- /**
- * Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using
- * the reference gain and shelf slope parameter.
- * \param gain 0 < gain
- * \param slope 0 < slope <= 1
- */
- static Real rcpQFromSlope(Real gain, Real slope)
- { return std::sqrt((gain + 1.0f/gain)*(1.0f/slope - 1.0f) + 2.0f); }
-
- /**
- * Calculates the rcpQ (i.e. 1/Q) coefficient for filters, using the
- * normalized reference frequency and bandwidth.
- * \param f0norm 0 < f0norm < 0.5.
- * \param bandwidth 0 < bandwidth
- */
- static Real rcpQFromBandwidth(Real f0norm, Real bandwidth)
- {
- const Real w0{al::MathDefs<Real>::Tau() * f0norm};
- return 2.0f*std::sinh(std::log(Real{2.0f})/2.0f*bandwidth*w0/std::sin(w0));
- }
-};
-
-using BiquadFilter = BiquadFilterR<float>;
-
-#endif /* FILTERS_BIQUAD_H */
diff --git a/Alc/filters/nfc.cpp b/Alc/filters/nfc.cpp
deleted file mode 100644
index 1a567f2c..00000000
--- a/Alc/filters/nfc.cpp
+++ /dev/null
@@ -1,391 +0,0 @@
-
-#include "config.h"
-
-#include "nfc.h"
-
-#include <algorithm>
-
-#include "alcmain.h"
-
-
-/* Near-field control filters are the basis for handling the near-field effect.
- * The near-field effect is a bass-boost present in the directional components
- * of a recorded signal, created as a result of the wavefront curvature (itself
- * a function of sound distance). Proper reproduction dictates this be
- * compensated for using a bass-cut given the playback speaker distance, to
- * avoid excessive bass in the playback.
- *
- * For real-time rendered audio, emulating the near-field effect based on the
- * sound source's distance, and subsequently compensating for it at output
- * based on the speaker distances, can create a more realistic perception of
- * sound distance beyond a simple 1/r attenuation.
- *
- * These filters do just that. Each one applies a low-shelf filter, created as
- * the combination of a bass-boost for a given sound source distance (near-
- * field emulation) along with a bass-cut for a given control/speaker distance
- * (near-field compensation).
- *
- * Note that it is necessary to apply a cut along with the boost, since the
- * boost alone is unstable in higher-order ambisonics as it causes an infinite
- * DC gain (even first-order ambisonics requires there to be no DC offset for
- * the boost to work). Consequently, ambisonics requires a control parameter to
- * be used to avoid an unstable boost-only filter. NFC-HOA defines this control
- * as a reference delay, calculated with:
- *
- * reference_delay = control_distance / speed_of_sound
- *
- * This means w0 (for input) or w1 (for output) should be set to:
- *
- * wN = 1 / (reference_delay * sample_rate)
- *
- * when dealing with NFC-HOA content. For FOA input content, which does not
- * specify a reference_delay variable, w0 should be set to 0 to apply only
- * near-field compensation for output. It's important that w1 be a finite,
- * positive, non-0 value or else the bass-boost will become unstable again.
- * Also, w0 should not be too large compared to w1, to avoid excessively loud
- * low frequencies.
- */
-
-namespace {
-
-constexpr float B[5][4] = {
- { 0.0f },
- { 1.0f },
- { 3.0f, 3.0f },
- { 3.6778f, 6.4595f, 2.3222f },
- { 4.2076f, 11.4877f, 5.7924f, 9.1401f }
-};
-
-NfcFilter1 NfcFilterCreate1(const float w0, const float w1) noexcept
-{
- NfcFilter1 nfc{};
- float b_00, g_0;
- float r;
-
- nfc.base_gain = 1.0f;
- nfc.gain = 1.0f;
-
- /* Calculate bass-boost coefficients. */
- r = 0.5f * w0;
- b_00 = B[1][0] * r;
- g_0 = 1.0f + b_00;
-
- nfc.gain *= g_0;
- nfc.b1 = 2.0f * b_00 / g_0;
-
- /* Calculate bass-cut coefficients. */
- r = 0.5f * w1;
- b_00 = B[1][0] * r;
- g_0 = 1.0f + b_00;
-
- nfc.base_gain /= g_0;
- nfc.gain /= g_0;
- nfc.a1 = 2.0f * b_00 / g_0;
-
- return nfc;
-}
-
-void NfcFilterAdjust1(NfcFilter1 *nfc, const float w0) noexcept
-{
- const float r{0.5f * w0};
- const float b_00{B[1][0] * r};
- const float g_0{1.0f + b_00};
-
- nfc->gain = nfc->base_gain * g_0;
- nfc->b1 = 2.0f * b_00 / g_0;
-}
-
-
-NfcFilter2 NfcFilterCreate2(const float w0, const float w1) noexcept
-{
- NfcFilter2 nfc{};
- float b_10, b_11, g_1;
- float r;
-
- nfc.base_gain = 1.0f;
- nfc.gain = 1.0f;
-
- /* Calculate bass-boost coefficients. */
- r = 0.5f * w0;
- b_10 = B[2][0] * r;
- b_11 = B[2][1] * r * r;
- g_1 = 1.0f + b_10 + b_11;
-
- nfc.gain *= g_1;
- nfc.b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
- nfc.b2 = 4.0f * b_11 / g_1;
-
- /* Calculate bass-cut coefficients. */
- r = 0.5f * w1;
- b_10 = B[2][0] * r;
- b_11 = B[2][1] * r * r;
- g_1 = 1.0f + b_10 + b_11;
-
- nfc.base_gain /= g_1;
- nfc.gain /= g_1;
- nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
- nfc.a2 = 4.0f * b_11 / g_1;
-
- return nfc;
-}
-
-void NfcFilterAdjust2(NfcFilter2 *nfc, const float w0) noexcept
-{
- const float r{0.5f * w0};
- const float b_10{B[2][0] * r};
- const float b_11{B[2][1] * r * r};
- const float g_1{1.0f + b_10 + b_11};
-
- nfc->gain = nfc->base_gain * g_1;
- nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
- nfc->b2 = 4.0f * b_11 / g_1;
-}
-
-
-NfcFilter3 NfcFilterCreate3(const float w0, const float w1) noexcept
-{
- NfcFilter3 nfc{};
- float b_10, b_11, g_1;
- float b_00, g_0;
- float r;
-
- nfc.base_gain = 1.0f;
- nfc.gain = 1.0f;
-
- /* Calculate bass-boost coefficients. */
- r = 0.5f * w0;
- b_10 = B[3][0] * r;
- b_11 = B[3][1] * r * r;
- b_00 = B[3][2] * r;
- g_1 = 1.0f + b_10 + b_11;
- g_0 = 1.0f + b_00;
-
- nfc.gain *= g_1 * g_0;
- nfc.b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
- nfc.b2 = 4.0f * b_11 / g_1;
- nfc.b3 = 2.0f * b_00 / g_0;
-
- /* Calculate bass-cut coefficients. */
- r = 0.5f * w1;
- b_10 = B[3][0] * r;
- b_11 = B[3][1] * r * r;
- b_00 = B[3][2] * r;
- g_1 = 1.0f + b_10 + b_11;
- g_0 = 1.0f + b_00;
-
- nfc.base_gain /= g_1 * g_0;
- nfc.gain /= g_1 * g_0;
- nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
- nfc.a2 = 4.0f * b_11 / g_1;
- nfc.a3 = 2.0f * b_00 / g_0;
-
- return nfc;
-}
-
-void NfcFilterAdjust3(NfcFilter3 *nfc, const float w0) noexcept
-{
- const float r{0.5f * w0};
- const float b_10{B[3][0] * r};
- const float b_11{B[3][1] * r * r};
- const float b_00{B[3][2] * r};
- const float g_1{1.0f + b_10 + b_11};
- const float g_0{1.0f + b_00};
-
- nfc->gain = nfc->base_gain * g_1 * g_0;
- nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
- nfc->b2 = 4.0f * b_11 / g_1;
- nfc->b3 = 2.0f * b_00 / g_0;
-}
-
-
-NfcFilter4 NfcFilterCreate4(const float w0, const float w1) noexcept
-{
- NfcFilter4 nfc{};
- float b_10, b_11, g_1;
- float b_00, b_01, g_0;
- float r;
-
- nfc.base_gain = 1.0f;
- nfc.gain = 1.0f;
-
- /* Calculate bass-boost coefficients. */
- r = 0.5f * w0;
- b_10 = B[4][0] * r;
- b_11 = B[4][1] * r * r;
- b_00 = B[4][2] * r;
- b_01 = B[4][3] * r * r;
- g_1 = 1.0f + b_10 + b_11;
- g_0 = 1.0f + b_00 + b_01;
-
- nfc.gain *= g_1 * g_0;
- nfc.b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
- nfc.b2 = 4.0f * b_11 / g_1;
- nfc.b3 = (2.0f*b_00 + 4.0f*b_01) / g_0;
- nfc.b4 = 4.0f * b_01 / g_0;
-
- /* Calculate bass-cut coefficients. */
- r = 0.5f * w1;
- b_10 = B[4][0] * r;
- b_11 = B[4][1] * r * r;
- b_00 = B[4][2] * r;
- b_01 = B[4][3] * r * r;
- g_1 = 1.0f + b_10 + b_11;
- g_0 = 1.0f + b_00 + b_01;
-
- nfc.base_gain /= g_1 * g_0;
- nfc.gain /= g_1 * g_0;
- nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
- nfc.a2 = 4.0f * b_11 / g_1;
- nfc.a3 = (2.0f*b_00 + 4.0f*b_01) / g_0;
- nfc.a4 = 4.0f * b_01 / g_0;
-
- return nfc;
-}
-
-void NfcFilterAdjust4(NfcFilter4 *nfc, const float w0) noexcept
-{
- const float r{0.5f * w0};
- const float b_10{B[4][0] * r};
- const float b_11{B[4][1] * r * r};
- const float b_00{B[4][2] * r};
- const float b_01{B[4][3] * r * r};
- const float g_1{1.0f + b_10 + b_11};
- const float g_0{1.0f + b_00 + b_01};
-
- nfc->gain = nfc->base_gain * g_1 * g_0;
- nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
- nfc->b2 = 4.0f * b_11 / g_1;
- nfc->b3 = (2.0f*b_00 + 4.0f*b_01) / g_0;
- nfc->b4 = 4.0f * b_01 / g_0;
-}
-
-} // namespace
-
-void NfcFilter::init(const float w1) noexcept
-{
- first = NfcFilterCreate1(0.0f, w1);
- second = NfcFilterCreate2(0.0f, w1);
- third = NfcFilterCreate3(0.0f, w1);
- fourth = NfcFilterCreate4(0.0f, w1);
-}
-
-void NfcFilter::adjust(const float w0) noexcept
-{
- NfcFilterAdjust1(&first, w0);
- NfcFilterAdjust2(&second, w0);
- NfcFilterAdjust3(&third, w0);
- NfcFilterAdjust4(&fourth, w0);
-}
-
-
-void NfcFilter::process1(float *RESTRICT dst, const float *RESTRICT src, const int count)
-{
- ASSUME(count > 0);
-
- const float gain{first.gain};
- const float b1{first.b1};
- const float a1{first.a1};
- float z1{first.z[0]};
- auto proc_sample = [gain,b1,a1,&z1](const float in) noexcept -> float
- {
- const float y{in*gain - a1*z1};
- const float out{y + b1*z1};
- z1 += y;
- return out;
- };
- std::transform(src, src+count, dst, proc_sample);
- first.z[0] = z1;
-}
-
-void NfcFilter::process2(float *RESTRICT dst, const float *RESTRICT src, const int count)
-{
- ASSUME(count > 0);
-
- const float gain{second.gain};
- const float b1{second.b1};
- const float b2{second.b2};
- const float a1{second.a1};
- const float a2{second.a2};
- float z1{second.z[0]};
- float z2{second.z[1]};
- auto proc_sample = [gain,b1,b2,a1,a2,&z1,&z2](const float in) noexcept -> float
- {
- const float y{in*gain - a1*z1 - a2*z2};
- const float out{y + b1*z1 + b2*z2};
- z2 += z1;
- z1 += y;
- return out;
- };
- std::transform(src, src+count, dst, proc_sample);
- second.z[0] = z1;
- second.z[1] = z2;
-}
-
-void NfcFilter::process3(float *RESTRICT dst, const float *RESTRICT src, const int count)
-{
- ASSUME(count > 0);
-
- const float gain{third.gain};
- const float b1{third.b1};
- const float b2{third.b2};
- const float b3{third.b3};
- const float a1{third.a1};
- const float a2{third.a2};
- const float a3{third.a3};
- float z1{third.z[0]};
- float z2{third.z[1]};
- float z3{third.z[2]};
- auto proc_sample = [gain,b1,b2,b3,a1,a2,a3,&z1,&z2,&z3](const float in) noexcept -> float
- {
- float y{in*gain - a1*z1 - a2*z2};
- float out{y + b1*z1 + b2*z2};
- z2 += z1;
- z1 += y;
-
- y = out - a3*z3;
- out = y + b3*z3;
- z3 += y;
- return out;
- };
- std::transform(src, src+count, dst, proc_sample);
- third.z[0] = z1;
- third.z[1] = z2;
- third.z[2] = z3;
-}
-
-void NfcFilter::process4(float *RESTRICT dst, const float *RESTRICT src, const int count)
-{
- ASSUME(count > 0);
-
- const float gain{fourth.gain};
- const float b1{fourth.b1};
- const float b2{fourth.b2};
- const float b3{fourth.b3};
- const float b4{fourth.b4};
- const float a1{fourth.a1};
- const float a2{fourth.a2};
- const float a3{fourth.a3};
- const float a4{fourth.a4};
- float z1{fourth.z[0]};
- float z2{fourth.z[1]};
- float z3{fourth.z[2]};
- float z4{fourth.z[3]};
- auto proc_sample = [gain,b1,b2,b3,b4,a1,a2,a3,a4,&z1,&z2,&z3,&z4](const float in) noexcept -> float
- {
- float y{in*gain - a1*z1 - a2*z2};
- float out{y + b1*z1 + b2*z2};
- z2 += z1;
- z1 += y;
-
- y = out - a3*z3 - a4*z4;
- out = y + b3*z3 + b4*z4;
- z4 += z3;
- z3 += y;
- return out;
- };
- std::transform(src, src+count, dst, proc_sample);
- fourth.z[0] = z1;
- fourth.z[1] = z2;
- fourth.z[2] = z3;
- fourth.z[3] = z4;
-}
diff --git a/Alc/filters/nfc.h b/Alc/filters/nfc.h
deleted file mode 100644
index b656850a..00000000
--- a/Alc/filters/nfc.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef FILTER_NFC_H
-#define FILTER_NFC_H
-
-struct NfcFilter1 {
- float base_gain, gain;
- float b1, a1;
- float z[1];
-};
-struct NfcFilter2 {
- float base_gain, gain;
- float b1, b2, a1, a2;
- float z[2];
-};
-struct NfcFilter3 {
- float base_gain, gain;
- float b1, b2, b3, a1, a2, a3;
- float z[3];
-};
-struct NfcFilter4 {
- float base_gain, gain;
- float b1, b2, b3, b4, a1, a2, a3, a4;
- float z[4];
-};
-
-class NfcFilter {
- NfcFilter1 first;
- NfcFilter2 second;
- NfcFilter3 third;
- NfcFilter4 fourth;
-
-public:
- /* NOTE:
- * w0 = speed_of_sound / (source_distance * sample_rate);
- * w1 = speed_of_sound / (control_distance * sample_rate);
- *
- * Generally speaking, the control distance should be approximately the
- * average speaker distance, or based on the reference delay if outputing
- * NFC-HOA. It must not be negative, 0, or infinite. The source distance
- * should not be too small relative to the control distance.
- */
-
- void init(const float w1) noexcept;
- void adjust(const float w0) noexcept;
-
- /* Near-field control filter for first-order ambisonic channels (1-3). */
- void process1(float *RESTRICT dst, const float *RESTRICT src, const int count);
-
- /* Near-field control filter for second-order ambisonic channels (4-8). */
- void process2(float *RESTRICT dst, const float *RESTRICT src, const int count);
-
- /* Near-field control filter for third-order ambisonic channels (9-15). */
- void process3(float *RESTRICT dst, const float *RESTRICT src, const int count);
-
- /* Near-field control filter for fourth-order ambisonic channels (16-24). */
- void process4(float *RESTRICT dst, const float *RESTRICT src, const int count);
-};
-
-#endif /* FILTER_NFC_H */
diff --git a/Alc/filters/splitter.cpp b/Alc/filters/splitter.cpp
deleted file mode 100644
index 09e7bfe8..00000000
--- a/Alc/filters/splitter.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-
-#include "config.h"
-
-#include "splitter.h"
-
-#include <cmath>
-#include <limits>
-#include <algorithm>
-
-#include "math_defs.h"
-
-template<typename Real>
-void BandSplitterR<Real>::init(Real f0norm)
-{
- const Real w{f0norm * al::MathDefs<Real>::Tau()};
- const Real cw{std::cos(w)};
- if(cw > std::numeric_limits<float>::epsilon())
- coeff = (std::sin(w) - 1.0f) / cw;
- else
- coeff = cw * -0.5f;
-
- lp_z1 = 0.0f;
- lp_z2 = 0.0f;
- ap_z1 = 0.0f;
-}
-
-template<typename Real>
-void BandSplitterR<Real>::process(Real *hpout, Real *lpout, const Real *input, const int count)
-{
- ASSUME(count > 0);
-
- const Real ap_coeff{this->coeff};
- const Real lp_coeff{this->coeff*0.5f + 0.5f};
- Real lp_z1{this->lp_z1};
- Real lp_z2{this->lp_z2};
- Real ap_z1{this->ap_z1};
- auto proc_sample = [ap_coeff,lp_coeff,&lp_z1,&lp_z2,&ap_z1,&lpout](const Real in) noexcept -> Real
- {
- /* Low-pass sample processing. */
- Real d{(in - lp_z1) * lp_coeff};
- Real lp_y{lp_z1 + d};
- lp_z1 = lp_y + d;
-
- d = (lp_y - lp_z2) * lp_coeff;
- lp_y = lp_z2 + d;
- lp_z2 = lp_y + d;
-
- *(lpout++) = lp_y;
-
- /* All-pass sample processing. */
- Real ap_y{in*ap_coeff + ap_z1};
- ap_z1 = in - ap_y*ap_coeff;
-
- /* High-pass generated from removing low-passed output. */
- return ap_y - lp_y;
- };
- std::transform(input, input+count, hpout, proc_sample);
- this->lp_z1 = lp_z1;
- this->lp_z2 = lp_z2;
- this->ap_z1 = ap_z1;
-}
-
-template<typename Real>
-void BandSplitterR<Real>::applyHfScale(Real *samples, const Real hfscale, const int count)
-{
- ASSUME(count > 0);
-
- const Real ap_coeff{this->coeff};
- const Real lp_coeff{this->coeff*0.5f + 0.5f};
- Real lp_z1{this->lp_z1};
- Real lp_z2{this->lp_z2};
- Real ap_z1{this->ap_z1};
- auto proc_sample = [hfscale,ap_coeff,lp_coeff,&lp_z1,&lp_z2,&ap_z1](const Real in) noexcept -> Real
- {
- /* Low-pass sample processing. */
- Real d{(in - lp_z1) * lp_coeff};
- Real lp_y{lp_z1 + d};
- lp_z1 = lp_y + d;
-
- d = (lp_y - lp_z2) * lp_coeff;
- lp_y = lp_z2 + d;
- lp_z2 = lp_y + d;
-
- /* All-pass sample processing. */
- Real ap_y{in*ap_coeff + ap_z1};
- ap_z1 = in - ap_y*ap_coeff;
-
- /* High-pass generated from removing low-passed output. */
- return (ap_y-lp_y)*hfscale + lp_y;
- };
- std::transform(samples, samples+count, samples, proc_sample);
- this->lp_z1 = lp_z1;
- this->lp_z2 = lp_z2;
- this->ap_z1 = ap_z1;
-}
-
-template<typename Real>
-void BandSplitterR<Real>::applyAllpass(Real *samples, const int count) const
-{
- ASSUME(count > 0);
-
- const Real coeff{this->coeff};
- Real z1{0.0f};
- auto proc_sample = [coeff,&z1](const Real in) noexcept -> Real
- {
- const Real out{in*coeff + z1};
- z1 = in - out*coeff;
- return out;
- };
- std::transform(samples, samples+count, samples, proc_sample);
-}
-
-
-template class BandSplitterR<float>;
-template class BandSplitterR<double>;
diff --git a/Alc/filters/splitter.h b/Alc/filters/splitter.h
deleted file mode 100644
index 927c4d17..00000000
--- a/Alc/filters/splitter.h
+++ /dev/null
@@ -1,50 +0,0 @@
-#ifndef FILTER_SPLITTER_H
-#define FILTER_SPLITTER_H
-
-#include "alcmain.h"
-#include "almalloc.h"
-
-
-/* Band splitter. Splits a signal into two phase-matching frequency bands. */
-template<typename Real>
-class BandSplitterR {
- Real coeff{0.0f};
- Real lp_z1{0.0f};
- Real lp_z2{0.0f};
- Real ap_z1{0.0f};
-
-public:
- BandSplitterR() = default;
- BandSplitterR(const BandSplitterR&) = default;
- BandSplitterR(Real f0norm) { init(f0norm); }
-
- void init(Real f0norm);
- void clear() noexcept { lp_z1 = lp_z2 = ap_z1 = 0.0f; }
- void process(Real *hpout, Real *lpout, const Real *input, const int count);
-
- void applyHfScale(Real *samples, const Real hfscale, const int count);
-
- /* The all-pass portion of the band splitter. Applies the same phase shift
- * without splitting the signal. Note that each use of this method is
- * indepedent, it does not track history between calls.
- */
- void applyAllpass(Real *samples, const int count) const;
-};
-using BandSplitter = BandSplitterR<float>;
-
-
-struct FrontStablizer {
- static constexpr size_t DelayLength{256u};
-
- alignas(16) float DelayBuf[MAX_OUTPUT_CHANNELS][DelayLength];
-
- BandSplitter LFilter, RFilter;
- alignas(16) float LSplit[2][BUFFERSIZE];
- alignas(16) float RSplit[2][BUFFERSIZE];
-
- alignas(16) float TempBuf[BUFFERSIZE + DelayLength];
-
- DEF_NEWDEL(FrontStablizer)
-};
-
-#endif /* FILTER_SPLITTER_H */
diff --git a/Alc/fpu_modes.h b/Alc/fpu_modes.h
deleted file mode 100644
index 5465e9cf..00000000
--- a/Alc/fpu_modes.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef FPU_MODES_H
-#define FPU_MODES_H
-
-class FPUCtl {
-#if defined(HAVE_SSE_INTRINSICS) || (defined(__GNUC__) && defined(HAVE_SSE))
- unsigned int sse_state{};
-#endif
- bool in_mode{};
-
-public:
- FPUCtl();
- /* HACK: 32-bit targets for GCC seem to have a problem here with certain
- * noexcept methods (which destructors are) causing an internal compiler
- * error. No idea why it's these methods specifically, but this is needed
- * to get it to compile.
- */
- ~FPUCtl() noexcept(false) { leave(); }
-
- FPUCtl(const FPUCtl&) = delete;
- FPUCtl& operator=(const FPUCtl&) = delete;
-
- void leave();
-};
-
-#endif /* FPU_MODES_H */
diff --git a/Alc/helpers.cpp b/Alc/helpers.cpp
deleted file mode 100644
index e86af6ce..00000000
--- a/Alc/helpers.cpp
+++ /dev/null
@@ -1,851 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2011 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#ifdef _WIN32
-#ifdef __MINGW32__
-#define _WIN32_IE 0x501
-#else
-#define _WIN32_IE 0x400
-#endif
-#endif
-
-#include "config.h"
-
-#include <algorithm>
-#include <cerrno>
-#include <cstdarg>
-#include <cstdlib>
-#include <cstdio>
-#include <cstring>
-#include <mutex>
-#include <string>
-
-#ifdef HAVE_DIRENT_H
-#include <dirent.h>
-#endif
-#ifdef HAVE_PROC_PIDPATH
-#include <libproc.h>
-#endif
-
-#ifdef __FreeBSD__
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#endif
-
-#ifndef AL_NO_UID_DEFS
-#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
-#define INITGUID
-#include <windows.h>
-#ifdef HAVE_GUIDDEF_H
-#include <guiddef.h>
-#else
-#include <initguid.h>
-#endif
-
-DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
-DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
-
-DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16);
-
-DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
-DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
-DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
-DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
-DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17);
-
-#ifdef HAVE_WASAPI
-#include <wtypes.h>
-#include <devpropdef.h>
-#include <propkeydef.h>
-DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
-DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
-DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
-#endif
-#endif
-#endif /* AL_NO_UID_DEFS */
-
-#ifdef HAVE_DLFCN_H
-#include <dlfcn.h>
-#endif
-#ifdef HAVE_INTRIN_H
-#include <intrin.h>
-#endif
-#ifdef HAVE_CPUID_H
-#include <cpuid.h>
-#endif
-#ifdef HAVE_SSE_INTRINSICS
-#include <xmmintrin.h>
-#endif
-#ifdef HAVE_SYS_SYSCONF_H
-#include <sys/sysconf.h>
-#endif
-
-#ifndef _WIN32
-#include <unistd.h>
-#elif defined(_WIN32_IE)
-#include <shlobj.h>
-#endif
-
-#include "alcmain.h"
-#include "almalloc.h"
-#include "compat.h"
-#include "cpu_caps.h"
-#include "fpu_modes.h"
-#include "logging.h"
-
-
-#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
- defined(_M_IX86) || defined(_M_X64))
-using reg_type = unsigned int;
-static inline void get_cpuid(int f, reg_type *regs)
-{ __get_cpuid(f, &regs[0], &regs[1], &regs[2], &regs[3]); }
-#define CAN_GET_CPUID
-#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
- defined(_M_IX86) || defined(_M_X64))
-using reg_type = int;
-static inline void get_cpuid(int f, reg_type *regs)
-{ (__cpuid)(regs, f); }
-#define CAN_GET_CPUID
-#endif
-
-int CPUCapFlags = 0;
-
-void FillCPUCaps(int capfilter)
-{
- int caps = 0;
-
-/* FIXME: We really should get this for all available CPUs in case different
- * CPUs have different caps (is that possible on one machine?). */
-#ifdef CAN_GET_CPUID
- union {
- reg_type regs[4];
- char str[sizeof(reg_type[4])];
- } cpuinf[3] = {{ { 0, 0, 0, 0 } }};
-
- get_cpuid(0, cpuinf[0].regs);
- if(cpuinf[0].regs[0] == 0)
- ERR("Failed to get CPUID\n");
- else
- {
- unsigned int maxfunc = cpuinf[0].regs[0];
- unsigned int maxextfunc;
-
- get_cpuid(0x80000000, cpuinf[0].regs);
- maxextfunc = cpuinf[0].regs[0];
-
- TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
-
- TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
- if(maxextfunc >= 0x80000004)
- {
- get_cpuid(0x80000002, cpuinf[0].regs);
- get_cpuid(0x80000003, cpuinf[1].regs);
- get_cpuid(0x80000004, cpuinf[2].regs);
- TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
- }
-
- if(maxfunc >= 1)
- {
- get_cpuid(1, cpuinf[0].regs);
- if((cpuinf[0].regs[3]&(1<<25)))
- caps |= CPU_CAP_SSE;
- if((caps&CPU_CAP_SSE) && (cpuinf[0].regs[3]&(1<<26)))
- caps |= CPU_CAP_SSE2;
- if((caps&CPU_CAP_SSE2) && (cpuinf[0].regs[2]&(1<<0)))
- caps |= CPU_CAP_SSE3;
- if((caps&CPU_CAP_SSE3) && (cpuinf[0].regs[2]&(1<<19)))
- caps |= CPU_CAP_SSE4_1;
- }
- }
-#else
- /* Assume support for whatever's supported if we can't check for it */
-#if defined(HAVE_SSE4_1)
-#warning "Assuming SSE 4.1 run-time support!"
- caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1;
-#elif defined(HAVE_SSE3)
-#warning "Assuming SSE 3 run-time support!"
- caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3;
-#elif defined(HAVE_SSE2)
-#warning "Assuming SSE 2 run-time support!"
- caps |= CPU_CAP_SSE | CPU_CAP_SSE2;
-#elif defined(HAVE_SSE)
-#warning "Assuming SSE run-time support!"
- caps |= CPU_CAP_SSE;
-#endif
-#endif
-#ifdef HAVE_NEON
- al::ifstream file{"/proc/cpuinfo"};
- if(!file.is_open())
- ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n");
- else
- {
- std::string features;
-
- auto getline = [](std::istream &f, std::string &output) -> bool
- {
- while(f.good() && f.peek() == '\n')
- f.ignore();
- return std::getline(f, output) && !output.empty();
-
- };
- while(getline(file, features))
- {
- if(features.compare(0, 10, "Features\t:", 10) == 0)
- break;
- }
- file.close();
-
- size_t extpos{9};
- while((extpos=features.find("neon", extpos+1)) != std::string::npos)
- {
- if((extpos == 0 || std::isspace(features[extpos-1])) &&
- (extpos+4 == features.length() || std::isspace(features[extpos+4])))
- {
- caps |= CPU_CAP_NEON;
- break;
- }
- }
- }
-#endif
-
- TRACE("Extensions:%s%s%s%s%s%s\n",
- ((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""),
- ((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""),
- ((capfilter&CPU_CAP_SSE3) ? ((caps&CPU_CAP_SSE3) ? " +SSE3" : " -SSE3") : ""),
- ((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""),
- ((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +NEON" : " -NEON") : ""),
- ((!capfilter) ? " -none-" : "")
- );
- CPUCapFlags = caps & capfilter;
-}
-
-
-FPUCtl::FPUCtl()
-{
-#if defined(HAVE_SSE_INTRINSICS)
- this->sse_state = _mm_getcsr();
- unsigned int sseState = this->sse_state;
- sseState |= 0x8000; /* set flush-to-zero */
- sseState |= 0x0040; /* set denormals-are-zero */
- _mm_setcsr(sseState);
-
-#elif defined(__GNUC__) && defined(HAVE_SSE)
-
- if((CPUCapFlags&CPU_CAP_SSE))
- {
- __asm__ __volatile__("stmxcsr %0" : "=m" (*&this->sse_state));
- unsigned int sseState = this->sse_state;
- sseState |= 0x8000; /* set flush-to-zero */
- if((CPUCapFlags&CPU_CAP_SSE2))
- sseState |= 0x0040; /* set denormals-are-zero */
- __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
- }
-#endif
-
- this->in_mode = true;
-}
-
-void FPUCtl::leave()
-{
- if(!this->in_mode) return;
-
-#if defined(HAVE_SSE_INTRINSICS)
- _mm_setcsr(this->sse_state);
-
-#elif defined(__GNUC__) && defined(HAVE_SSE)
-
- if((CPUCapFlags&CPU_CAP_SSE))
- __asm__ __volatile__("ldmxcsr %0" : : "m" (*&this->sse_state));
-#endif
- this->in_mode = false;
-}
-
-
-#ifdef _WIN32
-
-namespace al {
-
-auto filebuf::underflow() -> int_type
-{
- if(mFile != INVALID_HANDLE_VALUE && gptr() == egptr())
- {
- // Read in the next chunk of data, and set the pointers on success
- DWORD got{};
- if(ReadFile(mFile, mBuffer.data(), (DWORD)mBuffer.size(), &got, nullptr))
- setg(mBuffer.data(), mBuffer.data(), mBuffer.data()+got);
- }
- if(gptr() == egptr())
- return traits_type::eof();
- return traits_type::to_int_type(*gptr());
-}
-
-auto filebuf::seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) -> pos_type
-{
- if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
- return traits_type::eof();
-
- LARGE_INTEGER fpos{};
- switch(whence)
- {
- case std::ios_base::beg:
- fpos.QuadPart = offset;
- if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
- return traits_type::eof();
- break;
-
- case std::ios_base::cur:
- // If the offset remains in the current buffer range, just
- // update the pointer.
- if((offset >= 0 && offset < off_type(egptr()-gptr())) ||
- (offset < 0 && -offset <= off_type(gptr()-eback())))
- {
- // Get the current file offset to report the correct read
- // offset.
- fpos.QuadPart = 0;
- if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
- return traits_type::eof();
- setg(eback(), gptr()+offset, egptr());
- return fpos.QuadPart - off_type(egptr()-gptr());
- }
- // Need to offset for the file offset being at egptr() while
- // the requested offset is relative to gptr().
- offset -= off_type(egptr()-gptr());
- fpos.QuadPart = offset;
- if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
- return traits_type::eof();
- break;
-
- case std::ios_base::end:
- fpos.QuadPart = offset;
- if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_END))
- return traits_type::eof();
- break;
-
- default:
- return traits_type::eof();
- }
- setg(nullptr, nullptr, nullptr);
- return fpos.QuadPart;
-}
-
-auto filebuf::seekpos(pos_type pos, std::ios_base::openmode mode) -> pos_type
-{
- // Simplified version of seekoff
- if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
- return traits_type::eof();
-
- LARGE_INTEGER fpos{};
- fpos.QuadPart = pos;
- if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
- return traits_type::eof();
-
- setg(nullptr, nullptr, nullptr);
- return fpos.QuadPart;
-}
-
-filebuf::~filebuf()
-{
- if(mFile != INVALID_HANDLE_VALUE)
- CloseHandle(mFile);
- mFile = INVALID_HANDLE_VALUE;
-}
-
-bool filebuf::open(const wchar_t *filename, std::ios_base::openmode mode)
-{
- if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
- return false;
- HANDLE f{CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, nullptr)};
- if(f == INVALID_HANDLE_VALUE) return false;
-
- if(mFile != INVALID_HANDLE_VALUE)
- CloseHandle(mFile);
- mFile = f;
-
- setg(nullptr, nullptr, nullptr);
- return true;
-}
-bool filebuf::open(const char *filename, std::ios_base::openmode mode)
-{
- std::wstring wname{utf8_to_wstr(filename)};
- return open(wname.c_str(), mode);
-}
-
-
-ifstream::ifstream(const wchar_t *filename, std::ios_base::openmode mode)
- : std::istream{nullptr}
-{
- init(&mStreamBuf);
-
- // Set the failbit if the file failed to open.
- if((mode&std::ios_base::out) || !mStreamBuf.open(filename, mode|std::ios_base::in))
- clear(failbit);
-}
-
-ifstream::ifstream(const char *filename, std::ios_base::openmode mode)
- : std::istream{nullptr}
-{
- init(&mStreamBuf);
-
- // Set the failbit if the file failed to open.
- if((mode&std::ios_base::out) || !mStreamBuf.open(filename, mode|std::ios_base::in))
- clear(failbit);
-}
-
-/* This is only here to ensure the compiler doesn't define an implicit
- * destructor, which it tries to automatically inline and subsequently complain
- * it can't inline without excessive code growth.
- */
-ifstream::~ifstream() { }
-
-} // namespace al
-
-const PathNamePair &GetProcBinary()
-{
- static PathNamePair ret;
- if(!ret.fname.empty() || !ret.path.empty())
- return ret;
-
- al::vector<WCHAR> fullpath(256);
- DWORD len;
- while((len=GetModuleFileNameW(nullptr, fullpath.data(), static_cast<DWORD>(fullpath.size()))) == fullpath.size())
- fullpath.resize(fullpath.size() << 1);
- if(len == 0)
- {
- ERR("Failed to get process name: error %lu\n", GetLastError());
- return ret;
- }
-
- fullpath.resize(len);
- if(fullpath.back() != 0)
- fullpath.push_back(0);
-
- auto sep = std::find(fullpath.rbegin()+1, fullpath.rend(), '\\');
- sep = std::find(fullpath.rbegin()+1, sep, '/');
- if(sep != fullpath.rend())
- {
- *sep = 0;
- ret.fname = wstr_to_utf8(&*sep + 1);
- ret.path = wstr_to_utf8(fullpath.data());
- }
- else
- ret.fname = wstr_to_utf8(fullpath.data());
-
- TRACE("Got binary: %s, %s\n", ret.path.c_str(), ret.fname.c_str());
- return ret;
-}
-
-
-void *LoadLib(const char *name)
-{
- std::wstring wname{utf8_to_wstr(name)};
- return LoadLibraryW(wname.c_str());
-}
-void CloseLib(void *handle)
-{ FreeLibrary(static_cast<HMODULE>(handle)); }
-void *GetSymbol(void *handle, const char *name)
-{
- void *ret{reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name))};
- if(!ret) ERR("Failed to load %s\n", name);
- return ret;
-}
-
-
-void al_print(FILE *logfile, const char *fmt, ...)
-{
- al::vector<char> dynmsg;
- char stcmsg[256];
- char *str{stcmsg};
-
- va_list args, args2;
- va_start(args, fmt);
- va_copy(args2, args);
- int msglen{std::vsnprintf(str, sizeof(stcmsg), fmt, args)};
- if(UNLIKELY(msglen >= 0 && static_cast<size_t>(msglen) >= sizeof(stcmsg)))
- {
- dynmsg.resize(static_cast<size_t>(msglen) + 1u);
- str = dynmsg.data();
- msglen = std::vsnprintf(str, dynmsg.size(), fmt, args2);
- }
- va_end(args2);
- va_end(args);
-
- std::wstring wstr{utf8_to_wstr(str)};
- fprintf(logfile, "%ls", wstr.c_str());
- fflush(logfile);
-}
-
-
-static inline int is_slash(int c)
-{ return (c == '\\' || c == '/'); }
-
-static void DirectorySearch(const char *path, const char *ext, al::vector<std::string> *const results)
-{
- std::string pathstr{path};
- pathstr += "\\*";
- pathstr += ext;
- TRACE("Searching %s\n", pathstr.c_str());
-
- std::wstring wpath{utf8_to_wstr(pathstr.c_str())};
- WIN32_FIND_DATAW fdata;
- HANDLE hdl{FindFirstFileW(wpath.c_str(), &fdata)};
- if(hdl != INVALID_HANDLE_VALUE)
- {
- size_t base = results->size();
- do {
- results->emplace_back();
- std::string &str = results->back();
- str = path;
- str += '\\';
- str += wstr_to_utf8(fdata.cFileName);
- TRACE("Got result %s\n", str.c_str());
- } while(FindNextFileW(hdl, &fdata));
- FindClose(hdl);
-
- std::sort(results->begin()+base, results->end());
- }
-}
-
-al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
-{
- static std::mutex search_lock;
- std::lock_guard<std::mutex> _{search_lock};
-
- /* If the path is absolute, use it directly. */
- al::vector<std::string> results;
- if(isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2]))
- {
- std::string path{subdir};
- std::replace(path.begin(), path.end(), '/', '\\');
- DirectorySearch(path.c_str(), ext, &results);
- return results;
- }
- if(subdir[0] == '\\' && subdir[1] == '\\' && subdir[2] == '?' && subdir[3] == '\\')
- {
- DirectorySearch(subdir, ext, &results);
- return results;
- }
-
- std::string path;
-
- /* Search the app-local directory. */
- WCHAR *cwdbuf{_wgetenv(L"ALSOFT_LOCAL_PATH")};
- if(cwdbuf && *cwdbuf != '\0')
- {
- path = wstr_to_utf8(cwdbuf);
- if(is_slash(path.back()))
- path.pop_back();
- }
- else if(!(cwdbuf=_wgetcwd(nullptr, 0)))
- path = ".";
- else
- {
- path = wstr_to_utf8(cwdbuf);
- if(is_slash(path.back()))
- path.pop_back();
- free(cwdbuf);
- }
- std::replace(path.begin(), path.end(), '/', '\\');
- DirectorySearch(path.c_str(), ext, &results);
-
- /* Search the local and global data dirs. */
- static constexpr int ids[2]{ CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
- for(int id : ids)
- {
- WCHAR buffer[MAX_PATH];
- if(SHGetSpecialFolderPathW(nullptr, buffer, id, FALSE) == FALSE)
- continue;
-
- path = wstr_to_utf8(buffer);
- if(!is_slash(path.back()))
- path += '\\';
- path += subdir;
- std::replace(path.begin(), path.end(), '/', '\\');
-
- DirectorySearch(path.c_str(), ext, &results);
- }
-
- return results;
-}
-
-void SetRTPriority(void)
-{
- bool failed = false;
- if(RTPrioLevel > 0)
- failed = !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
- if(failed) ERR("Failed to set priority level for thread\n");
-}
-
-#else
-
-const PathNamePair &GetProcBinary()
-{
- static PathNamePair ret;
- if(!ret.fname.empty() || !ret.path.empty())
- return ret;
-
- al::vector<char> pathname;
-#ifdef __FreeBSD__
- size_t pathlen;
- int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
- if(sysctl(mib, 4, nullptr, &pathlen, nullptr, 0) == -1)
- WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno));
- else
- {
- pathname.resize(pathlen + 1);
- sysctl(mib, 4, pathname.data(), &pathlen, nullptr, 0);
- pathname.resize(pathlen);
- }
-#endif
-#ifdef HAVE_PROC_PIDPATH
- if(pathname.empty())
- {
- char procpath[PROC_PIDPATHINFO_MAXSIZE]{};
- const pid_t pid{getpid()};
- if(proc_pidpath(pid, procpath, sizeof(procpath)) < 1)
- ERR("proc_pidpath(%d, ...) failed: %s\n", pid, strerror(errno));
- else
- pathname.insert(pathname.end(), procpath, procpath+strlen(procpath));
- }
-#endif
- if(pathname.empty())
- {
- pathname.resize(256);
-
- const char *selfname{"/proc/self/exe"};
- ssize_t len{readlink(selfname, pathname.data(), pathname.size())};
- if(len == -1 && errno == ENOENT)
- {
- selfname = "/proc/self/file";
- len = readlink(selfname, pathname.data(), pathname.size());
- }
- if(len == -1 && errno == ENOENT)
- {
- selfname = "/proc/curproc/exe";
- len = readlink(selfname, pathname.data(), pathname.size());
- }
- if(len == -1 && errno == ENOENT)
- {
- selfname = "/proc/curproc/file";
- len = readlink(selfname, pathname.data(), pathname.size());
- }
-
- while(len > 0 && static_cast<size_t>(len) == pathname.size())
- {
- pathname.resize(pathname.size() << 1);
- len = readlink(selfname, pathname.data(), pathname.size());
- }
- if(len <= 0)
- {
- WARN("Failed to readlink %s: %s\n", selfname, strerror(errno));
- return ret;
- }
-
- pathname.resize(len);
- }
- while(!pathname.empty() && pathname.back() == 0)
- pathname.pop_back();
-
- auto sep = std::find(pathname.crbegin(), pathname.crend(), '/');
- if(sep != pathname.crend())
- {
- ret.path = std::string(pathname.cbegin(), sep.base()-1);
- ret.fname = std::string(sep.base(), pathname.cend());
- }
- else
- ret.fname = std::string(pathname.cbegin(), pathname.cend());
-
- TRACE("Got binary: %s, %s\n", ret.path.c_str(), ret.fname.c_str());
- return ret;
-}
-
-
-#ifdef HAVE_DLFCN_H
-
-void *LoadLib(const char *name)
-{
- dlerror();
- void *handle{dlopen(name, RTLD_NOW)};
- const char *err{dlerror()};
- if(err) handle = nullptr;
- return handle;
-}
-void CloseLib(void *handle)
-{ dlclose(handle); }
-void *GetSymbol(void *handle, const char *name)
-{
- dlerror();
- void *sym{dlsym(handle, name)};
- const char *err{dlerror()};
- if(err)
- {
- WARN("Failed to load %s: %s\n", name, err);
- sym = nullptr;
- }
- return sym;
-}
-
-#endif /* HAVE_DLFCN_H */
-
-void al_print(FILE *logfile, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vfprintf(logfile, fmt, ap);
- va_end(ap);
-
- fflush(logfile);
-}
-
-
-static void DirectorySearch(const char *path, const char *ext, al::vector<std::string> *const results)
-{
- TRACE("Searching %s for *%s\n", path, ext);
- DIR *dir{opendir(path)};
- if(dir != nullptr)
- {
- const size_t extlen = strlen(ext);
- size_t base = results->size();
-
- struct dirent *dirent;
- while((dirent=readdir(dir)) != nullptr)
- {
- if(strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
- continue;
-
- size_t len{strlen(dirent->d_name)};
- if(len <= extlen) continue;
- if(strcasecmp(dirent->d_name+len-extlen, ext) != 0)
- continue;
-
- results->emplace_back();
- std::string &str = results->back();
- str = path;
- if(str.back() != '/')
- str.push_back('/');
- str += dirent->d_name;
- TRACE("Got result %s\n", str.c_str());
- }
- closedir(dir);
-
- std::sort(results->begin()+base, results->end());
- }
-}
-
-al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
-{
- static std::mutex search_lock;
- std::lock_guard<std::mutex> _{search_lock};
-
- al::vector<std::string> results;
- if(subdir[0] == '/')
- {
- DirectorySearch(subdir, ext, &results);
- return results;
- }
-
- /* Search the app-local directory. */
- const char *str{getenv("ALSOFT_LOCAL_PATH")};
- if(str && *str != '\0')
- DirectorySearch(str, ext, &results);
- else
- {
- al::vector<char> cwdbuf(256);
- while(!getcwd(cwdbuf.data(), cwdbuf.size()))
- {
- if(errno != ERANGE)
- {
- cwdbuf.clear();
- break;
- }
- cwdbuf.resize(cwdbuf.size() << 1);
- }
- if(cwdbuf.empty())
- DirectorySearch(".", ext, &results);
- else
- {
- DirectorySearch(cwdbuf.data(), ext, &results);
- cwdbuf.clear();
- }
- }
-
- // Search local data dir
- if((str=getenv("XDG_DATA_HOME")) != nullptr && str[0] != '\0')
- {
- std::string path{str};
- if(path.back() != '/')
- path += '/';
- path += subdir;
- DirectorySearch(path.c_str(), ext, &results);
- }
- else if((str=getenv("HOME")) != nullptr && str[0] != '\0')
- {
- std::string path{str};
- if(path.back() == '/')
- path.pop_back();
- path += "/.local/share/";
- path += subdir;
- DirectorySearch(path.c_str(), ext, &results);
- }
-
- // Search global data dirs
- if((str=getenv("XDG_DATA_DIRS")) == nullptr || str[0] == '\0')
- str = "/usr/local/share/:/usr/share/";
-
- const char *next{str};
- while((str=next) != nullptr && str[0] != '\0')
- {
- next = strchr(str, ':');
-
- std::string path = (next ? std::string(str, next++) : std::string(str));
- if(path.empty()) continue;
-
- if(path.back() != '/')
- path += '/';
- path += subdir;
-
- DirectorySearch(path.c_str(), ext, &results);
- }
-
- return results;
-}
-
-void SetRTPriority()
-{
- bool failed = false;
-#if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
- if(RTPrioLevel > 0)
- {
- struct sched_param param;
- /* Use the minimum real-time priority possible for now (on Linux this
- * should be 1 for SCHED_RR) */
- param.sched_priority = sched_get_priority_min(SCHED_RR);
- failed = !!pthread_setschedparam(pthread_self(), SCHED_RR, &param);
- }
-#else
- /* Real-time priority not available */
- failed = (RTPrioLevel>0);
-#endif
- if(failed)
- ERR("Failed to set priority level for thread\n");
-}
-
-#endif
diff --git a/Alc/hrtf.cpp b/Alc/hrtf.cpp
deleted file mode 100644
index 786c4c5d..00000000
--- a/Alc/hrtf.cpp
+++ /dev/null
@@ -1,1400 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2011 by Chris Robinson
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "hrtf.h"
-
-#include <algorithm>
-#include <array>
-#include <cassert>
-#include <cctype>
-#include <cstdint>
-#include <cstdio>
-#include <cstring>
-#include <functional>
-#include <fstream>
-#include <iterator>
-#include <memory>
-#include <mutex>
-#include <new>
-#include <numeric>
-#include <utility>
-
-#include "AL/al.h"
-
-#include "alcmain.h"
-#include "alconfig.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-#include "aloptional.h"
-#include "alspan.h"
-#include "compat.h"
-#include "filters/splitter.h"
-#include "logging.h"
-#include "math_defs.h"
-#include "opthelpers.h"
-
-
-struct HrtfHandle {
- std::unique_ptr<HrtfEntry> entry;
- al::FlexArray<char> filename;
-
- HrtfHandle(size_t fname_len) : filename{fname_len} { }
- HrtfHandle(const HrtfHandle&) = delete;
- HrtfHandle& operator=(const HrtfHandle&) = delete;
-
- static std::unique_ptr<HrtfHandle> Create(size_t fname_len);
- static constexpr size_t Sizeof(size_t length) noexcept
- {
- return maxz(sizeof(HrtfHandle),
- al::FlexArray<char>::Sizeof(length, offsetof(HrtfHandle, filename)));
- }
-
- DEF_PLACE_NEWDEL()
-};
-
-std::unique_ptr<HrtfHandle> HrtfHandle::Create(size_t fname_len)
-{
- void *ptr{al_calloc(alignof(HrtfHandle), HrtfHandle::Sizeof(fname_len))};
- return std::unique_ptr<HrtfHandle>{new (ptr) HrtfHandle{fname_len}};
-}
-
-namespace {
-
-using namespace std::placeholders;
-
-using HrtfHandlePtr = std::unique_ptr<HrtfHandle>;
-
-/* Data set limits must be the same as or more flexible than those defined in
- * the makemhr utility.
- */
-#define MIN_IR_SIZE (8)
-#define MAX_IR_SIZE (512)
-#define MOD_IR_SIZE (2)
-
-#define MIN_FD_COUNT (1)
-#define MAX_FD_COUNT (16)
-
-#define MIN_FD_DISTANCE (0.05f)
-#define MAX_FD_DISTANCE (2.5f)
-
-#define MIN_EV_COUNT (5)
-#define MAX_EV_COUNT (128)
-
-#define MIN_AZ_COUNT (1)
-#define MAX_AZ_COUNT (128)
-
-#define MAX_HRIR_DELAY (HRTF_HISTORY_LENGTH-1)
-
-constexpr ALchar magicMarker00[8]{'M','i','n','P','H','R','0','0'};
-constexpr ALchar magicMarker01[8]{'M','i','n','P','H','R','0','1'};
-constexpr ALchar magicMarker02[8]{'M','i','n','P','H','R','0','2'};
-
-/* First value for pass-through coefficients (remaining are 0), used for omni-
- * directional sounds. */
-constexpr ALfloat PassthruCoeff{0.707106781187f/*sqrt(0.5)*/};
-
-std::mutex LoadedHrtfLock;
-al::vector<HrtfHandlePtr> LoadedHrtfs;
-
-
-class databuf final : public std::streambuf {
- int_type underflow() override
- { return traits_type::eof(); }
-
- pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override
- {
- if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
- return traits_type::eof();
-
- char_type *cur;
- switch(whence)
- {
- case std::ios_base::beg:
- if(offset < 0 || offset > egptr()-eback())
- return traits_type::eof();
- cur = eback() + offset;
- break;
-
- case std::ios_base::cur:
- if((offset >= 0 && offset > egptr()-gptr()) ||
- (offset < 0 && -offset > gptr()-eback()))
- return traits_type::eof();
- cur = gptr() + offset;
- break;
-
- case std::ios_base::end:
- if(offset > 0 || -offset > egptr()-eback())
- return traits_type::eof();
- cur = egptr() + offset;
- break;
-
- default:
- return traits_type::eof();
- }
-
- setg(eback(), cur, egptr());
- return cur - eback();
- }
-
- pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override
- {
- // Simplified version of seekoff
- if((mode&std::ios_base::out) || !(mode&std::ios_base::in))
- return traits_type::eof();
-
- if(pos < 0 || pos > egptr()-eback())
- return traits_type::eof();
-
- setg(eback(), eback() + static_cast<size_t>(pos), egptr());
- return pos;
- }
-
-public:
- databuf(const char_type *start, const char_type *end) noexcept
- {
- setg(const_cast<char_type*>(start), const_cast<char_type*>(start),
- const_cast<char_type*>(end));
- }
-};
-
-class idstream final : public std::istream {
- databuf mStreamBuf;
-
-public:
- idstream(const char *start, const char *end)
- : std::istream{nullptr}, mStreamBuf{start, end}
- { init(&mStreamBuf); }
-};
-
-
-struct IdxBlend { ALsizei idx; ALfloat blend; };
-/* Calculate the elevation index given the polar elevation in radians. This
- * will return an index between 0 and (evcount - 1).
- */
-IdxBlend CalcEvIndex(ALsizei evcount, ALfloat ev)
-{
- ev = (al::MathDefs<float>::Pi()*0.5f + ev) * (evcount-1) / al::MathDefs<float>::Pi();
- ALsizei idx{float2int(ev)};
-
- return IdxBlend{mini(idx, evcount-1), ev-idx};
-}
-
-/* Calculate the azimuth index given the polar azimuth in radians. This will
- * return an index between 0 and (azcount - 1).
- */
-IdxBlend CalcAzIndex(ALsizei azcount, ALfloat az)
-{
- az = (al::MathDefs<float>::Tau()+az) * azcount / al::MathDefs<float>::Tau();
- ALsizei idx{float2int(az)};
-
- return IdxBlend{idx%azcount, az-idx};
-}
-
-} // namespace
-
-
-/* Calculates static HRIR coefficients and delays for the given polar elevation
- * and azimuth in radians. The coefficients are normalized.
- */
-void GetHrtfCoeffs(const HrtfEntry *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat distance,
- ALfloat spread, HrirArray<ALfloat> &coeffs, ALsizei (&delays)[2])
-{
- const ALfloat dirfact{1.0f - (spread / al::MathDefs<float>::Tau())};
-
- const auto *field = Hrtf->field;
- const auto *field_end = field + Hrtf->fdCount-1;
- ALsizei ebase{0};
- while(distance < field->distance && field != field_end)
- {
- ebase += field->evCount;
- ++field;
- }
-
- /* Claculate the elevation indinces. */
- const auto elev0 = CalcEvIndex(field->evCount, elevation);
- const ALsizei elev1_idx{mini(elev0.idx+1, field->evCount-1)};
- const ALsizei ir0offset{Hrtf->elev[ebase + elev0.idx].irOffset};
- const ALsizei ir1offset{Hrtf->elev[ebase + elev1_idx].irOffset};
-
- /* Calculate azimuth indices. */
- const auto az0 = CalcAzIndex(Hrtf->elev[ebase + elev0.idx].azCount, azimuth);
- const auto az1 = CalcAzIndex(Hrtf->elev[ebase + elev1_idx].azCount, azimuth);
-
- /* Calculate the HRIR indices to blend. */
- ALsizei idx[4]{
- ir0offset + az0.idx,
- ir0offset + ((az0.idx+1) % Hrtf->elev[ebase + elev0.idx].azCount),
- ir1offset + az1.idx,
- ir1offset + ((az1.idx+1) % Hrtf->elev[ebase + elev1_idx].azCount)
- };
-
- /* Calculate bilinear blending weights, attenuated according to the
- * directional panning factor.
- */
- const ALfloat blend[4]{
- (1.0f-elev0.blend) * (1.0f-az0.blend) * dirfact,
- (1.0f-elev0.blend) * ( az0.blend) * dirfact,
- ( elev0.blend) * (1.0f-az1.blend) * dirfact,
- ( elev0.blend) * ( az1.blend) * dirfact
- };
-
- /* Calculate the blended HRIR delays. */
- delays[0] = fastf2i(
- Hrtf->delays[idx[0]][0]*blend[0] + Hrtf->delays[idx[1]][0]*blend[1] +
- Hrtf->delays[idx[2]][0]*blend[2] + Hrtf->delays[idx[3]][0]*blend[3]
- );
- delays[1] = fastf2i(
- Hrtf->delays[idx[0]][1]*blend[0] + Hrtf->delays[idx[1]][1]*blend[1] +
- Hrtf->delays[idx[2]][1]*blend[2] + Hrtf->delays[idx[3]][1]*blend[3]
- );
-
- const ALsizei irSize{Hrtf->irSize};
- ASSUME(irSize >= MIN_IR_SIZE);
-
- /* Calculate the sample offsets for the HRIR indices. */
- idx[0] *= irSize;
- idx[1] *= irSize;
- idx[2] *= irSize;
- idx[3] *= irSize;
-
- /* Calculate the blended HRIR coefficients. */
- ALfloat *coeffout{al::assume_aligned<16>(&coeffs[0][0])};
- coeffout[0] = PassthruCoeff * (1.0f-dirfact);
- coeffout[1] = PassthruCoeff * (1.0f-dirfact);
- std::fill(coeffout+2, coeffout + irSize*2, 0.0f);
- for(ALsizei c{0};c < 4;c++)
- {
- const ALfloat *srccoeffs{al::assume_aligned<16>(Hrtf->coeffs[idx[c]])};
- const ALfloat mult{blend[c]};
- auto blend_coeffs = [mult](const ALfloat src, const ALfloat coeff) noexcept -> ALfloat
- { return src*mult + coeff; };
- std::transform(srccoeffs, srccoeffs + irSize*2, coeffout, coeffout, blend_coeffs);
- }
-}
-
-
-std::unique_ptr<DirectHrtfState> DirectHrtfState::Create(size_t num_chans)
-{
- void *ptr{al_calloc(16, DirectHrtfState::Sizeof(num_chans))};
- return std::unique_ptr<DirectHrtfState>{new (ptr) DirectHrtfState{num_chans}};
-}
-
-void BuildBFormatHrtf(const HrtfEntry *Hrtf, DirectHrtfState *state, const ALuint NumChannels,
- const AngularPoint *AmbiPoints, const ALfloat (*RESTRICT AmbiMatrix)[MAX_AMBI_CHANNELS],
- const size_t AmbiCount, const ALfloat *RESTRICT AmbiOrderHFGain)
-{
- static constexpr int OrderFromChan[MAX_AMBI_CHANNELS]{
- 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3,
- };
- /* Set this to true for dual-band HRTF processing. May require better
- * calculation of the new IR length to deal with the head and tail
- * generated by the HF scaling.
- */
- static constexpr bool DualBand{true};
-
- ASSUME(NumChannels > 0);
- ASSUME(AmbiCount > 0);
-
- auto &field = Hrtf->field[0];
- ALsizei min_delay{HRTF_HISTORY_LENGTH};
- ALsizei max_delay{0};
- auto idx = al::vector<ALsizei>(AmbiCount);
- auto calc_idxs = [Hrtf,&field,&max_delay,&min_delay](const AngularPoint &pt) noexcept -> ALsizei
- {
- /* Calculate elevation index. */
- const auto evidx = clampi(
- static_cast<ALsizei>((90.0f+pt.Elev)*(field.evCount-1)/180.0f + 0.5f),
- 0, field.evCount-1);
-
- const ALsizei azcount{Hrtf->elev[evidx].azCount};
- const ALsizei iroffset{Hrtf->elev[evidx].irOffset};
-
- /* Calculate azimuth index for this elevation. */
- const auto azidx = static_cast<ALsizei>((360.0f+pt.Azim)*azcount/360.0f + 0.5f) % azcount;
-
- /* Calculate the index for the impulse response. */
- ALsizei idx{iroffset + azidx};
-
- min_delay = mini(min_delay, mini(Hrtf->delays[idx][0], Hrtf->delays[idx][1]));
- max_delay = maxi(max_delay, maxi(Hrtf->delays[idx][0], Hrtf->delays[idx][1]));
-
- return idx;
- };
- std::transform(AmbiPoints, AmbiPoints+AmbiCount, idx.begin(), calc_idxs);
-
- /* For dual-band processing, add a 16-sample delay to compensate for the HF
- * scale on the minimum-phase response.
- */
- static constexpr ALsizei base_delay{DualBand ? 16 : 0};
- const ALdouble xover_norm{400.0 / Hrtf->sampleRate};
- BandSplitterR<double> splitter{xover_norm};
-
- auto tmpres = al::vector<HrirArray<ALdouble>>(NumChannels);
- auto tmpfilt = al::vector<std::array<ALdouble,HRIR_LENGTH*4>>(3);
- for(size_t c{0u};c < AmbiCount;++c)
- {
- const ALfloat (*fir)[2]{&Hrtf->coeffs[idx[c] * Hrtf->irSize]};
- const ALsizei ldelay{Hrtf->delays[idx[c]][0] - min_delay + base_delay};
- const ALsizei rdelay{Hrtf->delays[idx[c]][1] - min_delay + base_delay};
-
- if(!DualBand)
- {
- /* For single-band decoding, apply the HF scale to the response. */
- for(ALuint i{0u};i < NumChannels;++i)
- {
- const ALdouble mult{ALdouble{AmbiOrderHFGain[OrderFromChan[i]]} *
- AmbiMatrix[c][i]};
- const ALsizei numirs{mini(Hrtf->irSize, HRIR_LENGTH-maxi(ldelay, rdelay))};
- ALsizei lidx{ldelay}, ridx{rdelay};
- for(ALsizei j{0};j < numirs;++j)
- {
- tmpres[i][lidx++][0] += fir[j][0] * mult;
- tmpres[i][ridx++][1] += fir[j][1] * mult;
- }
- }
- continue;
- }
-
- /* For dual-band processing, the HRIR needs to be split into low and
- * high frequency responses. The band-splitter alone creates frequency-
- * dependent phase-shifts, which is not ideal. To counteract it,
- * combine it with a backwards phase-shift.
- */
-
- /* Load the (left) HRIR backwards, into a temp buffer with padding. */
- std::fill(tmpfilt[2].begin(), tmpfilt[2].end(), 0.0);
- std::transform(fir, fir+Hrtf->irSize, tmpfilt[2].rbegin() + HRIR_LENGTH*3,
- [](const ALfloat (&ir)[2]) noexcept -> ALdouble { return ir[0]; });
-
- /* Apply the all-pass on the reversed signal and reverse the resulting
- * sample array. This produces the forward response with a backwards
- * phase-shift (+n degrees becomes -n degrees).
- */
- splitter.applyAllpass(tmpfilt[2].data(), static_cast<int>(tmpfilt[2].size()));
- std::reverse(tmpfilt[2].begin(), tmpfilt[2].end());
-
- /* Now apply the band-splitter. This applies the normal phase-shift,
- * which cancels out with the backwards phase-shift to get the original
- * phase on the split signal.
- */
- splitter.clear();
- splitter.process(tmpfilt[0].data(), tmpfilt[1].data(), tmpfilt[2].data(),
- static_cast<int>(tmpfilt[2].size()));
-
- /* Apply left ear response with delay and HF scale. */
- for(ALuint i{0u};i < NumChannels;++i)
- {
- const ALdouble mult{AmbiMatrix[c][i]};
- const ALdouble hfgain{AmbiOrderHFGain[OrderFromChan[i]]};
- ALsizei j{HRIR_LENGTH*3 - ldelay};
- for(ALsizei lidx{0};lidx < HRIR_LENGTH;++lidx,++j)
- tmpres[i][lidx][0] += (tmpfilt[0][j]*hfgain + tmpfilt[1][j]) * mult;
- }
-
- /* Now run the same process on the right HRIR. */
- std::fill(tmpfilt[2].begin(), tmpfilt[2].end(), 0.0);
- std::transform(fir, fir+Hrtf->irSize, tmpfilt[2].rbegin() + HRIR_LENGTH*3,
- [](const ALfloat (&ir)[2]) noexcept -> ALdouble { return ir[1]; });
-
- splitter.applyAllpass(tmpfilt[2].data(), static_cast<int>(tmpfilt[2].size()));
- std::reverse(tmpfilt[2].begin(), tmpfilt[2].end());
-
- splitter.clear();
- splitter.process(tmpfilt[0].data(), tmpfilt[1].data(), tmpfilt[2].data(),
- static_cast<int>(tmpfilt[2].size()));
-
- for(ALuint i{0u};i < NumChannels;++i)
- {
- const ALdouble mult{AmbiMatrix[c][i]};
- const ALdouble hfgain{AmbiOrderHFGain[OrderFromChan[i]]};
- ALsizei j{HRIR_LENGTH*3 - rdelay};
- for(ALsizei ridx{0};ridx < HRIR_LENGTH;++ridx,++j)
- tmpres[i][ridx][1] += (tmpfilt[0][j]*hfgain + tmpfilt[1][j]) * mult;
- }
- }
- tmpfilt.clear();
- idx.clear();
-
- for(ALuint i{0u};i < NumChannels;++i)
- {
- auto copy_arr = [](const std::array<double,2> &in) noexcept -> std::array<float,2>
- { return std::array<float,2>{{static_cast<float>(in[0]), static_cast<float>(in[1])}}; };
- std::transform(tmpres[i].begin(), tmpres[i].end(), state->Chan[i].Coeffs.begin(),
- copy_arr);
- }
- tmpres.clear();
-
- ALsizei max_length{HRIR_LENGTH};
- /* Increase the IR size by double the base delay with dual-band processing
- * to account for the head and tail from the HF response scale.
- */
- const ALsizei irsize{mini(Hrtf->irSize + base_delay*2, max_length)};
- max_length = mini(max_delay-min_delay + irsize, max_length);
-
- /* Round up to the next IR size multiple. */
- max_length += MOD_IR_SIZE-1;
- max_length -= max_length%MOD_IR_SIZE;
-
- TRACE("Skipped delay: %d, max delay: %d, new FIR length: %d\n",
- min_delay, max_delay-min_delay, max_length);
- state->IrSize = max_length;
-}
-
-
-namespace {
-
-std::unique_ptr<HrtfEntry> CreateHrtfStore(ALuint rate, ALsizei irSize, const ALsizei fdCount,
- const ALubyte *evCount, const ALfloat *distance, const ALushort *azCount,
- const ALushort *irOffset, ALsizei irCount, const ALfloat (*coeffs)[2],
- const ALubyte (*delays)[2], const char *filename)
-{
- std::unique_ptr<HrtfEntry> Hrtf;
-
- ALsizei evTotal{std::accumulate(evCount, evCount+fdCount, 0)};
- size_t total{sizeof(HrtfEntry)};
- total = RoundUp(total, alignof(HrtfEntry::Field)); /* Align for field infos */
- total += sizeof(HrtfEntry::Field)*fdCount;
- total = RoundUp(total, alignof(HrtfEntry::Elevation)); /* Align for elevation infos */
- total += sizeof(Hrtf->elev[0])*evTotal;
- total = RoundUp(total, 16); /* Align for coefficients using SIMD */
- total += sizeof(Hrtf->coeffs[0])*irSize*irCount;
- total += sizeof(Hrtf->delays[0])*irCount;
-
- Hrtf.reset(new (al_calloc(16, total)) HrtfEntry{});
- if(!Hrtf)
- ERR("Out of memory allocating storage for %s.\n", filename);
- else
- {
- InitRef(&Hrtf->ref, 1u);
- Hrtf->sampleRate = rate;
- Hrtf->irSize = irSize;
- Hrtf->fdCount = fdCount;
-
- /* Set up pointers to storage following the main HRTF struct. */
- char *base = reinterpret_cast<char*>(Hrtf.get());
- uintptr_t offset = sizeof(HrtfEntry);
-
- offset = RoundUp(offset, alignof(HrtfEntry::Field)); /* Align for field infos */
- auto field_ = reinterpret_cast<HrtfEntry::Field*>(base + offset);
- offset += sizeof(field_[0])*fdCount;
-
- offset = RoundUp(offset, alignof(HrtfEntry::Elevation)); /* Align for elevation infos */
- auto elev_ = reinterpret_cast<HrtfEntry::Elevation*>(base + offset);
- offset += sizeof(elev_[0])*evTotal;
-
- offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */
- auto coeffs_ = reinterpret_cast<ALfloat(*)[2]>(base + offset);
- offset += sizeof(coeffs_[0])*irSize*irCount;
-
- auto delays_ = reinterpret_cast<ALubyte(*)[2]>(base + offset);
- offset += sizeof(delays_[0])*irCount;
-
- assert(offset == total);
-
- /* Copy input data to storage. */
- for(ALsizei i{0};i < fdCount;i++)
- {
- field_[i].distance = distance[i];
- field_[i].evCount = evCount[i];
- }
- for(ALsizei i{0};i < evTotal;i++)
- {
- elev_[i].azCount = azCount[i];
- elev_[i].irOffset = irOffset[i];
- }
- for(ALsizei i{0};i < irSize*irCount;i++)
- {
- coeffs_[i][0] = coeffs[i][0];
- coeffs_[i][1] = coeffs[i][1];
- }
- for(ALsizei i{0};i < irCount;i++)
- {
- delays_[i][0] = delays[i][0];
- delays_[i][1] = delays[i][1];
- }
-
- /* Finally, assign the storage pointers. */
- Hrtf->field = field_;
- Hrtf->elev = elev_;
- Hrtf->coeffs = coeffs_;
- Hrtf->delays = delays_;
- }
-
- return Hrtf;
-}
-
-ALubyte GetLE_ALubyte(std::istream &data)
-{
- return static_cast<ALubyte>(data.get());
-}
-
-ALshort GetLE_ALshort(std::istream &data)
-{
- int ret = data.get();
- ret |= data.get() << 8;
- return static_cast<ALshort>((ret^32768) - 32768);
-}
-
-ALushort GetLE_ALushort(std::istream &data)
-{
- int ret = data.get();
- ret |= data.get() << 8;
- return static_cast<ALushort>(ret);
-}
-
-ALint GetLE_ALint24(std::istream &data)
-{
- int ret = data.get();
- ret |= data.get() << 8;
- ret |= data.get() << 16;
- return (ret^8388608) - 8388608;
-}
-
-ALuint GetLE_ALuint(std::istream &data)
-{
- int ret = data.get();
- ret |= data.get() << 8;
- ret |= data.get() << 16;
- ret |= data.get() << 24;
- return ret;
-}
-
-std::unique_ptr<HrtfEntry> LoadHrtf00(std::istream &data, const char *filename)
-{
- ALuint rate{GetLE_ALuint(data)};
- ALushort irCount{GetLE_ALushort(data)};
- ALushort irSize{GetLE_ALushort(data)};
- ALubyte evCount{GetLE_ALubyte(data)};
- if(!data || data.eof())
- {
- ERR("Failed reading %s\n", filename);
- return nullptr;
- }
-
- ALboolean failed{AL_FALSE};
- if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
- {
- ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
- irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
- failed = AL_TRUE;
- }
- if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
- {
- ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
- evCount, MIN_EV_COUNT, MAX_EV_COUNT);
- failed = AL_TRUE;
- }
- if(failed)
- return nullptr;
-
- al::vector<ALushort> evOffset(evCount);
- for(auto &val : evOffset)
- val = GetLE_ALushort(data);
- if(!data || data.eof())
- {
- ERR("Failed reading %s\n", filename);
- return nullptr;
- }
- for(ALsizei i{1};i < evCount;i++)
- {
- if(evOffset[i] <= evOffset[i-1])
- {
- ERR("Invalid evOffset: evOffset[%d]=%d (last=%d)\n",
- i, evOffset[i], evOffset[i-1]);
- failed = AL_TRUE;
- }
- }
- if(irCount <= evOffset.back())
- {
- ERR("Invalid evOffset: evOffset[%zu]=%d (irCount=%d)\n",
- evOffset.size()-1, evOffset.back(), irCount);
- failed = AL_TRUE;
- }
- if(failed)
- return nullptr;
-
- al::vector<ALushort> azCount(evCount);
- for(ALsizei i{1};i < evCount;i++)
- {
- azCount[i-1] = evOffset[i] - evOffset[i-1];
- if(azCount[i-1] < MIN_AZ_COUNT || azCount[i-1] > MAX_AZ_COUNT)
- {
- ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
- i-1, azCount[i-1], MIN_AZ_COUNT, MAX_AZ_COUNT);
- failed = AL_TRUE;
- }
- }
- azCount.back() = irCount - evOffset.back();
- if(azCount.back() < MIN_AZ_COUNT || azCount.back() > MAX_AZ_COUNT)
- {
- ERR("Unsupported azimuth count: azCount[%zu]=%d (%d to %d)\n",
- azCount.size()-1, azCount.back(), MIN_AZ_COUNT, MAX_AZ_COUNT);
- failed = AL_TRUE;
- }
- if(failed)
- return nullptr;
-
- al::vector<std::array<ALfloat,2>> coeffs(irSize*irCount);
- al::vector<std::array<ALubyte,2>> delays(irCount);
- for(auto &val : coeffs)
- val[0] = GetLE_ALshort(data) / 32768.0f;
- for(auto &val : delays)
- val[0] = GetLE_ALubyte(data);
- if(!data || data.eof())
- {
- ERR("Failed reading %s\n", filename);
- return nullptr;
- }
- for(ALsizei i{0};i < irCount;i++)
- {
- if(delays[i][0] > MAX_HRIR_DELAY)
- {
- ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY);
- failed = AL_TRUE;
- }
- }
- if(failed)
- return nullptr;
-
- /* Mirror the left ear responses to the right ear. */
- for(ALsizei i{0};i < evCount;i++)
- {
- const ALushort evoffset{evOffset[i]};
- const ALushort azcount{azCount[i]};
- for(ALsizei j{0};j < azcount;j++)
- {
- const ALsizei lidx{evoffset + j};
- const ALsizei ridx{evoffset + ((azcount-j) % azcount)};
-
- for(ALsizei k{0};k < irSize;k++)
- coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0];
- delays[ridx][1] = delays[lidx][0];
- }
- }
-
- static constexpr ALfloat distance{0.0f};
- return CreateHrtfStore(rate, irSize, 1, &evCount, &distance, azCount.data(), evOffset.data(),
- irCount, &reinterpret_cast<ALfloat(&)[2]>(coeffs[0]),
- &reinterpret_cast<ALubyte(&)[2]>(delays[0]), filename);
-}
-
-std::unique_ptr<HrtfEntry> LoadHrtf01(std::istream &data, const char *filename)
-{
- ALuint rate{GetLE_ALuint(data)};
- ALushort irSize{GetLE_ALubyte(data)};
- ALubyte evCount{GetLE_ALubyte(data)};
- if(!data || data.eof())
- {
- ERR("Failed reading %s\n", filename);
- return nullptr;
- }
-
- ALboolean failed{AL_FALSE};
- if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
- {
- ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
- irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
- failed = AL_TRUE;
- }
- if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
- {
- ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
- evCount, MIN_EV_COUNT, MAX_EV_COUNT);
- failed = AL_TRUE;
- }
- if(failed)
- return nullptr;
-
- al::vector<ALushort> azCount(evCount);
- std::generate(azCount.begin(), azCount.end(), std::bind(GetLE_ALubyte, std::ref(data)));
- if(!data || data.eof())
- {
- ERR("Failed reading %s\n", filename);
- return nullptr;
- }
- for(ALsizei i{0};i < evCount;++i)
- {
- if(azCount[i] < MIN_AZ_COUNT || azCount[i] > MAX_AZ_COUNT)
- {
- ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
- i, azCount[i], MIN_AZ_COUNT, MAX_AZ_COUNT);
- failed = AL_TRUE;
- }
- }
- if(failed)
- return nullptr;
-
- al::vector<ALushort> evOffset(evCount);
- evOffset[0] = 0;
- ALushort irCount{azCount[0]};
- for(ALsizei i{1};i < evCount;i++)
- {
- evOffset[i] = evOffset[i-1] + azCount[i-1];
- irCount += azCount[i];
- }
-
- al::vector<std::array<ALfloat,2>> coeffs(irSize*irCount);
- al::vector<std::array<ALubyte,2>> delays(irCount);
- for(auto &val : coeffs)
- val[0] = GetLE_ALshort(data) / 32768.0f;
- for(auto &val : delays)
- val[0] = GetLE_ALubyte(data);
- if(!data || data.eof())
- {
- ERR("Failed reading %s\n", filename);
- return nullptr;
- }
- for(ALsizei i{0};i < irCount;i++)
- {
- if(delays[i][0] > MAX_HRIR_DELAY)
- {
- ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY);
- failed = AL_TRUE;
- }
- }
- if(failed)
- return nullptr;
-
- /* Mirror the left ear responses to the right ear. */
- for(ALsizei i{0};i < evCount;i++)
- {
- const ALushort evoffset{evOffset[i]};
- const ALushort azcount{azCount[i]};
- for(ALsizei j{0};j < azcount;j++)
- {
- const ALsizei lidx{evoffset + j};
- const ALsizei ridx{evoffset + ((azcount-j) % azcount)};
-
- for(ALsizei k{0};k < irSize;k++)
- coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0];
- delays[ridx][1] = delays[lidx][0];
- }
- }
-
- static constexpr ALfloat distance{0.0f};
- return CreateHrtfStore(rate, irSize, 1, &evCount, &distance, azCount.data(), evOffset.data(),
- irCount, &reinterpret_cast<ALfloat(&)[2]>(coeffs[0]),
- &reinterpret_cast<ALubyte(&)[2]>(delays[0]), filename);
-}
-
-#define SAMPLETYPE_S16 0
-#define SAMPLETYPE_S24 1
-
-#define CHANTYPE_LEFTONLY 0
-#define CHANTYPE_LEFTRIGHT 1
-
-std::unique_ptr<HrtfEntry> LoadHrtf02(std::istream &data, const char *filename)
-{
- ALuint rate{GetLE_ALuint(data)};
- ALubyte sampleType{GetLE_ALubyte(data)};
- ALubyte channelType{GetLE_ALubyte(data)};
- ALushort irSize{GetLE_ALubyte(data)};
- ALubyte fdCount{GetLE_ALubyte(data)};
- if(!data || data.eof())
- {
- ERR("Failed reading %s\n", filename);
- return nullptr;
- }
-
- ALboolean failed{AL_FALSE};
- if(sampleType > SAMPLETYPE_S24)
- {
- ERR("Unsupported sample type: %d\n", sampleType);
- failed = AL_TRUE;
- }
- if(channelType > CHANTYPE_LEFTRIGHT)
- {
- ERR("Unsupported channel type: %d\n", channelType);
- failed = AL_TRUE;
- }
-
- if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
- {
- ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
- irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
- failed = AL_TRUE;
- }
- if(fdCount < 1 || fdCount > MAX_FD_COUNT)
- {
- ERR("Multiple field-depths not supported: fdCount=%d (%d to %d)\n",
- fdCount, MIN_FD_COUNT, MAX_FD_COUNT);
- failed = AL_TRUE;
- }
- if(failed)
- return nullptr;
-
- al::vector<ALfloat> distance(fdCount);
- al::vector<ALubyte> evCount(fdCount);
- al::vector<ALushort> azCount;
- for(ALsizei f{0};f < fdCount;f++)
- {
- distance[f] = GetLE_ALushort(data) / 1000.0f;
- evCount[f] = GetLE_ALubyte(data);
- if(!data || data.eof())
- {
- ERR("Failed reading %s\n", filename);
- return nullptr;
- }
-
- if(distance[f] < MIN_FD_DISTANCE || distance[f] > MAX_FD_DISTANCE)
- {
- ERR("Unsupported field distance[%d]=%f (%f to %f meters)\n", f,
- distance[f], MIN_FD_DISTANCE, MAX_FD_DISTANCE);
- failed = AL_TRUE;
- }
- if(f > 0 && distance[f] <= distance[f-1])
- {
- ERR("Field distance[%d] is not after previous (%f > %f)\n", f, distance[f],
- distance[f-1]);
- failed = AL_TRUE;
- }
- if(evCount[f] < MIN_EV_COUNT || evCount[f] > MAX_EV_COUNT)
- {
- ERR("Unsupported elevation count: evCount[%d]=%d (%d to %d)\n", f,
- evCount[f], MIN_EV_COUNT, MAX_EV_COUNT);
- failed = AL_TRUE;
- }
- if(failed)
- return nullptr;
-
- size_t ebase{azCount.size()};
- azCount.resize(ebase + evCount[f]);
- std::generate(azCount.begin()+ebase, azCount.end(),
- std::bind(GetLE_ALubyte, std::ref(data)));
- if(!data || data.eof())
- {
- ERR("Failed reading %s\n", filename);
- return nullptr;
- }
-
- for(ALsizei e{0};e < evCount[f];e++)
- {
- if(azCount[ebase+e] < MIN_AZ_COUNT || azCount[ebase+e] > MAX_AZ_COUNT)
- {
- ERR("Unsupported azimuth count: azCount[%d][%d]=%d (%d to %d)\n", f, e,
- azCount[ebase+e], MIN_AZ_COUNT, MAX_AZ_COUNT);
- failed = AL_TRUE;
- }
- }
- if(failed)
- return nullptr;
- }
-
- al::vector<ALushort> evOffset(azCount.size());
- evOffset[0] = 0;
- std::partial_sum(azCount.cbegin(), azCount.cend()-1, evOffset.begin()+1);
- const ALsizei irTotal{evOffset.back() + azCount.back()};
-
- al::vector<std::array<ALfloat,2>> coeffs(irSize*irTotal);
- al::vector<std::array<ALubyte,2>> delays(irTotal);
- if(channelType == CHANTYPE_LEFTONLY)
- {
- if(sampleType == SAMPLETYPE_S16)
- {
- for(auto &val : coeffs)
- val[0] = GetLE_ALshort(data) / 32768.0f;
- }
- else if(sampleType == SAMPLETYPE_S24)
- {
- for(auto &val : coeffs)
- val[0] = GetLE_ALint24(data) / 8388608.0f;
- }
- for(auto &val : delays)
- val[0] = GetLE_ALubyte(data);
- if(!data || data.eof())
- {
- ERR("Failed reading %s\n", filename);
- return nullptr;
- }
- for(ALsizei i{0};i < irTotal;++i)
- {
- if(delays[i][0] > MAX_HRIR_DELAY)
- {
- ERR("Invalid delays[%d][0]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY);
- failed = AL_TRUE;
- }
- }
- }
- else if(channelType == CHANTYPE_LEFTRIGHT)
- {
- if(sampleType == SAMPLETYPE_S16)
- {
- for(auto &val : coeffs)
- {
- val[0] = GetLE_ALshort(data) / 32768.0f;
- val[1] = GetLE_ALshort(data) / 32768.0f;
- }
- }
- else if(sampleType == SAMPLETYPE_S24)
- {
- for(auto &val : coeffs)
- {
- val[0] = GetLE_ALint24(data) / 8388608.0f;
- val[1] = GetLE_ALint24(data) / 8388608.0f;
- }
- }
- for(auto &val : delays)
- {
- val[0] = GetLE_ALubyte(data);
- val[1] = GetLE_ALubyte(data);
- }
- if(!data || data.eof())
- {
- ERR("Failed reading %s\n", filename);
- return nullptr;
- }
-
- for(ALsizei i{0};i < irTotal;++i)
- {
- if(delays[i][0] > MAX_HRIR_DELAY)
- {
- ERR("Invalid delays[%d][0]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY);
- failed = AL_TRUE;
- }
- if(delays[i][1] > MAX_HRIR_DELAY)
- {
- ERR("Invalid delays[%d][1]: %d (%d)\n", i, delays[i][1], MAX_HRIR_DELAY);
- failed = AL_TRUE;
- }
- }
- }
- if(failed)
- return nullptr;
-
- if(channelType == CHANTYPE_LEFTONLY)
- {
- /* Mirror the left ear responses to the right ear. */
- ALsizei ebase{0};
- for(ALsizei f{0};f < fdCount;f++)
- {
- for(ALsizei e{0};e < evCount[f];e++)
- {
- const ALushort evoffset{evOffset[ebase+e]};
- const ALushort azcount{azCount[ebase+e]};
- for(ALsizei a{0};a < azcount;a++)
- {
- const ALsizei lidx{evoffset + a};
- const ALsizei ridx{evoffset + ((azcount-a) % azcount)};
-
- for(ALsizei k{0};k < irSize;k++)
- coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0];
- delays[ridx][1] = delays[lidx][0];
- }
- }
- ebase += evCount[f];
- }
- }
-
- if(fdCount > 1)
- {
- auto distance_ = al::vector<ALfloat>(distance.size());
- auto evCount_ = al::vector<ALubyte>(evCount.size());
- auto azCount_ = al::vector<ALushort>(azCount.size());
- auto evOffset_ = al::vector<ALushort>(evOffset.size());
- auto coeffs_ = al::vector<float2>(coeffs.size());
- auto delays_ = al::vector<std::array<ALubyte,2>>(delays.size());
-
- /* Simple reverse for the per-field elements. */
- std::reverse_copy(distance.cbegin(), distance.cend(), distance_.begin());
- std::reverse_copy(evCount.cbegin(), evCount.cend(), evCount_.begin());
-
- /* Each field has a group of elevations, which each have an azimuth
- * count. Reverse the order of the groups, keeping the relative order
- * of per-group azimuth counts.
- */
- auto azcnt_end = azCount_.end();
- auto copy_azs = [&azCount,&azcnt_end](const size_t ebase, const ALubyte num_evs) -> size_t
- {
- auto azcnt_src = azCount.begin()+ebase;
- azcnt_end = std::copy_backward(azcnt_src, azcnt_src+num_evs, azcnt_end);
- return ebase + num_evs;
- };
- std::accumulate(evCount.cbegin(), evCount.cend(), size_t{0u}, copy_azs);
- assert(azCount_.begin() == azcnt_end);
-
- /* Reestablish the IR offset for each elevation index, given the new
- * ordering of elevations.
- */
- evOffset_[0] = 0;
- std::partial_sum(azCount_.cbegin(), azCount_.cend()-1, evOffset_.begin()+1);
-
- /* Reverse the order of each field's group of IRs. */
- auto coeffs_end = coeffs_.end();
- auto delays_end = delays_.end();
- auto copy_irs = [irSize,&azCount,&coeffs,&delays,&coeffs_end,&delays_end](const size_t ebase, const ALubyte num_evs) -> size_t
- {
- const ALsizei abase{std::accumulate(azCount.cbegin(), azCount.cbegin()+ebase, 0)};
- const ALsizei num_azs{std::accumulate(azCount.cbegin()+ebase,
- azCount.cbegin() + (ebase+num_evs), 0)};
-
- coeffs_end = std::copy_backward(coeffs.cbegin() + abase*irSize,
- coeffs.cbegin() + (abase+num_azs)*irSize, coeffs_end);
- delays_end = std::copy_backward(delays.cbegin() + abase,
- delays.cbegin() + (abase+num_azs), delays_end);
-
- return ebase + num_evs;
- };
- std::accumulate(evCount.cbegin(), evCount.cend(), size_t{0u}, copy_irs);
- assert(coeffs_.begin() == coeffs_end);
- assert(delays_.begin() == delays_end);
-
- distance = std::move(distance_);
- evCount = std::move(evCount_);
- azCount = std::move(azCount_);
- evOffset = std::move(evOffset_);
- coeffs = std::move(coeffs_);
- delays = std::move(delays_);
- }
-
- return CreateHrtfStore(rate, irSize, fdCount, evCount.data(), distance.data(), azCount.data(),
- evOffset.data(), irTotal, &reinterpret_cast<ALfloat(&)[2]>(coeffs[0]),
- &reinterpret_cast<ALubyte(&)[2]>(delays[0]), filename);
-}
-
-
-bool checkName(al::vector<EnumeratedHrtf> &list, const std::string &name)
-{
- return std::find_if(list.cbegin(), list.cend(),
- [&name](const EnumeratedHrtf &entry)
- { return name == entry.name; }
- ) != list.cend();
-}
-
-void AddFileEntry(al::vector<EnumeratedHrtf> &list, const std::string &filename)
-{
- /* Check if this file has already been loaded globally. */
- auto loaded_entry = LoadedHrtfs.begin();
- for(;loaded_entry != LoadedHrtfs.end();++loaded_entry)
- {
- if(filename != (*loaded_entry)->filename.data())
- continue;
-
- /* Check if this entry has already been added to the list. */
- auto iter = std::find_if(list.cbegin(), list.cend(),
- [loaded_entry](const EnumeratedHrtf &entry) -> bool
- { return loaded_entry->get() == entry.hrtf; }
- );
- if(iter != list.cend())
- {
- TRACE("Skipping duplicate file entry %s\n", filename.c_str());
- return;
- }
-
- break;
- }
-
- if(loaded_entry == LoadedHrtfs.end())
- {
- TRACE("Got new file \"%s\"\n", filename.c_str());
-
- LoadedHrtfs.emplace_back(HrtfHandle::Create(filename.length()+1));
- loaded_entry = LoadedHrtfs.end()-1;
- std::copy(filename.begin(), filename.end(), (*loaded_entry)->filename.begin());
- (*loaded_entry)->filename.back() = '\0';
- }
-
- /* TODO: Get a human-readable name from the HRTF data (possibly coming in a
- * format update). */
- size_t namepos = filename.find_last_of('/')+1;
- if(!namepos) namepos = filename.find_last_of('\\')+1;
-
- size_t extpos{filename.find_last_of('.')};
- if(extpos <= namepos) extpos = std::string::npos;
-
- const std::string basename{(extpos == std::string::npos) ?
- filename.substr(namepos) : filename.substr(namepos, extpos-namepos)};
- std::string newname{basename};
- int count{1};
- while(checkName(list, newname))
- {
- newname = basename;
- newname += " #";
- newname += std::to_string(++count);
- }
- list.emplace_back(EnumeratedHrtf{newname, loaded_entry->get()});
- const EnumeratedHrtf &entry = list.back();
-
- TRACE("Adding file entry \"%s\"\n", entry.name.c_str());
-}
-
-/* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer
- * for input instead of opening the given filename.
- */
-void AddBuiltInEntry(al::vector<EnumeratedHrtf> &list, const std::string &filename, ALuint residx)
-{
- auto loaded_entry = LoadedHrtfs.begin();
- for(;loaded_entry != LoadedHrtfs.end();++loaded_entry)
- {
- if(filename != (*loaded_entry)->filename.data())
- continue;
-
- /* Check if this entry has already been added to the list. */
- auto iter = std::find_if(list.cbegin(), list.cend(),
- [loaded_entry](const EnumeratedHrtf &entry) -> bool
- { return loaded_entry->get() == entry.hrtf; }
- );
- if(iter != list.cend())
- {
- TRACE("Skipping duplicate file entry %s\n", filename.c_str());
- return;
- }
-
- break;
- }
-
- if(loaded_entry == LoadedHrtfs.end())
- {
- TRACE("Got new file \"%s\"\n", filename.c_str());
-
- LoadedHrtfs.emplace_back(HrtfHandle::Create(filename.length()+32));
- loaded_entry = LoadedHrtfs.end()-1;
- snprintf((*loaded_entry)->filename.data(), (*loaded_entry)->filename.size(), "!%u_%s",
- residx, filename.c_str());
- }
-
- /* TODO: Get a human-readable name from the HRTF data (possibly coming in a
- * format update). */
-
- std::string newname{filename};
- int count{1};
- while(checkName(list, newname))
- {
- newname = filename;
- newname += " #";
- newname += std::to_string(++count);
- }
- list.emplace_back(EnumeratedHrtf{newname, loaded_entry->get()});
- const EnumeratedHrtf &entry = list.back();
-
- TRACE("Adding built-in entry \"%s\"\n", entry.name.c_str());
-}
-
-
-#define IDR_DEFAULT_44100_MHR 1
-#define IDR_DEFAULT_48000_MHR 2
-
-using ResData = al::span<const char>;
-#ifndef ALSOFT_EMBED_HRTF_DATA
-
-ResData GetResource(int /*name*/)
-{ return ResData{}; }
-
-#else
-
-#include "default-44100.mhr.h"
-#include "default-48000.mhr.h"
-
-ResData GetResource(int name)
-{
- if(name == IDR_DEFAULT_44100_MHR)
- return {reinterpret_cast<const char*>(hrtf_default_44100), sizeof(hrtf_default_44100)};
- if(name == IDR_DEFAULT_48000_MHR)
- return {reinterpret_cast<const char*>(hrtf_default_48000), sizeof(hrtf_default_48000)};
- return ResData{};
-}
-#endif
-
-} // namespace
-
-
-al::vector<EnumeratedHrtf> EnumerateHrtf(const char *devname)
-{
- al::vector<EnumeratedHrtf> list;
-
- bool usedefaults{true};
- if(auto pathopt = ConfigValueStr(devname, nullptr, "hrtf-paths"))
- {
- const char *pathlist{pathopt->c_str()};
- while(pathlist && *pathlist)
- {
- const char *next, *end;
-
- while(isspace(*pathlist) || *pathlist == ',')
- pathlist++;
- if(*pathlist == '\0')
- continue;
-
- next = strchr(pathlist, ',');
- if(next)
- end = next++;
- else
- {
- end = pathlist + strlen(pathlist);
- usedefaults = false;
- }
-
- while(end != pathlist && isspace(*(end-1)))
- --end;
- if(end != pathlist)
- {
- const std::string pname{pathlist, end};
- for(const auto &fname : SearchDataFiles(".mhr", pname.c_str()))
- AddFileEntry(list, fname);
- }
-
- pathlist = next;
- }
- }
- else if(ConfigValueExists(devname, nullptr, "hrtf_tables"))
- ERR("The hrtf_tables option is deprecated, please use hrtf-paths instead.\n");
-
- if(usedefaults)
- {
- for(const auto &fname : SearchDataFiles(".mhr", "openal/hrtf"))
- AddFileEntry(list, fname);
-
- if(!GetResource(IDR_DEFAULT_44100_MHR).empty())
- AddBuiltInEntry(list, "Built-In 44100hz", IDR_DEFAULT_44100_MHR);
-
- if(!GetResource(IDR_DEFAULT_48000_MHR).empty())
- AddBuiltInEntry(list, "Built-In 48000hz", IDR_DEFAULT_48000_MHR);
- }
-
- if(!list.empty())
- {
- if(auto defhrtfopt = ConfigValueStr(devname, nullptr, "default-hrtf"))
- {
- auto iter = std::find_if(list.begin(), list.end(),
- [&defhrtfopt](const EnumeratedHrtf &entry) -> bool
- { return entry.name == *defhrtfopt; }
- );
- if(iter == list.end())
- WARN("Failed to find default HRTF \"%s\"\n", defhrtfopt->c_str());
- else if(iter != list.begin())
- {
- EnumeratedHrtf entry{std::move(*iter)};
- list.erase(iter);
- list.insert(list.begin(), std::move(entry));
- }
- }
- }
-
- return list;
-}
-
-HrtfEntry *GetLoadedHrtf(HrtfHandle *handle)
-{
- std::lock_guard<std::mutex> _{LoadedHrtfLock};
-
- if(handle->entry)
- {
- HrtfEntry *hrtf{handle->entry.get()};
- hrtf->IncRef();
- return hrtf;
- }
-
- std::unique_ptr<std::istream> stream;
- const char *name{""};
- ALuint residx{};
- char ch{};
- if(sscanf(handle->filename.data(), "!%u%c", &residx, &ch) == 2 && ch == '_')
- {
- name = strchr(handle->filename.data(), ch)+1;
-
- TRACE("Loading %s...\n", name);
- ResData res{GetResource(residx)};
- if(res.empty())
- {
- ERR("Could not get resource %u, %s\n", residx, name);
- return nullptr;
- }
- stream = al::make_unique<idstream>(res.begin(), res.end());
- }
- else
- {
- name = handle->filename.data();
-
- TRACE("Loading %s...\n", handle->filename.data());
- auto fstr = al::make_unique<al::ifstream>(handle->filename.data(), std::ios::binary);
- if(!fstr->is_open())
- {
- ERR("Could not open %s\n", handle->filename.data());
- return nullptr;
- }
- stream = std::move(fstr);
- }
-
- std::unique_ptr<HrtfEntry> hrtf;
- char magic[sizeof(magicMarker02)];
- stream->read(magic, sizeof(magic));
- if(stream->gcount() < static_cast<std::streamsize>(sizeof(magicMarker02)))
- ERR("%s data is too short (%zu bytes)\n", name, stream->gcount());
- else if(memcmp(magic, magicMarker02, sizeof(magicMarker02)) == 0)
- {
- TRACE("Detected data set format v2\n");
- hrtf = LoadHrtf02(*stream, name);
- }
- else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0)
- {
- TRACE("Detected data set format v1\n");
- hrtf = LoadHrtf01(*stream, name);
- }
- else if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0)
- {
- TRACE("Detected data set format v0\n");
- hrtf = LoadHrtf00(*stream, name);
- }
- else
- ERR("Invalid header in %s: \"%.8s\"\n", name, magic);
- stream.reset();
-
- if(!hrtf)
- {
- ERR("Failed to load %s\n", name);
- return nullptr;
- }
-
- TRACE("Loaded HRTF support for format: %s %uhz\n",
- DevFmtChannelsString(DevFmtStereo), hrtf->sampleRate);
- handle->entry = std::move(hrtf);
-
- return handle->entry.get();
-}
-
-
-void HrtfEntry::IncRef()
-{
- auto ref = IncrementRef(&this->ref);
- TRACEREF("HrtfEntry %p increasing refcount to %u\n", this, ref);
-}
-
-void HrtfEntry::DecRef()
-{
- auto ref = DecrementRef(&this->ref);
- TRACEREF("HrtfEntry %p decreasing refcount to %u\n", this, ref);
- if(ref == 0)
- {
- std::lock_guard<std::mutex> _{LoadedHrtfLock};
-
- /* Go through and clear all unused HRTFs. */
- auto delete_unused = [](HrtfHandlePtr &handle) -> void
- {
- HrtfEntry *entry{handle->entry.get()};
- if(entry && ReadRef(&entry->ref) == 0)
- {
- TRACE("Unloading unused HRTF %s\n", handle->filename.data());
- handle->entry = nullptr;
- }
- };
- std::for_each(LoadedHrtfs.begin(), LoadedHrtfs.end(), delete_unused);
- }
-}
diff --git a/Alc/hrtf.h b/Alc/hrtf.h
deleted file mode 100644
index 6c41cb82..00000000
--- a/Alc/hrtf.h
+++ /dev/null
@@ -1,124 +0,0 @@
-#ifndef ALC_HRTF_H
-#define ALC_HRTF_H
-
-#include <array>
-#include <cstddef>
-#include <memory>
-#include <string>
-
-#include "AL/al.h"
-
-#include "almalloc.h"
-#include "ambidefs.h"
-#include "atomic.h"
-#include "vector.h"
-
-
-struct HrtfHandle;
-
-#define HRTF_HISTORY_BITS (6)
-#define HRTF_HISTORY_LENGTH (1<<HRTF_HISTORY_BITS)
-#define HRTF_HISTORY_MASK (HRTF_HISTORY_LENGTH-1)
-
-#define HRIR_BITS (7)
-#define HRIR_LENGTH (1<<HRIR_BITS)
-#define HRIR_MASK (HRIR_LENGTH-1)
-
-
-struct HrtfEntry {
- RefCount ref;
-
- ALuint sampleRate;
- ALsizei irSize;
-
- struct Field {
- ALfloat distance;
- ALubyte evCount;
- };
- /* NOTE: Fields are stored *backwards*. field[0] is the farthest field, and
- * field[fdCount-1] is the nearest.
- */
- ALsizei fdCount;
- const Field *field;
-
- struct Elevation {
- ALushort azCount;
- ALushort irOffset;
- };
- Elevation *elev;
- const ALfloat (*coeffs)[2];
- const ALubyte (*delays)[2];
-
- void IncRef();
- void DecRef();
-
- DEF_PLACE_NEWDEL()
-};
-
-struct EnumeratedHrtf {
- std::string name;
-
- HrtfHandle *hrtf;
-};
-
-
-using float2 = std::array<float,2>;
-
-template<typename T>
-using HrirArray = std::array<std::array<T,2>,HRIR_LENGTH>;
-
-struct HrtfState {
- alignas(16) std::array<ALfloat,HRTF_HISTORY_LENGTH> History;
- alignas(16) HrirArray<ALfloat> Values;
-};
-
-struct HrtfFilter {
- alignas(16) HrirArray<ALfloat> Coeffs;
- ALsizei Delay[2];
- ALfloat Gain;
-};
-
-struct DirectHrtfState {
- /* HRTF filter state for dry buffer content */
- ALsizei IrSize{0};
- struct ChanData {
- alignas(16) HrirArray<ALfloat> Values;
- alignas(16) HrirArray<ALfloat> Coeffs;
- };
- al::FlexArray<ChanData> Chan;
-
- DirectHrtfState(size_t numchans) : Chan{numchans} { }
- DirectHrtfState(const DirectHrtfState&) = delete;
- DirectHrtfState& operator=(const DirectHrtfState&) = delete;
-
- static std::unique_ptr<DirectHrtfState> Create(size_t num_chans);
- static constexpr size_t Sizeof(size_t numchans) noexcept
- { return al::FlexArray<ChanData>::Sizeof(numchans, offsetof(DirectHrtfState, Chan)); }
-
- DEF_PLACE_NEWDEL()
-};
-
-struct AngularPoint {
- ALfloat Elev;
- ALfloat Azim;
-};
-
-
-al::vector<EnumeratedHrtf> EnumerateHrtf(const char *devname);
-HrtfEntry *GetLoadedHrtf(HrtfHandle *handle);
-
-void GetHrtfCoeffs(const HrtfEntry *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat distance,
- ALfloat spread, HrirArray<ALfloat> &coeffs, ALsizei (&delays)[2]);
-
-/**
- * Produces HRTF filter coefficients for decoding B-Format, given a set of
- * virtual speaker positions, a matching decoding matrix, and per-order high-
- * frequency gains for the decoder. The calculated impulse responses are
- * ordered and scaled according to the matrix input. Note the specified virtual
- * positions should be in degrees, not radians!
- */
-void BuildBFormatHrtf(const HrtfEntry *Hrtf, DirectHrtfState *state, const ALuint NumChannels,
- const AngularPoint *AmbiPoints, const ALfloat (*RESTRICT AmbiMatrix)[MAX_AMBI_CHANNELS],
- const size_t AmbiCount, const ALfloat *RESTRICT AmbiOrderHFGain);
-
-#endif /* ALC_HRTF_H */
diff --git a/Alc/inprogext.h b/Alc/inprogext.h
deleted file mode 100644
index 15881b59..00000000
--- a/Alc/inprogext.h
+++ /dev/null
@@ -1,92 +0,0 @@
-#ifndef INPROGEXT_H
-#define INPROGEXT_H
-
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "AL/alext.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef ALC_SOFT_loopback_bformat
-#define ALC_SOFT_loopback_bformat 1
-#define ALC_AMBISONIC_LAYOUT_SOFT 0x1997
-#define ALC_AMBISONIC_SCALING_SOFT 0x1998
-#define ALC_AMBISONIC_ORDER_SOFT 0x1999
-#define ALC_MAX_AMBISONIC_ORDER_SOFT 0x199B
-
-#define ALC_BFORMAT3D_SOFT 0x1508
-
-/* Ambisonic layouts */
-#define ALC_FUMA_SOFT 0x0000
-#define ALC_ACN_SOFT 0x0001
-
-/* Ambisonic scalings (normalization) */
-/*#define ALC_FUMA_SOFT*/
-#define ALC_SN3D_SOFT 0x0001
-#define ALC_N3D_SOFT 0x0002
-#endif
-
-#ifndef AL_SOFT_map_buffer
-#define AL_SOFT_map_buffer 1
-typedef unsigned int ALbitfieldSOFT;
-#define AL_MAP_READ_BIT_SOFT 0x00000001
-#define AL_MAP_WRITE_BIT_SOFT 0x00000002
-#define AL_MAP_PERSISTENT_BIT_SOFT 0x00000004
-#define AL_PRESERVE_DATA_BIT_SOFT 0x00000008
-typedef void (AL_APIENTRY*LPALBUFFERSTORAGESOFT)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags);
-typedef void* (AL_APIENTRY*LPALMAPBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access);
-typedef void (AL_APIENTRY*LPALUNMAPBUFFERSOFT)(ALuint buffer);
-typedef void (AL_APIENTRY*LPALFLUSHMAPPEDBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length);
-#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags);
-AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access);
-AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer);
-AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length);
-#endif
-#endif
-
-#ifndef AL_SOFT_events
-#define AL_SOFT_events 1
-#define AL_EVENT_CALLBACK_FUNCTION_SOFT 0x1220
-#define AL_EVENT_CALLBACK_USER_PARAM_SOFT 0x1221
-#define AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT 0x1222
-#define AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT 0x1223
-#define AL_EVENT_TYPE_ERROR_SOFT 0x1224
-#define AL_EVENT_TYPE_PERFORMANCE_SOFT 0x1225
-#define AL_EVENT_TYPE_DEPRECATED_SOFT 0x1226
-#define AL_EVENT_TYPE_DISCONNECTED_SOFT 0x1227
-typedef void (AL_APIENTRY*ALEVENTPROCSOFT)(ALenum eventType, ALuint object, ALuint param,
- ALsizei length, const ALchar *message,
- void *userParam);
-typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable);
-typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam);
-typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname);
-typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values);
-#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable);
-AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam);
-AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname);
-AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values);
-#endif
-#endif
-
-#ifndef AL_SOFT_buffer_layers
-#define AL_SOFT_buffer_layers
-typedef void (AL_APIENTRY*LPALSOURCEQUEUEBUFFERLAYERSSOFT)(ALuint src, ALsizei nb, const ALuint *buffers);
-#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers);
-#endif
-#endif
-
-#ifndef AL_SOFT_effect_chain
-#define AL_SOFT_effect_chain
-#define AL_EFFECTSLOT_TARGET_SOFT 0xf000
-#endif
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* INPROGEXT_H */
diff --git a/Alc/logging.h b/Alc/logging.h
deleted file mode 100644
index 0bb0c87b..00000000
--- a/Alc/logging.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef LOGGING_H
-#define LOGGING_H
-
-#include <stdio.h>
-
-#include "opthelpers.h"
-
-
-#ifdef __GNUC__
-#define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z))))
-#else
-#define DECL_FORMAT(x, y, z)
-#endif
-
-
-extern FILE *gLogFile;
-
-void al_print(FILE *logfile, const char *fmt, ...) DECL_FORMAT(printf, 2,3);
-#if !defined(_WIN32)
-#define AL_PRINT(T, ...) fprintf(gLogFile, "AL lib: " T " " __VA_ARGS__)
-#else
-#define AL_PRINT(T, ...) al_print(gLogFile, "AL lib: " T " " __VA_ARGS__)
-#endif
-
-#ifdef __ANDROID__
-#include <android/log.h>
-#define LOG_ANDROID(T, ...) __android_log_print(T, "openal", "AL lib: " __VA_ARGS__)
-#else
-#define LOG_ANDROID(T, ...) ((void)0)
-#endif
-
-enum LogLevel {
- NoLog,
- LogError,
- LogWarning,
- LogTrace,
- LogRef
-};
-extern LogLevel gLogLevel;
-
-#define TRACEREF(...) do { \
- if(UNLIKELY(gLogLevel >= LogRef)) \
- AL_PRINT("(--)", __VA_ARGS__); \
-} while(0)
-
-#define TRACE(...) do { \
- if(UNLIKELY(gLogLevel >= LogTrace)) \
- AL_PRINT("(II)", __VA_ARGS__); \
- LOG_ANDROID(ANDROID_LOG_DEBUG, __VA_ARGS__); \
-} while(0)
-
-#define WARN(...) do { \
- if(UNLIKELY(gLogLevel >= LogWarning)) \
- AL_PRINT("(WW)", __VA_ARGS__); \
- LOG_ANDROID(ANDROID_LOG_WARN, __VA_ARGS__); \
-} while(0)
-
-#define ERR(...) do { \
- if(UNLIKELY(gLogLevel >= LogError)) \
- AL_PRINT("(EE)", __VA_ARGS__); \
- LOG_ANDROID(ANDROID_LOG_ERROR, __VA_ARGS__); \
-} while(0)
-
-#endif /* LOGGING_H */
diff --git a/Alc/mastering.cpp b/Alc/mastering.cpp
deleted file mode 100644
index 551fdcdf..00000000
--- a/Alc/mastering.cpp
+++ /dev/null
@@ -1,479 +0,0 @@
-#include "config.h"
-
-#include <cmath>
-#include <limits>
-#include <algorithm>
-#include <functional>
-
-#include "mastering.h"
-#include "alu.h"
-#include "almalloc.h"
-#include "math_defs.h"
-
-
-/* These structures assume BUFFERSIZE is a power of 2. */
-static_assert((BUFFERSIZE & (BUFFERSIZE-1)) == 0, "BUFFERSIZE is not a power of 2");
-
-struct SlidingHold {
- alignas(16) ALfloat mValues[BUFFERSIZE];
- ALsizei mExpiries[BUFFERSIZE];
- ALsizei mLowerIndex;
- ALsizei mUpperIndex;
- ALsizei mLength;
-};
-
-
-namespace {
-
-using namespace std::placeholders;
-
-/* This sliding hold follows the input level with an instant attack and a
- * fixed duration hold before an instant release to the next highest level.
- * It is a sliding window maximum (descending maxima) implementation based on
- * Richard Harter's ascending minima algorithm available at:
- *
- * http://www.richardhartersworld.com/cri/2001/slidingmin.html
- */
-ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALfloat in)
-{
- static constexpr ALsizei mask{BUFFERSIZE - 1};
- const ALsizei length{Hold->mLength};
- ALfloat (&values)[BUFFERSIZE] = Hold->mValues;
- ALsizei (&expiries)[BUFFERSIZE] = Hold->mExpiries;
- ALsizei lowerIndex{Hold->mLowerIndex};
- ALsizei upperIndex{Hold->mUpperIndex};
-
- ASSUME(upperIndex >= 0);
- ASSUME(lowerIndex >= 0);
-
- if(i >= expiries[upperIndex])
- upperIndex = (upperIndex + 1) & mask;
-
- if(in >= values[upperIndex])
- {
- values[upperIndex] = in;
- expiries[upperIndex] = i + length;
- lowerIndex = upperIndex;
- }
- else
- {
- do {
- do {
- if(!(in >= values[lowerIndex]))
- goto found_place;
- } while(lowerIndex--);
- lowerIndex = mask;
- } while(1);
- found_place:
-
- lowerIndex = (lowerIndex + 1) & mask;
- values[lowerIndex] = in;
- expiries[lowerIndex] = i + length;
- }
-
- Hold->mLowerIndex = lowerIndex;
- Hold->mUpperIndex = upperIndex;
-
- return values[upperIndex];
-}
-
-void ShiftSlidingHold(SlidingHold *Hold, const ALsizei n)
-{
- ASSUME(Hold->mUpperIndex >= 0);
- ASSUME(Hold->mLowerIndex >= 0);
-
- auto exp_begin = std::begin(Hold->mExpiries) + Hold->mUpperIndex;
- auto exp_last = std::begin(Hold->mExpiries) + Hold->mLowerIndex;
- if(exp_last < exp_begin)
- {
- std::transform(exp_begin, std::end(Hold->mExpiries), exp_begin,
- std::bind(std::minus<ALsizei>{}, _1, n));
- exp_begin = std::begin(Hold->mExpiries);
- }
- std::transform(exp_begin, exp_last+1, exp_begin, std::bind(std::minus<ALsizei>{}, _1, n));
-}
-
-
-/* Multichannel compression is linked via the absolute maximum of all
- * channels.
- */
-void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, const FloatBufferLine *OutBuffer)
-{
- const ALsizei index{Comp->mLookAhead};
- const ALuint numChans{Comp->mNumChans};
-
- ASSUME(SamplesToDo > 0);
- ASSUME(numChans > 0);
- ASSUME(index >= 0);
-
- auto side_begin = std::begin(Comp->mSideChain) + index;
- std::fill(side_begin, side_begin+SamplesToDo, 0.0f);
-
- auto fill_max = [SamplesToDo,side_begin](const FloatBufferLine &input) -> void
- {
- const ALfloat *RESTRICT buffer{al::assume_aligned<16>(input.data())};
- auto max_abs = std::bind(maxf, _1, std::bind(static_cast<float(&)(float)>(std::fabs), _2));
- std::transform(side_begin, side_begin+SamplesToDo, buffer, side_begin, max_abs);
- };
- std::for_each(OutBuffer, OutBuffer+numChans, fill_max);
-}
-
-/* This calculates the squared crest factor of the control signal for the
- * basic automation of the attack/release times. As suggested by the paper,
- * it uses an instantaneous squared peak detector and a squared RMS detector
- * both with 200ms release times.
- */
-static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
-{
- const ALfloat a_crest{Comp->mCrestCoeff};
- const ALsizei index{Comp->mLookAhead};
- ALfloat y2_peak{Comp->mLastPeakSq};
- ALfloat y2_rms{Comp->mLastRmsSq};
-
- ASSUME(SamplesToDo > 0);
- ASSUME(index >= 0);
-
- auto calc_crest = [&y2_rms,&y2_peak,a_crest](const ALfloat x_abs) noexcept -> ALfloat
- {
- ALfloat x2 = maxf(0.000001f, x_abs * x_abs);
-
- y2_peak = maxf(x2, lerp(x2, y2_peak, a_crest));
- y2_rms = lerp(x2, y2_rms, a_crest);
- return y2_peak / y2_rms;
- };
- auto side_begin = std::begin(Comp->mSideChain) + index;
- std::transform(side_begin, side_begin+SamplesToDo, std::begin(Comp->mCrestFactor), calc_crest);
-
- Comp->mLastPeakSq = y2_peak;
- Comp->mLastRmsSq = y2_rms;
-}
-
-/* The side-chain starts with a simple peak detector (based on the absolute
- * value of the incoming signal) and performs most of its operations in the
- * log domain.
- */
-void PeakDetector(Compressor *Comp, const ALsizei SamplesToDo)
-{
- const ALsizei index{Comp->mLookAhead};
-
- ASSUME(SamplesToDo > 0);
- ASSUME(index >= 0);
-
- /* Clamp the minimum amplitude to near-zero and convert to logarithm. */
- auto side_begin = std::begin(Comp->mSideChain) + index;
- std::transform(side_begin, side_begin+SamplesToDo, side_begin,
- std::bind(static_cast<float(&)(float)>(std::log), std::bind(maxf, 0.000001f, _1)));
-}
-
-/* An optional hold can be used to extend the peak detector so it can more
- * solidly detect fast transients. This is best used when operating as a
- * limiter.
- */
-void PeakHoldDetector(Compressor *Comp, const ALsizei SamplesToDo)
-{
- const ALsizei index{Comp->mLookAhead};
-
- ASSUME(SamplesToDo > 0);
- ASSUME(index >= 0);
-
- SlidingHold *hold{Comp->mHold};
- ALsizei i{0};
- auto detect_peak = [&i,hold](const ALfloat x_abs) -> ALfloat
- {
- const ALfloat x_G{std::log(maxf(0.000001f, x_abs))};
- return UpdateSlidingHold(hold, i++, x_G);
- };
- auto side_begin = std::begin(Comp->mSideChain) + index;
- std::transform(side_begin, side_begin+SamplesToDo, side_begin, detect_peak);
-
- ShiftSlidingHold(hold, SamplesToDo);
-}
-
-/* This is the heart of the feed-forward compressor. It operates in the log
- * domain (to better match human hearing) and can apply some basic automation
- * to knee width, attack/release times, make-up/post gain, and clipping
- * reduction.
- */
-void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
-{
- const bool autoKnee{Comp->mAuto.Knee};
- const bool autoAttack{Comp->mAuto.Attack};
- const bool autoRelease{Comp->mAuto.Release};
- const bool autoPostGain{Comp->mAuto.PostGain};
- const bool autoDeclip{Comp->mAuto.Declip};
- const ALsizei lookAhead{Comp->mLookAhead};
- const ALfloat threshold{Comp->mThreshold};
- const ALfloat slope{Comp->mSlope};
- const ALfloat attack{Comp->mAttack};
- const ALfloat release{Comp->mRelease};
- const ALfloat c_est{Comp->mGainEstimate};
- const ALfloat a_adp{Comp->mAdaptCoeff};
- const ALfloat (&crestFactor)[BUFFERSIZE] = Comp->mCrestFactor;
- ALfloat (&sideChain)[BUFFERSIZE*2] = Comp->mSideChain;
- ALfloat postGain{Comp->mPostGain};
- ALfloat knee{Comp->mKnee};
- ALfloat t_att{attack};
- ALfloat t_rel{release - attack};
- ALfloat a_att{std::exp(-1.0f / t_att)};
- ALfloat a_rel{std::exp(-1.0f / t_rel)};
- ALfloat y_1{Comp->mLastRelease};
- ALfloat y_L{Comp->mLastAttack};
- ALfloat c_dev{Comp->mLastGainDev};
-
- ASSUME(SamplesToDo > 0);
- ASSUME(lookAhead >= 0);
-
- for(ALsizei i{0};i < SamplesToDo;i++)
- {
- if(autoKnee)
- knee = maxf(0.0f, 2.5f * (c_dev + c_est));
- const ALfloat knee_h{0.5f * knee};
-
- /* This is the gain computer. It applies a static compression curve
- * to the control signal.
- */
- const ALfloat x_over{sideChain[lookAhead+i] - threshold};
- const ALfloat y_G{
- (x_over <= -knee_h) ? 0.0f :
- (std::fabs(x_over) < knee_h) ? (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee) :
- x_over
- };
-
- const ALfloat y2_crest{crestFactor[i]};
- if(autoAttack)
- {
- t_att = 2.0f*attack/y2_crest;
- a_att = std::exp(-1.0f / t_att);
- }
- if(autoRelease)
- {
- t_rel = 2.0f*release/y2_crest - t_att;
- a_rel = std::exp(-1.0f / t_rel);
- }
-
- /* Gain smoothing (ballistics) is done via a smooth decoupled peak
- * detector. The attack time is subtracted from the release time
- * above to compensate for the chained operating mode.
- */
- const ALfloat x_L{-slope * y_G};
- y_1 = maxf(x_L, lerp(x_L, y_1, a_rel));
- y_L = lerp(y_1, y_L, a_att);
-
- /* Knee width and make-up gain automation make use of a smoothed
- * measurement of deviation between the control signal and estimate.
- * The estimate is also used to bias the measurement to hot-start its
- * average.
- */
- c_dev = lerp(-(y_L+c_est), c_dev, a_adp);
-
- if(autoPostGain)
- {
- /* Clipping reduction is only viable when make-up gain is being
- * automated. It modifies the deviation to further attenuate the
- * control signal when clipping is detected. The adaptation time
- * is sufficiently long enough to suppress further clipping at the
- * same output level.
- */
- if(autoDeclip)
- c_dev = maxf(c_dev, sideChain[i] - y_L - threshold - c_est);
-
- postGain = -(c_dev + c_est);
- }
-
- sideChain[i] = std::exp(postGain - y_L);
- }
-
- Comp->mLastRelease = y_1;
- Comp->mLastAttack = y_L;
- Comp->mLastGainDev = c_dev;
-}
-
-/* Combined with the hold time, a look-ahead delay can improve handling of
- * fast transients by allowing the envelope time to converge prior to
- * reaching the offending impulse. This is best used when operating as a
- * limiter.
- */
-void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, FloatBufferLine *OutBuffer)
-{
- const ALuint numChans{Comp->mNumChans};
- const ALsizei lookAhead{Comp->mLookAhead};
-
- ASSUME(SamplesToDo > 0);
- ASSUME(numChans > 0);
- ASSUME(lookAhead > 0);
-
- for(ALuint c{0};c < numChans;c++)
- {
- ALfloat *inout{al::assume_aligned<16>(OutBuffer[c].data())};
- ALfloat *delaybuf{al::assume_aligned<16>(Comp->mDelay[c].data())};
-
- auto inout_end = inout + SamplesToDo;
- if(LIKELY(SamplesToDo >= lookAhead))
- {
- auto delay_end = std::rotate(inout, inout_end - lookAhead, inout_end);
- std::swap_ranges(inout, delay_end, delaybuf);
- }
- else
- {
- auto delay_start = std::swap_ranges(inout, inout_end, delaybuf);
- std::rotate(delaybuf, delay_start, delaybuf + lookAhead);
- }
- }
-}
-
-} // namespace
-
-/* The compressor is initialized with the following settings:
- *
- * NumChans - Number of channels to process.
- * SampleRate - Sample rate to process.
- * AutoKnee - Whether to automate the knee width parameter.
- * AutoAttack - Whether to automate the attack time parameter.
- * AutoRelease - Whether to automate the release time parameter.
- * AutoPostGain - Whether to automate the make-up (post) gain parameter.
- * AutoDeclip - Whether to automate clipping reduction. Ignored when
- * not automating make-up gain.
- * LookAheadTime - Look-ahead time (in seconds).
- * HoldTime - Peak hold-time (in seconds).
- * PreGainDb - Gain applied before detection (in dB).
- * PostGainDb - Make-up gain applied after compression (in dB).
- * ThresholdDb - Triggering threshold (in dB).
- * Ratio - Compression ratio (x:1). Set to INFINITY for true
- * limiting. Ignored when automating knee width.
- * KneeDb - Knee width (in dB). Ignored when automating knee
- * width.
- * AttackTimeMin - Attack time (in seconds). Acts as a maximum when
- * automating attack time.
- * ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
- * automating release time.
- */
-std::unique_ptr<Compressor> CompressorInit(const ALuint NumChans, const ALuint SampleRate,
- const ALboolean AutoKnee, const ALboolean AutoAttack, const ALboolean AutoRelease,
- const ALboolean AutoPostGain, const ALboolean AutoDeclip, const ALfloat LookAheadTime,
- const ALfloat HoldTime, const ALfloat PreGainDb, const ALfloat PostGainDb,
- const ALfloat ThresholdDb, const ALfloat Ratio, const ALfloat KneeDb, const ALfloat AttackTime,
- const ALfloat ReleaseTime)
-{
- const auto lookAhead = static_cast<ALsizei>(
- clampf(std::round(LookAheadTime*SampleRate), 0.0f, BUFFERSIZE-1));
- const auto hold = static_cast<ALsizei>(
- clampf(std::round(HoldTime*SampleRate), 0.0f, BUFFERSIZE-1));
-
- size_t size{sizeof(Compressor)};
- if(lookAhead > 0)
- {
- size += sizeof(*Compressor::mDelay) * NumChans;
- /* The sliding hold implementation doesn't handle a length of 1. A 1-
- * sample hold is useless anyway, it would only ever give back what was
- * just given to it.
- */
- if(hold > 1)
- size += sizeof(*Compressor::mHold);
- }
-
- auto Comp = std::unique_ptr<Compressor>{new (al_calloc(16, size)) Compressor{}};
- Comp->mNumChans = NumChans;
- Comp->mSampleRate = SampleRate;
- Comp->mAuto.Knee = AutoKnee != AL_FALSE;
- Comp->mAuto.Attack = AutoAttack != AL_FALSE;
- Comp->mAuto.Release = AutoRelease != AL_FALSE;
- Comp->mAuto.PostGain = AutoPostGain != AL_FALSE;
- Comp->mAuto.Declip = AutoPostGain && AutoDeclip;
- Comp->mLookAhead = lookAhead;
- Comp->mPreGain = std::pow(10.0f, PreGainDb / 20.0f);
- Comp->mPostGain = PostGainDb * std::log(10.0f) / 20.0f;
- Comp->mThreshold = ThresholdDb * std::log(10.0f) / 20.0f;
- Comp->mSlope = 1.0f / maxf(1.0f, Ratio) - 1.0f;
- Comp->mKnee = maxf(0.0f, KneeDb * std::log(10.0f) / 20.0f);
- Comp->mAttack = maxf(1.0f, AttackTime * SampleRate);
- Comp->mRelease = maxf(1.0f, ReleaseTime * SampleRate);
-
- /* Knee width automation actually treats the compressor as a limiter. By
- * varying the knee width, it can effectively be seen as applying
- * compression over a wide range of ratios.
- */
- if(AutoKnee)
- Comp->mSlope = -1.0f;
-
- if(lookAhead > 0)
- {
- if(hold > 1)
- {
- Comp->mHold = ::new (static_cast<void*>(Comp.get() + 1)) SlidingHold{};
- Comp->mHold->mValues[0] = -std::numeric_limits<float>::infinity();
- Comp->mHold->mExpiries[0] = hold;
- Comp->mHold->mLength = hold;
- Comp->mDelay = ::new (static_cast<void*>(Comp->mHold + 1)) FloatBufferLine[NumChans];
- }
- else
- {
- Comp->mDelay = ::new (static_cast<void*>(Comp.get() + 1)) FloatBufferLine[NumChans];
- }
- }
-
- Comp->mCrestCoeff = std::exp(-1.0f / (0.200f * SampleRate)); // 200ms
- Comp->mGainEstimate = Comp->mThreshold * -0.5f * Comp->mSlope;
- Comp->mAdaptCoeff = std::exp(-1.0f / (2.0f * SampleRate)); // 2s
-
- return Comp;
-}
-
-Compressor::~Compressor()
-{
- if(mHold)
- al::destroy_at(mHold);
- mHold = nullptr;
- if(mDelay)
- al::destroy_n(mDelay, mNumChans);
- mDelay = nullptr;
-}
-
-
-void Compressor::process(const ALsizei SamplesToDo, FloatBufferLine *OutBuffer)
-{
- const ALuint numChans{mNumChans};
-
- ASSUME(SamplesToDo > 0);
- ASSUME(numChans > 0);
-
- const ALfloat preGain{mPreGain};
- if(preGain != 1.0f)
- {
- auto apply_gain = [SamplesToDo,preGain](FloatBufferLine &input) noexcept -> void
- {
- ALfloat *buffer{al::assume_aligned<16>(input.data())};
- std::transform(buffer, buffer+SamplesToDo, buffer,
- std::bind(std::multiplies<float>{}, _1, preGain));
- };
- std::for_each(OutBuffer, OutBuffer+numChans, apply_gain);
- }
-
- LinkChannels(this, SamplesToDo, OutBuffer);
-
- if(mAuto.Attack || mAuto.Release)
- CrestDetector(this, SamplesToDo);
-
- if(mHold)
- PeakHoldDetector(this, SamplesToDo);
- else
- PeakDetector(this, SamplesToDo);
-
- GainCompressor(this, SamplesToDo);
-
- if(mDelay)
- SignalDelay(this, SamplesToDo, OutBuffer);
-
- const ALfloat (&sideChain)[BUFFERSIZE*2] = mSideChain;
- auto apply_comp = [SamplesToDo,&sideChain](FloatBufferLine &input) noexcept -> void
- {
- ALfloat *buffer{al::assume_aligned<16>(input.data())};
- const ALfloat *gains{al::assume_aligned<16>(&sideChain[0])};
- std::transform(gains, gains+SamplesToDo, buffer, buffer,
- std::bind(std::multiplies<float>{}, _1, _2));
- };
- std::for_each(OutBuffer, OutBuffer+numChans, apply_comp);
-
- ASSUME(mLookAhead >= 0);
- auto side_begin = std::begin(mSideChain) + SamplesToDo;
- std::copy(side_begin, side_begin+mLookAhead, std::begin(mSideChain));
-}
diff --git a/Alc/mastering.h b/Alc/mastering.h
deleted file mode 100644
index 34dc8dcb..00000000
--- a/Alc/mastering.h
+++ /dev/null
@@ -1,104 +0,0 @@
-#ifndef MASTERING_H
-#define MASTERING_H
-
-#include <memory>
-
-#include "AL/al.h"
-
-#include "almalloc.h"
-/* For FloatBufferLine/BUFFERSIZE. */
-#include "alcmain.h"
-
-
-struct SlidingHold;
-
-/* General topology and basic automation was based on the following paper:
- *
- * D. Giannoulis, M. Massberg and J. D. Reiss,
- * "Parameter Automation in a Dynamic Range Compressor,"
- * Journal of the Audio Engineering Society, v61 (10), Oct. 2013
- *
- * Available (along with supplemental reading) at:
- *
- * http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/
- */
-struct Compressor {
- ALuint mNumChans{0u};
- ALuint mSampleRate{0u};
-
- struct {
- bool Knee : 1;
- bool Attack : 1;
- bool Release : 1;
- bool PostGain : 1;
- bool Declip : 1;
- } mAuto{};
-
- ALsizei mLookAhead{0};
-
- ALfloat mPreGain{0.0f};
- ALfloat mPostGain{0.0f};
-
- ALfloat mThreshold{0.0f};
- ALfloat mSlope{0.0f};
- ALfloat mKnee{0.0f};
-
- ALfloat mAttack{0.0f};
- ALfloat mRelease{0.0f};
-
- alignas(16) ALfloat mSideChain[2*BUFFERSIZE]{};
- alignas(16) ALfloat mCrestFactor[BUFFERSIZE]{};
-
- SlidingHold *mHold{nullptr};
- FloatBufferLine *mDelay{nullptr};
-
- ALfloat mCrestCoeff{0.0f};
- ALfloat mGainEstimate{0.0f};
- ALfloat mAdaptCoeff{0.0f};
-
- ALfloat mLastPeakSq{0.0f};
- ALfloat mLastRmsSq{0.0f};
- ALfloat mLastRelease{0.0f};
- ALfloat mLastAttack{0.0f};
- ALfloat mLastGainDev{0.0f};
-
-
- ~Compressor();
- void process(const ALsizei SamplesToDo, FloatBufferLine *OutBuffer);
- ALsizei getLookAhead() const noexcept { return mLookAhead; }
-
- DEF_PLACE_NEWDEL()
-};
-
-/* The compressor is initialized with the following settings:
- *
- * NumChans - Number of channels to process.
- * SampleRate - Sample rate to process.
- * AutoKnee - Whether to automate the knee width parameter.
- * AutoAttack - Whether to automate the attack time parameter.
- * AutoRelease - Whether to automate the release time parameter.
- * AutoPostGain - Whether to automate the make-up (post) gain parameter.
- * AutoDeclip - Whether to automate clipping reduction. Ignored when
- * not automating make-up gain.
- * LookAheadTime - Look-ahead time (in seconds).
- * HoldTime - Peak hold-time (in seconds).
- * PreGainDb - Gain applied before detection (in dB).
- * PostGainDb - Make-up gain applied after compression (in dB).
- * ThresholdDb - Triggering threshold (in dB).
- * Ratio - Compression ratio (x:1). Set to INFINIFTY for true
- * limiting. Ignored when automating knee width.
- * KneeDb - Knee width (in dB). Ignored when automating knee
- * width.
- * AttackTimeMin - Attack time (in seconds). Acts as a maximum when
- * automating attack time.
- * ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
- * automating release time.
- */
-std::unique_ptr<Compressor> CompressorInit(const ALuint NumChans, const ALuint SampleRate,
- const ALboolean AutoKnee, const ALboolean AutoAttack, const ALboolean AutoRelease,
- const ALboolean AutoPostGain, const ALboolean AutoDeclip, const ALfloat LookAheadTime,
- const ALfloat HoldTime, const ALfloat PreGainDb, const ALfloat PostGainDb,
- const ALfloat ThresholdDb, const ALfloat Ratio, const ALfloat KneeDb, const ALfloat AttackTime,
- const ALfloat ReleaseTime);
-
-#endif /* MASTERING_H */
diff --git a/Alc/mixer/defs.h b/Alc/mixer/defs.h
deleted file mode 100644
index 3e5d1125..00000000
--- a/Alc/mixer/defs.h
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifndef MIXER_DEFS_H
-#define MIXER_DEFS_H
-
-#include "AL/alc.h"
-#include "AL/al.h"
-
-#include "alcmain.h"
-#include "alu.h"
-#include "alspan.h"
-
-
-struct MixGains;
-struct MixHrtfFilter;
-struct HrtfState;
-struct DirectHrtfState;
-
-
-struct CTag { };
-struct SSETag { };
-struct SSE2Tag { };
-struct SSE3Tag { };
-struct SSE4Tag { };
-struct NEONTag { };
-
-struct CopyTag { };
-struct PointTag { };
-struct LerpTag { };
-struct CubicTag { };
-struct BSincTag { };
-
-template<typename TypeTag, typename InstTag>
-const ALfloat *Resample_(const InterpState *state, const ALfloat *RESTRICT src, ALsizei frac, ALint increment, ALfloat *RESTRICT dst, ALsizei dstlen);
-
-template<typename InstTag>
-void Mix_(const ALfloat *data, const al::span<FloatBufferLine> OutBuffer, ALfloat *CurrentGains, const ALfloat *TargetGains, const ALsizei Counter, const ALsizei OutPos, const ALsizei BufferSize);
-template<typename InstTag>
-void MixRow_(FloatBufferLine &OutBuffer, const ALfloat *Gains, const al::span<const FloatBufferLine> InSamples, const ALsizei InPos, const ALsizei BufferSize);
-
-template<typename InstTag>
-void MixHrtf_(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, const ALfloat *InSamples, float2 *AccumSamples, const ALsizei OutPos, const ALsizei IrSize, MixHrtfFilter *hrtfparams, const ALsizei BufferSize);
-template<typename InstTag>
-void MixHrtfBlend_(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, const ALfloat *InSamples, float2 *AccumSamples, const ALsizei OutPos, const ALsizei IrSize, const HrtfFilter *oldparams, MixHrtfFilter *newparams, const ALsizei BufferSize);
-template<typename InstTag>
-void MixDirectHrtf_(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples, DirectHrtfState *State, const ALsizei BufferSize);
-
-/* Vectorized resampler helpers */
-inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *RESTRICT frac_arr, ALsizei *RESTRICT pos_arr, ALsizei size)
-{
- pos_arr[0] = 0;
- frac_arr[0] = frac;
- for(ALsizei i{1};i < size;i++)
- {
- ALint frac_tmp = frac_arr[i-1] + increment;
- pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS);
- frac_arr[i] = frac_tmp&FRACTIONMASK;
- }
-}
-
-#endif /* MIXER_DEFS_H */
diff --git a/Alc/mixer/hrtfbase.h b/Alc/mixer/hrtfbase.h
deleted file mode 100644
index a76bd62e..00000000
--- a/Alc/mixer/hrtfbase.h
+++ /dev/null
@@ -1,138 +0,0 @@
-#ifndef MIXER_HRTFBASE_H
-#define MIXER_HRTFBASE_H
-
-#include <algorithm>
-
-#include "alu.h"
-#include "../hrtf.h"
-#include "opthelpers.h"
-
-
-using ApplyCoeffsT = void(ALsizei Offset, float2 *RESTRICT Values, const ALsizei irSize,
- const HrirArray<ALfloat> &Coeffs, const ALfloat left, const ALfloat right);
-
-template<ApplyCoeffsT &ApplyCoeffs>
-inline void MixHrtfBase(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const ALfloat *InSamples, float2 *RESTRICT AccumSamples, const ALsizei OutPos,
- const ALsizei IrSize, MixHrtfFilter *hrtfparams, const ALsizei BufferSize)
-{
- ASSUME(OutPos >= 0);
- ASSUME(IrSize >= 4);
- ASSUME(BufferSize > 0);
-
- const auto &Coeffs = *hrtfparams->Coeffs;
- const ALfloat gainstep{hrtfparams->GainStep};
- const ALfloat gain{hrtfparams->Gain};
-
- ALsizei Delay[2]{
- HRTF_HISTORY_LENGTH - hrtfparams->Delay[0],
- HRTF_HISTORY_LENGTH - hrtfparams->Delay[1] };
- ASSUME(Delay[0] >= 0 && Delay[1] >= 0);
- ALfloat stepcount{0.0f};
- for(ALsizei i{0};i < BufferSize;++i)
- {
- const ALfloat g{gain + gainstep*stepcount};
- const ALfloat left{InSamples[Delay[0]++] * g};
- const ALfloat right{InSamples[Delay[1]++] * g};
- ApplyCoeffs(i, AccumSamples+i, IrSize, Coeffs, left, right);
-
- stepcount += 1.0f;
- }
-
- for(ALsizei i{0};i < BufferSize;++i)
- LeftOut[OutPos+i] += AccumSamples[i][0];
- for(ALsizei i{0};i < BufferSize;++i)
- RightOut[OutPos+i] += AccumSamples[i][1];
-
- hrtfparams->Gain = gain + gainstep*stepcount;
-}
-
-template<ApplyCoeffsT &ApplyCoeffs>
-inline void MixHrtfBlendBase(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const ALfloat *InSamples, float2 *RESTRICT AccumSamples, const ALsizei OutPos,
- const ALsizei IrSize, const HrtfFilter *oldparams, MixHrtfFilter *newparams,
- const ALsizei BufferSize)
-{
- const auto &OldCoeffs = oldparams->Coeffs;
- const ALfloat oldGain{oldparams->Gain};
- const ALfloat oldGainStep{-oldGain / static_cast<ALfloat>(BufferSize)};
- const auto &NewCoeffs = *newparams->Coeffs;
- const ALfloat newGainStep{newparams->GainStep};
-
- ASSUME(OutPos >= 0);
- ASSUME(IrSize >= 4);
- ASSUME(BufferSize > 0);
-
- ALsizei Delay[2]{
- HRTF_HISTORY_LENGTH - oldparams->Delay[0],
- HRTF_HISTORY_LENGTH - oldparams->Delay[1] };
- ASSUME(Delay[0] >= 0 && Delay[1] >= 0);
- ALfloat stepcount{0.0f};
- for(ALsizei i{0};i < BufferSize;++i)
- {
- const ALfloat g{oldGain + oldGainStep*stepcount};
- const ALfloat left{InSamples[Delay[0]++] * g};
- const ALfloat right{InSamples[Delay[1]++] * g};
- ApplyCoeffs(i, AccumSamples+i, IrSize, OldCoeffs, left, right);
-
- stepcount += 1.0f;
- }
-
- Delay[0] = HRTF_HISTORY_LENGTH - newparams->Delay[0];
- Delay[1] = HRTF_HISTORY_LENGTH - newparams->Delay[1];
- ASSUME(Delay[0] >= 0 && Delay[1] >= 0);
- stepcount = 0.0f;
- for(ALsizei i{0};i < BufferSize;++i)
- {
- const ALfloat g{newGainStep*stepcount};
- const ALfloat left{InSamples[Delay[0]++] * g};
- const ALfloat right{InSamples[Delay[1]++] * g};
- ApplyCoeffs(i, AccumSamples+i, IrSize, NewCoeffs, left, right);
-
- stepcount += 1.0f;
- }
-
- for(ALsizei i{0};i < BufferSize;++i)
- LeftOut[OutPos+i] += AccumSamples[i][0];
- for(ALsizei i{0};i < BufferSize;++i)
- RightOut[OutPos+i] += AccumSamples[i][1];
-
- newparams->Gain = newGainStep*stepcount;
-}
-
-template<ApplyCoeffsT &ApplyCoeffs>
-inline void MixDirectHrtfBase(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const al::span<const FloatBufferLine> InSamples, float2 *RESTRICT AccumSamples,
- DirectHrtfState *State, const ALsizei BufferSize)
-{
- ASSUME(BufferSize > 0);
-
- const ALsizei IrSize{State->IrSize};
- ASSUME(IrSize >= 4);
-
- auto chanstate = State->Chan.begin();
- for(const FloatBufferLine &input : InSamples)
- {
- const auto &Coeffs = chanstate->Coeffs;
-
- auto accum_iter = std::copy_n(chanstate->Values.begin(),
- chanstate->Values.size(), AccumSamples);
- std::fill_n(accum_iter, BufferSize, float2{});
-
- for(ALsizei i{0};i < BufferSize;++i)
- {
- const ALfloat insample{input[i]};
- ApplyCoeffs(i, AccumSamples+i, IrSize, Coeffs, insample, insample);
- }
- for(ALsizei i{0};i < BufferSize;++i)
- LeftOut[i] += AccumSamples[i][0];
- for(ALsizei i{0};i < BufferSize;++i)
- RightOut[i] += AccumSamples[i][1];
-
- std::copy_n(AccumSamples + BufferSize, chanstate->Values.size(),
- chanstate->Values.begin());
- ++chanstate;
- }
-}
-
-#endif /* MIXER_HRTFBASE_H */
diff --git a/Alc/mixer/mixer_c.cpp b/Alc/mixer/mixer_c.cpp
deleted file mode 100644
index 47c4a6f4..00000000
--- a/Alc/mixer/mixer_c.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-#include "config.h"
-
-#include <cassert>
-
-#include <limits>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "alSource.h"
-#include "alAuxEffectSlot.h"
-#include "defs.h"
-#include "hrtfbase.h"
-
-
-namespace {
-
-inline ALfloat do_point(const InterpState&, const ALfloat *RESTRICT vals, const ALsizei)
-{ return vals[0]; }
-inline ALfloat do_lerp(const InterpState&, const ALfloat *RESTRICT vals, const ALsizei frac)
-{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
-inline ALfloat do_cubic(const InterpState&, const ALfloat *RESTRICT vals, const ALsizei frac)
-{ return cubic(vals[0], vals[1], vals[2], vals[3], frac * (1.0f/FRACTIONONE)); }
-inline ALfloat do_bsinc(const InterpState &istate, const ALfloat *RESTRICT vals, const ALsizei frac)
-{
- ASSUME(istate.bsinc.m > 0);
-
- // Calculate the phase index and factor.
-#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
- const ALsizei pi{frac >> FRAC_PHASE_BITDIFF};
- const ALfloat pf{(frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF))};
-#undef FRAC_PHASE_BITDIFF
-
- const ALfloat *fil{istate.bsinc.filter + istate.bsinc.m*pi*4};
- const ALfloat *scd{fil + istate.bsinc.m};
- const ALfloat *phd{scd + istate.bsinc.m};
- const ALfloat *spd{phd + istate.bsinc.m};
-
- // Apply the scale and phase interpolated filter.
- ALfloat r{0.0f};
- for(ALsizei j_f{0};j_f < istate.bsinc.m;j_f++)
- r += (fil[j_f] + istate.bsinc.sf*scd[j_f] + pf*(phd[j_f] + istate.bsinc.sf*spd[j_f])) * vals[j_f];
- return r;
-}
-
-using SamplerT = ALfloat(const InterpState&, const ALfloat*RESTRICT, const ALsizei);
-template<SamplerT &Sampler>
-const ALfloat *DoResample(const InterpState *state, const ALfloat *RESTRICT src,
- ALsizei frac, ALint increment, ALfloat *RESTRICT dst, ALsizei numsamples)
-{
- ASSUME(numsamples > 0);
- ASSUME(increment > 0);
- ASSUME(frac >= 0);
-
- const InterpState istate{*state};
- auto proc_sample = [&src,&frac,istate,increment]() -> ALfloat
- {
- const ALfloat ret{Sampler(istate, src, frac)};
-
- frac += increment;
- src += frac>>FRACTIONBITS;
- frac &= FRACTIONMASK;
-
- return ret;
- };
- std::generate_n(dst, numsamples, proc_sample);
-
- return dst;
-}
-
-} // namespace
-
-template<>
-const ALfloat *Resample_<CopyTag,CTag>(const InterpState*, const ALfloat *RESTRICT src, ALsizei,
- ALint, ALfloat *RESTRICT dst, ALsizei dstlen)
-{
- ASSUME(dstlen > 0);
-#if defined(HAVE_SSE) || defined(HAVE_NEON)
- /* Avoid copying the source data if it's aligned like the destination. */
- if((reinterpret_cast<intptr_t>(src)&15) == (reinterpret_cast<intptr_t>(dst)&15))
- return src;
-#endif
- std::copy_n(src, dstlen, dst);
- return dst;
-}
-
-template<>
-const ALfloat *Resample_<PointTag,CTag>(const InterpState *state, const ALfloat *RESTRICT src,
- ALsizei frac, ALint increment, ALfloat *RESTRICT dst, ALsizei dstlen)
-{ return DoResample<do_point>(state, src, frac, increment, dst, dstlen); }
-
-template<>
-const ALfloat *Resample_<LerpTag,CTag>(const InterpState *state, const ALfloat *RESTRICT src,
- ALsizei frac, ALint increment, ALfloat *RESTRICT dst, ALsizei dstlen)
-{ return DoResample<do_lerp>(state, src, frac, increment, dst, dstlen); }
-
-template<>
-const ALfloat *Resample_<CubicTag,CTag>(const InterpState *state, const ALfloat *RESTRICT src,
- ALsizei frac, ALint increment, ALfloat *RESTRICT dst, ALsizei dstlen)
-{ return DoResample<do_cubic>(state, src-1, frac, increment, dst, dstlen); }
-
-template<>
-const ALfloat *Resample_<BSincTag,CTag>(const InterpState *state, const ALfloat *RESTRICT src,
- ALsizei frac, ALint increment, ALfloat *RESTRICT dst, ALsizei dstlen)
-{ return DoResample<do_bsinc>(state, src-state->bsinc.l, frac, increment, dst, dstlen); }
-
-
-static inline void ApplyCoeffs(ALsizei /*Offset*/, float2 *RESTRICT Values, const ALsizei IrSize,
- const HrirArray<ALfloat> &Coeffs, const ALfloat left, const ALfloat right)
-{
- ASSUME(IrSize >= 2);
- for(ALsizei c{0};c < IrSize;++c)
- {
- Values[c][0] += Coeffs[c][0] * left;
- Values[c][1] += Coeffs[c][1] * right;
- }
-}
-
-template<>
-void MixHrtf_<CTag>(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const ALfloat *InSamples, float2 *AccumSamples, const ALsizei OutPos, const ALsizei IrSize,
- MixHrtfFilter *hrtfparams, const ALsizei BufferSize)
-{
- MixHrtfBase<ApplyCoeffs>(LeftOut, RightOut, InSamples, AccumSamples, OutPos, IrSize,
- hrtfparams, BufferSize);
-}
-
-template<>
-void MixHrtfBlend_<CTag>(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const ALfloat *InSamples, float2 *AccumSamples, const ALsizei OutPos, const ALsizei IrSize,
- const HrtfFilter *oldparams, MixHrtfFilter *newparams, const ALsizei BufferSize)
-{
- MixHrtfBlendBase<ApplyCoeffs>(LeftOut, RightOut, InSamples, AccumSamples, OutPos, IrSize,
- oldparams, newparams, BufferSize);
-}
-
-template<>
-void MixDirectHrtf_<CTag>(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples, DirectHrtfState *State,
- const ALsizei BufferSize)
-{
- MixDirectHrtfBase<ApplyCoeffs>(LeftOut, RightOut, InSamples, AccumSamples, State, BufferSize);
-}
-
-
-template<>
-void Mix_<CTag>(const ALfloat *data, const al::span<FloatBufferLine> OutBuffer,
- ALfloat *CurrentGains, const ALfloat *TargetGains, const ALsizei Counter, const ALsizei OutPos,
- const ALsizei BufferSize)
-{
- ASSUME(BufferSize > 0);
-
- const ALfloat delta{(Counter > 0) ? 1.0f / static_cast<ALfloat>(Counter) : 0.0f};
- for(FloatBufferLine &output : OutBuffer)
- {
- ALfloat *RESTRICT dst{output.data()+OutPos};
- ALfloat gain{*CurrentGains};
- const ALfloat diff{*TargetGains - gain};
-
- ALsizei pos{0};
- if(std::fabs(diff) > std::numeric_limits<float>::epsilon())
- {
- ALsizei minsize{mini(BufferSize, Counter)};
- const ALfloat step{diff * delta};
- ALfloat step_count{0.0f};
- for(;pos < minsize;pos++)
- {
- dst[pos] += data[pos] * (gain + step*step_count);
- step_count += 1.0f;
- }
- if(pos == Counter)
- gain = *TargetGains;
- else
- gain += step*step_count;
- *CurrentGains = gain;
- }
- ++CurrentGains;
- ++TargetGains;
-
- if(!(std::fabs(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
- for(;pos < BufferSize;pos++)
- dst[pos] += data[pos]*gain;
- }
-}
-
-/* Basically the inverse of the above. Rather than one input going to multiple
- * outputs (each with its own gain), it's multiple inputs (each with its own
- * gain) going to one output. This applies one row (vs one column) of a matrix
- * transform. And as the matrices are more or less static once set up, no
- * stepping is necessary.
- */
-template<>
-void MixRow_<CTag>(FloatBufferLine &OutBuffer, const ALfloat *Gains,
- const al::span<const FloatBufferLine> InSamples, const ALsizei InPos, const ALsizei BufferSize)
-{
- ASSUME(BufferSize > 0);
-
- for(const FloatBufferLine &input : InSamples)
- {
- const ALfloat *RESTRICT src{input.data()+InPos};
- const ALfloat gain{*(Gains++)};
- if(!(std::fabs(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
-
- for(ALsizei i{0};i < BufferSize;i++)
- OutBuffer[i] += src[i] * gain;
- }
-}
diff --git a/Alc/mixer/mixer_neon.cpp b/Alc/mixer/mixer_neon.cpp
deleted file mode 100644
index fa487d97..00000000
--- a/Alc/mixer/mixer_neon.cpp
+++ /dev/null
@@ -1,307 +0,0 @@
-#include "config.h"
-
-#include <arm_neon.h>
-
-#include <limits>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "alcmain.h"
-#include "alu.h"
-#include "hrtf.h"
-#include "defs.h"
-#include "hrtfbase.h"
-
-
-
-template<>
-const ALfloat *Resample_<LerpTag,NEONTag>(const InterpState*, const ALfloat *RESTRICT src,
- ALsizei frac, ALint increment, ALfloat *RESTRICT dst, ALsizei dstlen)
-{
- const int32x4_t increment4 = vdupq_n_s32(increment*4);
- const float32x4_t fracOne4 = vdupq_n_f32(1.0f/FRACTIONONE);
- const int32x4_t fracMask4 = vdupq_n_s32(FRACTIONMASK);
- alignas(16) ALsizei pos_[4], frac_[4];
- int32x4_t pos4, frac4;
- ALsizei todo, pos, i;
-
- ASSUME(frac >= 0);
- ASSUME(increment > 0);
- ASSUME(dstlen > 0);
-
- InitiatePositionArrays(frac, increment, frac_, pos_, 4);
- frac4 = vld1q_s32(frac_);
- pos4 = vld1q_s32(pos_);
-
- todo = dstlen & ~3;
- for(i = 0;i < todo;i += 4)
- {
- const int pos0 = vgetq_lane_s32(pos4, 0);
- const int pos1 = vgetq_lane_s32(pos4, 1);
- const int pos2 = vgetq_lane_s32(pos4, 2);
- const int pos3 = vgetq_lane_s32(pos4, 3);
- const float32x4_t val1 = (float32x4_t){src[pos0], src[pos1], src[pos2], src[pos3]};
- const float32x4_t val2 = (float32x4_t){src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]};
-
- /* val1 + (val2-val1)*mu */
- const float32x4_t r0 = vsubq_f32(val2, val1);
- const float32x4_t mu = vmulq_f32(vcvtq_f32_s32(frac4), fracOne4);
- const float32x4_t out = vmlaq_f32(val1, mu, r0);
-
- vst1q_f32(&dst[i], out);
-
- frac4 = vaddq_s32(frac4, increment4);
- pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, FRACTIONBITS));
- frac4 = vandq_s32(frac4, fracMask4);
- }
-
- /* NOTE: These four elements represent the position *after* the last four
- * samples, so the lowest element is the next position to resample.
- */
- pos = vgetq_lane_s32(pos4, 0);
- frac = vgetq_lane_s32(frac4, 0);
-
- for(;i < dstlen;++i)
- {
- dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
-
- frac += increment;
- pos += frac>>FRACTIONBITS;
- frac &= FRACTIONMASK;
- }
- return dst;
-}
-
-template<>
-const ALfloat *Resample_<BSincTag,NEONTag>(const InterpState *state, const ALfloat *RESTRICT src,
- ALsizei frac, ALint increment, ALfloat *RESTRICT dst, ALsizei dstlen)
-{
- const ALfloat *const filter = state->bsinc.filter;
- const float32x4_t sf4 = vdupq_n_f32(state->bsinc.sf);
- const ALsizei m = state->bsinc.m;
- const float32x4_t *fil, *scd, *phd, *spd;
- ALsizei pi, i, j, offset;
- float32x4_t r4;
- ALfloat pf;
-
- ASSUME(m > 0);
- ASSUME(dstlen > 0);
- ASSUME(increment > 0);
- ASSUME(frac >= 0);
-
- src -= state->bsinc.l;
- for(i = 0;i < dstlen;i++)
- {
- // Calculate the phase index and factor.
-#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
- pi = frac >> FRAC_PHASE_BITDIFF;
- pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
-#undef FRAC_PHASE_BITDIFF
-
- offset = m*pi*4;
- fil = (const float32x4_t*)(filter + offset); offset += m;
- scd = (const float32x4_t*)(filter + offset); offset += m;
- phd = (const float32x4_t*)(filter + offset); offset += m;
- spd = (const float32x4_t*)(filter + offset);
-
- // Apply the scale and phase interpolated filter.
- r4 = vdupq_n_f32(0.0f);
- {
- const ALsizei count = m >> 2;
- const float32x4_t pf4 = vdupq_n_f32(pf);
-
- ASSUME(count > 0);
-
- for(j = 0;j < count;j++)
- {
- /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
- const float32x4_t f4 = vmlaq_f32(
- vmlaq_f32(fil[j], sf4, scd[j]),
- pf4, vmlaq_f32(phd[j], sf4, spd[j])
- );
- /* r += f*src */
- r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j*4]));
- }
- }
- r4 = vaddq_f32(r4, vcombine_f32(vrev64_f32(vget_high_f32(r4)),
- vrev64_f32(vget_low_f32(r4))));
- dst[i] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
-
- frac += increment;
- src += frac>>FRACTIONBITS;
- frac &= FRACTIONMASK;
- }
- return dst;
-}
-
-
-static inline void ApplyCoeffs(ALsizei /*Offset*/, float2 *RESTRICT Values, const ALsizei IrSize,
- const HrirArray<ALfloat> &Coeffs, const ALfloat left, const ALfloat right)
-{
- ASSUME(IrSize >= 2);
-
- float32x4_t leftright4;
- {
- float32x2_t leftright2 = vdup_n_f32(0.0);
- leftright2 = vset_lane_f32(left, leftright2, 0);
- leftright2 = vset_lane_f32(right, leftright2, 1);
- leftright4 = vcombine_f32(leftright2, leftright2);
- }
-
- for(ALsizei c{0};c < IrSize;c += 2)
- {
- float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[c ][0]),
- vld1_f32((float32_t*)&Values[c+1][0]));
- float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
-
- vals = vmlaq_f32(vals, coefs, leftright4);
-
- vst1_f32((float32_t*)&Values[c ][0], vget_low_f32(vals));
- vst1_f32((float32_t*)&Values[c+1][0], vget_high_f32(vals));
- }
-}
-
-template<>
-void MixHrtf_<NEONTag>(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const ALfloat *InSamples, float2 *AccumSamples, const ALsizei OutPos, const ALsizei IrSize,
- MixHrtfFilter *hrtfparams, const ALsizei BufferSize)
-{
- MixHrtfBase<ApplyCoeffs>(LeftOut, RightOut, InSamples, AccumSamples, OutPos, IrSize,
- hrtfparams, BufferSize);
-}
-
-template<>
-void MixHrtfBlend_<NEONTag>(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const ALfloat *InSamples, float2 *AccumSamples, const ALsizei OutPos, const ALsizei IrSize,
- const HrtfFilter *oldparams, MixHrtfFilter *newparams, const ALsizei BufferSize)
-{
- MixHrtfBlendBase<ApplyCoeffs>(LeftOut, RightOut, InSamples, AccumSamples, OutPos, IrSize,
- oldparams, newparams, BufferSize);
-}
-
-template<>
-void MixDirectHrtf_<NEONTag>(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples, DirectHrtfState *State,
- const ALsizei BufferSize)
-{
- MixDirectHrtfBase<ApplyCoeffs>(LeftOut, RightOut, InSamples, AccumSamples, State, BufferSize);
-}
-
-
-template<>
-void Mix_<NEONTag>(const ALfloat *data, const al::span<FloatBufferLine> OutBuffer,
- ALfloat *CurrentGains, const ALfloat *TargetGains, const ALsizei Counter, const ALsizei OutPos,
- const ALsizei BufferSize)
-{
- ASSUME(BufferSize > 0);
-
- const ALfloat delta{(Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f};
- for(FloatBufferLine &output : OutBuffer)
- {
- ALfloat *RESTRICT dst{al::assume_aligned<16>(output.data()+OutPos)};
- ALfloat gain{*CurrentGains};
- const ALfloat diff{*TargetGains - gain};
-
- ALsizei pos{0};
- if(std::fabs(diff) > std::numeric_limits<float>::epsilon())
- {
- ALsizei minsize{mini(BufferSize, Counter)};
- const ALfloat step{diff * delta};
- ALfloat step_count{0.0f};
- /* Mix with applying gain steps in aligned multiples of 4. */
- if(LIKELY(minsize > 3))
- {
- const float32x4_t four4{vdupq_n_f32(4.0f)};
- const float32x4_t step4{vdupq_n_f32(step)};
- const float32x4_t gain4{vdupq_n_f32(gain)};
- float32x4_t step_count4{vsetq_lane_f32(0.0f,
- vsetq_lane_f32(1.0f,
- vsetq_lane_f32(2.0f,
- vsetq_lane_f32(3.0f, vdupq_n_f32(0.0f), 3),
- 2), 1), 0
- )};
- ALsizei todo{minsize >> 2};
-
- do {
- const float32x4_t val4 = vld1q_f32(&data[pos]);
- float32x4_t dry4 = vld1q_f32(&dst[pos]);
- dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4));
- step_count4 = vaddq_f32(step_count4, four4);
- vst1q_f32(&dst[pos], dry4);
- pos += 4;
- } while(--todo);
- /* NOTE: step_count4 now represents the next four counts after
- * the last four mixed samples, so the lowest element
- * represents the next step count to apply.
- */
- step_count = vgetq_lane_f32(step_count4, 0);
- }
- /* Mix with applying left over gain steps that aren't aligned multiples of 4. */
- for(;pos < minsize;pos++)
- {
- dst[pos] += data[pos]*(gain + step*step_count);
- step_count += 1.0f;
- }
- if(pos == Counter)
- gain = *TargetGains;
- else
- gain += step*step_count;
- *CurrentGains = gain;
-
- /* Mix until pos is aligned with 4 or the mix is done. */
- minsize = mini(BufferSize, (pos+3)&~3);
- for(;pos < minsize;pos++)
- dst[pos] += data[pos]*gain;
- }
- ++CurrentGains;
- ++TargetGains;
-
- if(!(std::fabs(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
- if(LIKELY(BufferSize-pos > 3))
- {
- ALsizei todo{(BufferSize-pos) >> 2};
- const float32x4_t gain4 = vdupq_n_f32(gain);
- do {
- const float32x4_t val4 = vld1q_f32(&data[pos]);
- float32x4_t dry4 = vld1q_f32(&dst[pos]);
- dry4 = vmlaq_f32(dry4, val4, gain4);
- vst1q_f32(&dst[pos], dry4);
- pos += 4;
- } while(--todo);
- }
- for(;pos < BufferSize;pos++)
- dst[pos] += data[pos]*gain;
- }
-}
-
-template<>
-void MixRow_<NEONTag>(FloatBufferLine &OutBuffer, const ALfloat *Gains,
- const al::span<const FloatBufferLine> InSamples, const ALsizei InPos, const ALsizei BufferSize)
-{
- ASSUME(BufferSize > 0);
-
- for(const FloatBufferLine &input : InSamples)
- {
- const ALfloat *RESTRICT src{al::assume_aligned<16>(input.data()+InPos)};
- const ALfloat gain{*(Gains++)};
- if(!(std::fabs(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
-
- ALsizei pos{0};
- if(LIKELY(BufferSize > 3))
- {
- ALsizei todo{BufferSize >> 2};
- float32x4_t gain4{vdupq_n_f32(gain)};
- do {
- const float32x4_t val4 = vld1q_f32(&src[pos]);
- float32x4_t dry4 = vld1q_f32(&OutBuffer[pos]);
- dry4 = vmlaq_f32(dry4, val4, gain4);
- vst1q_f32(&OutBuffer[pos], dry4);
- pos += 4;
- } while(--todo);
- }
- for(;pos < BufferSize;pos++)
- OutBuffer[pos] += src[pos]*gain;
- }
-}
diff --git a/Alc/mixer/mixer_sse.cpp b/Alc/mixer/mixer_sse.cpp
deleted file mode 100644
index b763fdbd..00000000
--- a/Alc/mixer/mixer_sse.cpp
+++ /dev/null
@@ -1,262 +0,0 @@
-#include "config.h"
-
-#include <xmmintrin.h>
-
-#include <limits>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "alcmain.h"
-#include "alu.h"
-
-#include "alSource.h"
-#include "alAuxEffectSlot.h"
-#include "defs.h"
-#include "hrtfbase.h"
-
-
-template<>
-const ALfloat *Resample_<BSincTag,SSETag>(const InterpState *state, const ALfloat *RESTRICT src,
- ALsizei frac, ALint increment, ALfloat *RESTRICT dst, ALsizei dstlen)
-{
- const ALfloat *const filter{state->bsinc.filter};
- const __m128 sf4{_mm_set1_ps(state->bsinc.sf)};
- const ALsizei m{state->bsinc.m};
-
- ASSUME(m > 0);
- ASSUME(dstlen > 0);
- ASSUME(increment > 0);
- ASSUME(frac >= 0);
-
- src -= state->bsinc.l;
- for(ALsizei i{0};i < dstlen;i++)
- {
- // Calculate the phase index and factor.
-#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
- const ALsizei pi{frac >> FRAC_PHASE_BITDIFF};
- const ALfloat pf{(frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF))};
-#undef FRAC_PHASE_BITDIFF
-
- ALsizei offset{m*pi*4};
- const __m128 *fil{reinterpret_cast<const __m128*>(filter + offset)}; offset += m;
- const __m128 *scd{reinterpret_cast<const __m128*>(filter + offset)}; offset += m;
- const __m128 *phd{reinterpret_cast<const __m128*>(filter + offset)}; offset += m;
- const __m128 *spd{reinterpret_cast<const __m128*>(filter + offset)};
-
- // Apply the scale and phase interpolated filter.
- __m128 r4{_mm_setzero_ps()};
- {
- const ALsizei count{m >> 2};
- const __m128 pf4{_mm_set1_ps(pf)};
-
- ASSUME(count > 0);
-
-#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
- for(ALsizei j{0};j < count;j++)
- {
- /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
- const __m128 f4 = MLA4(
- MLA4(fil[j], sf4, scd[j]),
- pf4, MLA4(phd[j], sf4, spd[j])
- );
- /* r += f*src */
- r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j*4]));
- }
-#undef MLA4
- }
- r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
- r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
- dst[i] = _mm_cvtss_f32(r4);
-
- frac += increment;
- src += frac>>FRACTIONBITS;
- frac &= FRACTIONMASK;
- }
- return dst;
-}
-
-
-static inline void ApplyCoeffs(ALsizei Offset, float2 *RESTRICT Values, const ALsizei IrSize,
- const HrirArray<ALfloat> &Coeffs, const ALfloat left, const ALfloat right)
-{
- const __m128 lrlr{_mm_setr_ps(left, right, left, right)};
-
- ASSUME(IrSize >= 2);
-
- if((Offset&1))
- {
- __m128 imp0, imp1;
- __m128 coeffs{_mm_load_ps(&Coeffs[0][0])};
- __m128 vals{_mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<__m64*>(&Values[0][0]))};
- imp0 = _mm_mul_ps(lrlr, coeffs);
- vals = _mm_add_ps(imp0, vals);
- _mm_storel_pi(reinterpret_cast<__m64*>(&Values[0][0]), vals);
- ALsizei i{1};
- for(;i < IrSize-1;i += 2)
- {
- coeffs = _mm_load_ps(&Coeffs[i+1][0]);
- vals = _mm_load_ps(&Values[i][0]);
- imp1 = _mm_mul_ps(lrlr, coeffs);
- imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
- vals = _mm_add_ps(imp0, vals);
- _mm_store_ps(&Values[i][0], vals);
- imp0 = imp1;
- }
- vals = _mm_loadl_pi(vals, reinterpret_cast<__m64*>(&Values[i][0]));
- imp0 = _mm_movehl_ps(imp0, imp0);
- vals = _mm_add_ps(imp0, vals);
- _mm_storel_pi(reinterpret_cast<__m64*>(&Values[i][0]), vals);
- }
- else
- {
- for(ALsizei i{0};i < IrSize;i += 2)
- {
- __m128 coeffs{_mm_load_ps(&Coeffs[i][0])};
- __m128 vals{_mm_load_ps(&Values[i][0])};
- vals = _mm_add_ps(vals, _mm_mul_ps(lrlr, coeffs));
- _mm_store_ps(&Values[i][0], vals);
- }
- }
-}
-
-template<>
-void MixHrtf_<SSETag>(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const ALfloat *InSamples, float2 *AccumSamples, const ALsizei OutPos, const ALsizei IrSize,
- MixHrtfFilter *hrtfparams, const ALsizei BufferSize)
-{
- MixHrtfBase<ApplyCoeffs>(LeftOut, RightOut, InSamples, AccumSamples, OutPos, IrSize,
- hrtfparams, BufferSize);
-}
-
-template<>
-void MixHrtfBlend_<SSETag>(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const ALfloat *InSamples, float2 *AccumSamples, const ALsizei OutPos, const ALsizei IrSize,
- const HrtfFilter *oldparams, MixHrtfFilter *newparams, const ALsizei BufferSize)
-{
- MixHrtfBlendBase<ApplyCoeffs>(LeftOut, RightOut, InSamples, AccumSamples, OutPos, IrSize,
- oldparams, newparams, BufferSize);
-}
-
-template<>
-void MixDirectHrtf_<SSETag>(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
- const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples, DirectHrtfState *State,
- const ALsizei BufferSize)
-{
- MixDirectHrtfBase<ApplyCoeffs>(LeftOut, RightOut, InSamples, AccumSamples, State, BufferSize);
-}
-
-
-template<>
-void Mix_<SSETag>(const ALfloat *data, const al::span<FloatBufferLine> OutBuffer,
- ALfloat *CurrentGains, const ALfloat *TargetGains, const ALsizei Counter, const ALsizei OutPos,
- const ALsizei BufferSize)
-{
- ASSUME(BufferSize > 0);
-
- const ALfloat delta{(Counter > 0) ? 1.0f / static_cast<ALfloat>(Counter) : 0.0f};
- for(FloatBufferLine &output : OutBuffer)
- {
- ALfloat *RESTRICT dst{al::assume_aligned<16>(output.data()+OutPos)};
- ALfloat gain{*CurrentGains};
- const ALfloat diff{*TargetGains - gain};
-
- ALsizei pos{0};
- if(std::fabs(diff) > std::numeric_limits<float>::epsilon())
- {
- ALsizei minsize{mini(BufferSize, Counter)};
- const ALfloat step{diff * delta};
- ALfloat step_count{0.0f};
- /* Mix with applying gain steps in aligned multiples of 4. */
- if(LIKELY(minsize > 3))
- {
- const __m128 four4{_mm_set1_ps(4.0f)};
- const __m128 step4{_mm_set1_ps(step)};
- const __m128 gain4{_mm_set1_ps(gain)};
- __m128 step_count4{_mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f)};
- ALsizei todo{minsize >> 2};
- do {
- const __m128 val4{_mm_load_ps(&data[pos])};
- __m128 dry4{_mm_load_ps(&dst[pos])};
-#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
- /* dry += val * (gain + step*step_count) */
- dry4 = MLA4(dry4, val4, MLA4(gain4, step4, step_count4));
-#undef MLA4
- _mm_store_ps(&dst[pos], dry4);
- step_count4 = _mm_add_ps(step_count4, four4);
- pos += 4;
- } while(--todo);
- /* NOTE: step_count4 now represents the next four counts after
- * the last four mixed samples, so the lowest element
- * represents the next step count to apply.
- */
- step_count = _mm_cvtss_f32(step_count4);
- }
- /* Mix with applying left over gain steps that aren't aligned multiples of 4. */
- for(;pos < minsize;pos++)
- {
- dst[pos] += data[pos]*(gain + step*step_count);
- step_count += 1.0f;
- }
- if(pos == Counter)
- gain = *TargetGains;
- else
- gain += step*step_count;
- *CurrentGains = gain;
-
- /* Mix until pos is aligned with 4 or the mix is done. */
- minsize = mini(BufferSize, (pos+3)&~3);
- for(;pos < minsize;pos++)
- dst[pos] += data[pos]*gain;
- }
- ++CurrentGains;
- ++TargetGains;
-
- if(!(std::fabs(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
- if(LIKELY(BufferSize-pos > 3))
- {
- ALsizei todo{(BufferSize-pos) >> 2};
- const __m128 gain4{_mm_set1_ps(gain)};
- do {
- const __m128 val4{_mm_load_ps(&data[pos])};
- __m128 dry4{_mm_load_ps(&dst[pos])};
- dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
- _mm_store_ps(&dst[pos], dry4);
- pos += 4;
- } while(--todo);
- }
- for(;pos < BufferSize;pos++)
- dst[pos] += data[pos]*gain;
- }
-}
-
-template<>
-void MixRow_<SSETag>(FloatBufferLine &OutBuffer, const ALfloat *Gains,
- const al::span<const FloatBufferLine> InSamples, const ALsizei InPos, const ALsizei BufferSize)
-{
- ASSUME(BufferSize > 0);
-
- for(const FloatBufferLine &input : InSamples)
- {
- const ALfloat *RESTRICT src{al::assume_aligned<16>(input.data()+InPos)};
- const ALfloat gain{*(Gains++)};
- if(!(std::fabs(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
-
- ALsizei pos{0};
- if(LIKELY(BufferSize > 3))
- {
- ALsizei todo{BufferSize >> 2};
- const __m128 gain4 = _mm_set1_ps(gain);
- do {
- const __m128 val4{_mm_load_ps(&src[pos])};
- __m128 dry4{_mm_load_ps(&OutBuffer[pos])};
- dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
- _mm_store_ps(&OutBuffer[pos], dry4);
- pos += 4;
- } while(--todo);
- }
- for(;pos < BufferSize;pos++)
- OutBuffer[pos] += src[pos]*gain;
- }
-}
diff --git a/Alc/mixer/mixer_sse2.cpp b/Alc/mixer/mixer_sse2.cpp
deleted file mode 100644
index b5d00106..00000000
--- a/Alc/mixer/mixer_sse2.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2014 by Timothy Arceri <[email protected]>.
- * 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 <xmmintrin.h>
-#include <emmintrin.h>
-
-#include "alu.h"
-#include "defs.h"
-
-
-template<>
-const ALfloat *Resample_<LerpTag,SSE2Tag>(const InterpState*, const ALfloat *RESTRICT src,
- ALsizei frac, ALint increment, ALfloat *RESTRICT dst, ALsizei dstlen)
-{
- const __m128i increment4{_mm_set1_epi32(increment*4)};
- const __m128 fracOne4{_mm_set1_ps(1.0f/FRACTIONONE)};
- const __m128i fracMask4{_mm_set1_epi32(FRACTIONMASK)};
-
- ASSUME(frac > 0);
- ASSUME(increment > 0);
- ASSUME(dstlen >= 0);
-
- alignas(16) ALsizei pos_[4], frac_[4];
- InitiatePositionArrays(frac, increment, frac_, pos_, 4);
- __m128i frac4{_mm_setr_epi32(frac_[0], frac_[1], frac_[2], frac_[3])};
- __m128i pos4{_mm_setr_epi32(pos_[0], pos_[1], pos_[2], pos_[3])};
-
- const ALsizei todo{dstlen & ~3};
- for(ALsizei i{0};i < todo;i += 4)
- {
- const int pos0{_mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(0, 0, 0, 0)))};
- const int pos1{_mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(1, 1, 1, 1)))};
- const int pos2{_mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(2, 2, 2, 2)))};
- const int pos3{_mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(3, 3, 3, 3)))};
- const __m128 val1{_mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ])};
- const __m128 val2{_mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])};
-
- /* val1 + (val2-val1)*mu */
- const __m128 r0{_mm_sub_ps(val2, val1)};
- const __m128 mu{_mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4)};
- const __m128 out{_mm_add_ps(val1, _mm_mul_ps(mu, r0))};
-
- _mm_store_ps(&dst[i], out);
-
- frac4 = _mm_add_epi32(frac4, increment4);
- pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
- frac4 = _mm_and_si128(frac4, fracMask4);
- }
-
- /* NOTE: These four elements represent the position *after* the last four
- * samples, so the lowest element is the next position to resample.
- */
- ALsizei pos{_mm_cvtsi128_si32(pos4)};
- frac = _mm_cvtsi128_si32(frac4);
-
- for(ALsizei i{todo};i < dstlen;++i)
- {
- dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
-
- frac += increment;
- pos += frac>>FRACTIONBITS;
- frac &= FRACTIONMASK;
- }
- return dst;
-}
diff --git a/Alc/mixer/mixer_sse3.cpp b/Alc/mixer/mixer_sse3.cpp
deleted file mode 100644
index e69de29b..00000000
--- a/Alc/mixer/mixer_sse3.cpp
+++ /dev/null
diff --git a/Alc/mixer/mixer_sse41.cpp b/Alc/mixer/mixer_sse41.cpp
deleted file mode 100644
index 7efbda7b..00000000
--- a/Alc/mixer/mixer_sse41.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2014 by Timothy Arceri <[email protected]>.
- * 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 <xmmintrin.h>
-#include <emmintrin.h>
-#include <smmintrin.h>
-
-#include "alu.h"
-#include "defs.h"
-
-
-template<>
-const ALfloat *Resample_<LerpTag,SSE4Tag>(const InterpState*, const ALfloat *RESTRICT src,
- ALsizei frac, ALint increment, ALfloat *RESTRICT dst, ALsizei dstlen)
-{
- const __m128i increment4{_mm_set1_epi32(increment*4)};
- const __m128 fracOne4{_mm_set1_ps(1.0f/FRACTIONONE)};
- const __m128i fracMask4{_mm_set1_epi32(FRACTIONMASK)};
-
- ASSUME(frac > 0);
- ASSUME(increment > 0);
- ASSUME(dstlen >= 0);
-
- alignas(16) ALsizei pos_[4], frac_[4];
- InitiatePositionArrays(frac, increment, frac_, pos_, 4);
- __m128i frac4{_mm_setr_epi32(frac_[0], frac_[1], frac_[2], frac_[3])};
- __m128i pos4{_mm_setr_epi32(pos_[0], pos_[1], pos_[2], pos_[3])};
-
- const ALsizei todo{dstlen & ~3};
- for(ALsizei i{0};i < todo;i += 4)
- {
- const int pos0{_mm_extract_epi32(pos4, 0)};
- const int pos1{_mm_extract_epi32(pos4, 1)};
- const int pos2{_mm_extract_epi32(pos4, 2)};
- const int pos3{_mm_extract_epi32(pos4, 3)};
- const __m128 val1{_mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ])};
- const __m128 val2{_mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])};
-
- /* val1 + (val2-val1)*mu */
- const __m128 r0{_mm_sub_ps(val2, val1)};
- const __m128 mu{_mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4)};
- const __m128 out{_mm_add_ps(val1, _mm_mul_ps(mu, r0))};
-
- _mm_store_ps(&dst[i], out);
-
- frac4 = _mm_add_epi32(frac4, increment4);
- pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
- frac4 = _mm_and_si128(frac4, fracMask4);
- }
-
- /* NOTE: These four elements represent the position *after* the last four
- * samples, so the lowest element is the next position to resample.
- */
- ALsizei pos{_mm_cvtsi128_si32(pos4)};
- frac = _mm_cvtsi128_si32(frac4);
-
- for(ALsizei i{todo};i < dstlen;++i)
- {
- dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
-
- frac += increment;
- pos += frac>>FRACTIONBITS;
- frac &= FRACTIONMASK;
- }
- return dst;
-}
diff --git a/Alc/mixvoice.cpp b/Alc/mixvoice.cpp
deleted file mode 100644
index be872f6d..00000000
--- a/Alc/mixvoice.cpp
+++ /dev/null
@@ -1,954 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <algorithm>
-#include <array>
-#include <atomic>
-#include <cassert>
-#include <climits>
-#include <cstddef>
-#include <cstdint>
-#include <cstdlib>
-#include <cstring>
-#include <iterator>
-#include <memory>
-#include <new>
-#include <numeric>
-#include <string>
-#include <utility>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-
-#include "alBuffer.h"
-#include "alcmain.h"
-#include "alSource.h"
-#include "albyte.h"
-#include "alconfig.h"
-#include "alcontext.h"
-#include "alnumeric.h"
-#include "aloptional.h"
-#include "alspan.h"
-#include "alu.h"
-#include "cpu_caps.h"
-#include "filters/biquad.h"
-#include "filters/nfc.h"
-#include "filters/splitter.h"
-#include "hrtf.h"
-#include "inprogext.h"
-#include "logging.h"
-#include "mixer/defs.h"
-#include "opthelpers.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "vector.h"
-
-
-static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
- "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
-
-/* BSinc24 requires up to 23 extra samples before the current position, and 24 after. */
-static_assert(MAX_RESAMPLE_PADDING >= 24, "MAX_RESAMPLE_PADDING must be at least 24!");
-
-
-Resampler ResamplerDefault = LinearResampler;
-
-MixerFunc MixSamples = Mix_<CTag>;
-RowMixerFunc MixRowSamples = MixRow_<CTag>;
-static HrtfMixerFunc MixHrtfSamples = MixHrtf_<CTag>;
-static HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_<CTag>;
-
-static MixerFunc SelectMixer()
-{
-#ifdef HAVE_NEON
- if((CPUCapFlags&CPU_CAP_NEON))
- return Mix_<NEONTag>;
-#endif
-#ifdef HAVE_SSE
- if((CPUCapFlags&CPU_CAP_SSE))
- return Mix_<SSETag>;
-#endif
- return Mix_<CTag>;
-}
-
-static RowMixerFunc SelectRowMixer()
-{
-#ifdef HAVE_NEON
- if((CPUCapFlags&CPU_CAP_NEON))
- return MixRow_<NEONTag>;
-#endif
-#ifdef HAVE_SSE
- if((CPUCapFlags&CPU_CAP_SSE))
- return MixRow_<SSETag>;
-#endif
- return MixRow_<CTag>;
-}
-
-static inline HrtfMixerFunc SelectHrtfMixer()
-{
-#ifdef HAVE_NEON
- if((CPUCapFlags&CPU_CAP_NEON))
- return MixHrtf_<NEONTag>;
-#endif
-#ifdef HAVE_SSE
- if((CPUCapFlags&CPU_CAP_SSE))
- return MixHrtf_<SSETag>;
-#endif
- return MixHrtf_<CTag>;
-}
-
-static inline HrtfMixerBlendFunc SelectHrtfBlendMixer()
-{
-#ifdef HAVE_NEON
- if((CPUCapFlags&CPU_CAP_NEON))
- return MixHrtfBlend_<NEONTag>;
-#endif
-#ifdef HAVE_SSE
- if((CPUCapFlags&CPU_CAP_SSE))
- return MixHrtfBlend_<SSETag>;
-#endif
- return MixHrtfBlend_<CTag>;
-}
-
-ResamplerFunc SelectResampler(Resampler resampler)
-{
- switch(resampler)
- {
- case PointResampler:
- return Resample_<PointTag,CTag>;
- case LinearResampler:
-#ifdef HAVE_NEON
- if((CPUCapFlags&CPU_CAP_NEON))
- return Resample_<LerpTag,NEONTag>;
-#endif
-#ifdef HAVE_SSE4_1
- if((CPUCapFlags&CPU_CAP_SSE4_1))
- return Resample_<LerpTag,SSE4Tag>;
-#endif
-#ifdef HAVE_SSE2
- if((CPUCapFlags&CPU_CAP_SSE2))
- return Resample_<LerpTag,SSE2Tag>;
-#endif
- return Resample_<LerpTag,CTag>;
- case FIR4Resampler:
- return Resample_<CubicTag,CTag>;
- case BSinc12Resampler:
- case BSinc24Resampler:
-#ifdef HAVE_NEON
- if((CPUCapFlags&CPU_CAP_NEON))
- return Resample_<BSincTag,NEONTag>;
-#endif
-#ifdef HAVE_SSE
- if((CPUCapFlags&CPU_CAP_SSE))
- return Resample_<BSincTag,SSETag>;
-#endif
- return Resample_<BSincTag,CTag>;
- }
-
- return Resample_<PointTag,CTag>;
-}
-
-
-void aluInitMixer()
-{
- if(auto resopt = ConfigValueStr(nullptr, nullptr, "resampler"))
- {
- const char *str{resopt->c_str()};
- if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
- ResamplerDefault = PointResampler;
- else if(strcasecmp(str, "linear") == 0)
- ResamplerDefault = LinearResampler;
- else if(strcasecmp(str, "cubic") == 0)
- ResamplerDefault = FIR4Resampler;
- else if(strcasecmp(str, "bsinc12") == 0)
- ResamplerDefault = BSinc12Resampler;
- else if(strcasecmp(str, "bsinc24") == 0)
- ResamplerDefault = BSinc24Resampler;
- else if(strcasecmp(str, "bsinc") == 0)
- {
- WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str);
- ResamplerDefault = BSinc12Resampler;
- }
- else if(strcasecmp(str, "sinc4") == 0 || strcasecmp(str, "sinc8") == 0)
- {
- WARN("Resampler option \"%s\" is deprecated, using cubic\n", str);
- ResamplerDefault = FIR4Resampler;
- }
- else
- {
- char *end;
- long n = strtol(str, &end, 0);
- if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler))
- ResamplerDefault = static_cast<Resampler>(n);
- else
- WARN("Invalid resampler: %s\n", str);
- }
- }
-
- MixHrtfBlendSamples = SelectHrtfBlendMixer();
- MixHrtfSamples = SelectHrtfMixer();
- MixSamples = SelectMixer();
- MixRowSamples = SelectRowMixer();
-}
-
-
-namespace {
-
-/* A quick'n'dirty lookup table to decode a muLaw-encoded byte sample into a
- * signed 16-bit sample */
-constexpr ALshort muLawDecompressionTable[256] = {
- -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
- -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
- -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
- -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
- -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
- -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
- -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
- -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
- -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
- -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
- -876, -844, -812, -780, -748, -716, -684, -652,
- -620, -588, -556, -524, -492, -460, -428, -396,
- -372, -356, -340, -324, -308, -292, -276, -260,
- -244, -228, -212, -196, -180, -164, -148, -132,
- -120, -112, -104, -96, -88, -80, -72, -64,
- -56, -48, -40, -32, -24, -16, -8, 0,
- 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
- 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
- 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
- 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
- 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
- 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
- 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
- 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
- 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
- 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
- 876, 844, 812, 780, 748, 716, 684, 652,
- 620, 588, 556, 524, 492, 460, 428, 396,
- 372, 356, 340, 324, 308, 292, 276, 260,
- 244, 228, 212, 196, 180, 164, 148, 132,
- 120, 112, 104, 96, 88, 80, 72, 64,
- 56, 48, 40, 32, 24, 16, 8, 0
-};
-
-/* A quick'n'dirty lookup table to decode an aLaw-encoded byte sample into a
- * signed 16-bit sample */
-constexpr ALshort aLawDecompressionTable[256] = {
- -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
- -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
- -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
- -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
- -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
- -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
- -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472,
- -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
- -344, -328, -376, -360, -280, -264, -312, -296,
- -472, -456, -504, -488, -408, -392, -440, -424,
- -88, -72, -120, -104, -24, -8, -56, -40,
- -216, -200, -248, -232, -152, -136, -184, -168,
- -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
- -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
- -688, -656, -752, -720, -560, -528, -624, -592,
- -944, -912, -1008, -976, -816, -784, -880, -848,
- 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
- 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
- 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
- 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
- 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
- 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
- 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
- 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
- 344, 328, 376, 360, 280, 264, 312, 296,
- 472, 456, 504, 488, 408, 392, 440, 424,
- 88, 72, 120, 104, 24, 8, 56, 40,
- 216, 200, 248, 232, 152, 136, 184, 168,
- 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
- 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
- 688, 656, 752, 720, 560, 528, 624, 592,
- 944, 912, 1008, 976, 816, 784, 880, 848
-};
-
-
-void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
-{
- ALbitfieldSOFT enabledevt{context->EnabledEvts.load(std::memory_order_acquire)};
- if(!(enabledevt&EventType_SourceStateChange)) return;
-
- RingBuffer *ring{context->AsyncEvents.get()};
- auto evt_vec = ring->getWriteVector();
- if(evt_vec.first.len < 1) return;
-
- AsyncEvent *evt{new (evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}};
- evt->u.srcstate.id = id;
- evt->u.srcstate.state = AL_STOPPED;
-
- ring->writeAdvance(1);
- context->EventSem.post();
-}
-
-
-const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter, ALfloat *dst,
- const ALfloat *src, ALsizei numsamples, int type)
-{
- switch(type)
- {
- case AF_None:
- lpfilter->clear();
- hpfilter->clear();
- break;
-
- case AF_LowPass:
- lpfilter->process(dst, src, numsamples);
- hpfilter->clear();
- return dst;
- case AF_HighPass:
- lpfilter->clear();
- hpfilter->process(dst, src, numsamples);
- return dst;
-
- case AF_BandPass:
- lpfilter->process(dst, src, numsamples);
- hpfilter->process(dst, dst, numsamples);
- return dst;
- }
- return src;
-}
-
-
-/* Base template left undefined. Should be marked =delete, but Clang 3.8.1
- * chokes on that given the inline specializations.
- */
-template<FmtType T>
-inline ALfloat LoadSample(typename FmtTypeTraits<T>::Type val);
-
-template<> inline ALfloat LoadSample<FmtUByte>(FmtTypeTraits<FmtUByte>::Type val)
-{ return (val-128) * (1.0f/128.0f); }
-template<> inline ALfloat LoadSample<FmtShort>(FmtTypeTraits<FmtShort>::Type val)
-{ return val * (1.0f/32768.0f); }
-template<> inline ALfloat LoadSample<FmtFloat>(FmtTypeTraits<FmtFloat>::Type val)
-{ return val; }
-template<> inline ALfloat LoadSample<FmtDouble>(FmtTypeTraits<FmtDouble>::Type val)
-{ return static_cast<ALfloat>(val); }
-template<> inline ALfloat LoadSample<FmtMulaw>(FmtTypeTraits<FmtMulaw>::Type val)
-{ return muLawDecompressionTable[val] * (1.0f/32768.0f); }
-template<> inline ALfloat LoadSample<FmtAlaw>(FmtTypeTraits<FmtAlaw>::Type val)
-{ return aLawDecompressionTable[val] * (1.0f/32768.0f); }
-
-template<FmtType T>
-inline void LoadSampleArray(ALfloat *RESTRICT dst, const al::byte *src, ALint srcstep,
- const ptrdiff_t samples)
-{
- using SampleType = typename FmtTypeTraits<T>::Type;
-
- const SampleType *RESTRICT ssrc{reinterpret_cast<const SampleType*>(src)};
- for(ALsizei i{0};i < samples;i++)
- dst[i] += LoadSample<T>(ssrc[i*srcstep]);
-}
-
-void LoadSamples(ALfloat *RESTRICT dst, const al::byte *src, ALint srcstep, FmtType srctype,
- const ptrdiff_t samples)
-{
-#define HANDLE_FMT(T) case T: LoadSampleArray<T>(dst, src, srcstep, samples); break
- switch(srctype)
- {
- HANDLE_FMT(FmtUByte);
- HANDLE_FMT(FmtShort);
- HANDLE_FMT(FmtFloat);
- HANDLE_FMT(FmtDouble);
- HANDLE_FMT(FmtMulaw);
- HANDLE_FMT(FmtAlaw);
- }
-#undef HANDLE_FMT
-}
-
-ALfloat *LoadBufferStatic(ALbufferlistitem *BufferListItem, ALbufferlistitem *&BufferLoopItem,
- const ALsizei NumChannels, const ALsizei SampleSize, const ALsizei chan, ALsizei DataPosInt,
- al::span<ALfloat> SrcBuffer)
-{
- /* TODO: For static sources, loop points are taken from the first buffer
- * (should be adjusted by any buffer offset, to possibly be added later).
- */
- const ALbuffer *Buffer0{BufferListItem->buffers[0]};
- const ALsizei LoopStart{Buffer0->LoopStart};
- const ALsizei LoopEnd{Buffer0->LoopEnd};
- ASSUME(LoopStart >= 0);
- ASSUME(LoopEnd > LoopStart);
-
- /* If current pos is beyond the loop range, do not loop */
- if(!BufferLoopItem || DataPosInt >= LoopEnd)
- {
- BufferLoopItem = nullptr;
-
- auto load_buffer = [DataPosInt,NumChannels,SampleSize,chan,SrcBuffer](size_t CompLen, const ALbuffer *buffer) -> size_t
- {
- if(DataPosInt >= buffer->SampleLen)
- return CompLen;
-
- /* Load what's left to play from the buffer */
- const size_t DataSize{std::min<size_t>(SrcBuffer.size(),
- buffer->SampleLen - DataPosInt)};
- CompLen = std::max(CompLen, DataSize);
-
- const al::byte *Data{buffer->mData.data()};
- Data += (DataPosInt*NumChannels + chan)*SampleSize;
-
- LoadSamples(SrcBuffer.data(), Data, NumChannels, buffer->mFmtType, DataSize);
- return CompLen;
- };
- /* It's impossible to have a buffer list item with no entries. */
- ASSUME(BufferListItem->num_buffers > 0);
- auto buffers_end = BufferListItem->buffers + BufferListItem->num_buffers;
- SrcBuffer = SrcBuffer.subspan(std::accumulate(BufferListItem->buffers, buffers_end,
- size_t{0u}, load_buffer));
- }
- else
- {
- const al::span<ALfloat> SrcData{SrcBuffer.first(
- std::min<size_t>(SrcBuffer.size(), LoopEnd - DataPosInt))};
-
- auto load_buffer = [DataPosInt,NumChannels,SampleSize,chan,SrcData](size_t CompLen, const ALbuffer *buffer) -> size_t
- {
- if(DataPosInt >= buffer->SampleLen)
- return CompLen;
-
- /* Load what's left of this loop iteration */
- const size_t DataSize{std::min<size_t>(SrcData.size(),
- buffer->SampleLen - DataPosInt)};
- CompLen = std::max(CompLen, DataSize);
-
- const al::byte *Data{buffer->mData.data()};
- Data += (DataPosInt*NumChannels + chan)*SampleSize;
-
- LoadSamples(SrcData.data(), Data, NumChannels, buffer->mFmtType, DataSize);
- return CompLen;
- };
- ASSUME(BufferListItem->num_buffers > 0);
- auto buffers_end = BufferListItem->buffers + BufferListItem->num_buffers;
- SrcBuffer = SrcBuffer.subspan(std::accumulate(BufferListItem->buffers, buffers_end,
- size_t{0u}, load_buffer));
-
- const auto LoopSize = static_cast<size_t>(LoopEnd - LoopStart);
- while(!SrcBuffer.empty())
- {
- const al::span<ALfloat> SrcData{SrcBuffer.first(
- std::min<size_t>(SrcBuffer.size(), LoopSize))};
-
- auto load_buffer_loop = [LoopStart,NumChannels,SampleSize,chan,SrcData](size_t CompLen, const ALbuffer *buffer) -> size_t
- {
- if(LoopStart >= buffer->SampleLen)
- return CompLen;
-
- const size_t DataSize{std::min<size_t>(SrcData.size(),
- buffer->SampleLen-LoopStart)};
- CompLen = std::max(CompLen, DataSize);
-
- const al::byte *Data{buffer->mData.data()};
- Data += (LoopStart*NumChannels + chan)*SampleSize;
-
- LoadSamples(SrcData.data(), Data, NumChannels, buffer->mFmtType, DataSize);
- return CompLen;
- };
- SrcBuffer = SrcBuffer.subspan(std::accumulate(BufferListItem->buffers, buffers_end,
- size_t{0u}, load_buffer_loop));
- }
- }
- return SrcBuffer.begin();
-}
-
-ALfloat *LoadBufferQueue(ALbufferlistitem *BufferListItem, ALbufferlistitem *BufferLoopItem,
- const ALsizei NumChannels, const ALsizei SampleSize, const ALsizei chan, ALsizei DataPosInt,
- al::span<ALfloat> SrcBuffer)
-{
- /* Crawl the buffer queue to fill in the temp buffer */
- while(BufferListItem && !SrcBuffer.empty())
- {
- if(DataPosInt >= BufferListItem->max_samples)
- {
- DataPosInt -= BufferListItem->max_samples;
- BufferListItem = BufferListItem->next.load(std::memory_order_acquire);
- if(!BufferListItem) BufferListItem = BufferLoopItem;
- continue;
- }
-
- auto load_buffer = [DataPosInt,NumChannels,SampleSize,chan,SrcBuffer](size_t CompLen, const ALbuffer *buffer) -> size_t
- {
- if(!buffer) return CompLen;
- if(DataPosInt >= buffer->SampleLen)
- return CompLen;
-
- const size_t DataSize{std::min<size_t>(SrcBuffer.size(), buffer->SampleLen-DataPosInt)};
- CompLen = std::max(CompLen, DataSize);
-
- const al::byte *Data{buffer->mData.data()};
- Data += (DataPosInt*NumChannels + chan)*SampleSize;
-
- LoadSamples(SrcBuffer.data(), Data, NumChannels, buffer->mFmtType, DataSize);
- return CompLen;
- };
- ASSUME(BufferListItem->num_buffers > 0);
- auto buffers_end = BufferListItem->buffers + BufferListItem->num_buffers;
- SrcBuffer = SrcBuffer.subspan(std::accumulate(BufferListItem->buffers, buffers_end,
- size_t{0u}, load_buffer));
-
- if(SrcBuffer.empty())
- break;
- DataPosInt = 0;
- BufferListItem = BufferListItem->next.load(std::memory_order_acquire);
- if(!BufferListItem) BufferListItem = BufferLoopItem;
- }
-
- return SrcBuffer.begin();
-}
-
-} // namespace
-
-void MixVoice(ALvoice *voice, ALvoice::State vstate, const ALuint SourceID, ALCcontext *Context, const ALsizei SamplesToDo)
-{
- static constexpr ALfloat SilentTarget[MAX_OUTPUT_CHANNELS]{};
-
- ASSUME(SamplesToDo > 0);
-
- /* Get voice info */
- const bool isstatic{(voice->mFlags&VOICE_IS_STATIC) != 0};
- ALsizei DataPosInt{static_cast<ALsizei>(voice->mPosition.load(std::memory_order_relaxed))};
- ALsizei DataPosFrac{voice->mPositionFrac.load(std::memory_order_relaxed)};
- ALbufferlistitem *BufferListItem{voice->mCurrentBuffer.load(std::memory_order_relaxed)};
- ALbufferlistitem *BufferLoopItem{voice->mLoopBuffer.load(std::memory_order_relaxed)};
- const ALsizei NumChannels{voice->mNumChannels};
- const ALsizei SampleSize{voice->mSampleSize};
- const ALint increment{voice->mStep};
-
- ASSUME(DataPosInt >= 0);
- ASSUME(DataPosFrac >= 0);
- ASSUME(NumChannels > 0);
- ASSUME(SampleSize > 0);
- ASSUME(increment > 0);
-
- ALCdevice *Device{Context->Device};
- const ALsizei NumSends{Device->NumAuxSends};
- const ALsizei IrSize{Device->mHrtf ? Device->mHrtf->irSize : 0};
-
- ASSUME(NumSends >= 0);
- ASSUME(IrSize >= 0);
-
- ResamplerFunc Resample{(increment == FRACTIONONE && DataPosFrac == 0) ?
- Resample_<CopyTag,CTag> : voice->mResampler};
-
- ALsizei Counter{(voice->mFlags&VOICE_IS_FADING) ? SamplesToDo : 0};
- if(!Counter)
- {
- /* No fading, just overwrite the old/current params. */
- for(ALsizei chan{0};chan < NumChannels;chan++)
- {
- ALvoice::ChannelData &chandata = voice->mChans[chan];
- DirectParams &parms = chandata.mDryParams;
- if(!(voice->mFlags&VOICE_HAS_HRTF))
- std::copy(std::begin(parms.Gains.Target), std::end(parms.Gains.Target),
- std::begin(parms.Gains.Current));
- else
- parms.Hrtf.Old = parms.Hrtf.Target;
- for(ALsizei send{0};send < NumSends;++send)
- {
- if(voice->mSend[send].Buffer.empty())
- continue;
-
- SendParams &parms = chandata.mWetParams[send];
- std::copy(std::begin(parms.Gains.Target), std::end(parms.Gains.Target),
- std::begin(parms.Gains.Current));
- }
- }
- }
- else if((voice->mFlags&VOICE_HAS_HRTF))
- {
- for(ALsizei chan{0};chan < NumChannels;chan++)
- {
- DirectParams &parms = voice->mChans[chan].mDryParams;
- if(!(parms.Hrtf.Old.Gain > GAIN_SILENCE_THRESHOLD))
- {
- /* The old HRTF params are silent, so overwrite the old
- * coefficients with the new, and reset the old gain to 0. The
- * future mix will then fade from silence.
- */
- parms.Hrtf.Old = parms.Hrtf.Target;
- parms.Hrtf.Old.Gain = 0.0f;
- }
- }
- }
-
- ALsizei buffers_done{0};
- ALsizei OutPos{0};
- do {
- /* Figure out how many buffer samples will be needed */
- ALsizei DstBufferSize{SamplesToDo - OutPos};
-
- /* Calculate the last written dst sample pos. */
- int64_t DataSize64{DstBufferSize - 1};
- /* Calculate the last read src sample pos. */
- DataSize64 = (DataSize64*increment + DataPosFrac) >> FRACTIONBITS;
- /* +1 to get the src sample count, include padding. */
- DataSize64 += 1 + MAX_RESAMPLE_PADDING*2;
-
- auto SrcBufferSize = static_cast<ALuint>(
- mini64(DataSize64, BUFFERSIZE + MAX_RESAMPLE_PADDING*2 + 1));
- if(SrcBufferSize > BUFFERSIZE + MAX_RESAMPLE_PADDING*2)
- {
- SrcBufferSize = BUFFERSIZE + MAX_RESAMPLE_PADDING*2;
- /* If the source buffer got saturated, we can't fill the desired
- * dst size. Figure out how many samples we can actually mix from
- * this.
- */
- DataSize64 = SrcBufferSize - MAX_RESAMPLE_PADDING*2;
- DataSize64 = ((DataSize64<<FRACTIONBITS) - DataPosFrac + increment-1) / increment;
- DstBufferSize = static_cast<ALsizei>(mini64(DataSize64, DstBufferSize));
-
- /* Some mixers like having a multiple of 4, so try to give that
- * unless this is the last update.
- */
- if(DstBufferSize < SamplesToDo-OutPos)
- DstBufferSize &= ~3;
- }
-
- for(ALsizei chan{0};chan < NumChannels;chan++)
- {
- ALvoice::ChannelData &chandata = voice->mChans[chan];
- const al::span<ALfloat> SrcData{Device->SourceData, SrcBufferSize};
-
- /* Load the previous samples into the source data first, and clear the rest. */
- auto srciter = std::copy_n(chandata.mPrevSamples.begin(), MAX_RESAMPLE_PADDING,
- SrcData.begin());
- std::fill(srciter, SrcData.end(), 0.0f);
-
- if(UNLIKELY(!BufferListItem))
- srciter = std::copy(chandata.mPrevSamples.begin()+MAX_RESAMPLE_PADDING,
- chandata.mPrevSamples.end(), srciter);
- else if(isstatic)
- srciter = LoadBufferStatic(BufferListItem, BufferLoopItem, NumChannels,
- SampleSize, chan, DataPosInt, {srciter, SrcData.end()});
- else
- srciter = LoadBufferQueue(BufferListItem, BufferLoopItem, NumChannels,
- SampleSize, chan, DataPosInt, {srciter, SrcData.end()});
-
- if(UNLIKELY(srciter != SrcData.end()))
- {
- /* If the source buffer wasn't filled, copy the last sample for
- * the remaining buffer. Ideally it should have ended with
- * silence, but if not the gain fading should help avoid clicks
- * from sudden amplitude changes.
- */
- const ALfloat sample{*(srciter-1)};
- std::fill(srciter, SrcData.end(), sample);
- }
-
- /* Store the last source samples used for next time. */
- std::copy_n(&SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
- chandata.mPrevSamples.size(), chandata.mPrevSamples.begin());
-
- /* Resample, then apply ambisonic upsampling as needed. */
- const ALfloat *ResampledData{Resample(&voice->mResampleState,
- &SrcData[MAX_RESAMPLE_PADDING], DataPosFrac, increment,
- Device->ResampledData, DstBufferSize)};
- if((voice->mFlags&VOICE_IS_AMBISONIC))
- {
- const ALfloat hfscale{chandata.mAmbiScale};
- /* Beware the evil const_cast. It's safe since it's pointing to
- * either SourceData or ResampledData (both non-const), but the
- * resample method takes the source as const float* and may
- * return it without copying to output, making it currently
- * unavoidable.
- */
- chandata.mAmbiSplitter.applyHfScale(const_cast<ALfloat*>(ResampledData), hfscale,
- DstBufferSize);
- }
-
- /* Now filter and mix to the appropriate outputs. */
- {
- DirectParams &parms = chandata.mDryParams;
- const ALfloat *samples{DoFilters(&parms.LowPass, &parms.HighPass,
- Device->FilteredData, ResampledData, DstBufferSize,
- voice->mDirect.FilterType)};
-
- if((voice->mFlags&VOICE_HAS_HRTF))
- {
- const int OutLIdx{GetChannelIdxByName(Device->RealOut, FrontLeft)};
- const int OutRIdx{GetChannelIdxByName(Device->RealOut, FrontRight)};
- ASSUME(OutLIdx >= 0 && OutRIdx >= 0);
-
- auto &HrtfSamples = Device->HrtfSourceData;
- auto &AccumSamples = Device->HrtfAccumData;
- const ALfloat TargetGain{UNLIKELY(vstate == ALvoice::Stopping) ? 0.0f :
- parms.Hrtf.Target.Gain};
- ALsizei fademix{0};
-
- /* Copy the HRTF history and new input samples into a temp
- * buffer.
- */
- auto src_iter = std::copy(parms.Hrtf.State.History.begin(),
- parms.Hrtf.State.History.end(), std::begin(HrtfSamples));
- std::copy_n(samples, DstBufferSize, src_iter);
- /* Copy the last used samples back into the history buffer
- * for later.
- */
- std::copy_n(std::begin(HrtfSamples) + DstBufferSize,
- parms.Hrtf.State.History.size(), parms.Hrtf.State.History.begin());
-
- /* Copy the current filtered values being accumulated into
- * the temp buffer.
- */
- auto accum_iter = std::copy_n(parms.Hrtf.State.Values.begin(),
- parms.Hrtf.State.Values.size(), std::begin(AccumSamples));
-
- /* Clear the accumulation buffer that will start getting
- * filled in.
- */
- std::fill_n(accum_iter, DstBufferSize, float2{});
-
- /* If fading, the old gain is not silence, and this is the
- * first mixing pass, fade between the IRs.
- */
- if(Counter && (parms.Hrtf.Old.Gain > GAIN_SILENCE_THRESHOLD) && OutPos == 0)
- {
- fademix = mini(DstBufferSize, 128);
-
- ALfloat gain{TargetGain};
-
- /* The new coefficients need to fade in completely
- * since they're replacing the old ones. To keep the
- * gain fading consistent, interpolate between the old
- * and new target gains given how much of the fade time
- * this mix handles.
- */
- if(LIKELY(Counter > fademix))
- {
- const ALfloat a{static_cast<ALfloat>(fademix) /
- static_cast<ALfloat>(Counter)};
- gain = lerp(parms.Hrtf.Old.Gain, TargetGain, a);
- }
- MixHrtfFilter hrtfparams;
- hrtfparams.Coeffs = &parms.Hrtf.Target.Coeffs;
- hrtfparams.Delay[0] = parms.Hrtf.Target.Delay[0];
- hrtfparams.Delay[1] = parms.Hrtf.Target.Delay[1];
- hrtfparams.Gain = 0.0f;
- hrtfparams.GainStep = gain / static_cast<ALfloat>(fademix);
-
- MixHrtfBlendSamples(voice->mDirect.Buffer[OutLIdx],
- voice->mDirect.Buffer[OutRIdx], HrtfSamples, AccumSamples, OutPos,
- IrSize, &parms.Hrtf.Old, &hrtfparams, fademix);
- /* Update the old parameters with the result. */
- parms.Hrtf.Old = parms.Hrtf.Target;
- if(fademix < Counter)
- parms.Hrtf.Old.Gain = hrtfparams.Gain;
- else
- parms.Hrtf.Old.Gain = TargetGain;
- }
-
- if(LIKELY(fademix < DstBufferSize))
- {
- const ALsizei todo{DstBufferSize - fademix};
- ALfloat gain{TargetGain};
-
- /* Interpolate the target gain if the gain fading lasts
- * longer than this mix.
- */
- if(Counter > DstBufferSize)
- {
- const ALfloat a{static_cast<ALfloat>(todo) /
- static_cast<ALfloat>(Counter-fademix)};
- gain = lerp(parms.Hrtf.Old.Gain, TargetGain, a);
- }
-
- MixHrtfFilter hrtfparams;
- hrtfparams.Coeffs = &parms.Hrtf.Target.Coeffs;
- hrtfparams.Delay[0] = parms.Hrtf.Target.Delay[0];
- hrtfparams.Delay[1] = parms.Hrtf.Target.Delay[1];
- hrtfparams.Gain = parms.Hrtf.Old.Gain;
- hrtfparams.GainStep = (gain - parms.Hrtf.Old.Gain) /
- static_cast<ALfloat>(todo);
- MixHrtfSamples(voice->mDirect.Buffer[OutLIdx],
- voice->mDirect.Buffer[OutRIdx], HrtfSamples+fademix,
- AccumSamples+fademix, OutPos+fademix, IrSize, &hrtfparams, todo);
- /* Store the interpolated gain or the final target gain
- * depending if the fade is done.
- */
- if(DstBufferSize < Counter)
- parms.Hrtf.Old.Gain = gain;
- else
- parms.Hrtf.Old.Gain = TargetGain;
- }
-
- /* Copy the new in-progress accumulation values back for
- * the next mix.
- */
- std::copy_n(std::begin(AccumSamples) + DstBufferSize,
- parms.Hrtf.State.Values.size(), parms.Hrtf.State.Values.begin());
- }
- else if((voice->mFlags&VOICE_HAS_NFC))
- {
- const ALfloat *TargetGains{UNLIKELY(vstate == ALvoice::Stopping) ?
- SilentTarget : parms.Gains.Target};
-
- const size_t outcount{Device->NumChannelsPerOrder[0]};
- MixSamples(samples, voice->mDirect.Buffer.first(outcount), parms.Gains.Current,
- TargetGains, Counter, OutPos, DstBufferSize);
-
- ALfloat (&nfcsamples)[BUFFERSIZE] = Device->NfcSampleData;
- size_t chanoffset{outcount};
- using FilterProc = void (NfcFilter::*)(float*,const float*,int);
- auto apply_nfc = [voice,&parms,samples,TargetGains,DstBufferSize,Counter,OutPos,&chanoffset,&nfcsamples](const FilterProc process, const size_t outcount) -> void
- {
- if(outcount < 1) return;
- (parms.NFCtrlFilter.*process)(nfcsamples, samples, DstBufferSize);
- MixSamples(nfcsamples, voice->mDirect.Buffer.subspan(chanoffset, outcount),
- parms.Gains.Current+chanoffset, TargetGains+chanoffset, Counter,
- OutPos, DstBufferSize);
- chanoffset += outcount;
- };
- apply_nfc(&NfcFilter::process1, Device->NumChannelsPerOrder[1]);
- apply_nfc(&NfcFilter::process2, Device->NumChannelsPerOrder[2]);
- apply_nfc(&NfcFilter::process3, Device->NumChannelsPerOrder[3]);
- }
- else
- {
- const ALfloat *TargetGains{UNLIKELY(vstate == ALvoice::Stopping) ?
- SilentTarget : parms.Gains.Target};
- MixSamples(samples, voice->mDirect.Buffer, parms.Gains.Current, TargetGains,
- Counter, OutPos, DstBufferSize);
- }
- }
-
- ALfloat (&FilterBuf)[BUFFERSIZE] = Device->FilteredData;
- for(ALsizei send{0};send < NumSends;++send)
- {
- if(voice->mSend[send].Buffer.empty())
- continue;
-
- SendParams &parms = chandata.mWetParams[send];
- const ALfloat *samples{DoFilters(&parms.LowPass, &parms.HighPass,
- FilterBuf, ResampledData, DstBufferSize, voice->mSend[send].FilterType)};
-
- const ALfloat *TargetGains{UNLIKELY(vstate==ALvoice::Stopping) ? SilentTarget :
- parms.Gains.Target};
- MixSamples(samples, voice->mSend[send].Buffer, parms.Gains.Current, TargetGains,
- Counter, OutPos, DstBufferSize);
- };
- }
- /* Update positions */
- DataPosFrac += increment*DstBufferSize;
- DataPosInt += DataPosFrac>>FRACTIONBITS;
- DataPosFrac &= FRACTIONMASK;
-
- OutPos += DstBufferSize;
- Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
-
- if(UNLIKELY(!BufferListItem))
- {
- /* Do nothing extra when there's no buffers. */
- }
- else if(isstatic)
- {
- if(BufferLoopItem)
- {
- /* Handle looping static source */
- const ALbuffer *Buffer{BufferListItem->buffers[0]};
- const ALsizei LoopStart{Buffer->LoopStart};
- const ALsizei LoopEnd{Buffer->LoopEnd};
- if(DataPosInt >= LoopEnd)
- {
- assert(LoopEnd > LoopStart);
- DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
- }
- }
- else
- {
- /* Handle non-looping static source */
- if(DataPosInt >= BufferListItem->max_samples)
- {
- if(LIKELY(vstate == ALvoice::Playing))
- vstate = ALvoice::Stopped;
- BufferListItem = nullptr;
- break;
- }
- }
- }
- else while(1)
- {
- /* Handle streaming source */
- if(BufferListItem->max_samples > DataPosInt)
- break;
-
- DataPosInt -= BufferListItem->max_samples;
-
- buffers_done += BufferListItem->num_buffers;
- BufferListItem = BufferListItem->next.load(std::memory_order_relaxed);
- if(!BufferListItem && !(BufferListItem=BufferLoopItem))
- {
- if(LIKELY(vstate == ALvoice::Playing))
- vstate = ALvoice::Stopped;
- break;
- }
- }
- } while(OutPos < SamplesToDo);
-
- voice->mFlags |= VOICE_IS_FADING;
-
- /* Don't update positions and buffers if we were stopping. */
- if(UNLIKELY(vstate == ALvoice::Stopping))
- {
- voice->mPlayState.store(ALvoice::Stopped, std::memory_order_release);
- return;
- }
-
- /* Update voice info */
- voice->mPosition.store(DataPosInt, std::memory_order_relaxed);
- voice->mPositionFrac.store(DataPosFrac, std::memory_order_relaxed);
- voice->mCurrentBuffer.store(BufferListItem, std::memory_order_relaxed);
- if(vstate == ALvoice::Stopped)
- {
- voice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
- voice->mSourceID.store(0u, std::memory_order_relaxed);
- }
- std::atomic_thread_fence(std::memory_order_release);
-
- /* Send any events now, after the position/buffer info was updated. */
- ALbitfieldSOFT enabledevt{Context->EnabledEvts.load(std::memory_order_acquire)};
- if(buffers_done > 0 && (enabledevt&EventType_BufferCompleted))
- {
- RingBuffer *ring{Context->AsyncEvents.get()};
- auto evt_vec = ring->getWriteVector();
- if(evt_vec.first.len > 0)
- {
- AsyncEvent *evt{new (evt_vec.first.buf) AsyncEvent{EventType_BufferCompleted}};
- evt->u.bufcomp.id = SourceID;
- evt->u.bufcomp.count = buffers_done;
- ring->writeAdvance(1);
- Context->EventSem.post();
- }
- }
-
- if(vstate == ALvoice::Stopped)
- {
- /* If the voice just ended, set it to Stopping so the next render
- * ensures any residual noise fades to 0 amplitude.
- */
- voice->mPlayState.store(ALvoice::Stopping, std::memory_order_release);
- SendSourceStoppedEvent(Context, SourceID);
- }
-}
diff --git a/Alc/panning.cpp b/Alc/panning.cpp
deleted file mode 100644
index 3a67e33a..00000000
--- a/Alc/panning.cpp
+++ /dev/null
@@ -1,964 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2010 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cmath>
-#include <cstdlib>
-#include <cstring>
-#include <cctype>
-#include <cassert>
-
-#include <cmath>
-#include <chrono>
-#include <numeric>
-#include <algorithm>
-#include <functional>
-
-#include "alcmain.h"
-#include "alAuxEffectSlot.h"
-#include "alu.h"
-#include "alconfig.h"
-#include "ambdec.h"
-#include "bformatdec.h"
-#include "filters/splitter.h"
-#include "uhjfilter.h"
-#include "bs2b.h"
-
-#include "alspan.h"
-
-
-constexpr std::array<float,MAX_AMBI_CHANNELS> AmbiScale::FromN3D;
-constexpr std::array<float,MAX_AMBI_CHANNELS> AmbiScale::FromSN3D;
-constexpr std::array<float,MAX_AMBI_CHANNELS> AmbiScale::FromFuMa;
-constexpr std::array<int,MAX_AMBI_CHANNELS> AmbiIndex::FromFuMa;
-constexpr std::array<int,MAX_AMBI_CHANNELS> AmbiIndex::FromACN;
-constexpr std::array<int,MAX_AMBI2D_CHANNELS> AmbiIndex::From2D;
-constexpr std::array<int,MAX_AMBI_CHANNELS> AmbiIndex::From3D;
-
-
-namespace {
-
-using namespace std::placeholders;
-using std::chrono::seconds;
-using std::chrono::nanoseconds;
-
-inline const char *GetLabelFromChannel(Channel channel)
-{
- switch(channel)
- {
- case FrontLeft: return "front-left";
- case FrontRight: return "front-right";
- case FrontCenter: return "front-center";
- case LFE: return "lfe";
- case BackLeft: return "back-left";
- case BackRight: return "back-right";
- case BackCenter: return "back-center";
- case SideLeft: return "side-left";
- case SideRight: return "side-right";
-
- case UpperFrontLeft: return "upper-front-left";
- case UpperFrontRight: return "upper-front-right";
- case UpperBackLeft: return "upper-back-left";
- case UpperBackRight: return "upper-back-right";
- case LowerFrontLeft: return "lower-front-left";
- case LowerFrontRight: return "lower-front-right";
- case LowerBackLeft: return "lower-back-left";
- case LowerBackRight: return "lower-back-right";
-
- case Aux0: return "aux-0";
- case Aux1: return "aux-1";
- case Aux2: return "aux-2";
- case Aux3: return "aux-3";
- case Aux4: return "aux-4";
- case Aux5: return "aux-5";
- case Aux6: return "aux-6";
- case Aux7: return "aux-7";
- case Aux8: return "aux-8";
- case Aux9: return "aux-9";
- case Aux10: return "aux-10";
- case Aux11: return "aux-11";
- case Aux12: return "aux-12";
- case Aux13: return "aux-13";
- case Aux14: return "aux-14";
- case Aux15: return "aux-15";
-
- case MaxChannels: break;
- }
- return "(unknown)";
-}
-
-
-void AllocChannels(ALCdevice *device, const ALuint main_chans, const ALuint real_chans)
-{
- TRACE("Channel config, Main: %u, Real: %u\n", main_chans, real_chans);
-
- /* Allocate extra channels for any post-filter output. */
- const ALuint num_chans{main_chans + real_chans};
-
- TRACE("Allocating %u channels, %zu bytes\n", num_chans,
- num_chans*sizeof(device->MixBuffer[0]));
- device->MixBuffer.resize(num_chans);
- al::span<FloatBufferLine> buffer{device->MixBuffer.data(), device->MixBuffer.size()};
-
- device->Dry.Buffer = buffer.first(main_chans);
- buffer = buffer.subspan(main_chans);
- if(real_chans != 0)
- {
- device->RealOut.Buffer = buffer.first(real_chans);
- buffer = buffer.subspan(real_chans);
- }
- else
- device->RealOut.Buffer = device->Dry.Buffer;
-}
-
-
-struct ChannelMap {
- Channel ChanName;
- ALfloat Config[MAX_AMBI2D_CHANNELS];
-};
-
-bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
-{
- auto map_spkr = [device](const AmbDecConf::SpeakerConf &speaker) -> ALsizei
- {
- /* NOTE: AmbDec does not define any standard speaker names, however
- * for this to work we have to by able to find the output channel
- * the speaker definition corresponds to. Therefore, OpenAL Soft
- * requires these channel labels to be recognized:
- *
- * LF = Front left
- * RF = Front right
- * LS = Side left
- * RS = Side right
- * LB = Back left
- * RB = Back right
- * CE = Front center
- * CB = Back center
- *
- * Additionally, surround51 will acknowledge back speakers for side
- * channels, and surround51rear will acknowledge side speakers for
- * back channels, to avoid issues with an ambdec expecting 5.1 to
- * use the side channels when the device is configured for back,
- * and vice-versa.
- */
- Channel ch{};
- if(speaker.Name == "LF")
- ch = FrontLeft;
- else if(speaker.Name == "RF")
- ch = FrontRight;
- else if(speaker.Name == "CE")
- ch = FrontCenter;
- else if(speaker.Name == "LS")
- {
- if(device->FmtChans == DevFmtX51Rear)
- ch = BackLeft;
- else
- ch = SideLeft;
- }
- else if(speaker.Name == "RS")
- {
- if(device->FmtChans == DevFmtX51Rear)
- ch = BackRight;
- else
- ch = SideRight;
- }
- else if(speaker.Name == "LB")
- {
- if(device->FmtChans == DevFmtX51)
- ch = SideLeft;
- else
- ch = BackLeft;
- }
- else if(speaker.Name == "RB")
- {
- if(device->FmtChans == DevFmtX51)
- ch = SideRight;
- else
- ch = BackRight;
- }
- else if(speaker.Name == "CB")
- ch = BackCenter;
- else
- {
- const char *name{speaker.Name.c_str()};
- unsigned int n;
- char c;
-
- if(sscanf(name, "AUX%u%c", &n, &c) == 1 && n < 16)
- ch = static_cast<Channel>(Aux0+n);
- else
- {
- ERR("AmbDec speaker label \"%s\" not recognized\n", name);
- return -1;
- }
- }
- const int chidx{GetChannelIdxByName(device->RealOut, ch)};
- if(chidx == -1)
- ERR("Failed to lookup AmbDec speaker label %s\n", speaker.Name.c_str());
- return chidx;
- };
- std::transform(conf->Speakers.begin(), conf->Speakers.end(), std::begin(speakermap), map_spkr);
- /* Return success if no invalid entries are found. */
- auto speakermap_end = std::begin(speakermap) + conf->Speakers.size();
- return std::find(std::begin(speakermap), speakermap_end, -1) == speakermap_end;
-}
-
-
-constexpr ChannelMap MonoCfg[1] = {
- { FrontCenter, { 1.0f } },
-}, StereoCfg[2] = {
- { FrontLeft, { 5.00000000e-1f, 2.88675135e-1f, 5.52305643e-2f } },
- { FrontRight, { 5.00000000e-1f, -2.88675135e-1f, 5.52305643e-2f } },
-}, QuadCfg[4] = {
- { BackLeft, { 3.53553391e-1f, 2.04124145e-1f, -2.04124145e-1f } },
- { FrontLeft, { 3.53553391e-1f, 2.04124145e-1f, 2.04124145e-1f } },
- { FrontRight, { 3.53553391e-1f, -2.04124145e-1f, 2.04124145e-1f } },
- { BackRight, { 3.53553391e-1f, -2.04124145e-1f, -2.04124145e-1f } },
-}, X51SideCfg[4] = {
- { SideLeft, { 3.33000782e-1f, 1.89084803e-1f, -2.00042375e-1f, -2.12307769e-2f, -1.14579885e-2f } },
- { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 1.66295695e-1f, 7.30571517e-2f, 2.10901184e-2f } },
- { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 1.66295695e-1f, -7.30571517e-2f, 2.10901184e-2f } },
- { SideRight, { 3.33000782e-1f, -1.89084803e-1f, -2.00042375e-1f, 2.12307769e-2f, -1.14579885e-2f } },
-}, X51RearCfg[4] = {
- { BackLeft, { 3.33000782e-1f, 1.89084803e-1f, -2.00042375e-1f, -2.12307769e-2f, -1.14579885e-2f } },
- { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 1.66295695e-1f, 7.30571517e-2f, 2.10901184e-2f } },
- { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 1.66295695e-1f, -7.30571517e-2f, 2.10901184e-2f } },
- { BackRight, { 3.33000782e-1f, -1.89084803e-1f, -2.00042375e-1f, 2.12307769e-2f, -1.14579885e-2f } },
-}, X61Cfg[6] = {
- { SideLeft, { 2.04460341e-1f, 2.17177926e-1f, -4.39996780e-2f, -2.60790269e-2f, -6.87239792e-2f } },
- { FrontLeft, { 1.58923161e-1f, 9.21772680e-2f, 1.59658796e-1f, 6.66278083e-2f, 3.84686854e-2f } },
- { FrontRight, { 1.58923161e-1f, -9.21772680e-2f, 1.59658796e-1f, -6.66278083e-2f, 3.84686854e-2f } },
- { SideRight, { 2.04460341e-1f, -2.17177926e-1f, -4.39996780e-2f, 2.60790269e-2f, -6.87239792e-2f } },
- { BackCenter, { 2.50001688e-1f, 0.00000000e+0f, -2.50000094e-1f, 0.00000000e+0f, 6.05133395e-2f } },
-}, X71Cfg[6] = {
- { BackLeft, { 2.04124145e-1f, 1.08880247e-1f, -1.88586120e-1f, -1.29099444e-1f, 7.45355993e-2f, 3.73460789e-2f, 0.00000000e+0f } },
- { SideLeft, { 2.04124145e-1f, 2.17760495e-1f, 0.00000000e+0f, 0.00000000e+0f, -1.49071198e-1f, -3.73460789e-2f, 0.00000000e+0f } },
- { FrontLeft, { 2.04124145e-1f, 1.08880247e-1f, 1.88586120e-1f, 1.29099444e-1f, 7.45355993e-2f, 3.73460789e-2f, 0.00000000e+0f } },
- { FrontRight, { 2.04124145e-1f, -1.08880247e-1f, 1.88586120e-1f, -1.29099444e-1f, 7.45355993e-2f, -3.73460789e-2f, 0.00000000e+0f } },
- { SideRight, { 2.04124145e-1f, -2.17760495e-1f, 0.00000000e+0f, 0.00000000e+0f, -1.49071198e-1f, 3.73460789e-2f, 0.00000000e+0f } },
- { BackRight, { 2.04124145e-1f, -1.08880247e-1f, -1.88586120e-1f, 1.29099444e-1f, 7.45355993e-2f, -3.73460789e-2f, 0.00000000e+0f } },
-};
-
-void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order,
- const al::span<const ALuint,MAX_AMBI_ORDER+1> chans_per_order)
-{
- /* NFC is only used when AvgSpeakerDist is greater than 0. */
- const char *devname{device->DeviceName.c_str()};
- if(!GetConfigValueBool(devname, "decoder", "nfc", 0) || !(ctrl_dist > 0.0f))
- return;
-
- device->AvgSpeakerDist = clampf(ctrl_dist, 0.1f, 10.0f);
- TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist);
-
- auto iter = std::copy(chans_per_order.begin(), chans_per_order.begin()+order+1,
- std::begin(device->NumChannelsPerOrder));
- std::fill(iter, std::end(device->NumChannelsPerOrder), 0u);
-}
-
-void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, const ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
-{
- auto get_max = std::bind(maxf, _1,
- std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance), _2));
- const ALfloat maxdist{
- std::accumulate(conf->Speakers.begin(), conf->Speakers.end(), float{0.0f}, get_max)};
-
- const char *devname{device->DeviceName.c_str()};
- if(!GetConfigValueBool(devname, "decoder", "distance-comp", 1) || !(maxdist > 0.0f))
- return;
-
- const auto distSampleScale = static_cast<ALfloat>(device->Frequency)/SPEEDOFSOUNDMETRESPERSEC;
- const auto ChanDelay = device->ChannelDelay.as_span();
- size_t total{0u};
- for(size_t i{0u};i < conf->Speakers.size();i++)
- {
- const AmbDecConf::SpeakerConf &speaker = conf->Speakers[i];
- const ALsizei chan{speakermap[i]};
-
- /* Distance compensation only delays in steps of the sample rate. This
- * is a bit less accurate since the delay time falls to the nearest
- * sample time, but it's far simpler as it doesn't have to deal with
- * phase offsets. This means at 48khz, for instance, the distance delay
- * will be in steps of about 7 millimeters.
- */
- ALfloat delay{std::floor((maxdist - speaker.Distance)*distSampleScale + 0.5f)};
- if(delay > ALfloat{MAX_DELAY_LENGTH-1})
- {
- ERR("Delay for speaker \"%s\" exceeds buffer length (%f > %d)\n",
- speaker.Name.c_str(), delay, MAX_DELAY_LENGTH-1);
- delay = ALfloat{MAX_DELAY_LENGTH-1};
- }
-
- ChanDelay[chan].Length = static_cast<ALsizei>(delay);
- ChanDelay[chan].Gain = speaker.Distance / maxdist;
- TRACE("Channel %u \"%s\" distance compensation: %d samples, %f gain\n", chan,
- speaker.Name.c_str(), ChanDelay[chan].Length, ChanDelay[chan].Gain);
-
- /* Round up to the next 4th sample, so each channel buffer starts
- * 16-byte aligned.
- */
- total += RoundUp(ChanDelay[chan].Length, 4);
- }
-
- if(total > 0)
- {
- device->ChannelDelay.setSampleCount(total);
- ChanDelay[0].Buffer = device->ChannelDelay.getSamples();
- auto set_bufptr = [](const DistanceComp::DistData &last, const DistanceComp::DistData &cur) -> DistanceComp::DistData
- {
- DistanceComp::DistData ret{cur};
- ret.Buffer = last.Buffer + RoundUp(last.Length, 4);
- return ret;
- };
- std::partial_sum(ChanDelay.begin(), ChanDelay.end(), ChanDelay.begin(), set_bufptr);
- }
-}
-
-
-auto GetAmbiScales(AmbiNorm scaletype) noexcept -> const std::array<float,MAX_AMBI_CHANNELS>&
-{
- if(scaletype == AmbiNorm::FuMa) return AmbiScale::FromFuMa;
- if(scaletype == AmbiNorm::SN3D) return AmbiScale::FromSN3D;
- return AmbiScale::FromN3D;
-}
-
-auto GetAmbiLayout(AmbiLayout layouttype) noexcept -> const std::array<int,MAX_AMBI_CHANNELS>&
-{
- if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa;
- return AmbiIndex::FromACN;
-}
-
-
-void InitPanning(ALCdevice *device)
-{
- al::span<const ChannelMap> chanmap;
- ALuint coeffcount{};
-
- switch(device->FmtChans)
- {
- case DevFmtMono:
- chanmap = MonoCfg;
- coeffcount = 1;
- break;
-
- case DevFmtStereo:
- chanmap = StereoCfg;
- coeffcount = 3;
- break;
-
- case DevFmtQuad:
- chanmap = QuadCfg;
- coeffcount = 3;
- break;
-
- case DevFmtX51:
- chanmap = X51SideCfg;
- coeffcount = 5;
- break;
-
- case DevFmtX51Rear:
- chanmap = X51RearCfg;
- coeffcount = 5;
- break;
-
- case DevFmtX61:
- chanmap = X61Cfg;
- coeffcount = 5;
- break;
-
- case DevFmtX71:
- chanmap = X71Cfg;
- coeffcount = 7;
- break;
-
- case DevFmtAmbi3D:
- break;
- }
-
- if(device->FmtChans == DevFmtAmbi3D)
- {
- const char *devname{device->DeviceName.c_str()};
- const std::array<int,MAX_AMBI_CHANNELS> &acnmap = GetAmbiLayout(device->mAmbiLayout);
- const std::array<float,MAX_AMBI_CHANNELS> &n3dscale = GetAmbiScales(device->mAmbiScale);
-
- /* For DevFmtAmbi3D, the ambisonic order is already set. */
- const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)};
- std::transform(acnmap.begin(), acnmap.begin()+count, std::begin(device->Dry.AmbiMap),
- [&n3dscale](const ALsizei &acn) noexcept -> BFChannelConfig
- { return BFChannelConfig{1.0f/n3dscale[acn], acn}; }
- );
- AllocChannels(device, static_cast<ALuint>(count), 0);
-
- ALfloat nfc_delay{ConfigValueFloat(devname, "decoder", "nfc-ref-delay").value_or(0.0f)};
- if(nfc_delay > 0.0f)
- {
- static constexpr ALuint chans_per_order[MAX_AMBI_ORDER+1]{ 1, 3, 5, 7 };
- InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC, device->mAmbiOrder,
- chans_per_order);
- }
- }
- else
- {
- ChannelDec chancoeffs[MAX_OUTPUT_CHANNELS]{};
- ALsizei idxmap[MAX_OUTPUT_CHANNELS]{};
- for(size_t i{0u};i < chanmap.size();++i)
- {
- const ALint idx{GetChannelIdxByName(device->RealOut, chanmap[i].ChanName)};
- if(idx < 0)
- {
- ERR("Failed to find %s channel in device\n",
- GetLabelFromChannel(chanmap[i].ChanName));
- continue;
- }
- idxmap[i] = idx;
- std::copy_n(chanmap[i].Config, coeffcount, chancoeffs[i]);
- }
-
- /* For non-DevFmtAmbi3D, set the ambisonic order given the mixing
- * channel count. Built-in speaker decoders are always 2D, so just
- * reverse that calculation.
- */
- device->mAmbiOrder = static_cast<ALsizei>((coeffcount-1) / 2);
-
- std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+coeffcount,
- std::begin(device->Dry.AmbiMap),
- [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
- );
- AllocChannels(device, coeffcount, device->channelsFromFmt());
-
- TRACE("Enabling %s-order%s ambisonic decoder\n",
- (coeffcount > 5) ? "third" :
- (coeffcount > 3) ? "second" : "first",
- ""
- );
- device->AmbiDecoder = al::make_unique<BFormatDec>(coeffcount,
- static_cast<ALsizei>(chanmap.size()), chancoeffs, idxmap);
- }
-}
-
-void InitCustomPanning(ALCdevice *device, bool hqdec, const AmbDecConf *conf, const ALsizei (&speakermap)[MAX_OUTPUT_CHANNELS])
-{
- static constexpr ALuint chans_per_order2d[MAX_AMBI_ORDER+1] = { 1, 2, 2, 2 };
- static constexpr ALuint chans_per_order3d[MAX_AMBI_ORDER+1] = { 1, 3, 5, 7 };
-
- if(!hqdec && conf->FreqBands != 1)
- ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
- conf->XOverFreq);
-
- ALsizei order{(conf->ChanMask > AMBI_2ORDER_MASK) ? 3 :
- (conf->ChanMask > AMBI_1ORDER_MASK) ? 2 : 1};
- device->mAmbiOrder = order;
-
- ALuint count;
- if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
- {
- count = static_cast<ALuint>(AmbiChannelsFromOrder(order));
- std::transform(AmbiIndex::From3D.begin(), AmbiIndex::From3D.begin()+count,
- std::begin(device->Dry.AmbiMap),
- [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
- );
- }
- else
- {
- count = static_cast<ALuint>(Ambi2DChannelsFromOrder(order));
- std::transform(AmbiIndex::From2D.begin(), AmbiIndex::From2D.begin()+count,
- std::begin(device->Dry.AmbiMap),
- [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
- );
- }
- AllocChannels(device, count, device->channelsFromFmt());
-
- TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
- (!hqdec || conf->FreqBands == 1) ? "single" : "dual",
- (conf->ChanMask > AMBI_2ORDER_MASK) ? "third" :
- (conf->ChanMask > AMBI_1ORDER_MASK) ? "second" : "first",
- (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
- );
- device->AmbiDecoder = al::make_unique<BFormatDec>(conf, hqdec, count, device->Frequency,
- speakermap);
-
- auto accum_spkr_dist = std::bind(std::plus<float>{}, _1,
- std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance), _2));
- const ALfloat avg_dist{
- std::accumulate(conf->Speakers.begin(), conf->Speakers.end(), float{0.0f},
- accum_spkr_dist) / static_cast<ALfloat>(conf->Speakers.size())
- };
- InitNearFieldCtrl(device, avg_dist, order,
- (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? chans_per_order3d : chans_per_order2d);
-
- InitDistanceComp(device, conf, speakermap);
-}
-
-void InitHrtfPanning(ALCdevice *device)
-{
- /* NOTE: In degrees, and azimuth goes clockwise. */
- static constexpr AngularPoint AmbiPoints[]{
- { 35.264390f, -45.000000f },
- { 35.264390f, 45.000000f },
- { 35.264390f, 135.000000f },
- { 35.264390f, -135.000000f },
- { -35.264390f, -45.000000f },
- { -35.264390f, 45.000000f },
- { -35.264390f, 135.000000f },
- { -35.264390f, -135.000000f },
- { 0.000000f, -20.905157f },
- { 0.000000f, 20.905157f },
- { 0.000000f, 159.094843f },
- { 0.000000f, -159.094843f },
- { 20.905157f, -90.000000f },
- { -20.905157f, -90.000000f },
- { -20.905157f, 90.000000f },
- { 20.905157f, 90.000000f },
- { 69.094843f, 0.000000f },
- { -69.094843f, 0.000000f },
- { -69.094843f, 180.000000f },
- { 69.094843f, 180.000000f },
- };
- static constexpr ALfloat AmbiMatrix[][MAX_AMBI_CHANNELS]{
- { 5.00000000e-02f, 5.00000000e-02f, 5.00000000e-02f, 5.00000000e-02f, 6.45497224e-02f, 6.45497224e-02f, 0.00000000e+00f, 6.45497224e-02f, 0.00000000e+00f, 1.48264644e-02f, 6.33865691e-02f, 1.01126676e-01f, -7.36485380e-02f, -1.09260065e-02f, 7.08683387e-02f, -1.01622099e-01f },
- { 5.00000000e-02f, -5.00000000e-02f, 5.00000000e-02f, 5.00000000e-02f, -6.45497224e-02f, -6.45497224e-02f, 0.00000000e+00f, 6.45497224e-02f, 0.00000000e+00f, -1.48264644e-02f, -6.33865691e-02f, -1.01126676e-01f, -7.36485380e-02f, -1.09260065e-02f, 7.08683387e-02f, -1.01622099e-01f },
- { 5.00000000e-02f, -5.00000000e-02f, 5.00000000e-02f, -5.00000000e-02f, 6.45497224e-02f, -6.45497224e-02f, 0.00000000e+00f, -6.45497224e-02f, 0.00000000e+00f, -1.48264644e-02f, 6.33865691e-02f, -1.01126676e-01f, -7.36485380e-02f, 1.09260065e-02f, 7.08683387e-02f, 1.01622099e-01f },
- { 5.00000000e-02f, 5.00000000e-02f, 5.00000000e-02f, -5.00000000e-02f, -6.45497224e-02f, 6.45497224e-02f, 0.00000000e+00f, -6.45497224e-02f, 0.00000000e+00f, 1.48264644e-02f, -6.33865691e-02f, 1.01126676e-01f, -7.36485380e-02f, 1.09260065e-02f, 7.08683387e-02f, 1.01622099e-01f },
- { 5.00000000e-02f, 5.00000000e-02f, -5.00000000e-02f, 5.00000000e-02f, 6.45497224e-02f, -6.45497224e-02f, 0.00000000e+00f, -6.45497224e-02f, 0.00000000e+00f, 1.48264644e-02f, -6.33865691e-02f, 1.01126676e-01f, 7.36485380e-02f, -1.09260065e-02f, -7.08683387e-02f, -1.01622099e-01f },
- { 5.00000000e-02f, -5.00000000e-02f, -5.00000000e-02f, 5.00000000e-02f, -6.45497224e-02f, 6.45497224e-02f, 0.00000000e+00f, -6.45497224e-02f, 0.00000000e+00f, -1.48264644e-02f, 6.33865691e-02f, -1.01126676e-01f, 7.36485380e-02f, -1.09260065e-02f, -7.08683387e-02f, -1.01622099e-01f },
- { 5.00000000e-02f, -5.00000000e-02f, -5.00000000e-02f, -5.00000000e-02f, 6.45497224e-02f, 6.45497224e-02f, 0.00000000e+00f, 6.45497224e-02f, 0.00000000e+00f, -1.48264644e-02f, -6.33865691e-02f, -1.01126676e-01f, 7.36485380e-02f, 1.09260065e-02f, -7.08683387e-02f, 1.01622099e-01f },
- { 5.00000000e-02f, 5.00000000e-02f, -5.00000000e-02f, -5.00000000e-02f, -6.45497224e-02f, -6.45497224e-02f, 0.00000000e+00f, 6.45497224e-02f, 0.00000000e+00f, 1.48264644e-02f, 6.33865691e-02f, 1.01126676e-01f, 7.36485380e-02f, 1.09260065e-02f, -7.08683387e-02f, 1.01622099e-01f },
- { 5.00000000e-02f, 3.09016994e-02f, 0.00000000e+00f, 8.09016994e-02f, 6.45497224e-02f, 0.00000000e+00f, -5.59016994e-02f, 0.00000000e+00f, 7.21687836e-02f, 7.76323754e-02f, 0.00000000e+00f, -1.49775925e-01f, 0.00000000e+00f, -2.95083663e-02f, 0.00000000e+00f, 7.76323754e-02f },
- { 5.00000000e-02f, -3.09016994e-02f, 0.00000000e+00f, 8.09016994e-02f, -6.45497224e-02f, 0.00000000e+00f, -5.59016994e-02f, 0.00000000e+00f, 7.21687836e-02f, -7.76323754e-02f, 0.00000000e+00f, 1.49775925e-01f, 0.00000000e+00f, -2.95083663e-02f, 0.00000000e+00f, 7.76323754e-02f },
- { 5.00000000e-02f, -3.09016994e-02f, 0.00000000e+00f, -8.09016994e-02f, 6.45497224e-02f, 0.00000000e+00f, -5.59016994e-02f, 0.00000000e+00f, 7.21687836e-02f, -7.76323754e-02f, 0.00000000e+00f, 1.49775925e-01f, 0.00000000e+00f, 2.95083663e-02f, 0.00000000e+00f, -7.76323754e-02f },
- { 5.00000000e-02f, 3.09016994e-02f, 0.00000000e+00f, -8.09016994e-02f, -6.45497224e-02f, 0.00000000e+00f, -5.59016994e-02f, 0.00000000e+00f, 7.21687836e-02f, 7.76323754e-02f, 0.00000000e+00f, -1.49775925e-01f, 0.00000000e+00f, 2.95083663e-02f, 0.00000000e+00f, -7.76323754e-02f },
- { 5.00000000e-02f, 8.09016994e-02f, 3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 6.45497224e-02f, -3.45491503e-02f, 0.00000000e+00f, -8.44966837e-02f, -4.79794466e-02f, 0.00000000e+00f, -6.77901327e-02f, 3.03448665e-02f, 0.00000000e+00f, -1.65948192e-01f, 0.00000000e+00f },
- { 5.00000000e-02f, 8.09016994e-02f, -3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, -6.45497224e-02f, -3.45491503e-02f, 0.00000000e+00f, -8.44966837e-02f, -4.79794466e-02f, 0.00000000e+00f, -6.77901327e-02f, -3.03448665e-02f, 0.00000000e+00f, 1.65948192e-01f, 0.00000000e+00f },
- { 5.00000000e-02f, -8.09016994e-02f, -3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 6.45497224e-02f, -3.45491503e-02f, 0.00000000e+00f, -8.44966837e-02f, 4.79794466e-02f, 0.00000000e+00f, 6.77901327e-02f, -3.03448665e-02f, 0.00000000e+00f, 1.65948192e-01f, 0.00000000e+00f },
- { 5.00000000e-02f, -8.09016994e-02f, 3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, -6.45497224e-02f, -3.45491503e-02f, 0.00000000e+00f, -8.44966837e-02f, 4.79794466e-02f, 0.00000000e+00f, 6.77901327e-02f, 3.03448665e-02f, 0.00000000e+00f, -1.65948192e-01f, 0.00000000e+00f },
- { 5.00000000e-02f, 0.00000000e+00f, 8.09016994e-02f, 3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 9.04508497e-02f, 6.45497224e-02f, 1.23279000e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, 7.94438918e-02f, 1.12611206e-01f, -2.42115150e-02f, 1.25611822e-01f },
- { 5.00000000e-02f, 0.00000000e+00f, -8.09016994e-02f, 3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 9.04508497e-02f, -6.45497224e-02f, 1.23279000e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -7.94438918e-02f, 1.12611206e-01f, 2.42115150e-02f, 1.25611822e-01f },
- { 5.00000000e-02f, 0.00000000e+00f, -8.09016994e-02f, -3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 9.04508497e-02f, 6.45497224e-02f, 1.23279000e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -7.94438918e-02f, -1.12611206e-01f, 2.42115150e-02f, -1.25611822e-01f },
- { 5.00000000e-02f, 0.00000000e+00f, 8.09016994e-02f, -3.09016994e-02f, 0.00000000e+00f, 0.00000000e+00f, 9.04508497e-02f, -6.45497224e-02f, 1.23279000e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, 7.94438918e-02f, -1.12611206e-01f, -2.42115150e-02f, -1.25611822e-01f }
- };
- static constexpr ALfloat AmbiOrderHFGain1O[MAX_AMBI_ORDER+1]{
- 3.16227766e+00f, 1.82574186e+00f
- }, AmbiOrderHFGain2O[MAX_AMBI_ORDER+1]{
- 2.35702260e+00f, 1.82574186e+00f, 9.42809042e-01f
- }, AmbiOrderHFGain3O[MAX_AMBI_ORDER+1]{
- 1.86508671e+00f, 1.60609389e+00f, 1.14205530e+00f, 5.68379553e-01f
- };
- static constexpr ALuint ChansPerOrder[MAX_AMBI_ORDER+1]{ 1, 3, 5, 7 };
- const ALfloat *AmbiOrderHFGain{AmbiOrderHFGain1O};
-
- static_assert(al::size(AmbiPoints) == al::size(AmbiMatrix), "Ambisonic HRTF mismatch");
-
- /* Don't bother with HOA when using full HRTF rendering. Nothing needs it,
- * and it eases the CPU/memory load.
- */
- device->mRenderMode = HrtfRender;
- ALsizei ambi_order{1};
- if(auto modeopt = ConfigValueStr(device->DeviceName.c_str(), nullptr, "hrtf-mode"))
- {
- const char *mode{modeopt->c_str()};
- if(strcasecmp(mode, "basic") == 0)
- {
- ERR("HRTF mode \"%s\" deprecated, substituting \"%s\"\n", mode, "ambi2");
- mode = "ambi2";
- }
-
- if(strcasecmp(mode, "full") == 0)
- device->mRenderMode = HrtfRender;
- else if(strcasecmp(mode, "ambi1") == 0)
- {
- device->mRenderMode = NormalRender;
- ambi_order = 1;
- }
- else if(strcasecmp(mode, "ambi2") == 0)
- {
- device->mRenderMode = NormalRender;
- ambi_order = 2;
- }
- else if(strcasecmp(mode, "ambi3") == 0)
- {
- device->mRenderMode = NormalRender;
- ambi_order = 3;
- }
- else
- ERR("Unexpected hrtf-mode: %s\n", mode);
- }
- TRACE("%s HRTF rendering enabled, using \"%s\"\n",
- (device->mRenderMode == HrtfRender) ? "Full" :
- (ambi_order >= 3) ? "Third-Order" :
- (ambi_order == 2) ? "Second-Order" :
- (ambi_order == 1) ? "First-Order" : "Unknown",
- device->HrtfName.c_str());
-
- if(ambi_order >= 3)
- AmbiOrderHFGain = AmbiOrderHFGain3O;
- else if(ambi_order == 2)
- AmbiOrderHFGain = AmbiOrderHFGain2O;
- else if(ambi_order == 1)
- AmbiOrderHFGain = AmbiOrderHFGain1O;
- device->mAmbiOrder = ambi_order;
-
- const size_t count{AmbiChannelsFromOrder(ambi_order)};
- device->mHrtfState = DirectHrtfState::Create(count);
-
- std::transform(AmbiIndex::From3D.begin(), AmbiIndex::From3D.begin()+count,
- std::begin(device->Dry.AmbiMap),
- [](const ALsizei &index) noexcept { return BFChannelConfig{1.0f, index}; }
- );
- AllocChannels(device, static_cast<ALuint>(count), device->channelsFromFmt());
-
- BuildBFormatHrtf(device->mHrtf, device->mHrtfState.get(), static_cast<ALuint>(count),
- AmbiPoints, AmbiMatrix, al::size(AmbiPoints), AmbiOrderHFGain);
-
- HrtfEntry *Hrtf{device->mHrtf};
- InitNearFieldCtrl(device, Hrtf->field[0].distance, ambi_order, ChansPerOrder);
-}
-
-void InitUhjPanning(ALCdevice *device)
-{
- /* UHJ is always 2D first-order. */
- static constexpr size_t count{Ambi2DChannelsFromOrder(1)};
-
- device->mAmbiOrder = 1;
-
- auto acnmap_end = AmbiIndex::FromFuMa.begin() + count;
- std::transform(AmbiIndex::FromFuMa.begin(), acnmap_end, std::begin(device->Dry.AmbiMap),
- [](const ALsizei &acn) noexcept -> BFChannelConfig
- { return BFChannelConfig{1.0f/AmbiScale::FromFuMa[acn], acn}; }
- );
- AllocChannels(device, ALuint{count}, device->channelsFromFmt());
-}
-
-} // namespace
-
-void aluInitRenderer(ALCdevice *device, ALint hrtf_id, HrtfRequestMode hrtf_appreq, HrtfRequestMode hrtf_userreq)
-{
- /* Hold the HRTF the device last used, in case it's used again. */
- HrtfEntry *old_hrtf{device->mHrtf};
-
- device->mHrtfState = nullptr;
- device->mHrtf = nullptr;
- device->HrtfName.clear();
- device->mRenderMode = NormalRender;
-
- if(device->FmtChans != DevFmtStereo)
- {
- if(old_hrtf)
- old_hrtf->DecRef();
- old_hrtf = nullptr;
- if(hrtf_appreq == Hrtf_Enable)
- device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
-
- const char *layout{nullptr};
- switch(device->FmtChans)
- {
- case DevFmtQuad: layout = "quad"; break;
- case DevFmtX51: /* fall-through */
- case DevFmtX51Rear: layout = "surround51"; break;
- case DevFmtX61: layout = "surround61"; break;
- case DevFmtX71: layout = "surround71"; break;
- /* Mono, Stereo, and Ambisonics output don't use custom decoders. */
- case DevFmtMono:
- case DevFmtStereo:
- case DevFmtAmbi3D:
- break;
- }
-
- const char *devname{device->DeviceName.c_str()};
- ALsizei speakermap[MAX_OUTPUT_CHANNELS];
- AmbDecConf *pconf{nullptr};
- AmbDecConf conf{};
- if(layout)
- {
- if(auto decopt = ConfigValueStr(devname, "decoder", layout))
- {
- if(!conf.load(decopt->c_str()))
- ERR("Failed to load layout file %s\n", decopt->c_str());
- else if(conf.Speakers.size() > MAX_OUTPUT_CHANNELS)
- ERR("Unsupported speaker count %zu (max %d)\n", conf.Speakers.size(),
- MAX_OUTPUT_CHANNELS);
- else if(conf.ChanMask > AMBI_3ORDER_MASK)
- ERR("Unsupported channel mask 0x%04x (max 0x%x)\n", conf.ChanMask,
- AMBI_3ORDER_MASK);
- else if(MakeSpeakerMap(device, &conf, speakermap))
- pconf = &conf;
- }
- }
-
- if(!pconf)
- InitPanning(device);
- else
- {
- int hqdec{GetConfigValueBool(devname, "decoder", "hq-mode", 0)};
- InitCustomPanning(device, !!hqdec, pconf, speakermap);
- }
- if(device->AmbiDecoder)
- device->PostProcess = ProcessAmbiDec;
- return;
- }
-
- bool headphones{device->IsHeadphones != AL_FALSE};
- if(device->Type != Loopback)
- {
- if(auto modeopt = ConfigValueStr(device->DeviceName.c_str(), nullptr, "stereo-mode"))
- {
- const char *mode{modeopt->c_str()};
- if(strcasecmp(mode, "headphones") == 0)
- headphones = true;
- else if(strcasecmp(mode, "speakers") == 0)
- headphones = false;
- else if(strcasecmp(mode, "auto") != 0)
- ERR("Unexpected stereo-mode: %s\n", mode);
- }
- }
-
- if(hrtf_userreq == Hrtf_Default)
- {
- bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) ||
- (hrtf_appreq == Hrtf_Enable);
- if(!usehrtf) goto no_hrtf;
-
- device->HrtfStatus = ALC_HRTF_ENABLED_SOFT;
- if(headphones && hrtf_appreq != Hrtf_Disable)
- device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
- }
- else
- {
- if(hrtf_userreq != Hrtf_Enable)
- {
- if(hrtf_appreq == Hrtf_Enable)
- device->HrtfStatus = ALC_HRTF_DENIED_SOFT;
- goto no_hrtf;
- }
- device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT;
- }
-
- if(device->HrtfList.empty())
- device->HrtfList = EnumerateHrtf(device->DeviceName.c_str());
-
- if(hrtf_id >= 0 && static_cast<size_t>(hrtf_id) < device->HrtfList.size())
- {
- const EnumeratedHrtf &entry = device->HrtfList[hrtf_id];
- HrtfEntry *hrtf{GetLoadedHrtf(entry.hrtf)};
- if(hrtf && hrtf->sampleRate == device->Frequency)
- {
- device->mHrtf = hrtf;
- device->HrtfName = entry.name;
- }
- else if(hrtf)
- hrtf->DecRef();
- }
-
- if(!device->mHrtf)
- {
- auto find_hrtf = [device](const EnumeratedHrtf &entry) -> bool
- {
- HrtfEntry *hrtf{GetLoadedHrtf(entry.hrtf)};
- if(!hrtf) return false;
- if(hrtf->sampleRate != device->Frequency)
- {
- hrtf->DecRef();
- return false;
- }
- device->mHrtf = hrtf;
- device->HrtfName = entry.name;
- return true;
- };
- std::find_if(device->HrtfList.cbegin(), device->HrtfList.cend(), find_hrtf);
- }
-
- if(device->mHrtf)
- {
- if(old_hrtf)
- old_hrtf->DecRef();
- old_hrtf = nullptr;
-
- InitHrtfPanning(device);
- device->PostProcess = ProcessHrtf;
- return;
- }
- device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
-
-no_hrtf:
- if(old_hrtf)
- old_hrtf->DecRef();
- old_hrtf = nullptr;
-
- device->mRenderMode = StereoPair;
-
- if(device->Type != Loopback)
- {
- if(auto cflevopt = ConfigValueInt(device->DeviceName.c_str(), nullptr, "cf_level"))
- {
- if(*cflevopt > 0 && *cflevopt <= 6)
- {
- device->Bs2b = al::make_unique<bs2b>();
- bs2b_set_params(device->Bs2b.get(), *cflevopt, device->Frequency);
- TRACE("BS2B enabled\n");
- InitPanning(device);
- device->PostProcess = ProcessBs2b;
- return;
- }
- }
- }
-
- if(auto encopt = ConfigValueStr(device->DeviceName.c_str(), nullptr, "stereo-encoding"))
- {
- const char *mode{encopt->c_str()};
- if(strcasecmp(mode, "uhj") == 0)
- device->mRenderMode = NormalRender;
- else if(strcasecmp(mode, "panpot") != 0)
- ERR("Unexpected stereo-encoding: %s\n", mode);
- }
- if(device->mRenderMode == NormalRender)
- {
- device->Uhj_Encoder = al::make_unique<Uhj2Encoder>();
- TRACE("UHJ enabled\n");
- InitUhjPanning(device);
- device->PostProcess = ProcessUhj;
- return;
- }
-
- TRACE("Stereo rendering\n");
- InitPanning(device);
- device->PostProcess = ProcessAmbiDec;
-}
-
-
-void aluInitEffectPanning(ALeffectslot *slot, ALCdevice *device)
-{
- const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)};
- slot->MixBuffer.resize(count);
- slot->MixBuffer.shrink_to_fit();
-
- auto acnmap_end = AmbiIndex::From3D.begin() + count;
- auto iter = std::transform(AmbiIndex::From3D.begin(), acnmap_end, slot->Wet.AmbiMap.begin(),
- [](const ALsizei &acn) noexcept -> BFChannelConfig
- { return BFChannelConfig{1.0f, acn}; }
- );
- std::fill(iter, slot->Wet.AmbiMap.end(), BFChannelConfig{});
- slot->Wet.Buffer = {slot->MixBuffer.data(), slot->MixBuffer.size()};
-}
-
-
-void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
- ALfloat (&coeffs)[MAX_AMBI_CHANNELS])
-{
- /* Zeroth-order */
- coeffs[0] = 1.0f; /* ACN 0 = 1 */
- /* First-order */
- coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */
- coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */
- coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */
- /* Second-order */
- coeffs[4] = 3.872983346f * x * y; /* ACN 4 = sqrt(15) * X * Y */
- coeffs[5] = 3.872983346f * y * z; /* ACN 5 = sqrt(15) * Y * Z */
- coeffs[6] = 1.118033989f * (z*z*3.0f - 1.0f); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */
- coeffs[7] = 3.872983346f * x * z; /* ACN 7 = sqrt(15) * X * Z */
- coeffs[8] = 1.936491673f * (x*x - y*y); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */
- /* Third-order */
- coeffs[9] = 2.091650066f * y * (x*x*3.0f - y*y); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */
- coeffs[10] = 10.246950766f * z * x * y; /* ACN 10 = sqrt(105) * Z * X * Y */
- coeffs[11] = 1.620185175f * y * (z*z*5.0f - 1.0f); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */
- coeffs[12] = 1.322875656f * z * (z*z*5.0f - 3.0f); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */
- coeffs[13] = 1.620185175f * x * (z*z*5.0f - 1.0f); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */
- coeffs[14] = 5.123475383f * z * (x*x - y*y); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
- coeffs[15] = 2.091650066f * x * (x*x - y*y*3.0f); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
- /* Fourth-order */
- /* ACN 16 = sqrt(35)*3/2 * X * Y * (X*X - Y*Y) */
- /* ACN 17 = sqrt(35/2)*3/2 * (3*X*X - Y*Y) * Y * Z */
- /* ACN 18 = sqrt(5)*3/2 * X * Y * (7*Z*Z - 1) */
- /* ACN 19 = sqrt(5/2)*3/2 * Y * Z * (7*Z*Z - 3) */
- /* ACN 20 = 3/8 * (35*Z*Z*Z*Z - 30*Z*Z + 3) */
- /* ACN 21 = sqrt(5/2)*3/2 * X * Z * (7*Z*Z - 3) */
- /* ACN 22 = sqrt(5)*3/4 * (X*X - Y*Y) * (7*Z*Z - 1) */
- /* ACN 23 = sqrt(35/2)*3/2 * (X*X - 3*Y*Y) * X * Z */
- /* ACN 24 = sqrt(35)*3/8 * (X*X*X*X - 6*X*X*Y*Y + Y*Y*Y*Y) */
-
- if(spread > 0.0f)
- {
- /* Implement the spread by using a spherical source that subtends the
- * angle spread. See:
- * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
- *
- * When adjusted for N3D normalization instead of SN3D, these
- * calculations are:
- *
- * ZH0 = -sqrt(pi) * (-1+ca);
- * ZH1 = 0.5*sqrt(pi) * sa*sa;
- * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1);
- * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1);
- * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3);
- * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
- *
- * The gain of the source is compensated for size, so that the
- * loudness doesn't depend on the spread. Thus:
- *
- * ZH0 = 1.0f;
- * ZH1 = 0.5f * (ca+1.0f);
- * ZH2 = 0.5f * (ca+1.0f)*ca;
- * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f);
- * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca;
- * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
- */
- ALfloat ca = std::cos(spread * 0.5f);
- /* Increase the source volume by up to +3dB for a full spread. */
- ALfloat scale = std::sqrt(1.0f + spread/al::MathDefs<float>::Tau());
-
- ALfloat ZH0_norm = scale;
- ALfloat ZH1_norm = 0.5f * (ca+1.f) * scale;
- ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca * scale;
- ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f) * scale;
-
- /* Zeroth-order */
- coeffs[0] *= ZH0_norm;
- /* First-order */
- coeffs[1] *= ZH1_norm;
- coeffs[2] *= ZH1_norm;
- coeffs[3] *= ZH1_norm;
- /* Second-order */
- coeffs[4] *= ZH2_norm;
- coeffs[5] *= ZH2_norm;
- coeffs[6] *= ZH2_norm;
- coeffs[7] *= ZH2_norm;
- coeffs[8] *= ZH2_norm;
- /* Third-order */
- coeffs[9] *= ZH3_norm;
- coeffs[10] *= ZH3_norm;
- coeffs[11] *= ZH3_norm;
- coeffs[12] *= ZH3_norm;
- coeffs[13] *= ZH3_norm;
- coeffs[14] *= ZH3_norm;
- coeffs[15] *= ZH3_norm;
- }
-}
-
-void ComputePanGains(const MixParams *mix, const ALfloat *RESTRICT coeffs, ALfloat ingain, ALfloat (&gains)[MAX_OUTPUT_CHANNELS])
-{
- auto ambimap = mix->AmbiMap.cbegin();
-
- auto iter = std::transform(ambimap, ambimap+mix->Buffer.size(), std::begin(gains),
- [coeffs,ingain](const BFChannelConfig &chanmap) noexcept -> ALfloat
- {
- ASSUME(chanmap.Index >= 0);
- return chanmap.Scale * coeffs[chanmap.Index] * ingain;
- }
- );
- std::fill(iter, std::end(gains), 0.0f);
-}
diff --git a/Alc/ringbuffer.cpp b/Alc/ringbuffer.cpp
deleted file mode 100644
index 6ef576a5..00000000
--- a/Alc/ringbuffer.cpp
+++ /dev/null
@@ -1,253 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <cstring>
-#include <cstdlib>
-#include <climits>
-
-#include <algorithm>
-
-#include "ringbuffer.h"
-#include "atomic.h"
-#include "threads.h"
-#include "almalloc.h"
-#include "compat.h"
-
-
-RingBufferPtr CreateRingBuffer(size_t sz, size_t elem_sz, int limit_writes)
-{
- size_t power_of_two{0u};
- if(sz > 0)
- {
- power_of_two = sz;
- power_of_two |= power_of_two>>1;
- power_of_two |= power_of_two>>2;
- power_of_two |= power_of_two>>4;
- power_of_two |= power_of_two>>8;
- power_of_two |= power_of_two>>16;
-#if SIZE_MAX > UINT_MAX
- power_of_two |= power_of_two>>32;
-#endif
- }
- ++power_of_two;
- if(power_of_two < sz) return nullptr;
-
- const size_t bufbytes{power_of_two * elem_sz};
- RingBufferPtr rb{new (al_calloc(16, sizeof(*rb) + bufbytes)) RingBuffer{bufbytes}};
- rb->mWriteSize = limit_writes ? sz : (power_of_two-1);
- rb->mSizeMask = power_of_two - 1;
- rb->mElemSize = elem_sz;
-
- return rb;
-}
-
-void RingBuffer::reset() noexcept
-{
- mWritePtr.store(0, std::memory_order_relaxed);
- mReadPtr.store(0, std::memory_order_relaxed);
- std::fill_n(mBuffer.begin(), (mSizeMask+1)*mElemSize, al::byte{});
-}
-
-
-size_t RingBuffer::readSpace() const noexcept
-{
- size_t w = mWritePtr.load(std::memory_order_acquire);
- size_t r = mReadPtr.load(std::memory_order_acquire);
- return (w-r) & mSizeMask;
-}
-
-size_t RingBuffer::writeSpace() const noexcept
-{
- size_t w = mWritePtr.load(std::memory_order_acquire);
- size_t r = mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask;
- return (r-w-1) & mSizeMask;
-}
-
-
-size_t RingBuffer::read(void *dest, size_t cnt) noexcept
-{
- const size_t free_cnt{readSpace()};
- if(free_cnt == 0) return 0;
-
- const size_t to_read{std::min(cnt, free_cnt)};
- size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask};
-
- size_t n1, n2;
- const size_t cnt2{read_ptr + to_read};
- if(cnt2 > mSizeMask+1)
- {
- n1 = mSizeMask+1 - read_ptr;
- n2 = cnt2 & mSizeMask;
- }
- else
- {
- n1 = to_read;
- n2 = 0;
- }
-
- auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize,
- static_cast<al::byte*>(dest));
- read_ptr += n1;
- if(n2 > 0)
- {
- std::copy_n(mBuffer.begin(), n2*mElemSize, outiter);
- read_ptr += n2;
- }
- mReadPtr.store(read_ptr, std::memory_order_release);
- return to_read;
-}
-
-size_t RingBuffer::peek(void *dest, size_t cnt) const noexcept
-{
- const size_t free_cnt{readSpace()};
- if(free_cnt == 0) return 0;
-
- const size_t to_read{std::min(cnt, free_cnt)};
- size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask};
-
- size_t n1, n2;
- const size_t cnt2{read_ptr + to_read};
- if(cnt2 > mSizeMask+1)
- {
- n1 = mSizeMask+1 - read_ptr;
- n2 = cnt2 & mSizeMask;
- }
- else
- {
- n1 = to_read;
- n2 = 0;
- }
-
- auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize,
- static_cast<al::byte*>(dest));
- if(n2 > 0)
- std::copy_n(mBuffer.begin(), n2*mElemSize, outiter);
- return to_read;
-}
-
-size_t RingBuffer::write(const void *src, size_t cnt) noexcept
-{
- const size_t free_cnt{writeSpace()};
- if(free_cnt == 0) return 0;
-
- const size_t to_write{std::min(cnt, free_cnt)};
- size_t write_ptr{mWritePtr.load(std::memory_order_relaxed) & mSizeMask};
-
- size_t n1, n2;
- const size_t cnt2{write_ptr + to_write};
- if(cnt2 > mSizeMask+1)
- {
- n1 = mSizeMask+1 - write_ptr;
- n2 = cnt2 & mSizeMask;
- }
- else
- {
- n1 = to_write;
- n2 = 0;
- }
-
- auto srcbytes = static_cast<const al::byte*>(src);
- std::copy_n(srcbytes, n1*mElemSize, mBuffer.begin() + write_ptr*mElemSize);
- write_ptr += n1;
- if(n2 > 0)
- {
- std::copy_n(srcbytes + n1*mElemSize, n2*mElemSize, mBuffer.begin());
- write_ptr += n2;
- }
- mWritePtr.store(write_ptr, std::memory_order_release);
- return to_write;
-}
-
-
-void RingBuffer::readAdvance(size_t cnt) noexcept
-{
- mReadPtr.fetch_add(cnt, std::memory_order_acq_rel);
-}
-
-void RingBuffer::writeAdvance(size_t cnt) noexcept
-{
- mWritePtr.fetch_add(cnt, std::memory_order_acq_rel);
-}
-
-
-ll_ringbuffer_data_pair RingBuffer::getReadVector() const noexcept
-{
- ll_ringbuffer_data_pair ret;
-
- size_t w{mWritePtr.load(std::memory_order_acquire)};
- size_t r{mReadPtr.load(std::memory_order_acquire)};
- w &= mSizeMask;
- r &= mSizeMask;
- const size_t free_cnt{(w-r) & mSizeMask};
-
- const size_t cnt2{r + free_cnt};
- if(cnt2 > mSizeMask+1)
- {
- /* Two part vector: the rest of the buffer after the current read ptr,
- * plus some from the start of the buffer. */
- ret.first.buf = const_cast<al::byte*>(mBuffer.data() + r*mElemSize);
- ret.first.len = mSizeMask+1 - r;
- ret.second.buf = const_cast<al::byte*>(mBuffer.data());
- ret.second.len = cnt2 & mSizeMask;
- }
- else
- {
- /* Single part vector: just the rest of the buffer */
- ret.first.buf = const_cast<al::byte*>(mBuffer.data() + r*mElemSize);
- ret.first.len = free_cnt;
- ret.second.buf = nullptr;
- ret.second.len = 0;
- }
-
- return ret;
-}
-
-ll_ringbuffer_data_pair RingBuffer::getWriteVector() const noexcept
-{
- ll_ringbuffer_data_pair ret;
-
- size_t w{mWritePtr.load(std::memory_order_acquire)};
- size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask};
- w &= mSizeMask;
- r &= mSizeMask;
- const size_t free_cnt{(r-w-1) & mSizeMask};
-
- const size_t cnt2{w + free_cnt};
- if(cnt2 > mSizeMask+1)
- {
- /* Two part vector: the rest of the buffer after the current write ptr,
- * plus some from the start of the buffer. */
- ret.first.buf = const_cast<al::byte*>(mBuffer.data() + w*mElemSize);
- ret.first.len = mSizeMask+1 - w;
- ret.second.buf = const_cast<al::byte*>(mBuffer.data());
- ret.second.len = cnt2 & mSizeMask;
- }
- else
- {
- ret.first.buf = const_cast<al::byte*>(mBuffer.data() + w*mElemSize);
- ret.first.len = free_cnt;
- ret.second.buf = nullptr;
- ret.second.len = 0;
- }
-
- return ret;
-}
diff --git a/Alc/ringbuffer.h b/Alc/ringbuffer.h
deleted file mode 100644
index 84139b66..00000000
--- a/Alc/ringbuffer.h
+++ /dev/null
@@ -1,99 +0,0 @@
-#ifndef RINGBUFFER_H
-#define RINGBUFFER_H
-
-#include <stddef.h>
-
-#include <atomic>
-#include <memory>
-#include <utility>
-
-#include "albyte.h"
-#include "almalloc.h"
-
-
-/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
- * to include an element size. Consequently, parameters and return values for a
- * size or count is in 'elements', not bytes. Additionally, it only supports
- * single-consumer/single-provider operation.
- */
-
-struct ll_ringbuffer_data {
- al::byte *buf;
- size_t len;
-};
-using ll_ringbuffer_data_pair = std::pair<ll_ringbuffer_data,ll_ringbuffer_data>;
-
-
-struct RingBuffer {
- std::atomic<size_t> mWritePtr{0u};
- std::atomic<size_t> mReadPtr{0u};
- size_t mWriteSize{0u};
- size_t mSizeMask{0u};
- size_t mElemSize{0u};
-
- al::FlexArray<al::byte, 16> mBuffer;
-
- RingBuffer(const size_t count) : mBuffer{count} { }
- RingBuffer(const RingBuffer&) = delete;
- RingBuffer& operator=(const RingBuffer&) = delete;
-
- /** Reset the read and write pointers to zero. This is not thread safe. */
- void reset() noexcept;
-
- /**
- * The non-copying data reader. Returns two ringbuffer data pointers that
- * hold the current readable data. If the readable data is in one segment
- * the second segment has zero length.
- */
- ll_ringbuffer_data_pair getReadVector() const noexcept;
- /**
- * The non-copying data writer. Returns two ringbuffer data pointers that
- * hold the current writeable data. If the writeable data is in one segment
- * the second segment has zero length.
- */
- ll_ringbuffer_data_pair getWriteVector() const noexcept;
-
- /**
- * Return the number of elements available for reading. This is the number
- * of elements in front of the read pointer and behind the write pointer.
- */
- size_t readSpace() const noexcept;
- /**
- * The copying data reader. Copy at most `cnt' elements into `dest'.
- * Returns the actual number of elements copied.
- */
- size_t read(void *dest, size_t cnt) noexcept;
- /**
- * The copying data reader w/o read pointer advance. Copy at most `cnt'
- * elements into `dest'. Returns the actual number of elements copied.
- */
- size_t peek(void *dest, size_t cnt) const noexcept;
- /** Advance the read pointer `cnt' places. */
- void readAdvance(size_t cnt) noexcept;
-
- /**
- * Return the number of elements available for writing. This is the number
- * of elements in front of the write pointer and behind the read pointer.
- */
- size_t writeSpace() const noexcept;
- /**
- * The copying data writer. Copy at most `cnt' elements from `src'. Returns
- * the actual number of elements copied.
- */
- size_t write(const void *src, size_t cnt) noexcept;
- /** Advance the write pointer `cnt' places. */
- void writeAdvance(size_t cnt) noexcept;
-
- DEF_PLACE_NEWDEL()
-};
-using RingBufferPtr = std::unique_ptr<RingBuffer>;
-
-
-/**
- * Create a new ringbuffer to hold at least `sz' elements of `elem_sz' bytes.
- * The number of elements is rounded up to the next power of two (even if it is
- * already a power of two, to ensure the requested amount can be written).
- */
-RingBufferPtr CreateRingBuffer(size_t sz, size_t elem_sz, int limit_writes);
-
-#endif /* RINGBUFFER_H */
diff --git a/Alc/uhjfilter.cpp b/Alc/uhjfilter.cpp
deleted file mode 100644
index 55999647..00000000
--- a/Alc/uhjfilter.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-
-#include "config.h"
-
-#include "uhjfilter.h"
-
-#include <algorithm>
-
-#include "alu.h"
-
-namespace {
-
-/* This is the maximum number of samples processed for each inner loop
- * iteration. */
-#define MAX_UPDATE_SAMPLES 128
-
-
-constexpr ALfloat Filter1CoeffSqr[4] = {
- 0.479400865589f, 0.876218493539f, 0.976597589508f, 0.997499255936f
-};
-constexpr ALfloat Filter2CoeffSqr[4] = {
- 0.161758498368f, 0.733028932341f, 0.945349700329f, 0.990599156685f
-};
-
-void allpass_process(AllPassState *state, ALfloat *dst, const ALfloat *src, const ALfloat aa, ALsizei todo)
-{
- ALfloat z1{state->z[0]};
- ALfloat z2{state->z[1]};
- auto proc_sample = [aa,&z1,&z2](ALfloat input) noexcept -> ALfloat
- {
- ALfloat output = input*aa + z1;
- z1 = z2; z2 = output*aa - input;
- return output;
- };
- std::transform(src, src+todo, dst, proc_sample);
- state->z[0] = z1;
- state->z[1] = z2;
-}
-
-} // namespace
-
-
-/* NOTE: There seems to be a bit of an inconsistency in how this encoding is
- * supposed to work. Some references, such as
- *
- * http://members.tripod.com/martin_leese/Ambisonic/UHJ_file_format.html
- *
- * specify a pre-scaling of sqrt(2) on the W channel input, while other
- * references, such as
- *
- * https://en.wikipedia.org/wiki/Ambisonic_UHJ_format#Encoding.5B1.5D
- * and
- * https://wiki.xiph.org/Ambisonics#UHJ_format
- *
- * do not. The sqrt(2) scaling is in line with B-Format decoder coefficients
- * which include such a scaling for the W channel input, however the original
- * source for this equation is a 1985 paper by Michael Gerzon, which does not
- * apparently include the scaling. Applying the extra scaling creates a louder
- * result with a narrower stereo image compared to not scaling, and I don't
- * know which is the intended result.
- */
-
-void Uhj2Encoder::encode(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, FloatBufferLine *InSamples, const ALsizei SamplesToDo)
-{
- alignas(16) ALfloat D[MAX_UPDATE_SAMPLES], S[MAX_UPDATE_SAMPLES];
- alignas(16) ALfloat temp[MAX_UPDATE_SAMPLES];
-
- ASSUME(SamplesToDo > 0);
-
- auto winput = InSamples[0].cbegin();
- auto xinput = InSamples[1].cbegin();
- auto yinput = InSamples[2].cbegin();
- for(ALsizei base{0};base < SamplesToDo;)
- {
- const ALsizei todo{mini(SamplesToDo - base, MAX_UPDATE_SAMPLES)};
- ASSUME(todo > 0);
-
- /* D = 0.6554516*Y */
- std::transform(yinput, yinput+todo, std::begin(temp),
- [](const float y) noexcept -> float { return 0.6554516f*y; });
- allpass_process(&mFilter1_Y[0], temp, temp, Filter1CoeffSqr[0], todo);
- allpass_process(&mFilter1_Y[1], temp, temp, Filter1CoeffSqr[1], todo);
- allpass_process(&mFilter1_Y[2], temp, temp, Filter1CoeffSqr[2], todo);
- allpass_process(&mFilter1_Y[3], temp, temp, Filter1CoeffSqr[3], todo);
- /* NOTE: Filter1 requires a 1 sample delay for the final output, so
- * take the last processed sample from the previous run as the first
- * output sample.
- */
- D[0] = mLastY;
- for(ALsizei i{1};i < todo;i++)
- D[i] = temp[i-1];
- mLastY = temp[todo-1];
-
- /* D += j(-0.3420201*W + 0.5098604*X) */
- std::transform(winput, winput+todo, xinput, std::begin(temp),
- [](const float w, const float x) noexcept -> float
- { return -0.3420201f*w + 0.5098604f*x; });
- allpass_process(&mFilter2_WX[0], temp, temp, Filter2CoeffSqr[0], todo);
- allpass_process(&mFilter2_WX[1], temp, temp, Filter2CoeffSqr[1], todo);
- allpass_process(&mFilter2_WX[2], temp, temp, Filter2CoeffSqr[2], todo);
- allpass_process(&mFilter2_WX[3], temp, temp, Filter2CoeffSqr[3], todo);
- for(ALsizei i{0};i < todo;i++)
- D[i] += temp[i];
-
- /* S = 0.9396926*W + 0.1855740*X */
- std::transform(winput, winput+todo, xinput, std::begin(temp),
- [](const float w, const float x) noexcept -> float
- { return 0.9396926f*w + 0.1855740f*x; });
- allpass_process(&mFilter1_WX[0], temp, temp, Filter1CoeffSqr[0], todo);
- allpass_process(&mFilter1_WX[1], temp, temp, Filter1CoeffSqr[1], todo);
- allpass_process(&mFilter1_WX[2], temp, temp, Filter1CoeffSqr[2], todo);
- allpass_process(&mFilter1_WX[3], temp, temp, Filter1CoeffSqr[3], todo);
- S[0] = mLastWX;
- for(ALsizei i{1};i < todo;i++)
- S[i] = temp[i-1];
- mLastWX = temp[todo-1];
-
- /* Left = (S + D)/2.0 */
- ALfloat *RESTRICT left = al::assume_aligned<16>(LeftOut.data()+base);
- for(ALsizei i{0};i < todo;i++)
- left[i] += (S[i] + D[i]) * 0.5f;
- /* Right = (S - D)/2.0 */
- ALfloat *RESTRICT right = al::assume_aligned<16>(RightOut.data()+base);
- for(ALsizei i{0};i < todo;i++)
- right[i] += (S[i] - D[i]) * 0.5f;
-
- winput += todo;
- xinput += todo;
- yinput += todo;
- base += todo;
- }
-}
diff --git a/Alc/uhjfilter.h b/Alc/uhjfilter.h
deleted file mode 100644
index 53e4f89e..00000000
--- a/Alc/uhjfilter.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef UHJFILTER_H
-#define UHJFILTER_H
-
-#include "AL/al.h"
-
-#include "alcmain.h"
-#include "almalloc.h"
-
-
-struct AllPassState {
- ALfloat z[2]{0.0f, 0.0f};
-};
-
-/* Encoding 2-channel UHJ from B-Format is done as:
- *
- * S = 0.9396926*W + 0.1855740*X
- * D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y
- *
- * Left = (S + D)/2.0
- * Right = (S - D)/2.0
- *
- * where j is a wide-band +90 degree phase shift.
- *
- * The phase shift is done using a Hilbert transform, described here:
- * https://web.archive.org/web/20060708031958/http://www.biochem.oulu.fi/~oniemita/dsp/hilbert/
- * It works using 2 sets of 4 chained filters. The first filter chain produces
- * a phase shift of varying magnitude over a wide range of frequencies, while
- * the second filter chain produces a phase shift 90 degrees ahead of the
- * first over the same range.
- *
- * Combining these two stages requires the use of three filter chains. S-
- * channel output uses a Filter1 chain on the W and X channel mix, while the D-
- * channel output uses a Filter1 chain on the Y channel plus a Filter2 chain on
- * the W and X channel mix. This results in the W and X input mix on the D-
- * channel output having the required +90 degree phase shift relative to the
- * other inputs.
- */
-
-struct Uhj2Encoder {
- AllPassState mFilter1_Y[4];
- AllPassState mFilter2_WX[4];
- AllPassState mFilter1_WX[4];
- ALfloat mLastY{0.0f}, mLastWX{0.0f};
-
- /* Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
- * signal. The input must use FuMa channel ordering and scaling.
- */
- void encode(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, FloatBufferLine *InSamples,
- const ALsizei SamplesToDo);
-
- DEF_NEWDEL(Uhj2Encoder)
-};
-
-#endif /* UHJFILTER_H */
diff --git a/Alc/vector.h b/Alc/vector.h
deleted file mode 100644
index 1b69d6a7..00000000
--- a/Alc/vector.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef AL_VECTOR_H
-#define AL_VECTOR_H
-
-#include <vector>
-
-#include "almalloc.h"
-
-namespace al {
-
-template<typename T, size_t alignment=alignof(T)>
-using vector = std::vector<T, al::allocator<T, alignment>>;
-
-} // namespace al
-
-#endif /* AL_VECTOR_H */